1 // Copyright 2015-present 650 Industries. All rights reserved. 2 3 import Foundation 4 5 /** 6 * The build data stored by the configuration is subject to change when 7 * a user updates the binary. 8 * 9 * This can lead to inconsistent update loading behavior, for 10 * example: https://github.com/expo/expo/issues/14372 11 * 12 * This class wipes the updates when any of the tracked build data 13 * changes. This leaves the user in the same situation as a fresh install. 14 * 15 * So far we only know that `releaseChannel` and 16 * `requestHeaders[expo-channel-name]` are dangerous to change, but have 17 * included a few more that both seem unlikely to change (so we clear 18 * the updates cache rarely) and likely to 19 * cause bugs when they do. The tracked fields are: 20 * 21 * EXUpdatesReleaseChannel 22 * EXUpdatesURL 23 * 24 * and all of the values in json 25 * 26 * EXUpdatesRequestHeaders 27 */ 28 internal final class UpdatesBuildData { ensureBuildDataIsConsistentAsyncnull29 static func ensureBuildDataIsConsistentAsync(database: UpdatesDatabase, config: UpdatesConfig) { 30 database.databaseQueue.async { 31 guard let scopeKey = config.scopeKey else { 32 NSException( 33 name: .internalInconsistencyException, 34 reason: "expo-updates was configured with no scope key. Make sure a valid URL is configured under EXUpdatesURL." 35 ).raise() 36 return 37 } 38 39 let staticBuildData: [AnyHashable: Any]? 40 do { 41 staticBuildData = try database.staticBuildData(withScopeKey: scopeKey) 42 } catch { 43 NSLog("Error getting static build data: %@", [error.localizedDescription]) 44 return 45 } 46 47 if let staticBuildData = staticBuildData { 48 let impliedStaticBuildData = self.getBuildDataFromConfig(config) 49 // safest dictionary comparison conversion is still in objective-c 50 // swiftlint:disable:next legacy_objc_type 51 if !NSDictionary(dictionary: staticBuildData).isEqual(to: impliedStaticBuildData) { 52 clearAllUpdatesAndSetStaticBuildData(database: database, config: config, scopeKey: scopeKey) 53 } 54 } else { 55 do { 56 try database.setStaticBuildData(getBuildDataFromConfig(config), withScopeKey: scopeKey) 57 } catch { 58 NSLog("Error setting static build data: %@", [error.localizedDescription]) 59 return 60 } 61 } 62 } 63 } 64 getBuildDataFromConfignull65 static func getBuildDataFromConfig(_ config: UpdatesConfig) -> [String: Any] { 66 return [ 67 "EXUpdatesURL": config.updateUrl.require("Must supply updateUrl in config").absoluteString, 68 "EXUpdatesReleaseChannel": config.releaseChannel, 69 "EXUpdatesRequestHeaders": config.requestHeaders 70 ] 71 } 72 clearAllUpdatesAndSetStaticBuildDatanull73 static func clearAllUpdatesAndSetStaticBuildData(database: UpdatesDatabase, config: UpdatesConfig, scopeKey: String) { 74 let allUpdates: [Update] 75 do { 76 allUpdates = try database.allUpdates(withConfig: config) 77 } catch { 78 NSLog("Error loading updates from database: %@", [error.localizedDescription]) 79 return 80 } 81 82 do { 83 try database.deleteUpdates(allUpdates) 84 } catch { 85 NSLog("Error clearing all updates from database: %@", [error.localizedDescription]) 86 return 87 } 88 89 do { 90 try database.setStaticBuildData(getBuildDataFromConfig(config), withScopeKey: scopeKey) 91 } catch { 92 NSLog("Error setting static build data: %@", [error.localizedDescription]) 93 return 94 } 95 } 96 } 97