1
1
#import " RCTBridgeModule.h"
2
2
#import " RCTEventDispatcher.h"
3
+ #import " RCTConvert.h"
3
4
#import " RCTRootView.h"
4
5
#import " RCTUtils.h"
5
6
#import " CodePush.h"
@@ -39,7 +40,7 @@ + (NSURL *)getBundleUrl
39
40
NSDictionary *appFileAttribs = [[NSFileManager defaultManager ] attributesOfItemAtPath: packageFile error: nil ];
40
41
NSDate *binaryDate = [binaryFileAttributes objectForKey: NSFileModificationDate ];
41
42
NSDate *packageDate = [appFileAttribs objectForKey: NSFileModificationDate ];
42
-
43
+
43
44
if ([binaryDate compare: packageDate] == NSOrderedAscending) {
44
45
// Return package file because it is newer than the app store binary's JS bundle
45
46
return [[NSURL alloc ] initFileURLWithPath: packageFile];
@@ -56,38 +57,78 @@ - (void)cancelRollbackTimer
56
57
});
57
58
}
58
59
59
- - (CodePush *) init
60
- {
61
- self = [super init ];
60
+ - (void ) checkForPendingUpdate : ( BOOL ) isAppStart {
61
+ NSUserDefaults *preferences = [ NSUserDefaults standardUserDefaults ];
62
+ NSDictionary *pendingUpdate = [preferences objectForKey: PendingUpdateKey ];
62
63
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];
66
69
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
+ {
77
79
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];
79
84
80
85
// Clear the pending update and sync
81
86
[preferences removeObjectForKey: PendingUpdateKey];
82
87
[preferences synchronize ];
83
88
}
84
89
}
85
90
}
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
+ }
86
125
87
126
return self;
88
127
}
89
128
90
- - (void )initializeUpdateWithRollbackTimeout : (int )rollbackTimeout needsRestart : (BOOL )needsRestart {
129
+ - (void )initializeUpdateWithRollbackTimeout : (int )rollbackTimeout
130
+ needsRestart : (BOOL )needsRestart
131
+ {
91
132
didUpdate = YES ;
92
133
93
134
if (needsRestart) {
@@ -101,7 +142,8 @@ - (void)initializeUpdateWithRollbackTimeout:(int)rollbackTimeout needsRestart:(B
101
142
}
102
143
}
103
144
104
- - (BOOL )isFailedHash : (NSString *)packageHash {
145
+ - (BOOL )isFailedHash : (NSString *)packageHash
146
+ {
105
147
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults ];
106
148
NSMutableArray *failedUpdates = [preferences objectForKey: FailedUpdatesKey];
107
149
return (failedUpdates != nil && [failedUpdates containsObject: packageHash]);
@@ -146,13 +188,17 @@ - (void)saveFailedUpdate:(NSString *)packageHash {
146
188
}
147
189
148
190
- (void )savePendingUpdate : (NSString *)packageHash
149
- rollbackTimeout : (int )rollbackTimeout {
191
+ rollbackTimeout : (int )rollbackTimeout
192
+ allowRestartOnResume : (BOOL )allowRestartOnResume
193
+ {
150
194
// Since we're not restarting, we need to store the fact that the update
151
195
// was applied, but hasn't yet become "active".
152
196
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults ];
153
197
NSDictionary *pendingUpdate = [[NSDictionary alloc ] initWithObjectsAndKeys:
154
198
packageHash,@" hash" ,
155
- rollbackTimeout,@" rollbackTimeout" , nil ];
199
+ [NSNumber numberWithInt: rollbackTimeout],@" rollbackTimeout" ,
200
+ [NSNumber numberWithBool: allowRestartOnResume],@" allowRestartOnResume" ,
201
+ nil ];
156
202
157
203
[preferences setObject: pendingUpdate forKey: PendingUpdateKey];
158
204
[preferences synchronize ];
@@ -170,10 +216,10 @@ - (void)startRollbackTimer:(int)rollbackTimeout
170
216
171
217
// JavaScript-exported module methods
172
218
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)
177
223
{
178
224
dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
179
225
NSError *error;
@@ -183,10 +229,13 @@ - (void)startRollbackTimer:(int)rollbackTimeout
183
229
if (error) {
184
230
reject (error);
185
231
} else {
186
- if (restartImmediately ) {
232
+ if (restartMode == CodePushRestartModeImmediate ) {
187
233
[self initializeUpdateWithRollbackTimeout: rollbackTimeout needsRestart: YES ];
188
234
} else {
189
- [self savePendingUpdate: updatePackage[@" packageHash" ] rollbackTimeout: rollbackTimeout];
235
+ BOOL allowsRestartOnResume = (restartMode == CodePushRestartModeOnNextResume);
236
+ [self savePendingUpdate: updatePackage[@" packageHash" ]
237
+ rollbackTimeout: rollbackTimeout
238
+ allowRestartOnResume: allowsRestartOnResume];
190
239
}
191
240
}
192
241
});
@@ -256,10 +305,10 @@ - (void)startRollbackTimer:(int)rollbackTimeout
256
305
{
257
306
NSError *error;
258
307
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
+
263
312
resolve (@(isFirstRun));
264
313
}
265
314
0 commit comments