1// Copyright 2015-present 650 Industries. All rights reserved. 2 3#import "EXVersions.h" 4#import "EXKernelUtil.h" 5 6@interface EXVersions () 7 8- (void)_loadVersions; 9 10@end 11 12@implementation NSString (EXVersions) 13 14- (NSArray <NSNumber *>*)versionComponents 15{ 16 NSArray *stringComponents = [self componentsSeparatedByString:@"."]; 17 NSMutableArray <NSNumber *>* components = [NSMutableArray arrayWithCapacity:stringComponents.count]; 18 for (NSString *component in stringComponents) { 19 [components addObject:@([component integerValue])]; 20 } 21 return components; 22} 23 24@end 25 26@implementation EXVersions 27 28+ (nonnull instancetype)sharedInstance 29{ 30 static EXVersions *theVersions; 31 static dispatch_once_t once; 32 dispatch_once(&once, ^{ 33 if (!theVersions) { 34 theVersions = [[EXVersions alloc] init]; 35 } 36 }); 37 return theVersions; 38} 39 40- (instancetype)init 41{ 42 if (self = [super init]) { 43 [self _loadVersions]; 44 } 45 return self; 46} 47 48+ (NSString *)versionedString:(NSString *)string withPrefix:(NSString *)symbolPrefix 49{ 50 if (!string || !symbolPrefix) { 51 return nil; 52 } 53 return [NSString stringWithFormat:@"%@%@", symbolPrefix, string]; 54} 55 56- (NSString *)symbolPrefixForSdkVersion:(NSString *)version isKernel:(BOOL)isKernel 57{ 58 NSDictionary *detachedVersions = _versions[@"detachedNativeVersions"]; 59 if (detachedVersions) { 60 if (!isKernel && detachedVersions[@"shell"]) { 61 // we are in a detached shell scenario, so we always want to leave the shell unprefixed 62 return @""; 63 } 64 if (isKernel && detachedVersions[@"shell"] && detachedVersions[@"kernel"]) { 65 if ([detachedVersions[@"shell"] isEqualToString:detachedVersions[@"kernel"]]) { 66 // kernel version matches shell version, so run them both unprefixed 67 return @""; 68 } else { 69 // kernel needs to run on prefixed code for the given kernel version, continue 70 version = detachedVersions[@"kernel"]; 71 } 72 } 73 } 74 if (version && version.length) { 75 return [[@"ABI" stringByAppendingString:version] stringByReplacingOccurrencesOfString:@"." withString:@"_"]; 76 } 77 return @""; 78} 79 80- (NSString *)availableSdkVersionForManifest: (NSDictionary * _Nullable)manifest 81{ 82 return [self _versionForManifest:manifest]; 83} 84 85#pragma mark - Internal 86 87- (NSString *)_versionForManifest:(NSDictionary * _Nullable)manifest 88{ 89 if (manifest && manifest[@"sdkVersion"]) { 90 NSString *sdkVersion = manifest[@"sdkVersion"]; 91 NSArray *sdkVersions = _versions[@"sdkVersions"]; 92 if (sdkVersion && sdkVersions) { 93 for (NSString *availableVersion in sdkVersions) { 94 if ([sdkVersion isEqualToString:availableVersion]) { 95 if (_temporarySdkVersion) { 96 NSArray <NSNumber *>* versionComponents = [availableVersion versionComponents]; 97 BOOL isTemporary = (versionComponents.count > 1 && versionComponents[1].integerValue != 0); 98 if (isTemporary && [availableVersion isEqualToString:_temporarySdkVersion]) { 99 // no prefix if we're just using the current version 100 break; 101 } 102 } 103 return availableVersion; 104 } 105 } 106 } 107 } 108 return @""; 109} 110 111- (void)_loadVersions 112{ 113 NSString *versionsPath = [[NSBundle mainBundle] pathForResource:@"EXSDKVersions" ofType:@"plist"]; 114 NSMutableDictionary *mutableVersions = (versionsPath) ? [NSMutableDictionary dictionaryWithContentsOfFile:versionsPath] : [NSMutableDictionary dictionary]; 115 if (mutableVersions[@"detachedNativeVersions"]) { 116 NSDictionary *detachedNativeVersions = mutableVersions[@"detachedNativeVersions"]; 117 _temporarySdkVersion = detachedNativeVersions[@"shell"]; 118 } else { 119#ifdef TEMPORARY_SDK_VERSION 120 _temporarySdkVersion = TEMPORARY_SDK_VERSION; 121#endif 122 } 123 if (_temporarySdkVersion) { 124 if (mutableVersions[@"sdkVersions"]) { 125 NSArray *existingVersions = mutableVersions[@"sdkVersions"]; 126 if ([existingVersions indexOfObject:_temporarySdkVersion] == NSNotFound) { 127 mutableVersions[@"sdkVersions"] = [[existingVersions mutableCopy] arrayByAddingObject:_temporarySdkVersion]; 128 } 129 } 130 } else { 131 // no temporary sdk version specified in any way, fall back to using the highest version 132 NSArray *sdkVersions = mutableVersions[@"sdkVersions"]; 133 NSUInteger highestVersion = 0; 134 if (sdkVersions) { 135 for (NSString *availableVersion in sdkVersions) { 136 NSArray <NSNumber *>* versionComponents = [availableVersion versionComponents]; 137 if (versionComponents.count > 1 && versionComponents[0].integerValue > highestVersion) { 138 highestVersion = versionComponents[0].integerValue; 139 _temporarySdkVersion = availableVersion; 140 } 141 } 142 } 143 } 144 145 NSAssert((mutableVersions[@"sdkVersions"] != nil), @"No SDK versions are specified for the Expo kernel. Is the project missing EXSDKVersions.plist?"); 146 147 _versions = mutableVersions; 148} 149 150@end 151