xref: /expo/ios/Client/HomeAppLoader.swift (revision 5f54863a)
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