Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit c56a6f2

Browse files
committed
Restart on resume
1 parent 92112ab commit c56a6f2

File tree

6 files changed

+115
-36
lines changed

6 files changed

+115
-36
lines changed

CodePush.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,10 @@ failCallback:(void (^)(NSError *err))failCallback;
7070

7171
+ (void)rollbackPackage;
7272

73-
@end
73+
@end
74+
75+
typedef NS_ENUM(NSInteger, CodePushRestartMode) {
76+
CodePushRestartModeNone,
77+
CodePushRestartModeImmediate,
78+
CodePushRestartModeOnNextResume
79+
};

CodePush.ios.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ var CodePush = {
206206
notifyApplicationReady: NativeCodePush.notifyApplicationReady,
207207
setUpTestDependencies: setUpTestDependencies,
208208
sync: sync,
209+
RestartMode: {
210+
NONE: NativeCodePush.codePushRestartModeNone, // Don't artificially restart the app. Allow the update to be "picked up" on the next app restart
211+
IMMEDIATE: NativeCodePush.codePushRestartModeImmediate, // Restart the app immediately
212+
ON_NEXT_RESUME: NativeCodePush.codePushRestartModeOnNextResume // Restart the app the next time it is resumed from the background
213+
},
209214
SyncStatus: {
210215
UP_TO_DATE: 0, // The running app is up-to-date
211216
UPDATE_IGNORED: 1, // The app had an optional update and the end-user chose to ignore it

CodePush.m

Lines changed: 81 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#import "RCTBridgeModule.h"
22
#import "RCTEventDispatcher.h"
3+
#import "RCTConvert.h"
34
#import "RCTRootView.h"
45
#import "RCTUtils.h"
56
#import "CodePush.h"
@@ -39,7 +40,7 @@ + (NSURL *)getBundleUrl
3940
NSDictionary *appFileAttribs = [[NSFileManager defaultManager] attributesOfItemAtPath:packageFile error:nil];
4041
NSDate *binaryDate = [binaryFileAttributes objectForKey:NSFileModificationDate];
4142
NSDate *packageDate = [appFileAttribs objectForKey:NSFileModificationDate];
42-
43+
4344
if ([binaryDate compare:packageDate] == NSOrderedAscending) {
4445
// Return package file because it is newer than the app store binary's JS bundle
4546
return [[NSURL alloc] initFileURLWithPath:packageFile];
@@ -56,38 +57,78 @@ - (void)cancelRollbackTimer
5657
});
5758
}
5859

