@@ -76,32 +76,32 @@ + (NSURL *)bundleURLForResource:(NSString *)resourceName
76
76
{
77
77
bundleResourceName = resourceName;
78
78
bundleResourceExtension = resourceExtension;
79
-
79
+
80
80
[self ensureBinaryBundleExists ];
81
-
81
+
82
82
NSString *logMessageFormat = @" Loading JS bundle from %@ " ;
83
-
83
+
84
84
NSError *error;
85
85
NSString *packageFile = [CodePushPackage getCurrentPackageBundlePath: &error];
86
86
NSURL *binaryBundleURL = [self binaryBundleURL ];
87
-
87
+
88
88
if (error || !packageFile) {
89
89
NSLog (logMessageFormat, binaryBundleURL);
90
90
isRunningBinaryVersion = YES ;
91
91
return binaryBundleURL;
92
92
}
93
-
93
+
94
94
NSString *binaryAppVersion = [[CodePushConfig current ] appVersion ];
95
95
NSDictionary *currentPackageMetadata = [CodePushPackage getCurrentPackage: &error];
96
96
if (error || !currentPackageMetadata) {
97
97
NSLog (logMessageFormat, binaryBundleURL);
98
98
isRunningBinaryVersion = YES ;
99
99
return binaryBundleURL;
100
100
}
101
-
101
+
102
102
NSString *packageDate = [currentPackageMetadata objectForKey: BinaryBundleDateKey];
103
103
NSString *packageAppVersion = [currentPackageMetadata objectForKey: AppVersionKey];
104
-
104
+
105
105
if ([[CodePushUpdateUtils modifiedDateStringOfFileAtURL: binaryBundleURL] isEqualToString: packageDate] && ([CodePush isUsingTestConfiguration ] ||[binaryAppVersion isEqualToString: packageAppVersion])) {
106
106
// Return package file because it is newer than the app store binary's JS bundle
107
107
NSURL *packageUrl = [[NSURL alloc ] initFileURLWithPath: packageFile];
@@ -113,11 +113,11 @@ + (NSURL *)bundleURLForResource:(NSString *)resourceName
113
113
#ifndef DEBUG
114
114
isRelease = YES ;
115
115
#endif
116
-
116
+
117
117
if (isRelease || ![binaryAppVersion isEqualToString: packageAppVersion]) {
118
118
[CodePush clearUpdates ];
119
119
}
120
-
120
+
121
121
NSLog (logMessageFormat, binaryBundleURL);
122
122
isRunningBinaryVersion = YES ;
123
123
return binaryBundleURL;
@@ -172,6 +172,29 @@ + (void)setUsingTestConfiguration:(BOOL)shouldUseTestConfiguration
172
172
@synthesize bridge = _bridge;
173
173
@synthesize methodQueue = _methodQueue;
174
174
175
+ /*
176
+ * This method is used to clear updates that are installed
177
+ * under a different app version and hence don't apply anymore,
178
+ * during a debug run configuration and when the bridge is
179
+ * running the JS bundle from the dev server.
180
+ */
181
+ - (void )clearDebugUpdates
182
+ {
183
+ dispatch_async (dispatch_get_main_queue (), ^{
184
+ if ([_bridge.bundleURL.scheme hasPrefix: @" http" ]) {
185
+ NSError *error;
186
+ NSString *binaryAppVersion = [[CodePushConfig current ] appVersion ];
187
+ NSDictionary *currentPackageMetadata = [CodePushPackage getCurrentPackage: &error];
188
+ if (currentPackageMetadata) {
189
+ NSString *packageAppVersion = [currentPackageMetadata objectForKey: AppVersionKey];
190
+ if (![binaryAppVersion isEqualToString: packageAppVersion]) {
191
+ [CodePush clearUpdates ];
192
+ }
193
+ }
194
+ }
195
+ });
196
+ }
197
+
175
198
/*
176
199
* This method is used by the React Native bridge to allow
177
200
* our plugin to expose constants to the JS-side. In our case
@@ -186,7 +209,7 @@ - (NSDictionary *)constantsToExport
186
209
@" codePushInstallModeOnNextRestart" :@(CodePushInstallModeOnNextRestart),
187
210
@" codePushInstallModeImmediate" : @(CodePushInstallModeImmediate),
188
211
@" codePushInstallModeOnNextResume" : @(CodePushInstallModeOnNextResume),
189
-
212
+
190
213
@" codePushUpdateStateRunning" : @(CodePushUpdateStateRunning),
191
214
@" codePushUpdateStatePending" : @(CodePushUpdateStatePending),
192
215
@" codePushUpdateStateLatest" : @(CodePushUpdateStateLatest)
@@ -208,35 +231,35 @@ + (void)ensureBinaryBundleExists
208
231
{
209
232
if (![self binaryBundleURL ]) {
210
233
NSString *errorMessage;
211
-
234
+
212
235
#if TARGET_IPHONE_SIMULATOR
213
236
errorMessage = @" React Native doesn't generate your app's JS bundle by default when deploying to the simulator. "
214
237
" If you'd like to test CodePush using the simulator, you can do one of three things depending on your React "
215
238
" Native version and/or preferred workflow:\n\n "
216
-
239
+
217
240
" 1. Update your AppDelegate.m file to load the JS bundle from the packager instead of from CodePush. "
218
241
" You can still test your CodePush update experience using this workflow (debug builds only).\n\n "
219
-
242
+
220
243
" 2. Force the JS bundle to be generated in simulator builds by removing the if block that echoes "
221
244
" \" Skipping bundling for Simulator platform\" in the \" node_modules/react-native/packager/react-native-xcode.sh\" file.\n\n "
222
-
245
+
223
246
" 3. Deploy a release build to the simulator, which unlike debug builds, will generate the JS bundle (React Native >=0.22.0 only)." ;
224
247
#else
225
248
errorMessage = [NSString stringWithFormat: @" The specified JS bundle file wasn't found within the app's binary. Is \" %@ \" the correct file name?" , [bundleResourceName stringByAppendingPathExtension: bundleResourceExtension]];
226
249
#endif
227
-
250
+
228
251
RCTFatal ([CodePushErrorUtils errorWithMessage: errorMessage]);
229
252
}
230
253
}
231
254
232
255
- (instancetype )init
233
256
{
234
257
self = [super init ];
235
-
258
+
236
259
if (self) {
237
260
[self initializeUpdateAfterRestart ];
238
261
}
239
-
262
+
240
263
return self;
241
264
}
242
265
@@ -247,6 +270,10 @@ - (instancetype)init
247
270
*/
248
271
- (void )initializeUpdateAfterRestart
249
272
{
273
+ #ifdef DEBUG
274
+ [self clearDebugUpdates ];
275
+ #endif
276
+
250
277
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults ];
251
278
NSDictionary *pendingUpdate = [preferences objectForKey: PendingUpdateKey];
252
279
if (pendingUpdate) {
@@ -291,7 +318,7 @@ - (BOOL)isFailedHash:(NSString*)packageHash
291
318
}
292
319
}
293
320
}
294
-
321
+
295
322
return NO ;
296
323
}
297
324
}
@@ -305,13 +332,13 @@ - (BOOL)isPendingUpdate:(NSString*)packageHash
305
332
{
306
333
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults ];
307
334
NSDictionary *pendingUpdate = [preferences objectForKey: PendingUpdateKey];
308
-
335
+
309
336
// If there is a pending update whose "state" isn't loading, then we consider it "pending".
310
337
// Additionally, if a specific hash was provided, we ensure it matches that of the pending update.
311
338
BOOL updateIsPending = pendingUpdate &&
312
339
[pendingUpdate[PendingUpdateIsLoadingKey] boolValue ] == NO &&
313
340
(!packageHash || [pendingUpdate[PendingUpdateHashKey] isEqualToString: packageHash]);
314
-
341
+
315
342
return updateIsPending;
316
343
}
317
344
@@ -332,7 +359,7 @@ - (void)loadBundle
332
359
if ([CodePush isUsingTestConfiguration ] || ![_bridge.bundleURL.scheme hasPrefix: @" http" ]) {
333
360
[_bridge setValue: [CodePush bundleURL ] forKey: @" bundleURL" ];
334
361
}
335
-
362
+
336
363
[_bridge reload ];
337
364
});
338
365
}
@@ -348,10 +375,10 @@ - (void)rollbackPackage
348
375
{
349
376
NSError *error;
350
377
NSDictionary *failedPackage = [CodePushPackage getCurrentPackage: &error];
351
-
378
+
352
379
// Write the current package's metadata to the "failed list"
353
380
[self saveFailedUpdate: failedPackage];
354
-
381
+
355
382
// Rollback to the previous version and de-register the new update
356
383
[CodePushPackage rollbackPackage ];
357
384
[CodePush removePendingUpdate ];
@@ -374,7 +401,7 @@ - (void)saveFailedUpdate:(NSDictionary *)failedPackage
374
401
// objects, regardless if you stored something mutable.
375
402
failedUpdates = [failedUpdates mutableCopy ];
376
403
}
377
-
404
+
378
405
[failedUpdates addObject: failedPackage];
379
406
[preferences setObject: failedUpdates forKey: FailedUpdatesKey];
380
407
[preferences synchronize ];
@@ -416,7 +443,7 @@ - (void)savePendingUpdate:(NSString *)packageHash
416
443
NSDictionary *pendingUpdate = [[NSDictionary alloc ] initWithObjectsAndKeys:
417
444
packageHash,PendingUpdateHashKey,
418
445
[NSNumber numberWithBool: isLoading],PendingUpdateIsLoadingKey, nil ];
419
-
446
+
420
447
[preferences setObject: pendingUpdate forKey: PendingUpdateKey];
421
448
[preferences synchronize ];
422
449
}
@@ -457,7 +484,7 @@ - (void)applicationWillResignActive
457
484
[mutableUpdatePackage setValue: [CodePushUpdateUtils modifiedDateStringOfFileAtURL: binaryBundleURL]
458
485
forKey: BinaryBundleDateKey];
459
486
}
460
-
487
+
461
488
[CodePushPackage
462
489
downloadPackage: mutableUpdatePackage
463
490
expectedBundleFileName: [bundleResourceName stringByAppendingPathExtension: bundleResourceExtension]
@@ -478,11 +505,11 @@ - (void)applicationWillResignActive
478
505
dispatch_async (_methodQueue, ^{
479
506
NSError *err;
480
507
NSDictionary *newPackage = [CodePushPackage getPackage: mutableUpdatePackage[PackageHashKey] error: &err];
481
-
508
+
482
509
if (err) {
483
510
return reject ([NSString stringWithFormat: @" %lu " , (long )err.code], err.localizedDescription , err);
484
511
}
485
-
512
+
486
513
resolve (newPackage);
487
514
});
488
515
}
@@ -492,7 +519,7 @@ - (void)applicationWillResignActive
492
519
if ([CodePushErrorUtils isCodePushError: err]) {
493
520
[self saveFailedUpdate: mutableUpdatePackage];
494
521
}
495
-
522
+
496
523
reject ([NSString stringWithFormat: @" %lu " , (long )err.code], err.localizedDescription , err);
497
524
});
498
525
}];
@@ -517,21 +544,21 @@ - (void)applicationWillResignActive
517
544
resolve (configuration);
518
545
return ;
519
546
}
520
-
547
+
521
548
if (binaryHash == nil ) {
522
549
// The hash was not generated either due to a previous unknown error or the fact that
523
550
// the React Native assets were not bundled in the binary (e.g. during dev/simulator)
524
551
// builds.
525
552
resolve (configuration);
526
553
return ;
527
554
}
528
-
555
+
529
556
NSMutableDictionary *mutableConfiguration = [configuration mutableCopy ];
530
557
[mutableConfiguration setObject: binaryHash forKey: PackageHashKey];
531
558
resolve (mutableConfiguration);
532
559
return ;
533
560
}
534
-
561
+
535
562
resolve (configuration);
536
563
}
537
564
@@ -553,10 +580,10 @@ - (void)applicationWillResignActive
553
580
// wanted to retrieve the pending or running update.
554
581
return resolve (nil );
555
582
}
556
-
583
+
557
584
// We have a CodePush update, so let's see if it's currently in a pending state.
558
585
BOOL currentUpdateIsPending = [self isPendingUpdate: [package objectForKey: PackageHashKey]];
559
-
586
+
560
587
if (updateState == CodePushUpdateStatePending && !currentUpdateIsPending) {
561
588
// The caller wanted a pending update
562
589
// but there isn't currently one.
@@ -576,7 +603,7 @@ - (void)applicationWillResignActive
576
603
// disk that is not actually running.
577
604
[package setObject: @(YES ) forKey: @" _isDebugOnly" ];
578
605
}
579
-
606
+
580
607
// Enable differentiating pending vs. non-pending updates
581
608
[package setObject: @(currentUpdateIsPending) forKey: PackageIsPendingKey];
582
609
resolve (package);
@@ -596,16 +623,16 @@ - (void)applicationWillResignActive
596
623
[CodePushPackage installPackage: updatePackage
597
624
removePendingUpdate: [self isPendingUpdate: nil ]
598
625
error: &error];
599
-
626
+
600
627
if (error) {
601
628
reject ([NSString stringWithFormat: @" %lu " , (long )error.code], error.localizedDescription , error);
602
629
} else {
603
630
[self savePendingUpdate: updatePackage[PackageHashKey]
604
631
isLoading: NO ];
605
-
632
+
606
633
if (installMode == CodePushInstallModeOnNextResume) {
607
634
_minimumBackgroundDuration = minimumBackgroundDuration;
608
-
635
+
609
636
if (!_hasResumeListener) {
610
637
// Ensure we do not add the listener twice.
611
638
// Register for app resume notifications so that we
@@ -614,16 +641,16 @@ - (void)applicationWillResignActive
614
641
selector: @selector (applicationWillEnterForeground )
615
642
name: UIApplicationWillEnterForegroundNotification
616
643
object: [UIApplication sharedApplication ]];
617
-
644
+
618
645
[[NSNotificationCenter defaultCenter ] addObserver: self
619
646
selector: @selector (applicationWillResignActive )
620
647
name: UIApplicationWillResignActiveNotification
621
648
object: [UIApplication sharedApplication ]];
622
-
649
+
623
650
_hasResumeListener = YES ;
624
651
}
625
652
}
626
-
653
+
627
654
// Signal to JS that the update has been applied.
628
655
resolve (nil );
629
656
}
@@ -654,7 +681,7 @@ - (void)applicationWillResignActive
654
681
&& nil != packageHash
655
682
&& [packageHash length ] > 0
656
683
&& [packageHash isEqualToString: [CodePushPackage getCurrentPackageHash: &error]];
657
-
684
+
658
685
resolve (@(isFirstRun));
659
686
}
660
687
@@ -701,7 +728,7 @@ - (void)applicationWillResignActive
701
728
*/
702
729
RCT_EXPORT_METHOD (getNewStatusReport:(RCTPromiseResolveBlock)resolve
703
730
rejecter:(RCTPromiseRejectBlock)reject)
704
- {
731
+ {
705
732
if (needToReportRollback) {
706
733
needToReportRollback = NO ;
707
734
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults ];
@@ -725,7 +752,7 @@ - (void)applicationWillResignActive
725
752
resolve ([CodePushTelemetryManager getBinaryUpdateReport: appVersion]);
726
753
return ;
727
754
}
728
-
755
+
729
756
resolve (nil );
730
757
}
731
758
0 commit comments