@revopush/react-native-code-push 2.5.1 → 2.5.2
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/.claude/settings.local.json +5 -1
- package/README.md +2 -1
- package/android/build.gradle +12 -1
- package/android/codepush.gradle +21 -44
- package/android/gradle.properties +2 -0
- package/android/proguard-rules.pro +18 -2
- package/android/settings.gradle +8 -9
- package/android/src/main/java/com/microsoft/codepush/react/BaseBundleStore.java +78 -0
- package/android/src/main/java/com/microsoft/codepush/react/CodePush.java +15 -4
- package/android/src/main/java/com/microsoft/codepush/react/CodePushConstants.java +2 -0
- package/android/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java +74 -18
- package/android/src/main/java/com/microsoft/codepush/react/CodePushUpdateManager.java +56 -11
- package/android/src/main/java/com/microsoft/codepush/react/FileUtils.java +7 -5
- package/docs/setup-android.md +27 -1
- package/ios/CodePush/CodePush.h +14 -0
- package/ios/CodePush/CodePush.m +17 -44
- package/ios/CodePush/CodePushBaseBundleStore.m +104 -0
- package/ios/CodePush/CodePushPackage.m +82 -9
- package/ios/CodePush.xcodeproj/project.pbxproj +6 -0
- package/ios/Frameworks/DiffUpdates.xcframework/ios-arm64/DiffUpdates.framework/DiffUpdates +0 -0
- package/ios/Frameworks/DiffUpdates.xcframework/ios-arm64/DiffUpdates.framework/Headers/DiffUpdates.h +18 -0
- package/ios/Frameworks/DiffUpdates.xcframework/ios-arm64/DiffUpdates.framework/Headers/hpatch_objc.h +23 -0
- package/ios/Frameworks/DiffUpdates.xcframework/ios-arm64/DiffUpdates.framework/Info.plist +0 -0
- package/ios/Frameworks/DiffUpdates.xcframework/ios-arm64_x86_64-simulator/DiffUpdates.framework/DiffUpdates +0 -0
- package/ios/Frameworks/DiffUpdates.xcframework/ios-arm64_x86_64-simulator/DiffUpdates.framework/Headers/DiffUpdates.h +18 -0
- package/ios/Frameworks/DiffUpdates.xcframework/ios-arm64_x86_64-simulator/DiffUpdates.framework/Headers/hpatch_objc.h +23 -0
- package/ios/Frameworks/DiffUpdates.xcframework/ios-arm64_x86_64-simulator/DiffUpdates.framework/Info.plist +0 -0
- package/ios/Frameworks/DiffUpdates.xcframework/ios-arm64_x86_64-simulator/DiffUpdates.framework/_CodeSignature/CodeDirectory +0 -0
- package/ios/Frameworks/DiffUpdates.xcframework/ios-arm64_x86_64-simulator/DiffUpdates.framework/_CodeSignature/CodeResources +12 -9
- package/package.json +10 -14
- package/tsconfig.json +1 -4
- package/typings/react-native-code-push.d.ts +18 -0
- package/ios/Frameworks/DiffUpdates.xcframework/ios-arm64_x86_64-simulator/DiffUpdates.framework/_CodeSignature/CodeRequirements-1 +0 -0
- package/scripts/postlink/android/postlink.js +0 -87
- package/scripts/postlink/ios/postlink.js +0 -116
- package/scripts/postlink/run.js +0 -11
- package/scripts/postunlink/android/postunlink.js +0 -74
- package/scripts/postunlink/ios/postunlink.js +0 -87
- package/scripts/postunlink/run.js +0 -11
- package/scripts/tools/linkToolsAndroid.js +0 -57
- package/scripts/tools/linkToolsIos.js +0 -130
|
@@ -43,6 +43,10 @@ public class CodePushUpdateManager {
|
|
|
43
43
|
return appendPathComponent(getCodePushPath(), CodePushConstants.UNZIPPED_FOLDER_NAME);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
private BaseBundleStore getBaseBundleStore() {
|
|
47
|
+
return new BaseBundleStore(appendPathComponent(getCodePushPath(), CodePushConstants.BASES_FOLDER_NAME));
|
|
48
|
+
}
|
|
49
|
+
|
|
46
50
|
private String getDocumentsDirectory() {
|
|
47
51
|
return mDocumentsDirectory;
|
|
48
52
|
}
|
|
@@ -94,22 +98,22 @@ public class CodePushUpdateManager {
|
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
public String getCurrentPackageBundlePath(String bundleFileName) {
|
|
97
|
-
|
|
98
|
-
|
|
101
|
+
return getPackageBundlePath(getCurrentPackageHash(), bundleFileName);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private String getPackageBundlePath(String packageHash, String bundleFileName) {
|
|
105
|
+
if (packageHash == null) {
|
|
99
106
|
return null;
|
|
100
107
|
}
|
|
101
108
|
|
|
102
|
-
JSONObject
|
|
103
|
-
if (
|
|
109
|
+
JSONObject packageMetadata = getPackage(packageHash);
|
|
110
|
+
if (packageMetadata == null) {
|
|
104
111
|
return null;
|
|
105
112
|
}
|
|
106
113
|
|
|
107
|
-
String relativeBundlePath =
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
} else {
|
|
111
|
-
return appendPathComponent(packageFolder, relativeBundlePath);
|
|
112
|
-
}
|
|
114
|
+
String relativeBundlePath = packageMetadata.optString(CodePushConstants.RELATIVE_BUNDLE_PATH_KEY, null);
|
|
115
|
+
return appendPathComponent(getPackageFolderPath(packageHash),
|
|
116
|
+
relativeBundlePath != null ? relativeBundlePath : bundleFileName);
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
public String getPackageFolderPath(String packageHash) {
|
|
@@ -154,7 +158,47 @@ public class CodePushUpdateManager {
|
|
|
154
158
|
}
|
|
155
159
|
}
|
|
156
160
|
|
|
161
|
+
/** A base bundle on disk, and the package hash it is stored under. */
|
|
162
|
+
private static final class BaseBundle {
|
|
163
|
+
final File file;
|
|
164
|
+
final String packageHash;
|
|
165
|
+
|
|
166
|
+
BaseBundle(File file, String packageHash) {
|
|
167
|
+
this.file = file;
|
|
168
|
+
this.packageHash = packageHash;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** The base named by basePackage: the saved copy, else that package's own bundle. */
|
|
173
|
+
private BaseBundle findBaseBundle(JSONObject updatePackage, String expectedBundleFileName) {
|
|
174
|
+
JSONObject basePackage = updatePackage.optJSONObject(CodePushConstants.BASE_PACKAGE_KEY);
|
|
175
|
+
if (basePackage == null) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
String packageHash = basePackage.optString(CODE_PUSH_PACKAGE_HASH, null);
|
|
179
|
+
if (packageHash == null || packageHash.isEmpty()) {
|
|
180
|
+
// Named a base with no hash: server and SDK disagree on the field name.
|
|
181
|
+
CodePushUtils.log("basePackage has no packageHash; patching against the binary.");
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
File bundle = getBaseBundleStore().lookup(packageHash);
|
|
185
|
+
if (bundle == null) {
|
|
186
|
+
String bundlePath = getPackageBundlePath(packageHash, expectedBundleFileName);
|
|
187
|
+
bundle = bundlePath == null ? null : new File(bundlePath);
|
|
188
|
+
}
|
|
189
|
+
if (bundle == null || !bundle.isFile()) {
|
|
190
|
+
CodePushUtils.log("Base " + packageHash + " not held; patching against the binary.");
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
return new BaseBundle(bundle, packageHash);
|
|
194
|
+
}
|
|
195
|
+
|
|
157
196
|
public void downloadPackage(JSONObject updatePackage, String expectedBundleFileName, DownloadProgressCallback progressCallback, String stringPublicKey) throws IOException {
|
|
197
|
+
BaseBundle base = findBaseBundle(updatePackage, expectedBundleFileName);
|
|
198
|
+
if (base != null) {
|
|
199
|
+
getBaseBundleStore().save(base.file, base.packageHash);
|
|
200
|
+
}
|
|
201
|
+
|
|
158
202
|
UpdatePackage updatePack = fromJson(updatePackage);
|
|
159
203
|
|
|
160
204
|
String newUpdateFolderPath = getPackageFolderPath(updatePack.getNewUpdateHash());
|
|
@@ -164,7 +208,8 @@ public class CodePushUpdateManager {
|
|
|
164
208
|
expectedBundleFileName, // exp bundle filename
|
|
165
209
|
stringPublicKey, // pub key
|
|
166
210
|
updatePack.getNewUpdateHash(), // new update hash
|
|
167
|
-
getCurrentPackage() // current package
|
|
211
|
+
getCurrentPackage(), // current package
|
|
212
|
+
base == null ? null : base.file.getAbsolutePath() // base bundle
|
|
168
213
|
);
|
|
169
214
|
|
|
170
215
|
|
|
@@ -27,11 +27,13 @@ public class FileUtils {
|
|
|
27
27
|
public static void deleteFileOrFolderSilently(File file) {
|
|
28
28
|
if (file.isDirectory()) {
|
|
29
29
|
File[] files = file.listFiles();
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
if (files != null) {
|
|
31
|
+
for (File fileEntry : files) {
|
|
32
|
+
if (fileEntry.isDirectory()) {
|
|
33
|
+
deleteFileOrFolderSilently(fileEntry);
|
|
34
|
+
} else {
|
|
35
|
+
fileEntry.delete();
|
|
36
|
+
}
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
39
|
}
|
package/docs/setup-android.md
CHANGED
|
@@ -18,7 +18,7 @@ In order to integrate CodePush into your Android project, please perform the fol
|
|
|
18
18
|
|
|
19
19
|
2. Update the `MainApplication` file to use CodePush via the following changes:
|
|
20
20
|
|
|
21
|
-
For React Native 0.76
|
|
21
|
+
For React Native 0.76 - 0.82: update the `MainApplication.kt`
|
|
22
22
|
|
|
23
23
|
**Important! : PackageList must be instantiated only one in application lifetime.**
|
|
24
24
|
|
|
@@ -45,6 +45,32 @@ In order to integrate CodePush into your Android project, please perform the fol
|
|
|
45
45
|
}
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
+
For React Native 0.83 and above: update the `MainApplication.kt`
|
|
49
|
+
|
|
50
|
+
```kotlin
|
|
51
|
+
...
|
|
52
|
+
// 1. Import the plugin class.
|
|
53
|
+
import com.microsoft.codepush.react.CodePush
|
|
54
|
+
|
|
55
|
+
class MainApplication : Application(), ReactApplication {
|
|
56
|
+
|
|
57
|
+
override val reactHost: ReactHost by lazy {
|
|
58
|
+
getDefaultReactHost(
|
|
59
|
+
context = applicationContext,
|
|
60
|
+
packageList =
|
|
61
|
+
PackageList(this).packages.apply {
|
|
62
|
+
// Packages that cannot be autolinked yet can be added manually here, for example:
|
|
63
|
+
// add(MyReactNativePackage())
|
|
64
|
+
},
|
|
65
|
+
// 2. Override the jsBundleFilePath in order to let
|
|
66
|
+
// the CodePush runtime determine where to get the JS
|
|
67
|
+
// bundle location from on each app start
|
|
68
|
+
jsBundleFilePath = CodePush.getJSBundleFile(),
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
48
74
|
|
|
49
75
|
3. Add the Deployment key to `strings.xml`:
|
|
50
76
|
|
package/ios/CodePush/CodePush.h
CHANGED
|
@@ -136,6 +136,7 @@
|
|
|
136
136
|
error:(NSError **)error;
|
|
137
137
|
|
|
138
138
|
+ (NSString *)getPackageFolderPath:(NSString *)packageHash;
|
|
139
|
+
+ (NSString *)getCodePushPath;
|
|
139
140
|
|
|
140
141
|
+ (BOOL)installPackage:(NSDictionary *)updatePackage
|
|
141
142
|
removePendingUpdate:(BOOL)removePendingUpdate
|
|
@@ -149,6 +150,19 @@
|
|
|
149
150
|
|
|
150
151
|
@end
|
|
151
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Bundles kept as diff bases, named by the package hash of the release they came from.
|
|
155
|
+
* A base a package folder no longer holds is still findable here.
|
|
156
|
+
*/
|
|
157
|
+
@interface CodePushBaseBundleStore : NSObject
|
|
158
|
+
|
|
159
|
+
/** Path of the retained bundle for this package hash, or nil if the store does not hold it. */
|
|
160
|
+
+ (NSString *)lookup:(NSString *)packageHash;
|
|
161
|
+
|
|
162
|
+
+ (BOOL)save:(NSString *)sourceBundlePath forPackageHash:(NSString *)packageHash;
|
|
163
|
+
|
|
164
|
+
@end
|
|
165
|
+
|
|
152
166
|
@interface CodePushTelemetryManager : NSObject
|
|
153
167
|
|
|
154
168
|
+ (NSDictionary *)getBinaryUpdateReport:(NSString *)appVersion;
|
package/ios/CodePush/CodePush.m
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
#import "CodePush.h"
|
|
21
21
|
|
|
22
|
-
@interface CodePush () <RCTBridgeModule
|
|
22
|
+
@interface CodePush () <RCTBridgeModule>
|
|
23
23
|
@end
|
|
24
24
|
|
|
25
25
|
@implementation CodePush {
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
|
|
33
33
|
// Used to coordinate the dispatching of download progress events to JS.
|
|
34
34
|
long long _latestExpectedContentLength;
|
|
35
|
-
long long
|
|
36
|
-
|
|
35
|
+
long long _latestReceivedContentLength;
|
|
36
|
+
NSTimeInterval _lastProgressEmitTimestamp;
|
|
37
37
|
|
|
38
38
|
BOOL _allowed;
|
|
39
39
|
BOOL _restartInProgress;
|
|
@@ -263,18 +263,6 @@ static NSString *const LatestRollbackCountKey = @"count";
|
|
|
263
263
|
#pragma mark - Private API methods
|
|
264
264
|
|
|
265
265
|
@synthesize methodQueue = _methodQueue;
|
|
266
|
-
@synthesize pauseCallback = _pauseCallback;
|
|
267
|
-
@synthesize paused = _paused;
|
|
268
|
-
|
|
269
|
-
- (void)setPaused:(BOOL)paused
|
|
270
|
-
{
|
|
271
|
-
if (_paused != paused) {
|
|
272
|
-
_paused = paused;
|
|
273
|
-
if (_pauseCallback) {
|
|
274
|
-
_pauseCallback();
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
266
|
|
|
279
267
|
/*
|
|
280
268
|
* This method is used to clear updates that are installed
|
|
@@ -340,7 +328,7 @@ static NSString *const LatestRollbackCountKey = @"count";
|
|
|
340
328
|
@"totalBytes" : [NSNumber
|
|
341
329
|
numberWithLongLong:_latestExpectedContentLength],
|
|
342
330
|
@"receivedBytes" : [NSNumber
|
|
343
|
-
numberWithLongLong:
|
|
331
|
+
numberWithLongLong:_latestReceivedContentLength]
|
|
344
332
|
}];
|
|
345
333
|
}
|
|
346
334
|
|
|
@@ -404,7 +392,6 @@ static NSString *const LatestRollbackCountKey = @"count";
|
|
|
404
392
|
#ifdef DEBUG
|
|
405
393
|
[self clearDebugUpdates];
|
|
406
394
|
#endif
|
|
407
|
-
self.paused = YES;
|
|
408
395
|
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
|
|
409
396
|
NSDictionary *pendingUpdate = [preferences objectForKey:PendingUpdateKey];
|
|
410
397
|
if (pendingUpdate) {
|
|
@@ -546,7 +533,7 @@ static NSString *const LatestRollbackCountKey = @"count";
|
|
|
546
533
|
// file (since Chrome wouldn't support it). Otherwise, update
|
|
547
534
|
// the current bundle URL to point at the latest update
|
|
548
535
|
if ([CodePush isUsingTestConfiguration] || ![super.bridge.bundleURL.scheme hasPrefix:@"http"]) {
|
|
549
|
-
[
|
|
536
|
+
RCTReloadCommandSetBundleURL([CodePush bundleURL]);
|
|
550
537
|
}
|
|
551
538
|
|
|
552
539
|
RCTTriggerReloadCommandListeners(@"react-native-code-push: Restart");
|
|
@@ -727,10 +714,7 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
|
|
|
727
714
|
}
|
|
728
715
|
|
|
729
716
|
if (notifyProgress) {
|
|
730
|
-
|
|
731
|
-
// progress events every frame if the progress is updated.
|
|
732
|
-
_didUpdateProgress = NO;
|
|
733
|
-
self.paused = NO;
|
|
717
|
+
_lastProgressEmitTimestamp = 0;
|
|
734
718
|
}
|
|
735
719
|
|
|
736
720
|
NSString * publicKey = [[CodePushConfig current] publicKey];
|
|
@@ -742,17 +726,21 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
|
|
|
742
726
|
operationQueue:_methodQueue
|
|
743
727
|
// The download is progressing forward
|
|
744
728
|
progressCallback:^(long long expectedContentLength, long long receivedContentLength) {
|
|
745
|
-
|
|
729
|
+
if (!notifyProgress) {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
|
|
746
733
|
_latestExpectedContentLength = expectedContentLength;
|
|
747
|
-
|
|
748
|
-
_didUpdateProgress = YES;
|
|
734
|
+
_latestReceivedContentLength = receivedContentLength;
|
|
749
735
|
|
|
750
|
-
// If the download is completed, stop observing frame
|
|
751
|
-
// updates and synchronously send the last event.
|
|
752
736
|
if (expectedContentLength == receivedContentLength) {
|
|
753
|
-
_didUpdateProgress = NO;
|
|
754
|
-
self.paused = YES;
|
|
755
737
|
[self dispatchDownloadProgressEvent];
|
|
738
|
+
} else {
|
|
739
|
+
NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970];
|
|
740
|
+
if (timestamp - _lastProgressEmitTimestamp > 0.3) {
|
|
741
|
+
_lastProgressEmitTimestamp = timestamp;
|
|
742
|
+
[self dispatchDownloadProgressEvent];
|
|
743
|
+
}
|
|
756
744
|
}
|
|
757
745
|
}
|
|
758
746
|
// The download completed
|
|
@@ -773,9 +761,6 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
|
|
|
773
761
|
[self saveFailedUpdate:mutableUpdatePackage];
|
|
774
762
|
}
|
|
775
763
|
|
|
776
|
-
// Stop observing frame updates if the download fails.
|
|
777
|
-
_didUpdateProgress = NO;
|
|
778
|
-
self.paused = YES;
|
|
779
764
|
reject([NSString stringWithFormat: @"%lu", (long)err.code], err.localizedDescription, err);
|
|
780
765
|
}];
|
|
781
766
|
}
|
|
@@ -1128,16 +1113,4 @@ RCT_EXPORT_METHOD(saveStatusReportForRetry:(NSDictionary *)statusReport)
|
|
|
1128
1113
|
[CodePushTelemetryManager saveStatusReportForRetry:statusReport];
|
|
1129
1114
|
}
|
|
1130
1115
|
|
|
1131
|
-
#pragma mark - RCTFrameUpdateObserver Methods
|
|
1132
|
-
|
|
1133
|
-
- (void)didUpdateFrame:(RCTFrameUpdate *)update
|
|
1134
|
-
{
|
|
1135
|
-
if (!_didUpdateProgress) {
|
|
1136
|
-
return;
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
[self dispatchDownloadProgressEvent];
|
|
1140
|
-
_didUpdateProgress = NO;
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
1116
|
@end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#import "CodePush.h"
|
|
2
|
+
|
|
3
|
+
// CPLog
|
|
4
|
+
#import <DiffUpdates/DiffUpdates.h>
|
|
5
|
+
|
|
6
|
+
@implementation CodePushBaseBundleStore
|
|
7
|
+
|
|
8
|
+
static NSString *const BasesFolderName = @"bases";
|
|
9
|
+
static const NSUInteger MaxSavedBases = 3;
|
|
10
|
+
|
|
11
|
+
+ (NSString *)lookup:(NSString *)packageHash
|
|
12
|
+
{
|
|
13
|
+
NSString *path = [self pathForPackageHash:packageHash];
|
|
14
|
+
return path && [[NSFileManager defaultManager] fileExistsAtPath:path] ? path : nil;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Copies the bundle into the store so it outlives its package folder: installPackage deletes the
|
|
19
|
+
* previous package on every install, but the server can still name that release as a diff base.
|
|
20
|
+
* Never fails an update - a base we could not save is simply one we do not hold.
|
|
21
|
+
*/
|
|
22
|
+
+ (BOOL)save:(NSString *)sourceBundlePath forPackageHash:(NSString *)packageHash
|
|
23
|
+
{
|
|
24
|
+
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
25
|
+
NSString *destinationPath = [self pathForPackageHash:packageHash];
|
|
26
|
+
if (!destinationPath || ![fileManager fileExistsAtPath:sourceBundlePath]) {
|
|
27
|
+
return NO;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if ([fileManager fileExistsAtPath:destinationPath]) {
|
|
31
|
+
[self touch:destinationPath];
|
|
32
|
+
return YES;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Copied aside and renamed: a copy cut short by process death must not leave a truncated file
|
|
36
|
+
// under the hash, which lookup would then hand out forever.
|
|
37
|
+
NSString *tempPath = [destinationPath stringByAppendingPathExtension:@"tmp"];
|
|
38
|
+
[fileManager removeItemAtPath:tempPath error:nil];
|
|
39
|
+
|
|
40
|
+
NSError *error = nil;
|
|
41
|
+
if (![fileManager createDirectoryAtPath:[self basesFolderPath] withIntermediateDirectories:YES attributes:nil error:&error]
|
|
42
|
+
|| ![fileManager copyItemAtPath:sourceBundlePath toPath:tempPath error:&error]
|
|
43
|
+
|| ![fileManager moveItemAtPath:tempPath toPath:destinationPath error:&error]) {
|
|
44
|
+
CPLog(@"Could not save base bundle %@: %@", packageHash, error);
|
|
45
|
+
[fileManager removeItemAtPath:tempPath error:nil];
|
|
46
|
+
return NO;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
[self touch:destinationPath];
|
|
50
|
+
[self evict];
|
|
51
|
+
return YES;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
+ (NSString *)basesFolderPath
|
|
55
|
+
{
|
|
56
|
+
return [[CodePushPackage getCodePushPath] stringByAppendingPathComponent:BasesFolderName];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** An entry is named by its hash alone, so a hash naming anything else is refused. */
|
|
60
|
+
+ (NSString *)pathForPackageHash:(NSString *)packageHash
|
|
61
|
+
{
|
|
62
|
+
if (packageHash.length == 0
|
|
63
|
+
|| [packageHash isEqualToString:@".."]
|
|
64
|
+
|| ![packageHash isEqualToString:packageHash.lastPathComponent]) {
|
|
65
|
+
return nil;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return [[self basesFolderPath] stringByAppendingPathComponent:packageHash];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// A copy keeps the source's timestamp and reuse has to count as use, so the store stamps every
|
|
72
|
+
// save itself. evict orders by this.
|
|
73
|
+
+ (void)touch:(NSString *)path
|
|
74
|
+
{
|
|
75
|
+
[[NSFileManager defaultManager] setAttributes:@{NSFileModificationDate: [NSDate date]}
|
|
76
|
+
ofItemAtPath:path
|
|
77
|
+
error:nil];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Keeps the MaxSavedBases most recently saved entries. */
|
|
81
|
+
+ (void)evict
|
|
82
|
+
{
|
|
83
|
+
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
84
|
+
NSArray<NSURL *> *entries = [fileManager contentsOfDirectoryAtURL:[NSURL fileURLWithPath:[self basesFolderPath]]
|
|
85
|
+
includingPropertiesForKeys:@[NSURLContentModificationDateKey]
|
|
86
|
+
options:0
|
|
87
|
+
error:nil];
|
|
88
|
+
if (entries.count <= MaxSavedBases) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
NSArray<NSURL *> *oldestFirst = [entries sortedArrayUsingComparator:^(NSURL *a, NSURL *b) {
|
|
93
|
+
NSDate *dateA = nil, *dateB = nil;
|
|
94
|
+
[a getResourceValue:&dateA forKey:NSURLContentModificationDateKey error:nil];
|
|
95
|
+
[b getResourceValue:&dateB forKey:NSURLContentModificationDateKey error:nil];
|
|
96
|
+
return [dateA compare:dateB];
|
|
97
|
+
}];
|
|
98
|
+
|
|
99
|
+
for (NSUInteger i = 0; i < oldestFirst.count - MaxSavedBases; i++) {
|
|
100
|
+
[fileManager removeItemAtURL:oldestFirst[i] error:nil];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@end
|
|
@@ -90,12 +90,26 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
|
|
90
90
|
NSString *singleUrl = updatePackage[@"downloadUrl"] ?: updatePackage[@"download_url"];
|
|
91
91
|
|
|
92
92
|
__block BOOL cameFromDiffHandler = (bundleDiffUrl.length > 0);
|
|
93
|
-
|
|
93
|
+
__block BOOL fallbackStarted = NO;
|
|
94
|
+
// Assigned below. commonDone reaches back into it to retry a diff that verified badly.
|
|
95
|
+
__block void (^startFullDownload)(void) = nil;
|
|
94
96
|
|
|
95
97
|
void (^commonDone)(BOOL) = ^(BOOL isZip) {
|
|
96
98
|
NSError *error = nil;
|
|
97
99
|
NSString * unzippedFolderPath = [CodePushPackage getUnzippedFolderPath];
|
|
98
100
|
NSMutableDictionary * mutableUpdatePackage = [updatePackage mutableCopy];
|
|
101
|
+
|
|
102
|
+
// A diff patched against the wrong base still produces a well-formed folder, just one
|
|
103
|
+
// holding the wrong bytes. Retry as a full download rather than failing the update.
|
|
104
|
+
BOOL (^recoverFromIntegrityFailure)(void) = ^BOOL {
|
|
105
|
+
if (!cameFromDiffHandler || fallbackStarted || singleUrl.length == 0) {
|
|
106
|
+
return NO;
|
|
107
|
+
}
|
|
108
|
+
CPLog(@"Falling back to a full download.");
|
|
109
|
+
startFullDownload();
|
|
110
|
+
return YES;
|
|
111
|
+
};
|
|
112
|
+
|
|
99
113
|
if (isZip) {
|
|
100
114
|
if ([[NSFileManager defaultManager] fileExistsAtPath:unzippedFolderPath]) {
|
|
101
115
|
// This removes any unzipped download data that could have been left
|
|
@@ -257,6 +271,9 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
|
|
257
271
|
expectedHash:newUpdateHash
|
|
258
272
|
error:&error]) {
|
|
259
273
|
CPLog(@"The update contents failed the data integrity check.");
|
|
274
|
+
if (recoverFromIntegrityFailure()) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
260
277
|
if (!error) {
|
|
261
278
|
error = [CodePushErrorUtils errorWithMessage:@"The update contents failed the data integrity check."];
|
|
262
279
|
}
|
|
@@ -305,6 +322,9 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
|
|
305
322
|
expectedHash:newUpdateHash
|
|
306
323
|
error:&error]) {
|
|
307
324
|
CPLog(@"The update contents failed the data integrity check.");
|
|
325
|
+
if (recoverFromIntegrityFailure()) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
308
328
|
if (!error) {
|
|
309
329
|
error = [CodePushErrorUtils errorWithMessage:@"The update contents failed the data integrity check."];
|
|
310
330
|
}
|
|
@@ -347,15 +367,16 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
|
|
347
367
|
}
|
|
348
368
|
};
|
|
349
369
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
void (^startFullDownload)(void) = ^{
|
|
370
|
+
startFullDownload = ^{
|
|
353
371
|
if (fallbackStarted) return;
|
|
354
372
|
fallbackStarted = YES;
|
|
355
373
|
|
|
356
374
|
NSError *cleanupErr = nil;
|
|
357
375
|
[[NSFileManager defaultManager] removeItemAtPath:downloadFilePath error:&cleanupErr];
|
|
358
376
|
[[NSFileManager defaultManager] removeItemAtPath:[CodePushPackage getUnzippedFolderPath] error:nil];
|
|
377
|
+
// A diff that got as far as the integrity check already assembled this folder. Whatever it
|
|
378
|
+
// left behind is wrong, and copyEntriesInFolder overlays rather than replaces.
|
|
379
|
+
[[NSFileManager defaultManager] removeItemAtPath:newUpdateFolderPath error:nil];
|
|
359
380
|
|
|
360
381
|
CodePushDownloadHandler *downloadHandler = [[CodePushDownloadHandler alloc]
|
|
361
382
|
init:downloadFilePath
|
|
@@ -378,8 +399,12 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
|
|
378
399
|
|
|
379
400
|
|
|
380
401
|
if (cameFromDiffHandler) {
|
|
381
|
-
|
|
382
|
-
NSString *
|
|
402
|
+
// Retained now: this is the last moment we know the release naming it is still on disk.
|
|
403
|
+
NSString *baseHash = [self basePackageHash:updatePackage];
|
|
404
|
+
NSString *baseBundlePath = [self findBaseBundleForPackageHash:baseHash];
|
|
405
|
+
if (baseBundlePath) {
|
|
406
|
+
[CodePushBaseBundleStore save:baseBundlePath forPackageHash:baseHash];
|
|
407
|
+
}
|
|
383
408
|
|
|
384
409
|
NSError *pkgErr = nil;
|
|
385
410
|
NSDictionary *currentPackage = [CodePushPackage getCurrentPackage:&pkgErr];
|
|
@@ -396,10 +421,13 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
|
|
396
421
|
|
|
397
422
|
CPLog(@"Diff download handler");
|
|
398
423
|
|
|
399
|
-
[diffHandler downloadWithDiffUrl:bundleDiffUrl
|
|
424
|
+
[diffHandler downloadWithDiffUrl:bundleDiffUrl
|
|
425
|
+
assetsUrl:assetsUrl
|
|
426
|
+
assetHash:assetHash
|
|
427
|
+
nativeAssetHash:nativeAssetHash
|
|
428
|
+
currentPackage:currentPackage
|
|
429
|
+
baseBundlePath:baseBundlePath];
|
|
400
430
|
} else {
|
|
401
|
-
NSString *singleUrl = updatePackage[@"downloadUrl"] ?: updatePackage[@"download_url"];
|
|
402
|
-
|
|
403
431
|
CodePushDownloadHandler *downloadHandler = [[CodePushDownloadHandler alloc]
|
|
404
432
|
init:downloadFilePath
|
|
405
433
|
operationQueue:operationQueue
|
|
@@ -540,6 +568,51 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
|
|
540
568
|
return [[self getCodePushPath] stringByAppendingPathComponent:packageHash];
|
|
541
569
|
}
|
|
542
570
|
|
|
571
|
+
/** The package hash the server named as the diff base, or nil if it named none we can use. */
|
|
572
|
+
+ (NSString *)basePackageHash:(NSDictionary *)updatePackage
|
|
573
|
+
{
|
|
574
|
+
NSDictionary *basePackage = updatePackage[@"basePackage"];
|
|
575
|
+
if (![basePackage isKindOfClass:NSDictionary.class]) {
|
|
576
|
+
return nil;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
NSString *packageHash = basePackage[@"packageHash"];
|
|
580
|
+
if (packageHash.length == 0) {
|
|
581
|
+
// Named a base with no hash: server and SDK disagree on the field name.
|
|
582
|
+
CPLog(@"basePackage has no packageHash. Patching against the binary.");
|
|
583
|
+
return nil;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return packageHash;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* The bundle to patch against: the retained copy of that release, else the release's own bundle
|
|
591
|
+
* while its package folder still exists. nil means patch against the binary's bundle instead.
|
|
592
|
+
*/
|
|
593
|
+
+ (NSString *)findBaseBundleForPackageHash:(NSString *)packageHash
|
|
594
|
+
{
|
|
595
|
+
if (packageHash.length == 0) {
|
|
596
|
+
return nil;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
NSString *bundlePath = [CodePushBaseBundleStore lookup:packageHash];
|
|
600
|
+
if (bundlePath) {
|
|
601
|
+
return bundlePath;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
NSString *relativeBundlePath = [self getPackage:packageHash error:nil][RelativeBundlePathKey];
|
|
605
|
+
bundlePath = relativeBundlePath.length > 0
|
|
606
|
+
? [[self getPackageFolderPath:packageHash] stringByAppendingPathComponent:relativeBundlePath]
|
|
607
|
+
: nil;
|
|
608
|
+
if (bundlePath && [[NSFileManager defaultManager] fileExistsAtPath:bundlePath]) {
|
|
609
|
+
return bundlePath;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
CPLog(@"Base %@ not held. Patching against the binary.", packageHash);
|
|
613
|
+
return nil;
|
|
614
|
+
}
|
|
615
|
+
|
|
543
616
|
+ (NSDictionary *)getPreviousPackage:(NSError **)error
|
|
544
617
|
{
|
|
545
618
|
NSString *packageHash = [self getPreviousPackageHash:error];
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
6463C82E1EBA0CFB0095B8CD /* CodePush.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* CodePush.m */; };
|
|
82
82
|
6463C82F1EBA0CFB0095B8CD /* CodePushConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 81D51F391B6181C2000DA084 /* CodePushConfig.m */; };
|
|
83
83
|
6463C8321EBA0CFB0095B8CD /* CodePushPackage.m in Sources */ = {isa = PBXBuildFile; fileRef = 810D4E6C1B96935000B397E9 /* CodePushPackage.m */; };
|
|
84
|
+
BA5EB00D0000000000000003 /* CodePushBaseBundleStore.m in Sources */ = {isa = PBXBuildFile; fileRef = BA5EB00D0000000000000001 /* CodePushBaseBundleStore.m */; };
|
|
84
85
|
6463C8331EBA0CFB0095B8CD /* CodePushTelemetryManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5421FE301C58AD5A00986A55 /* CodePushTelemetryManager.m */; };
|
|
85
86
|
6463C8341EBA0CFB0095B8CD /* CodePushUpdateUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 540D20111C7684FE00D6EF41 /* CodePushUpdateUtils.m */; };
|
|
86
87
|
6463C8351EBA0CFB0095B8CD /* RCTConvert+CodePushInstallMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m */; };
|
|
@@ -88,6 +89,7 @@
|
|
|
88
89
|
6463C8451EBA0D1F0095B8CD /* CodePush.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 13BE3DEC1AC21097009241FE /* CodePush.h */; };
|
|
89
90
|
6463C8471EBA0D290095B8CD /* CodePush.h in Headers */ = {isa = PBXBuildFile; fileRef = 13BE3DEC1AC21097009241FE /* CodePush.h */; };
|
|
90
91
|
810D4E6D1B96935000B397E9 /* CodePushPackage.m in Sources */ = {isa = PBXBuildFile; fileRef = 810D4E6C1B96935000B397E9 /* CodePushPackage.m */; };
|
|
92
|
+
BA5EB00D0000000000000002 /* CodePushBaseBundleStore.m in Sources */ = {isa = PBXBuildFile; fileRef = BA5EB00D0000000000000001 /* CodePushBaseBundleStore.m */; };
|
|
91
93
|
81D51F3A1B6181C2000DA084 /* CodePushConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 81D51F391B6181C2000DA084 /* CodePushConfig.m */; };
|
|
92
94
|
8482F84C1E24C58300F793DB /* CodePush.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 13BE3DEC1AC21097009241FE /* CodePush.h */; };
|
|
93
95
|
8482F84E1E24C66300F793DB /* CodePush.h in Headers */ = {isa = PBXBuildFile; fileRef = 13BE3DEC1AC21097009241FE /* CodePush.h */; };
|
|
@@ -210,6 +212,7 @@
|
|
|
210
212
|
540D20111C7684FE00D6EF41 /* CodePushUpdateUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CodePushUpdateUtils.m; path = CodePush/CodePushUpdateUtils.m; sourceTree = "<group>"; };
|
|
211
213
|
5421FE301C58AD5A00986A55 /* CodePushTelemetryManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CodePushTelemetryManager.m; path = CodePush/CodePushTelemetryManager.m; sourceTree = "<group>"; };
|
|
212
214
|
810D4E6C1B96935000B397E9 /* CodePushPackage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CodePushPackage.m; path = CodePush/CodePushPackage.m; sourceTree = "<group>"; };
|
|
215
|
+
BA5EB00D0000000000000001 /* CodePushBaseBundleStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CodePushBaseBundleStore.m; path = CodePush/CodePushBaseBundleStore.m; sourceTree = "<group>"; };
|
|
213
216
|
81D51F391B6181C2000DA084 /* CodePushConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CodePushConfig.m; path = CodePush/CodePushConfig.m; sourceTree = "<group>"; };
|
|
214
217
|
F85736731F4F03BF00C9C00A /* MF_Base64Additions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MF_Base64Additions.h; sourceTree = "<group>"; };
|
|
215
218
|
F85736741F4F03BF00C9C00A /* MF_Base64Additions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MF_Base64Additions.m; sourceTree = "<group>"; };
|
|
@@ -370,6 +373,7 @@
|
|
|
370
373
|
13BE3DED1AC21097009241FE /* CodePush.m */,
|
|
371
374
|
81D51F391B6181C2000DA084 /* CodePushConfig.m */,
|
|
372
375
|
810D4E6C1B96935000B397E9 /* CodePushPackage.m */,
|
|
376
|
+
BA5EB00D0000000000000001 /* CodePushBaseBundleStore.m */,
|
|
373
377
|
5421FE301C58AD5A00986A55 /* CodePushTelemetryManager.m */,
|
|
374
378
|
540D20111C7684FE00D6EF41 /* CodePushUpdateUtils.m */,
|
|
375
379
|
1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m */,
|
|
@@ -763,6 +767,7 @@
|
|
|
763
767
|
F88664511F4AD1EE0036D01B /* JWTAlgorithmNone.m in Sources */,
|
|
764
768
|
3221E4612C8ABE1300268379 /* mz_zip_rw.c in Sources */,
|
|
765
769
|
810D4E6D1B96935000B397E9 /* CodePushPackage.m in Sources */,
|
|
770
|
+
BA5EB00D0000000000000002 /* CodePushBaseBundleStore.m in Sources */,
|
|
766
771
|
3221E4552C8ABE1300268379 /* mz_strm_pkcrypt.c in Sources */,
|
|
767
772
|
F88664531F4AD1EE0036D01B /* JWTAlgorithmESBase.m in Sources */,
|
|
768
773
|
3221E4532C8ABE1300268379 /* mz_strm_os_posix.c in Sources */,
|
|
@@ -783,6 +788,7 @@
|
|
|
783
788
|
6463C82F1EBA0CFB0095B8CD /* CodePushConfig.m in Sources */,
|
|
784
789
|
3221E46E2C8ABE1300268379 /* mz_crypt.c in Sources */,
|
|
785
790
|
6463C8321EBA0CFB0095B8CD /* CodePushPackage.m in Sources */,
|
|
791
|
+
BA5EB00D0000000000000003 /* CodePushBaseBundleStore.m in Sources */,
|
|
786
792
|
6463C8331EBA0CFB0095B8CD /* CodePushTelemetryManager.m in Sources */,
|
|
787
793
|
6463C8341EBA0CFB0095B8CD /* CodePushUpdateUtils.m in Sources */,
|
|
788
794
|
3221E45C2C8ABE1300268379 /* mz_strm_zlib.c in Sources */,
|
|
Binary file
|
package/ios/Frameworks/DiffUpdates.xcframework/ios-arm64/DiffUpdates.framework/Headers/DiffUpdates.h
CHANGED
|
@@ -8,6 +8,12 @@ FOUNDATION_EXPORT const unsigned char DiffUpdatesVersionString[];
|
|
|
8
8
|
|
|
9
9
|
// In this header, you should import all the public headers of your framework using statements like #import <DiffUpdates/PublicHeader.h>
|
|
10
10
|
|
|
11
|
+
#if __has_include(<DiffUpdates/hpatch_objc.h>)
|
|
12
|
+
#import <DiffUpdates/hpatch_objc.h>
|
|
13
|
+
#else
|
|
14
|
+
#import "hpatch_objc.h"
|
|
15
|
+
#endif
|
|
16
|
+
|
|
11
17
|
@interface CodePushDownloadHandler : NSObject <NSURLConnectionDelegate>
|
|
12
18
|
|
|
13
19
|
@property (strong) NSOutputStream *outputFileStream;
|
|
@@ -43,6 +49,18 @@ FOUNDATION_EXPORT const unsigned char DiffUpdatesVersionString[];
|
|
|
43
49
|
doneCallback:(void (^)(BOOL))doneCallback
|
|
44
50
|
failCallback:(void (^)(NSError *err))failCallback;
|
|
45
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Patches against baseBundlePath, or against the binary's own bundle when it is nil.
|
|
54
|
+
* The patched bundle always keeps the binary bundle's file name, whatever the base was.
|
|
55
|
+
*/
|
|
56
|
+
- (void)downloadWithDiffUrl:(NSString *)diffUrl
|
|
57
|
+
assetsUrl:(NSString * _Nullable)assetsUrl
|
|
58
|
+
assetHash:(NSString * _Nullable)assetHash
|
|
59
|
+
nativeAssetHash:(NSString * _Nullable)nativeAssetHash
|
|
60
|
+
currentPackage:(NSDictionary * _Nullable)currentPackage
|
|
61
|
+
baseBundlePath:(NSString * _Nullable)baseBundlePath;
|
|
62
|
+
|
|
63
|
+
/** Patches against the binary's own bundle. Kept so an older SDK links against a newer framework. */
|
|
46
64
|
- (void)downloadWithDiffUrl:(NSString *)diffUrl
|
|
47
65
|
assetsUrl:(NSString * _Nullable)assetsUrl
|
|
48
66
|
assetHash:(NSString * _Nullable)assetHash
|