59-
- (CodePush *)init
60-
{
61-
self = [super init];
60+
- (void)checkForPendingUpdate:(BOOL)isAppStart {
61+
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
62+
NSDictionary *pendingUpdate = [preferences objectForKey:PendingUpdateKey];
6263

63-
if (self) {
64-
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
65-
NSDictionary *pendingUpdate = [preferences objectForKey:PendingUpdateKey];
64+
if (pendingUpdate)
65+
{
66+
NSError *error;
67+
NSString *pendingHash = pendingUpdate[@"hash"];
68+
NSString *currentHash = [CodePushPackage getCurrentPackageHash:&error];
6669

67-
if (pendingUpdate)
68-
{
69-
NSError *error;
70-
NSString *pendingHash = pendingUpdate[@"hash"];
71-
NSString *currentHash = [CodePushPackage getCurrentPackageHash:&error];
72-
73-
// If the current hash is equivalent to the pending hash, then the app
74-
// restart "picked up" the new update, but we need to kick off the
75-
// rollback timer and ensure that the necessary state is setup.
76-
if ([pendingHash isEqualToString:currentHash]) {
70+
// If the current hash is equivalent to the pending hash, then the app
71+
// restart "picked up" the new update, but we need to kick off the
72+
// rollback timer and ensure that the necessary state is setup.
73+
if ([pendingHash isEqualToString:currentHash]) {
74+
// We only want to initialize the rollback timer in two scenarios:
75+
// 1) The app has been restarted, and therefore, the pending update is already applied
76+
// 2) The app has been resumed, and the pending update indicates it supports being restarted on resume
77+
if (isAppStart || (!isAppStart && [pendingUpdate[@"allowRestartOnResume"] boolValue]))
78+
{
7779
int rollbackTimeout = [pendingUpdate[@"rollbackTimeout"] intValue];
78-
[self initializeUpdateWithRollbackTimeout:rollbackTimeout needsRestart:NO];
80+
81+
// If the app wasn't restarted "naturally", then we need to restart it manually
82+
BOOL needsRestart = !isAppStart;
83+
[self initializeUpdateWithRollbackTimeout:rollbackTimeout needsRestart:needsRestart];
7984

8085
// Clear the pending update and sync
8186
[preferences removeObjectForKey:PendingUpdateKey];
8287
[preferences synchronize];
8388
}
8489
}
8590
}
91+
}
92+
93+
- (void)checkForPendingUpdateDuringResume {
94+
[self checkForPendingUpdate:NO];
95+
}
96+
97+
- (NSDictionary *)constantsToExport
98+
{
99+
// Export the values of the CodePushRestartMode enum
100+
// so that the script-side can easily stay in sync
101+
return @{ @"codePushRestartModeNone": @(CodePushRestartModeNone),
102+
@"codePushRestartModeImmediate": @(CodePushRestartModeImmediate),
103+
@"codePushRestartModeOnNextResume": @(CodePushRestartModeOnNextResume)
104+
};
105+
};
106+
107+
- (void)dealloc {
108+
[[NSNotificationCenter defaultCenter] removeObserver:self];
109+
}
110+
111+
- (CodePush *)init
112+
{
113+
self = [super init];
114+
115+
if (self) {
116+
[self checkForPendingUpdate:YES];
117+
118+
// Register for app resume notifications so that we
119+
// can check for pending updates which support "restart on resume"
120+
[[NSNotificationCenter defaultCenter] addObserver:self
121+
selector:@selector(checkForPendingUpdateDuringResume)
122+
name:UIApplicationWillEnterForegroundNotification
123+
object:[UIApplication sharedApplication]];
124+
}
86125

87126
return self;
88127
}
89128

90-
- (void)initializeUpdateWithRollbackTimeout:(int)rollbackTimeout needsRestart:(BOOL)needsRestart {
129+
- (void)initializeUpdateWithRollbackTimeout:(int)rollbackTimeout
130+
needsRestart:(BOOL)needsRestart
131+
{
91132
didUpdate = YES;
92133

93134
if (needsRestart) {
@@ -101,7 +142,8 @@ - (void)initializeUpdateWithRollbackTimeout:(int)rollbackTimeout needsRestart:(B
101142
}
102143
}
103144

104-
- (BOOL)isFailedHash:(NSString*)packageHash {
145+
- (BOOL)isFailedHash:(NSString*)packageHash
146+
{
105147
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
106148
NSMutableArray *failedUpdates = [preferences objectForKey:FailedUpdatesKey];
107149
return (failedUpdates != nil && [failedUpdates containsObject:packageHash]);
@@ -146,13 +188,17 @@ - (void)saveFailedUpdate:(NSString *)packageHash {
146188
}
147189

148190
- (void)savePendingUpdate:(NSString *)packageHash
149-
rollbackTimeout:(int)rollbackTimeout {
191+
rollbackTimeout:(int)rollbackTimeout
192+
allowRestartOnResume:(BOOL)allowRestartOnResume
193+
{
150194
// Since we're not restarting, we need to store the fact that the update
151195
// was applied, but hasn't yet become "active".
152196
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
153197
NSDictionary *pendingUpdate = [[NSDictionary alloc] initWithObjectsAndKeys:
154198
packageHash,@"hash",
155-
rollbackTimeout,@"rollbackTimeout", nil];
199+
[NSNumber numberWithInt:rollbackTimeout],@"rollbackTimeout",
200+
[NSNumber numberWithBool:allowRestartOnResume],@"allowRestartOnResume",
201+
nil];
156202

157203
[preferences setObject:pendingUpdate forKey:PendingUpdateKey];
158204
[preferences synchronize];
@@ -170,10 +216,10 @@ - (void)startRollbackTimer:(int)rollbackTimeout
170216

171217
// JavaScript-exported module methods
172218
RCT_EXPORT_METHOD(applyUpdate:(NSDictionary*)updatePackage
173-
rollbackTimeout:(int)rollbackTimeout
174-
restartImmediately:(BOOL)restartImmediately
175-
resolver:(RCTPromiseResolveBlock)resolve
176-
rejecter:(RCTPromiseRejectBlock)reject)
219+
rollbackTimeout:(int)rollbackTimeout
220+
restartMode:(CodePushRestartMode)restartMode
221+
resolver:(RCTPromiseResolveBlock)resolve
222+
rejecter:(RCTPromiseRejectBlock)reject)
177223
{
178224
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
179225
NSError *error;
@@ -183,10 +229,13 @@ - (void)startRollbackTimer:(int)rollbackTimeout
183229
if (error) {
184230
reject(error);
185231
} else {
186-
if (restartImmediately) {
232+
if (restartMode == CodePushRestartModeImmediate) {
187233
[self initializeUpdateWithRollbackTimeout:rollbackTimeout needsRestart:YES];
188234
} else {
189-
[self savePendingUpdate:updatePackage[@"packageHash"] rollbackTimeout:rollbackTimeout];
235+
BOOL allowsRestartOnResume = (restartMode == CodePushRestartModeOnNextResume);
236+
[self savePendingUpdate:updatePackage[@"packageHash"]
237+
rollbackTimeout:rollbackTimeout
238+
allowRestartOnResume:allowsRestartOnResume];
190239
}
191240
}
192241
});
@@ -256,10 +305,10 @@ - (void)startRollbackTimer:(int)rollbackTimeout
256305
{
257306
NSError *error;
258307
BOOL isFirstRun = didUpdate
259-
&& nil != packageHash
260-
&& [packageHash length] > 0
261-
&& [packageHash isEqualToString:[CodePushPackage getCurrentPackageHash:&error]];
262-
308+
&& nil != packageHash
309+
&& [packageHash length] > 0
310+
&& [packageHash isEqualToString:[CodePushPackage getCurrentPackageHash:&error]];
311+
263312
resolve(@(isFirstRun));
264313
}
265314

CodePush.xcodeproj/project.pbxproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
/* Begin PBXBuildFile section */
1010
13BE3DEE1AC21097009241FE /* CodePush.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* CodePush.m */; };
11-
54FFEDE01BF550630061DD23 /* CodePushDownloadHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 54FFEDDF1BF550630061DD23 /* CodePushDownloadHandler.m */; settings = {ASSET_TAGS = (); }; };
11+
1B23B9141BF9267B000BB2F0 /* RCTConvert+CodePushRestartMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushRestartMode.m */; };
12+
54FFEDE01BF550630061DD23 /* CodePushDownloadHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 54FFEDDF1BF550630061DD23 /* CodePushDownloadHandler.m */; };
1213
810D4E6D1B96935000B397E9 /* CodePushPackage.m in Sources */ = {isa = PBXBuildFile; fileRef = 810D4E6C1B96935000B397E9 /* CodePushPackage.m */; };
1314
81D51F3A1B6181C2000DA084 /* CodePushConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 81D51F391B6181C2000DA084 /* CodePushConfig.m */; };
1415
/* End PBXBuildFile section */
@@ -29,6 +30,7 @@
2930
134814201AA4EA6300B7C361 /* libCodePush.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCodePush.a; sourceTree = BUILT_PRODUCTS_DIR; };
3031
13BE3DEC1AC21097009241FE /* CodePush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodePush.h; sourceTree = "<group>"; };
3132
13BE3DED1AC21097009241FE /* CodePush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CodePush.m; sourceTree = "<group>"; };
33+
1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushRestartMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+CodePushRestartMode.m"; sourceTree = "<group>"; };
3234
54FFEDDF1BF550630061DD23 /* CodePushDownloadHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CodePushDownloadHandler.m; sourceTree = "<group>"; };
3335
810D4E6C1B96935000B397E9 /* CodePushPackage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CodePushPackage.m; sourceTree = "<group>"; };
3436
81D51F391B6181C2000DA084 /* CodePushConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CodePushConfig.m; sourceTree = "<group>"; };
@@ -56,6 +58,7 @@
5658
58B511D21A9E6C8500147676 = {
5759
isa = PBXGroup;
5860
children = (
61+
1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushRestartMode.m */,
5962
54FFEDDF1BF550630061DD23 /* CodePushDownloadHandler.m */,
6063
810D4E6C1B96935000B397E9 /* CodePushPackage.m */,
6164
81D51F391B6181C2000DA084 /* CodePushConfig.m */,
@@ -121,6 +124,7 @@
121124
isa = PBXSourcesBuildPhase;
122125
buildActionMask = 2147483647;
123126
files = (
127+
1B23B9141BF9267B000BB2F0 /* RCTConvert+CodePushRestartMode.m in Sources */,
124128
81D51F3A1B6181C2000DA084 /* CodePushConfig.m in Sources */,
125129
54FFEDE01BF550630061DD23 /* CodePushDownloadHandler.m in Sources */,
126130
13BE3DEE1AC21097009241FE /* CodePush.m in Sources */,

RCTConvert+CodePushRestartMode.m

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#import "CodePush.h"
2+
#import "RCTConvert.h"
3+
4+
// Extending the RCTConvert class allows the React Native
5+
// bridge to handle args of type "CodePushRestartMode"
6+
@implementation RCTConvert (CodePushRestartMode)
7+
8+
RCT_ENUM_CONVERTER(CodePushRestartMode, (@{ @"codePushRestartModeNone": @(CodePushRestartModeNone),
9+
@"codePushRestartModeImmediate": @(CodePushRestartModeImmediate),
10+
@"codePushRestartModeOnNextResume": @(CodePushRestartModeOnNextResume) }),
11+
CodePushRestartModeImmediate, // Default enum value
12+
integerValue)
13+
14+
@end

package-mixins.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
var extend = require("extend");
22
var { NativeAppEventEmitter } = require("react-native");
3+
var { RestartMode } = require("react-native-code-push");
34

45
module.exports = (NativeCodePush) => {
56
var remote = {
@@ -36,8 +37,8 @@ module.exports = (NativeCodePush) => {
3637
};
3738

3839
var local = {
39-
apply: function apply(rollbackTimeout = 0, restartImmediately = true) {
40-
return NativeCodePush.applyUpdate(this, rollbackTimeout, restartImmediately);
40+
apply: function apply(rollbackTimeout = 0, restartMode = RestartMode.IMMEDIATE) {
41+
return NativeCodePush.applyUpdate(this, rollbackTimeout, restartMode);
4142
}
4243
};
4344

0 commit comments

Comments
 (0)