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