1// Copyright 2020-present 650 Industries. All rights reserved. 2 3#if __has_include(<EXFirebaseCore/EXFirebaseCore.h>) 4#import "EXScopedFirebaseCore.h" 5#import <EXFirebaseCore/EXFirebaseCore+FIROptions.h> 6#import <ExpoModulesCore/EXConstantsInterface.h> 7 8@interface NSObject (Private) 9- (NSString*)_methodDescription; 10@end 11 12@implementation EXScopedFirebaseCore { 13 NSDictionary* _protectedAppNames; 14} 15 16- (instancetype)initWithScopeKey:(NSString *)scopeKey manifest:(EXManifestsManifest *)manifest constantsBinding:(EXConstantsBinding *)constantsBinding 17{ 18 if (![@"expo" isEqualToString:constantsBinding.appOwnership]) { 19 return [super init]; 20 } 21 22 // Setup the protected app names 23 NSMutableDictionary* protectedAppNames = [NSMutableDictionary dictionaryWithDictionary:@{ 24 @"__FIRAPP_DEFAULT": @YES, 25 @"[DEFAULT]": @YES 26 }]; 27 _protectedAppNames = protectedAppNames; 28 29 // Make sure the [DEFAULT] app is initialized 30 NSString *path = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"]; 31 if (path && ![FIRApp defaultApp]) { 32 [FIRApp configure]; 33 } 34 if ([FIRApp defaultApp]) [protectedAppNames setValue:@YES forKey:[FIRApp defaultApp].name]; 35 36 // Determine project app name & options 37 NSString *encodedScopeKey = [self.class encodedResourceName:scopeKey]; 38 NSString* appName = [NSString stringWithFormat:@"__sandbox_%@", encodedScopeKey]; 39 NSDictionary* googleServicesFile = [self.class googleServicesFileFromManifest:manifest]; 40 FIROptions* options = [self.class optionsWithGoogleServicesFile:googleServicesFile]; 41 42 // Delete all previously created (project) apps, except for the currently 43 // loaded project and the "protected" ones 44 NSDictionary<NSString *,FIRApp *>* apps = [FIRApp allApps]; 45 NSArray<NSString*>* names = [apps allKeys]; 46 for (NSString* name in names) { 47 if (!protectedAppNames[name] && (!options || ![name isEqualToString:appName])) { 48 [[FIRApp appNamed:name] deleteApp:^(BOOL success) { 49 if (!success) { 50 EXLogWarn(@"Failed to delete Firebase app: %@", name); 51 } 52 }]; 53 } 54 } 55 56 // Initialize the sandboxed firebase app 57 return [super initWithAppName:appName options:options]; 58} 59 60# pragma mark - Overriden methods 61 62- (BOOL)isAppAccessible:(nonnull NSString *)name 63{ 64 // Deny access to the protected default app on the Expo Go 65 if (_protectedAppNames && _protectedAppNames[name]) { 66 return NO; 67 } 68 return [super isAppAccessible:name]; 69} 70 71 72# pragma mark - Project methods 73 74+ (NSString *)encodedResourceName:(NSString *)name 75{ 76 NSData *data = [name dataUsingEncoding:NSUTF8StringEncoding]; 77 NSString *base64 = [data base64EncodedStringWithOptions:kNilOptions]; 78 return [base64 stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]]; 79} 80 81+ (nullable NSDictionary *)googleServicesFileFromManifest:(EXManifestsManifest *)manifest 82{ 83 // load GoogleService-Info.plist from manifest 84 @try { 85 NSString* googleServicesFile = manifest.iosGoogleServicesFile; 86 if (!googleServicesFile) return nil; 87 NSData *data = [[NSData alloc] initWithBase64EncodedString:googleServicesFile options:0]; 88 NSError* error; 89 NSDictionary* plist = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:nil error:&error]; 90 if (error) EXLogWarn(@"Invalid googleServicesFile: %@", error); 91 return plist; 92 } 93 @catch(NSException* exception) { 94 EXLogWarn(@"Invalid googleServicesFile: %@", exception); 95 return nil; 96 } 97} 98 99+ (nullable FIROptions *)optionsWithGoogleServicesFile:(nullable NSDictionary *)plist 100{ 101 if (!plist) return nil; 102 103 FIROptions *firOptions = [[FIROptions alloc] initWithGoogleAppID:plist[@"GOOGLE_APP_ID"] GCMSenderID:plist[@"GCM_SENDER_ID"]]; 104 105 firOptions.APIKey = plist[@"API_KEY"]; 106 firOptions.bundleID = plist[@"BUNDLE_ID"]; 107 firOptions.clientID = plist[@"CLIENT_ID"]; 108 firOptions.trackingID = plist[@"TRACKING_ID"]; 109 firOptions.projectID = plist[@"PROJECT_ID"]; 110 firOptions.androidClientID = plist[@"ANDROID_CLIENT_ID"]; 111 firOptions.databaseURL = plist[@"DATABASE_URL"]; 112 firOptions.storageBucket = plist[@"STORAGE_BUCKET"]; 113 114 return firOptions; 115} 116 117@end 118 119#endif 120