expo-updates 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/updates/UpdatesController.kt +1 -1
- package/android/src/main/java/expo/modules/updates/UpdatesDevLauncherController.kt +2 -1
- package/android/src/main/java/expo/modules/updates/UpdatesInterface.kt +1 -0
- package/android/src/main/java/expo/modules/updates/UpdatesModule.kt +8 -4
- package/android/src/main/java/expo/modules/updates/UpdatesService.kt +4 -0
- package/android/src/main/java/expo/modules/updates/loader/FileDownloader.kt +23 -4
- package/android/src/main/java/expo/modules/updates/loader/LoaderTask.kt +1 -1
- package/android/src/main/java/expo/modules/updates/loader/RemoteLoader.kt +8 -5
- package/ios/EXUpdates/AppLauncher/EXUpdatesAppLauncherWithDatabase.m +2 -2
- package/ios/EXUpdates/AppLoader/EXUpdatesAppLoader+Private.h +1 -0
- package/ios/EXUpdates/AppLoader/EXUpdatesAppLoader.h +1 -0
- package/ios/EXUpdates/AppLoader/EXUpdatesAppLoader.m +2 -0
- package/ios/EXUpdates/AppLoader/EXUpdatesAppLoaderTask.m +4 -2
- package/ios/EXUpdates/AppLoader/EXUpdatesFileDownloader.h +10 -0
- package/ios/EXUpdates/AppLoader/EXUpdatesFileDownloader.m +21 -0
- package/ios/EXUpdates/AppLoader/EXUpdatesRemoteAppLoader.m +18 -7
- package/ios/EXUpdates/EXUpdatesAppController.m +1 -1
- package/ios/EXUpdates/EXUpdatesDevLauncherController.m +1 -1
- package/ios/EXUpdates/EXUpdatesModule.m +5 -6
- package/ios/EXUpdates/EXUpdatesService.h +1 -0
- package/ios/EXUpdates/EXUpdatesService.m +6 -0
- package/ios/EXUpdates/ReactDelegateHandler/ExpoUpdatesAppDelegateSubscriber.swift +1 -1
- package/ios/Tests/EXUpdatesFileDownloaderTests.m +47 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,17 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 0.13.0 — 2022-04-21
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- Fix asset hash validation. ([#17152](https://github.com/expo/expo/pull/17152) by [@wschurman](https://github.com/wschurman))
|
|
18
|
+
|
|
19
|
+
### 💡 Others
|
|
20
|
+
|
|
21
|
+
- Add current and embedded update headers to manifest requests. ([#17033](https://github.com/expo/expo/pull/17033) by [@esamelson](https://github.com/esamelson))
|
|
22
|
+
- Fix return value in AppDelegateSubscriber (used with expo-dev-client). ([#17111](https://github.com/expo/expo/pull/17111) by [@esamelson](https://github.com/esamelson))
|
|
23
|
+
|
|
13
24
|
## 0.12.0 — 2022-04-18
|
|
14
25
|
|
|
15
26
|
### 🛠 Breaking changes
|
package/android/build.gradle
CHANGED
|
@@ -4,7 +4,7 @@ apply plugin: 'kotlin-kapt'
|
|
|
4
4
|
apply plugin: 'maven-publish'
|
|
5
5
|
|
|
6
6
|
group = 'host.exp.exponent'
|
|
7
|
-
version = '0.
|
|
7
|
+
version = '0.13.0'
|
|
8
8
|
|
|
9
9
|
apply from: "../scripts/create-manifest-android.gradle"
|
|
10
10
|
|
|
@@ -77,7 +77,7 @@ android {
|
|
|
77
77
|
minSdkVersion safeExtGet("minSdkVersion", 21)
|
|
78
78
|
targetSdkVersion safeExtGet("targetSdkVersion", 31)
|
|
79
79
|
versionCode 31
|
|
80
|
-
versionName '0.
|
|
80
|
+
versionName '0.13.0'
|
|
81
81
|
consumerProguardFiles("proguard-rules.pro")
|
|
82
82
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
83
83
|
// uncomment below to export the database schema when making changes
|
|
@@ -307,7 +307,7 @@ class UpdatesController private constructor(
|
|
|
307
307
|
}
|
|
308
308
|
remoteLoadStatus = ErrorRecoveryDelegate.RemoteLoadStatus.NEW_UPDATE_LOADING
|
|
309
309
|
val database = getDatabase()
|
|
310
|
-
val remoteLoader = RemoteLoader(context, updatesConfiguration, database, fileDownloader, updatesDirectory)
|
|
310
|
+
val remoteLoader = RemoteLoader(context, updatesConfiguration, database, fileDownloader, updatesDirectory, launchedUpdate)
|
|
311
311
|
remoteLoader.start(object : Loader.LoaderCallback {
|
|
312
312
|
override fun onFailure(e: Exception) {
|
|
313
313
|
setRemoteLoadStatus(ErrorRecoveryDelegate.RemoteLoadStatus.IDLE)
|
|
@@ -52,7 +52,8 @@ class UpdatesDevLauncherController : UpdatesInterface {
|
|
|
52
52
|
updatesConfiguration,
|
|
53
53
|
databaseHolder.database,
|
|
54
54
|
controller.fileDownloader,
|
|
55
|
-
controller.updatesDirectory
|
|
55
|
+
controller.updatesDirectory,
|
|
56
|
+
null
|
|
56
57
|
)
|
|
57
58
|
loader.start(object : Loader.LoaderCallback {
|
|
58
59
|
override fun onFailure(e: Exception) {
|
|
@@ -12,10 +12,10 @@ import expo.modules.core.interfaces.ExpoMethod
|
|
|
12
12
|
import expo.modules.updates.db.entity.AssetEntity
|
|
13
13
|
import expo.modules.updates.db.entity.UpdateEntity
|
|
14
14
|
import expo.modules.updates.launcher.Launcher.LauncherCallback
|
|
15
|
+
import expo.modules.updates.loader.FileDownloader
|
|
15
16
|
import expo.modules.updates.loader.FileDownloader.ManifestDownloadCallback
|
|
16
17
|
import expo.modules.updates.loader.Loader
|
|
17
18
|
import expo.modules.updates.loader.RemoteLoader
|
|
18
|
-
import expo.modules.updates.manifest.ManifestMetadata
|
|
19
19
|
import expo.modules.updates.manifest.UpdateManifest
|
|
20
20
|
|
|
21
21
|
// these unused imports must stay because of versioning
|
|
@@ -127,8 +127,11 @@ class UpdatesModule(
|
|
|
127
127
|
return
|
|
128
128
|
}
|
|
129
129
|
val databaseHolder = updatesServiceLocal.databaseHolder
|
|
130
|
-
val extraHeaders =
|
|
131
|
-
databaseHolder.database,
|
|
130
|
+
val extraHeaders = FileDownloader.getExtraHeaders(
|
|
131
|
+
databaseHolder.database,
|
|
132
|
+
updatesServiceLocal.configuration,
|
|
133
|
+
updatesServiceLocal.launchedUpdate,
|
|
134
|
+
updatesServiceLocal.embeddedUpdate
|
|
132
135
|
)
|
|
133
136
|
databaseHolder.releaseDatabase()
|
|
134
137
|
updatesServiceLocal.fileDownloader.downloadManifest(
|
|
@@ -194,7 +197,8 @@ class UpdatesModule(
|
|
|
194
197
|
updatesServiceLocal.configuration,
|
|
195
198
|
databaseHolder.database,
|
|
196
199
|
updatesServiceLocal.fileDownloader,
|
|
197
|
-
updatesServiceLocal.directory
|
|
200
|
+
updatesServiceLocal.directory,
|
|
201
|
+
updatesServiceLocal.launchedUpdate
|
|
198
202
|
)
|
|
199
203
|
.start(
|
|
200
204
|
object : Loader.LoaderCallback {
|
|
@@ -14,6 +14,8 @@ import java.io.File
|
|
|
14
14
|
/* ktlint-disable no-unused-imports */
|
|
15
15
|
import expo.modules.updates.UpdatesConfiguration
|
|
16
16
|
import expo.modules.updates.UpdatesController
|
|
17
|
+
import expo.modules.updates.manifest.EmbeddedManifest
|
|
18
|
+
|
|
17
19
|
/* ktlint-enable no-unused-imports */
|
|
18
20
|
|
|
19
21
|
open class UpdatesService(protected var context: Context) : InternalModule, UpdatesInterface {
|
|
@@ -40,6 +42,8 @@ open class UpdatesService(protected var context: Context) : InternalModule, Upda
|
|
|
40
42
|
return configuration.isEnabled && launchedUpdate != null
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
override val embeddedUpdate: UpdateEntity?
|
|
46
|
+
get() = EmbeddedManifest.get(context, configuration)?.updateEntity
|
|
43
47
|
override val launchedUpdate: UpdateEntity?
|
|
44
48
|
get() = UpdatesController.instance.launchedUpdate
|
|
45
49
|
override val localAssetFiles: Map<AssetEntity, String>?
|
|
@@ -7,9 +7,6 @@ import expo.modules.updates.UpdatesConfiguration
|
|
|
7
7
|
import expo.modules.updates.UpdatesUtils
|
|
8
8
|
import expo.modules.updates.db.entity.AssetEntity
|
|
9
9
|
import expo.modules.updates.launcher.NoDatabaseLauncher
|
|
10
|
-
import expo.modules.updates.manifest.ManifestFactory
|
|
11
|
-
import expo.modules.updates.manifest.ManifestHeaderData
|
|
12
|
-
import expo.modules.updates.manifest.UpdateManifest
|
|
13
10
|
import expo.modules.updates.selectionpolicy.SelectionPolicies
|
|
14
11
|
import okhttp3.*
|
|
15
12
|
import org.json.JSONArray
|
|
@@ -27,6 +24,9 @@ import expo.modules.easclient.EASClientID
|
|
|
27
24
|
import okhttp3.Headers.Companion.toHeaders
|
|
28
25
|
import expo.modules.jsonutils.getNullable
|
|
29
26
|
import expo.modules.updates.codesigning.ValidationResult
|
|
27
|
+
import expo.modules.updates.db.UpdatesDatabase
|
|
28
|
+
import expo.modules.updates.db.entity.UpdateEntity
|
|
29
|
+
import expo.modules.updates.manifest.*
|
|
30
30
|
import java.security.cert.CertificateException
|
|
31
31
|
|
|
32
32
|
open class FileDownloader(private val client: OkHttpClient) {
|
|
@@ -331,7 +331,7 @@ open class FileDownloader(private val client: OkHttpClient) {
|
|
|
331
331
|
override fun onSuccess(file: File, hash: ByteArray) {
|
|
332
332
|
// base64url - https://datatracker.ietf.org/doc/html/rfc4648#section-5
|
|
333
333
|
val hashBase64String = Base64.encodeToString(hash, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
|
|
334
|
-
val expectedAssetHash = asset.expectedHash
|
|
334
|
+
val expectedAssetHash = asset.expectedHash
|
|
335
335
|
if (expectedAssetHash != null && expectedAssetHash != hashBase64String) {
|
|
336
336
|
callback.onFailure(Exception("Asset hash invalid: ${asset.key}; expectedHash: $expectedAssetHash; actualHash: $hashBase64String"), asset)
|
|
337
337
|
return
|
|
@@ -568,5 +568,24 @@ open class FileDownloader(private val client: OkHttpClient) {
|
|
|
568
568
|
private fun getCacheDirectory(context: Context): File {
|
|
569
569
|
return File(context.cacheDir, "okhttp")
|
|
570
570
|
}
|
|
571
|
+
|
|
572
|
+
fun getExtraHeaders(
|
|
573
|
+
database: UpdatesDatabase,
|
|
574
|
+
configuration: UpdatesConfiguration,
|
|
575
|
+
launchedUpdate: UpdateEntity?,
|
|
576
|
+
embeddedUpdate: UpdateEntity?
|
|
577
|
+
): JSONObject {
|
|
578
|
+
val extraHeaders =
|
|
579
|
+
ManifestMetadata.getServerDefinedHeaders(database, configuration) ?: JSONObject()
|
|
580
|
+
|
|
581
|
+
launchedUpdate?.let {
|
|
582
|
+
extraHeaders.put("Expo-Current-Update-ID", it.id.toString().lowercase())
|
|
583
|
+
}
|
|
584
|
+
embeddedUpdate?.let {
|
|
585
|
+
extraHeaders.put("Expo-Embedded-Update-ID", it.id.toString().lowercase())
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
return extraHeaders
|
|
589
|
+
}
|
|
571
590
|
}
|
|
572
591
|
}
|
|
@@ -274,7 +274,7 @@ class LoaderTask(
|
|
|
274
274
|
private fun launchRemoteUpdateInBackground(context: Context, remoteUpdateCallback: Callback) {
|
|
275
275
|
AsyncTask.execute {
|
|
276
276
|
val database = databaseHolder.database
|
|
277
|
-
RemoteLoader(context, configuration, database, fileDownloader, directory)
|
|
277
|
+
RemoteLoader(context, configuration, database, fileDownloader, directory, candidateLauncher?.launchedUpdate)
|
|
278
278
|
.start(object : LoaderCallback {
|
|
279
279
|
override fun onFailure(e: Exception) {
|
|
280
280
|
databaseHolder.releaseDatabase()
|
|
@@ -4,9 +4,9 @@ import android.content.Context
|
|
|
4
4
|
import expo.modules.updates.UpdatesConfiguration
|
|
5
5
|
import expo.modules.updates.db.UpdatesDatabase
|
|
6
6
|
import expo.modules.updates.db.entity.AssetEntity
|
|
7
|
+
import expo.modules.updates.db.entity.UpdateEntity
|
|
7
8
|
import expo.modules.updates.loader.FileDownloader.AssetDownloadCallback
|
|
8
9
|
import expo.modules.updates.loader.FileDownloader.ManifestDownloadCallback
|
|
9
|
-
import expo.modules.updates.manifest.ManifestMetadata
|
|
10
10
|
import java.io.File
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -22,15 +22,17 @@ class RemoteLoader internal constructor(
|
|
|
22
22
|
database: UpdatesDatabase,
|
|
23
23
|
private val mFileDownloader: FileDownloader,
|
|
24
24
|
updatesDirectory: File?,
|
|
25
|
-
|
|
25
|
+
private val launchedUpdate: UpdateEntity?,
|
|
26
|
+
private val loaderFiles: LoaderFiles
|
|
26
27
|
) : Loader(context, configuration, database, updatesDirectory, loaderFiles) {
|
|
27
28
|
constructor(
|
|
28
29
|
context: Context,
|
|
29
30
|
configuration: UpdatesConfiguration,
|
|
30
31
|
database: UpdatesDatabase,
|
|
31
32
|
fileDownloader: FileDownloader,
|
|
32
|
-
updatesDirectory: File
|
|
33
|
-
|
|
33
|
+
updatesDirectory: File?,
|
|
34
|
+
launchedUpdate: UpdateEntity?
|
|
35
|
+
) : this(context, configuration, database, fileDownloader, updatesDirectory, launchedUpdate, LoaderFiles())
|
|
34
36
|
|
|
35
37
|
override fun loadManifest(
|
|
36
38
|
context: Context,
|
|
@@ -38,7 +40,8 @@ class RemoteLoader internal constructor(
|
|
|
38
40
|
configuration: UpdatesConfiguration,
|
|
39
41
|
callback: ManifestDownloadCallback
|
|
40
42
|
) {
|
|
41
|
-
val
|
|
43
|
+
val embeddedUpdate = loaderFiles.readEmbeddedManifest(context, configuration)?.updateEntity
|
|
44
|
+
val extraHeaders = FileDownloader.getExtraHeaders(database, configuration, launchedUpdate, embeddedUpdate)
|
|
42
45
|
mFileDownloader.downloadManifest(configuration, extraHeaders, context, callback)
|
|
43
46
|
}
|
|
44
47
|
|
|
@@ -295,10 +295,10 @@ static NSString * const EXUpdatesAppLauncherErrorDomain = @"AppLauncher";
|
|
|
295
295
|
successBlock:^(NSData *data, NSURLResponse *response) {
|
|
296
296
|
dispatch_async(self->_launcherQueue, ^{
|
|
297
297
|
NSString *hashBase64String = [EXUpdatesUtils base64UrlEncodedSHA256WithData:data];
|
|
298
|
-
if (asset.expectedHash && ![asset.expectedHash
|
|
298
|
+
if (asset.expectedHash && ![asset.expectedHash isEqualToString:hashBase64String]) {
|
|
299
299
|
completion([NSError errorWithDomain:EXUpdatesAppLauncherErrorDomain
|
|
300
300
|
code:1016
|
|
301
|
-
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Asset hash invalid: %@; expectedHash: %@; actualHash: %@", asset.key, asset.expectedHash
|
|
301
|
+
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Asset hash invalid: %@; expectedHash: %@; actualHash: %@", asset.key, asset.expectedHash, hashBase64String]}],
|
|
302
302
|
asset,
|
|
303
303
|
assetLocalUrl);
|
|
304
304
|
return;
|
|
@@ -12,6 +12,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
12
12
|
@property (nonatomic, strong) EXUpdatesConfig *config;
|
|
13
13
|
@property (nonatomic, strong) EXUpdatesDatabase *database;
|
|
14
14
|
@property (nonatomic, strong) NSURL *directory;
|
|
15
|
+
@property (nonatomic, strong, nullable) EXUpdatesUpdate *launchedUpdate;
|
|
15
16
|
@property (nonatomic, strong) EXUpdatesUpdate *updateManifest;
|
|
16
17
|
@property (nonatomic, copy) EXUpdatesAppLoaderManifestBlock manifestBlock;
|
|
17
18
|
@property (nonatomic, copy) EXUpdatesAppLoaderAssetBlock assetBlock;
|
|
@@ -17,6 +17,7 @@ typedef void (^EXUpdatesAppLoaderErrorBlock)(NSError *error);
|
|
|
17
17
|
- (instancetype)initWithConfig:(EXUpdatesConfig *)config
|
|
18
18
|
database:(EXUpdatesDatabase *)database
|
|
19
19
|
directory:(NSURL *)directory
|
|
20
|
+
launchedUpdate:(nullable EXUpdatesUpdate *)launchedUpdate
|
|
20
21
|
completionQueue:(dispatch_queue_t)completionQueue;
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -28,6 +28,7 @@ static NSString * const EXUpdatesAppLoaderErrorDomain = @"EXUpdatesAppLoader";
|
|
|
28
28
|
- (instancetype)initWithConfig:(EXUpdatesConfig *)config
|
|
29
29
|
database:(EXUpdatesDatabase *)database
|
|
30
30
|
directory:(NSURL *)directory
|
|
31
|
+
launchedUpdate:(nullable EXUpdatesUpdate *)launchedUpdate
|
|
31
32
|
completionQueue:(dispatch_queue_t)completionQueue
|
|
32
33
|
{
|
|
33
34
|
if (self = [super init]) {
|
|
@@ -39,6 +40,7 @@ static NSString * const EXUpdatesAppLoaderErrorDomain = @"EXUpdatesAppLoader";
|
|
|
39
40
|
_config = config;
|
|
40
41
|
_database = database;
|
|
41
42
|
_directory = directory;
|
|
43
|
+
_launchedUpdate = launchedUpdate;
|
|
42
44
|
_completionQueue = completionQueue;
|
|
43
45
|
}
|
|
44
46
|
return self;
|
|
@@ -210,7 +210,9 @@ static NSString * const EXUpdatesAppLoaderTaskErrorDomain = @"EXUpdatesAppLoader
|
|
|
210
210
|
[self->_selectionPolicy shouldLoadNewUpdate:[EXUpdatesEmbeddedAppLoader embeddedManifestWithConfig:self->_config database:self->_database]
|
|
211
211
|
withLaunchedUpdate:launchableUpdate
|
|
212
212
|
filters:manifestFilters]) {
|
|
213
|
-
|
|
213
|
+
// launchedUpdate is nil because we don't yet have one, and it doesn't matter as we won't
|
|
214
|
+
// be sending an HTTP request from EXUpdatesEmbeddedAppLoader
|
|
215
|
+
self->_embeddedAppLoader = [[EXUpdatesEmbeddedAppLoader alloc] initWithConfig:self->_config database:self->_database directory:self->_directory launchedUpdate:nil completionQueue:self->_loaderTaskQueue];
|
|
214
216
|
[self->_embeddedAppLoader loadUpdateFromEmbeddedManifestWithCallback:^BOOL(EXUpdatesUpdate * _Nonnull update) {
|
|
215
217
|
// we already checked using selection policy, so we don't need to check again
|
|
216
218
|
return YES;
|
|
@@ -238,7 +240,7 @@ static NSString * const EXUpdatesAppLoaderTaskErrorDomain = @"EXUpdatesAppLoader
|
|
|
238
240
|
|
|
239
241
|
- (void)_loadRemoteUpdateWithCompletion:(void (^)(NSError * _Nullable error, EXUpdatesUpdate * _Nullable update))completion
|
|
240
242
|
{
|
|
241
|
-
_remoteAppLoader = [[EXUpdatesRemoteAppLoader alloc] initWithConfig:_config database:_database directory:_directory completionQueue:_loaderTaskQueue];
|
|
243
|
+
_remoteAppLoader = [[EXUpdatesRemoteAppLoader alloc] initWithConfig:_config database:_database directory:_directory launchedUpdate:_candidateLauncher.launchedUpdate completionQueue:_loaderTaskQueue];
|
|
242
244
|
[_remoteAppLoader loadUpdateFromUrl:_config.updateUrl onManifest:^BOOL(EXUpdatesUpdate * _Nonnull update) {
|
|
243
245
|
if ([self->_selectionPolicy shouldLoadNewUpdate:update withLaunchedUpdate:self->_candidateLauncher.launchedUpdate filters:update.manifestFilters]) {
|
|
244
246
|
self->_isUpToDate = NO;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Copyright © 2019 650 Industries. All rights reserved.
|
|
2
2
|
|
|
3
3
|
#import <EXUpdates/EXUpdatesConfig.h>
|
|
4
|
+
#import <EXUpdates/EXUpdatesDatabase.h>
|
|
4
5
|
#import <EXUpdates/EXUpdatesUpdate.h>
|
|
5
6
|
|
|
6
7
|
NS_ASSUME_NONNULL_BEGIN
|
|
@@ -34,6 +35,15 @@ typedef void (^EXUpdatesFileDownloaderErrorBlock)(NSError *error);
|
|
|
34
35
|
|
|
35
36
|
+ (dispatch_queue_t)assetFilesQueue;
|
|
36
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Get extra (stateful) headers to pass into `downloadManifestFromURL:`
|
|
40
|
+
* Must be called on the database queue
|
|
41
|
+
*/
|
|
42
|
+
+ (NSDictionary *)extraHeadersWithDatabase:(EXUpdatesDatabase *)database
|
|
43
|
+
config:(EXUpdatesConfig *)config
|
|
44
|
+
launchedUpdate:(nullable EXUpdatesUpdate *)launchedUpdate
|
|
45
|
+
embeddedUpdate:(nullable EXUpdatesUpdate *)embeddedUpdate;
|
|
46
|
+
|
|
37
47
|
/**
|
|
38
48
|
* For test purposes; shouldn't be needed in application code
|
|
39
49
|
*/
|
|
@@ -670,6 +670,27 @@ certificateChainFromManifestResponse:(nullable NSString *)certificateChainFromMa
|
|
|
670
670
|
}
|
|
671
671
|
}
|
|
672
672
|
|
|
673
|
+
+ (NSDictionary *)extraHeadersWithDatabase:(EXUpdatesDatabase *)database
|
|
674
|
+
config:(EXUpdatesConfig *)config
|
|
675
|
+
launchedUpdate:(nullable EXUpdatesUpdate *)launchedUpdate
|
|
676
|
+
embeddedUpdate:(nullable EXUpdatesUpdate *)embeddedUpdate
|
|
677
|
+
{
|
|
678
|
+
NSError *headersError;
|
|
679
|
+
NSMutableDictionary *extraHeaders = [database serverDefinedHeadersWithScopeKey:config.scopeKey error:&headersError].mutableCopy ?: [NSMutableDictionary new];
|
|
680
|
+
if (headersError) {
|
|
681
|
+
NSLog(@"Error selecting serverDefinedHeaders from database: %@", headersError.localizedDescription);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
if (launchedUpdate) {
|
|
685
|
+
extraHeaders[@"Expo-Current-Update-ID"] = launchedUpdate.updateId.UUIDString.lowercaseString;
|
|
686
|
+
}
|
|
687
|
+
if (embeddedUpdate) {
|
|
688
|
+
extraHeaders[@"Expo-Embedded-Update-ID"] = embeddedUpdate.updateId.UUIDString.lowercaseString;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
return extraHeaders.copy;
|
|
692
|
+
}
|
|
693
|
+
|
|
673
694
|
#pragma mark - NSURLSessionTaskDelegate
|
|
674
695
|
|
|
675
696
|
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
#import <EXUpdates/EXUpdatesRemoteAppLoader.h>
|
|
4
4
|
#import <EXUpdates/EXUpdatesCrypto.h>
|
|
5
|
+
#import <EXUpdates/EXUpdatesEmbeddedAppLoader.h>
|
|
5
6
|
#import <EXUpdates/EXUpdatesFileDownloader.h>
|
|
7
|
+
#import <EXUpdates/EXUpdatesUtils.h>
|
|
6
8
|
#import <ExpoModulesCore/EXUtilities.h>
|
|
7
9
|
|
|
8
10
|
NS_ASSUME_NONNULL_BEGIN
|
|
@@ -22,9 +24,10 @@ static NSString * const EXUpdatesRemoteAppLoaderErrorDomain = @"EXUpdatesRemoteA
|
|
|
22
24
|
- (instancetype)initWithConfig:(EXUpdatesConfig *)config
|
|
23
25
|
database:(EXUpdatesDatabase *)database
|
|
24
26
|
directory:(NSURL *)directory
|
|
27
|
+
launchedUpdate:(nullable EXUpdatesUpdate *)launchedUpdate
|
|
25
28
|
completionQueue:(dispatch_queue_t)completionQueue
|
|
26
29
|
{
|
|
27
|
-
if (self = [super initWithConfig:config database:database directory:directory completionQueue:completionQueue]) {
|
|
30
|
+
if (self = [super initWithConfig:config database:database directory:directory launchedUpdate:launchedUpdate completionQueue:completionQueue]) {
|
|
28
31
|
_downloader = [[EXUpdatesFileDownloader alloc] initWithUpdatesConfig:self.config];
|
|
29
32
|
_completionQueue = completionQueue;
|
|
30
33
|
}
|
|
@@ -65,11 +68,11 @@ static NSString * const EXUpdatesRemoteAppLoaderErrorDomain = @"EXUpdatesRemoteA
|
|
|
65
68
|
};
|
|
66
69
|
|
|
67
70
|
dispatch_async(self.database.databaseQueue, ^{
|
|
68
|
-
|
|
69
|
-
NSDictionary *extraHeaders = [
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
EXUpdatesUpdate *embeddedUpdate = [EXUpdatesEmbeddedAppLoader embeddedManifestWithConfig:self.config database:self.database];
|
|
72
|
+
NSDictionary *extraHeaders = [EXUpdatesFileDownloader extraHeadersWithDatabase:self.database
|
|
73
|
+
config:self.config
|
|
74
|
+
launchedUpdate:self.launchedUpdate
|
|
75
|
+
embeddedUpdate:embeddedUpdate];
|
|
73
76
|
[self->_downloader downloadManifestFromURL:url withDatabase:self.database extraHeaders:extraHeaders successBlock:^(EXUpdatesUpdate *update) {
|
|
74
77
|
self->_remoteUpdate = update;
|
|
75
78
|
[self startLoadingFromManifest:update];
|
|
@@ -102,7 +105,15 @@ static NSString * const EXUpdatesRemoteAppLoaderErrorDomain = @"EXUpdatesRemoteA
|
|
|
102
105
|
extraHeaders:asset.extraRequestHeaders ?: @{}
|
|
103
106
|
successBlock:^(NSData *data, NSURLResponse *response) {
|
|
104
107
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
105
|
-
[
|
|
108
|
+
NSString *hashBase64String = [EXUpdatesUtils base64UrlEncodedSHA256WithData:data];
|
|
109
|
+
if (asset.expectedHash && ![asset.expectedHash isEqualToString:hashBase64String]) {
|
|
110
|
+
NSError *error = [NSError errorWithDomain:EXUpdatesRemoteAppLoaderErrorDomain
|
|
111
|
+
code:1016
|
|
112
|
+
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Asset hash invalid: %@; expectedHash: %@; actualHash: %@", asset.key, asset.expectedHash, hashBase64String]}];
|
|
113
|
+
[self handleAssetDownloadWithError:error asset:asset];
|
|
114
|
+
} else {
|
|
115
|
+
[self handleAssetDownloadWithData:data response:response asset:asset];
|
|
116
|
+
}
|
|
106
117
|
});
|
|
107
118
|
}
|
|
108
119
|
errorBlock:^(NSError *error) {
|
|
@@ -367,7 +367,7 @@ static NSString * const EXUpdatesErrorEventName = @"error";
|
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
_remoteLoadStatus = EXUpdatesRemoteLoadStatusLoading;
|
|
370
|
-
EXUpdatesAppLoader *remoteAppLoader = [[EXUpdatesRemoteAppLoader alloc] initWithConfig:_config database:_database directory:_updatesDirectory completionQueue:_controllerQueue];
|
|
370
|
+
EXUpdatesAppLoader *remoteAppLoader = [[EXUpdatesRemoteAppLoader alloc] initWithConfig:_config database:_database directory:_updatesDirectory launchedUpdate:self.launchedUpdate completionQueue:_controllerQueue];
|
|
371
371
|
[remoteAppLoader loadUpdateFromUrl:_config.updateUrl onManifest:^BOOL(EXUpdatesUpdate *update) {
|
|
372
372
|
return [self->_selectionPolicy shouldLoadNewUpdate:update withLaunchedUpdate:self.launchedUpdate filters:update.manifestFilters];
|
|
373
373
|
} asset:^(EXUpdatesAsset *asset, NSUInteger successfulAssetCount, NSUInteger failedAssetCount, NSUInteger totalAssetCount) {
|
|
@@ -95,7 +95,7 @@ typedef NS_ENUM(NSInteger, EXUpdatesDevLauncherErrorCode) {
|
|
|
95
95
|
[self _setDevelopmentSelectionPolicy];
|
|
96
96
|
[controller setConfigurationInternal:updatesConfiguration];
|
|
97
97
|
|
|
98
|
-
EXUpdatesRemoteAppLoader *loader = [[EXUpdatesRemoteAppLoader alloc] initWithConfig:updatesConfiguration database:controller.database directory:controller.updatesDirectory completionQueue:controller.controllerQueue];
|
|
98
|
+
EXUpdatesRemoteAppLoader *loader = [[EXUpdatesRemoteAppLoader alloc] initWithConfig:updatesConfiguration database:controller.database directory:controller.updatesDirectory launchedUpdate:nil completionQueue:controller.controllerQueue];
|
|
99
99
|
[loader loadUpdateFromUrl:updatesConfiguration.updateUrl onManifest:^BOOL(EXUpdatesUpdate * _Nonnull update) {
|
|
100
100
|
return manifestBlock(update.manifest.rawManifestJSON);
|
|
101
101
|
} asset:^(EXUpdatesAsset * _Nonnull asset, NSUInteger successfulAssetCount, NSUInteger failedAssetCount, NSUInteger totalAssetCount) {
|
|
@@ -104,11 +104,10 @@ EX_EXPORT_METHOD_AS(checkForUpdateAsync,
|
|
|
104
104
|
|
|
105
105
|
__block NSDictionary *extraHeaders;
|
|
106
106
|
dispatch_sync(_updatesService.database.databaseQueue, ^{
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
107
|
+
extraHeaders = [EXUpdatesFileDownloader extraHeadersWithDatabase:self->_updatesService.database
|
|
108
|
+
config:self->_updatesService.config
|
|
109
|
+
launchedUpdate:self->_updatesService.launchedUpdate
|
|
110
|
+
embeddedUpdate:self->_updatesService.embeddedUpdate];
|
|
112
111
|
});
|
|
113
112
|
|
|
114
113
|
EXUpdatesFileDownloader *fileDownloader = [[EXUpdatesFileDownloader alloc] initWithUpdatesConfig:_updatesService.config];
|
|
@@ -146,7 +145,7 @@ EX_EXPORT_METHOD_AS(fetchUpdateAsync,
|
|
|
146
145
|
return;
|
|
147
146
|
}
|
|
148
147
|
|
|
149
|
-
EXUpdatesRemoteAppLoader *remoteAppLoader = [[EXUpdatesRemoteAppLoader alloc] initWithConfig:_updatesService.config database:_updatesService.database directory:_updatesService.directory completionQueue:self.methodQueue];
|
|
148
|
+
EXUpdatesRemoteAppLoader *remoteAppLoader = [[EXUpdatesRemoteAppLoader alloc] initWithConfig:_updatesService.config database:_updatesService.database directory:_updatesService.directory launchedUpdate:_updatesService.launchedUpdate completionQueue:self.methodQueue];
|
|
150
149
|
[remoteAppLoader loadUpdateFromUrl:_updatesService.config.updateUrl onManifest:^BOOL(EXUpdatesUpdate * _Nonnull update) {
|
|
151
150
|
return [self->_updatesService.selectionPolicy shouldLoadNewUpdate:update withLaunchedUpdate:self->_updatesService.launchedUpdate filters:update.manifestFilters];
|
|
152
151
|
} asset:^(EXUpdatesAsset *asset, NSUInteger successfulAssetCount, NSUInteger failedAssetCount, NSUInteger totalAssetCount) {
|
|
@@ -17,6 +17,7 @@ typedef void (^EXUpdatesAppRelaunchCompletionBlock)(BOOL success);
|
|
|
17
17
|
@property (nonatomic, readonly) EXUpdatesSelectionPolicy *selectionPolicy;
|
|
18
18
|
@property (nonatomic, readonly) NSURL *directory;
|
|
19
19
|
|
|
20
|
+
@property (nullable, nonatomic, readonly, strong) EXUpdatesUpdate *embeddedUpdate;
|
|
20
21
|
@property (nullable, nonatomic, readonly, strong) EXUpdatesUpdate *launchedUpdate;
|
|
21
22
|
@property (nullable, nonatomic, readonly, strong) NSDictionary *assetFilesMap;
|
|
22
23
|
@property (nonatomic, readonly, assign) BOOL isUsingEmbeddedAssets;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Copyright 2020-present 650 Industries. All rights reserved.
|
|
2
2
|
|
|
3
3
|
#import <EXUpdates/EXUpdatesAppController.h>
|
|
4
|
+
#import <EXUpdates/EXUpdatesEmbeddedAppLoader.h>
|
|
4
5
|
#import <EXUpdates/EXUpdatesService.h>
|
|
5
6
|
#import <ExpoModulesCore/EXUtilities.h>
|
|
6
7
|
|
|
@@ -35,6 +36,11 @@ EX_REGISTER_MODULE();
|
|
|
35
36
|
return EXUpdatesAppController.sharedInstance.updatesDirectory;
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
- (nullable EXUpdatesUpdate *)embeddedUpdate
|
|
40
|
+
{
|
|
41
|
+
return [EXUpdatesEmbeddedAppLoader embeddedManifestWithConfig:self.config database:self.database];
|
|
42
|
+
}
|
|
43
|
+
|
|
38
44
|
- (nullable EXUpdatesUpdate *)launchedUpdate
|
|
39
45
|
{
|
|
40
46
|
return EXUpdatesAppController.sharedInstance.launchedUpdate;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
#import <EXUpdates/EXUpdatesConfig.h>
|
|
6
6
|
#import <EXUpdates/EXUpdatesFileDownloader.h>
|
|
7
|
+
#import <EXUpdates/EXUpdatesUpdate.h>
|
|
7
8
|
|
|
8
9
|
@interface EXUpdatesFileDownloaderTests : XCTestCase
|
|
9
10
|
|
|
@@ -79,6 +80,52 @@
|
|
|
79
80
|
XCTAssertEqualObjects(@"custom", [actual valueForHTTPHeaderField:@"expo-updates-environment"]);
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
#pragma clang diagnostic push
|
|
84
|
+
#pragma clang diagnostic ignored "-Wnonnull"
|
|
85
|
+
- (void)testGetExtraHeaders
|
|
86
|
+
{
|
|
87
|
+
NSString *launchedUpdateUUIDString = @"7c1d2bd0-f88b-454d-998c-7fa92a924dbf";
|
|
88
|
+
EXUpdatesUpdate *launchedUpdate = [EXUpdatesUpdate updateWithId:[[NSUUID alloc] initWithUUIDString:launchedUpdateUUIDString]
|
|
89
|
+
scopeKey:@"test"
|
|
90
|
+
commitTime:[NSDate date]
|
|
91
|
+
runtimeVersion:@"1.0"
|
|
92
|
+
manifest:nil
|
|
93
|
+
status:0
|
|
94
|
+
keep:YES
|
|
95
|
+
config:nil
|
|
96
|
+
database:nil];
|
|
97
|
+
NSString *embeddedUpdateUUIDString = @"9433b1ed-4006-46b8-8aa7-fdc7eeb203fd";
|
|
98
|
+
EXUpdatesUpdate *embeddedUpdate = [EXUpdatesUpdate updateWithId:[[NSUUID alloc] initWithUUIDString:embeddedUpdateUUIDString]
|
|
99
|
+
scopeKey:@"test"
|
|
100
|
+
commitTime:[NSDate date]
|
|
101
|
+
runtimeVersion:@"1.0"
|
|
102
|
+
manifest:nil
|
|
103
|
+
status:0
|
|
104
|
+
keep:YES
|
|
105
|
+
config:nil
|
|
106
|
+
database:nil];
|
|
107
|
+
NSDictionary *extraHeaders = [EXUpdatesFileDownloader extraHeadersWithDatabase:nil
|
|
108
|
+
config:nil
|
|
109
|
+
launchedUpdate:launchedUpdate
|
|
110
|
+
embeddedUpdate:embeddedUpdate];
|
|
111
|
+
XCTAssertEqualObjects(launchedUpdateUUIDString, extraHeaders[@"Expo-Current-Update-ID"]);
|
|
112
|
+
XCTAssertEqualObjects(embeddedUpdateUUIDString, extraHeaders[@"Expo-Embedded-Update-ID"]);
|
|
113
|
+
}
|
|
114
|
+
#pragma clang diagnostic pop
|
|
115
|
+
|
|
116
|
+
#pragma clang diagnostic push
|
|
117
|
+
#pragma clang diagnostic ignored "-Wnonnull"
|
|
118
|
+
- (void)testGetExtraHeaders_NoLaunchedOrEmbeddedUpdate
|
|
119
|
+
{
|
|
120
|
+
NSDictionary *extraHeaders = [EXUpdatesFileDownloader extraHeadersWithDatabase:nil
|
|
121
|
+
config:nil
|
|
122
|
+
launchedUpdate:nil
|
|
123
|
+
embeddedUpdate:nil];
|
|
124
|
+
XCTAssertNil(extraHeaders[@"Expo-Current-Update-ID"]);
|
|
125
|
+
XCTAssertNil(extraHeaders[@"Expo-Embedded-Update-ID"]);
|
|
126
|
+
}
|
|
127
|
+
#pragma clang diagnostic pop
|
|
128
|
+
|
|
82
129
|
- (void)testAssetExtraHeaders_OverrideOrder
|
|
83
130
|
{
|
|
84
131
|
EXUpdatesConfig *config = [EXUpdatesConfig configWithDictionary:@{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-updates",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Fetches and manages remotely-hosted assets and updates to your app's JS bundle.",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"peerDependencies": {
|
|
65
65
|
"expo": "*"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "0f796e10a2b2f258c40f9d56aa141c372cd63fc1"
|
|
68
68
|
}
|