// Copyright © 2019 650 Industries. All rights reserved. #import NS_ASSUME_NONNULL_BEGIN @interface EXUpdatesConfig () @property (nonatomic, readwrite, assign) BOOL isEnabled; @property (nonatomic, readwrite, strong) NSString *scopeKey; @property (nonatomic, readwrite, strong) NSURL *updateUrl; @property (nonatomic, readwrite, strong) NSDictionary *requestHeaders; @property (nonatomic, readwrite, strong) NSString *releaseChannel; @property (nonatomic, readwrite, strong) NSNumber *launchWaitMs; @property (nonatomic, readwrite, assign) EXUpdatesCheckAutomaticallyConfig checkOnLaunch; @property (nullable, nonatomic, readwrite, strong) NSString *sdkVersion; @property (nullable, nonatomic, readwrite, strong) NSString *runtimeVersion; @end static NSString * const EXUpdatesDefaultReleaseChannelName = @"default"; static NSString * const EXUpdatesConfigEnabledKey = @"EXUpdatesEnabled"; static NSString * const EXUpdatesConfigScopeKeyKey = @"EXUpdatesScopeKey"; static NSString * const EXUpdatesConfigUpdateUrlKey = @"EXUpdatesURL"; static NSString * const EXUpdatesConfigRequestHeadersKey = @"EXUpdatesRequestHeaders"; static NSString * const EXUpdatesConfigReleaseChannelKey = @"EXUpdatesReleaseChannel"; static NSString * const EXUpdatesConfigLaunchWaitMsKey = @"EXUpdatesLaunchWaitMs"; static NSString * const EXUpdatesConfigCheckOnLaunchKey = @"EXUpdatesCheckOnLaunch"; static NSString * const EXUpdatesConfigSDKVersionKey = @"EXUpdatesSDKVersion"; static NSString * const EXUpdatesConfigRuntimeVersionKey = @"EXUpdatesRuntimeVersion"; static NSString * const EXUpdatesConfigUsesLegacyManifestKey = @"EXUpdatesUsesLegacyManifest"; static NSString * const EXUpdatesConfigHasEmbeddedUpdateKey = @"EXUpdatesHasEmbeddedUpdate"; static NSString * const EXUpdatesConfigAlwaysString = @"ALWAYS"; static NSString * const EXUpdatesConfigWifiOnlyString = @"WIFI_ONLY"; static NSString * const EXUpdatesConfigNeverString = @"NEVER"; @implementation EXUpdatesConfig - (instancetype)init { if (self = [super init]) { _isEnabled = YES; _requestHeaders = @{}; _releaseChannel = EXUpdatesDefaultReleaseChannelName; _launchWaitMs = @(0); _checkOnLaunch = EXUpdatesCheckAutomaticallyConfigAlways; _usesLegacyManifest = YES; _hasEmbeddedUpdate = YES; } return self; } + (instancetype)configWithDictionary:(NSDictionary *)config { EXUpdatesConfig *updatesConfig = [[EXUpdatesConfig alloc] init]; [updatesConfig loadConfigFromDictionary:config]; return updatesConfig; } - (void)loadConfigFromDictionary:(NSDictionary *)config { id isEnabled = config[EXUpdatesConfigEnabledKey]; if (isEnabled && [isEnabled isKindOfClass:[NSNumber class]]) { _isEnabled = [(NSNumber *)isEnabled boolValue]; } id updateUrl = config[EXUpdatesConfigUpdateUrlKey]; if (updateUrl && [updateUrl isKindOfClass:[NSString class]]) { NSURL *url = [NSURL URLWithString:(NSString *)updateUrl]; _updateUrl = url; } id scopeKey = config[EXUpdatesConfigScopeKeyKey]; if (scopeKey && [scopeKey isKindOfClass:[NSString class]]) { _scopeKey = (NSString *)scopeKey; } // set updateUrl as the default value if none is provided if (!_scopeKey) { if (_updateUrl) { _scopeKey = [[self class] normalizedURLOrigin:_updateUrl]; } else { @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"expo-updates must be configured with a valid update URL or scope key." userInfo:@{}]; } } id requestHeaders = config[EXUpdatesConfigRequestHeadersKey]; if (requestHeaders && [requestHeaders isKindOfClass:[NSDictionary class]]) { _requestHeaders = (NSDictionary *)requestHeaders; } id releaseChannel = config[EXUpdatesConfigReleaseChannelKey]; if (releaseChannel && [releaseChannel isKindOfClass:[NSString class]]) { _releaseChannel = (NSString *)releaseChannel; } id launchWaitMs = config[EXUpdatesConfigLaunchWaitMsKey]; if (launchWaitMs && [launchWaitMs isKindOfClass:[NSNumber class]]) { _launchWaitMs = (NSNumber *)launchWaitMs; } else if (launchWaitMs && [launchWaitMs isKindOfClass:[NSString class]]) { NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; formatter.numberStyle = NSNumberFormatterNoStyle; _launchWaitMs = [formatter numberFromString:(NSString *)launchWaitMs]; } id checkOnLaunch = config[EXUpdatesConfigCheckOnLaunchKey]; if (checkOnLaunch && [checkOnLaunch isKindOfClass:[NSString class]]) { if ([EXUpdatesConfigNeverString isEqualToString:(NSString *)checkOnLaunch]) { _checkOnLaunch = EXUpdatesCheckAutomaticallyConfigNever; } else if ([EXUpdatesConfigWifiOnlyString isEqualToString:(NSString *)checkOnLaunch]) { _checkOnLaunch = EXUpdatesCheckAutomaticallyConfigWifiOnly; } else if ([EXUpdatesConfigAlwaysString isEqualToString:(NSString *)checkOnLaunch]) { _checkOnLaunch = EXUpdatesCheckAutomaticallyConfigAlways; } } id sdkVersion = config[EXUpdatesConfigSDKVersionKey]; if (sdkVersion && [sdkVersion isKindOfClass:[NSString class]]) { _sdkVersion = (NSString *)sdkVersion; } id runtimeVersion = config[EXUpdatesConfigRuntimeVersionKey]; if (runtimeVersion && [runtimeVersion isKindOfClass:[NSString class]]) { _runtimeVersion = (NSString *)runtimeVersion; } NSAssert(_sdkVersion || _runtimeVersion, @"One of EXUpdatesSDKVersion or EXUpdatesRuntimeVersion must be configured in expo-updates"); id usesLegacyManifest = config[EXUpdatesConfigUsesLegacyManifestKey]; if (usesLegacyManifest && [usesLegacyManifest isKindOfClass:[NSNumber class]]) { _usesLegacyManifest = [(NSNumber *)usesLegacyManifest boolValue]; } id hasEmbeddedUpdate = config[EXUpdatesConfigHasEmbeddedUpdateKey]; if (hasEmbeddedUpdate && [hasEmbeddedUpdate isKindOfClass:[NSNumber class]]) { _hasEmbeddedUpdate = [(NSNumber *)hasEmbeddedUpdate boolValue]; } } + (NSString *)normalizedURLOrigin:(NSURL *)url { NSString *scheme = url.scheme; NSNumber *port = url.port; if (port && port.integerValue > -1 && [port isEqual:[[self class] defaultPortForScheme:scheme]]) { port = nil; } return (port && port.integerValue > -1) ? [NSString stringWithFormat:@"%@://%@:%ld", scheme, url.host, (long)port.integerValue] : [NSString stringWithFormat:@"%@://%@", scheme, url.host]; } + (nullable NSNumber *)defaultPortForScheme:(NSString *)scheme { if ([@"http" isEqualToString:scheme] || [@"ws" isEqualToString:scheme]) { return @(80); } else if ([@"https" isEqualToString:scheme] || [@"wss" isEqualToString:scheme]) { return @(443); } else if ([@"ftp" isEqualToString:scheme]) { return @(21); } return nil; } @end NS_ASSUME_NONNULL_END