1 // Copyright 2015-present 650 Industries. All rights reserved. 2 3 // swiftlint:disable closure_body_length 4 5 import Foundation 6 import EXUpdates 7 import EXManifests 8 9 /** 10 An AppLoader implementation that starts with an already existing manifest and is only responsible 11 for downloading assets, primarily the bundle. 12 */ 13 final class HomeAppLoader: AppLoader { 14 private static let ErrorDomain = "HomeAppLoader" 15 16 private let manifestAndAssetRequestHeaders: ManifestAndAssetRequestHeaders 17 18 private let downloader: FileDownloader 19 private let completionQueue: DispatchQueue 20 21 required init( 22 manifestAndAssetRequestHeaders: ManifestAndAssetRequestHeaders, 23 config: UpdatesConfig, 24 database: UpdatesDatabase, 25 directory: URL, 26 launchedUpdate: Update?, 27 completionQueue: DispatchQueue 28 ) { 29 self.manifestAndAssetRequestHeaders = manifestAndAssetRequestHeaders 30 self.downloader = FileDownloader(config: config) 31 self.completionQueue = completionQueue 32 super.init(config: config, database: database, directory: directory, launchedUpdate: launchedUpdate, completionQueue: completionQueue) 33 } 34 35 func loadHome( 36 onUpdateResponse updateResponseBlockArg: @escaping AppLoaderUpdateResponseBlock, 37 asset assetBlockArg: @escaping AppLoaderAssetBlock, 38 success successBlockArg: @escaping AppLoaderSuccessBlock, 39 error errorBlockArg: @escaping AppLoaderErrorBlock 40 ) { 41 self.updateResponseBlock = updateResponseBlockArg 42 self.assetBlock = assetBlockArg 43 self.errorBlock = errorBlockArg 44 self.successBlock = successBlockArg 45 46 database.databaseQueue.async { 47 let update: Update 48 if let manifest = self.manifestAndAssetRequestHeaders.manifest as? LegacyManifest { 49 update = LegacyUpdate.update( 50 withLegacyManifest: manifest, 51 config: self.config, 52 database: self.database 53 ) 54 } else if let manifest = self.manifestAndAssetRequestHeaders.manifest as? NewManifest { 55 update = NewUpdate.update( 56 withNewManifest: manifest, 57 extensions: ["assetRequestHeaders": self.manifestAndAssetRequestHeaders.assetRequestHeaders], 58 config: self.config, 59 database: self.database 60 ) 61 } else { 62 // swiftlint:disable force_cast 63 update = BareUpdate.update( 64 withBareManifest: self.manifestAndAssetRequestHeaders.manifest as! BareManifest, 65 config: self.config, 66 database: self.database 67 ) 68 // swiftlint:enable force_cast 69 } 70 71 self.startLoading(fromUpdateResponse: UpdateResponse( 72 responseHeaderData: nil, 73 manifestUpdateResponsePart: ManifestUpdateResponsePart(updateManifest: update), 74 directiveUpdateResponsePart: nil 75 )) 76 } 77 } 78 downloadAssetnull79 override func downloadAsset(_ asset: UpdateAsset) { 80 let urlOnDisk = self.directory.appendingPathComponent(asset.filename) 81 82 FileDownloader.assetFilesQueue.async { 83 if FileManager.default.fileExists(atPath: urlOnDisk.path) { 84 // file already exists, we don't need to download it again 85 DispatchQueue.global().async { 86 self.handleAssetDownloadAlreadyExists(asset) 87 } 88 } else { 89 guard let assetUrl = asset.url else { 90 self.handleAssetDownload( 91 withError: NSError( 92 domain: HomeAppLoader.ErrorDomain, 93 code: 1006, 94 userInfo: [ 95 NSLocalizedDescriptionKey: "Failed to download asset with no URL provided" 96 ] 97 ), 98 asset: asset 99 ) 100 return 101 } 102 103 self.downloader.downloadFile( 104 fromURL: assetUrl, 105 verifyingHash: asset.expectedHash, 106 toPath: urlOnDisk.path, 107 extraHeaders: asset.extraRequestHeaders ?? [:] 108 ) { data, response, _ in 109 DispatchQueue.global().async { 110 self.handleAssetDownload(withData: data, response: response, asset: asset) 111 } 112 } errorBlock: { error in 113 DispatchQueue.global().async { 114 self.handleAssetDownload(withError: error, asset: asset) 115 } 116 } 117 } 118 } 119 } 120 } 121 122 // swiftlint:enable closure_body_length 123