1// Copyright 2015-present 650 Industries. All rights reserved. 2#import "EXScopedFileSystemModule.h" 3 4// TODO @sjchmiela: Should this be versioned? It is only used in detached scenario. 5NSString * const EXShellManifestResourceName = @"shell-app-manifest"; 6 7@implementation EXScopedFileSystemModule 8 9- (instancetype)initWithExperienceId:(NSString *)experienceId andConstantsBinding:(EXConstantsBinding *)constantsBinding 10{ 11 NSString *escapedExperienceId = [EXScopedFileSystemModule escapedResourceName:experienceId]; 12 13 NSString *mainDocumentDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; 14 NSString *exponentDocumentDirectory = [mainDocumentDirectory stringByAppendingPathComponent:@"ExponentExperienceData"]; 15 NSString *experienceDocumentDirectory = [[exponentDocumentDirectory stringByAppendingPathComponent:escapedExperienceId] stringByStandardizingPath]; 16 17 NSString *mainCachesDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; 18 NSString *exponentCachesDirectory = [mainCachesDirectory stringByAppendingPathComponent:@"ExponentExperienceData"]; 19 NSString *experienceCachesDirectory = [[exponentCachesDirectory stringByAppendingPathComponent:escapedExperienceId] stringByStandardizingPath]; 20 21 if (![@"expo" isEqualToString:constantsBinding.appOwnership]) { 22 [self ensureOldFilesAreMigratedFrom:experienceDocumentDirectory to:mainDocumentDirectory]; 23 24 return [super init]; 25 } 26 27 return [super initWithDocumentDirectory:experienceDocumentDirectory 28 cachesDirectory:experienceCachesDirectory 29 bundleDirectory:nil]; 30} 31 32- (NSDictionary *)constantsToExport 33{ 34 NSMutableDictionary *constants = [[NSMutableDictionary alloc] initWithDictionary:[super constantsToExport]]; 35 constants[@"bundledAssets"] = [self bundledAssets] ?: [NSNull null]; 36 return constants; 37} 38 39+ (NSString *)escapedResourceName:(NSString *)name 40{ 41 NSString *charactersToEscape = @"!*'();:@&=+$,/?%#[]"; 42 NSCharacterSet *allowedCharacters = [[NSCharacterSet characterSetWithCharactersInString:charactersToEscape] invertedSet]; 43 return [name stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters]; 44} 45 46- (NSArray<NSString *> *)bundledAssets 47{ 48 static NSArray<NSString *> *bundledAssets = nil; 49 static dispatch_once_t once; 50 dispatch_once(&once, ^{ 51 NSString *manifestBundlePath = [[NSBundle mainBundle] pathForResource:EXShellManifestResourceName ofType:@"json"]; 52 NSData *data = [NSData dataWithContentsOfFile:manifestBundlePath]; 53 if (data.length == 0) { 54 return; 55 } 56 __block NSError *error; 57 id manifest = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; 58 if (error) { 59 UMLogError(@"Error parsing bundled manifest: %@", error); 60 return; 61 } 62 bundledAssets = manifest[@"bundledAssets"]; 63 }); 64 return bundledAssets; 65} 66 67// This method ensures that data are migrated from the old scoped path to the new, unscoped one. 68// It needs to be called in case somebody wants to update their standalone app. 69// This method can be removed when SDK32 is phased out. 70- (void)ensureOldFilesAreMigratedFrom:(NSString *)fromDirectory to:(NSString *)toDirectory 71{ 72 NSFileManager *fileManager = [NSFileManager defaultManager]; 73 if ([fileManager fileExistsAtPath:fromDirectory]) { 74 NSArray<NSString *> *files = [fileManager contentsOfDirectoryAtPath:fromDirectory error:nil]; 75 76 for (NSString *file in files) { 77 [fileManager moveItemAtPath:[fromDirectory stringByAppendingPathComponent:file] 78 toPath:[toDirectory stringByAppendingPathComponent:file] 79 error:nil]; 80 } 81 [fileManager removeItemAtPath:fromDirectory error:nil]; 82 } 83} 84 85@end 86