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

Commit 8e1d431

Browse files
committed
Merge pull request #104 from Microsoft/remove-rollback-timeout
Remove rollback timeout
2 parents 2ef6cf1 + 388e926 commit 8e1d431

File tree

5 files changed

+132
-151
lines changed

5 files changed

+132
-151
lines changed

CodePush.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ function sync(options = {}, syncStatusChangeCallback, downloadProgressCallback)
167167
deploymentKey: null,
168168
ignoreFailedUpdates: true,
169169
installMode: CodePush.InstallMode.ON_NEXT_RESTART,
170-
rollbackTimeout: 0,
171170
updateDialog: null,
172171

173172
...options
@@ -236,7 +235,7 @@ function sync(options = {}, syncStatusChangeCallback, downloadProgressCallback)
236235
remotePackage.download(downloadProgressCallback)
237236
.then((localPackage) => {
238237
syncStatusChangeCallback(CodePush.SyncStatus.INSTALLING_UPDATE);
239-
return localPackage.install(syncOptions.rollbackTimeout, syncOptions.installMode, () => {
238+
return localPackage.install(syncOptions.installMode, () => {
240239
syncStatusChangeCallback(CodePush.SyncStatus.UPDATE_INSTALLED);
241240
resolve(CodePush.SyncStatus.UPDATE_INSTALLED);
242241
});

CodePush.m

Lines changed: 45 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ @implementation CodePush {
2222
// These keys are already "namespaced" by the PendingUpdateKey, so
2323
// their values don't need to be obfuscated to prevent collision with app data
2424
static NSString *const PendingUpdateHashKey = @"hash";
25-
static NSString *const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
25+
static NSString *const PendingUpdateIsLoadingKey = @"isLoading";
2626

2727
@synthesize bridge = _bridge;
2828

@@ -70,20 +70,6 @@ + (NSString *)getApplicationSupportDirectory
7070

7171
// Private API methods
7272

73-
/*
74-
* This method cancels the currently running rollback
75-
* timer, which has the effect of stopping an automatic
76-
* rollback from occurring.
77-
*
78-
* Note: This method is safe to call from any thread.
79-
*/
80-
- (void)cancelRollbackTimer
81-
{
82-
dispatch_async(dispatch_get_main_queue(), ^{
83-
[_timer invalidate];
84-
});
85-
}
86-
8773
/*
8874
* This method is used by the React Native bridge to allow
8975
* our plugin to expose constants to the JS-side. In our case
@@ -120,26 +106,27 @@ - (instancetype)init
120106
}
121107

122108
/*
123-
* This method starts the rollback protection timer
124-
* and is used when a new update is initialized.
109+
* This method is used when the app is started to either
110+
* initialize a pending update or rollback a faulty update
111+
* to the previous version.
125112
*/
126113
- (void)initializeUpdateAfterRestart
127114
{
128115
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
129116
NSDictionary *pendingUpdate = [preferences objectForKey:PendingUpdateKey];
130-
131117
if (pendingUpdate) {
132118
_isFirstRunAfterUpdate = YES;
133-
int rollbackTimeout = [pendingUpdate[PendingUpdateRollbackTimeoutKey] intValue];
134-
if (0 != rollbackTimeout) {
135-
dispatch_async(dispatch_get_main_queue(), ^{
136-
[self startRollbackTimer:rollbackTimeout];
137-
});
119+
BOOL updateIsLoading = [pendingUpdate[PendingUpdateIsLoadingKey] boolValue];
120+
if (updateIsLoading) {
121+
// Pending update was initialized, but notifyApplicationReady was not called.
122+
// Therefore, deduce that it is a broken update and rollback.
123+
[self rollbackPackage];
124+
} else {
125+
// Mark that we tried to initialize the new update, so that if it crashes,
126+
// we will know that we need to rollback when the app next starts.
127+
[self savePendingUpdate:pendingUpdate[PendingUpdateHashKey]
128+
isLoading:YES];
138129
}
139-
140-
// Clear the pending update and sync
141-
[preferences removeObjectForKey:PendingUpdateKey];
142-
[preferences synchronize];
143130
}
144131
}
145132

@@ -161,15 +148,19 @@ - (BOOL)isFailedHash:(NSString*)packageHash
161148
*/
162149
- (void)loadBundle
163150
{
164-
// If the current bundle URL is using http(s), then assume the dev
165-
// is debugging and therefore, shouldn't be redirected to a local
166-
// file (since Chrome wouldn't support it). Otherwise, update
167-
// the current bundle URL to point at the latest update
168-
if (![_bridge.bundleURL.scheme hasPrefix:@"http"]) {
169-
_bridge.bundleURL = [CodePush bundleURL];
170-
}
171-
172-
[_bridge reload];
151+
// This needs to be async dispatched because the _bridge is not set on init
152+
// when the app first starts, therefore rollbacks will not take effect.
153+
dispatch_async(dispatch_get_main_queue(), ^{
154+
// If the current bundle URL is using http(s), then assume the dev
155+
// is debugging and therefore, shouldn't be redirected to a local
156+
// file (since Chrome wouldn't support it). Otherwise, update
157+
// the current bundle URL to point at the latest update
158+
if (![_bridge.bundleURL.scheme hasPrefix:@"http"]) {
159+
_bridge.bundleURL = [CodePush bundleURL];
160+
}
161+
162+
[_bridge reload];
163+
});
173164
}
174165

175166
/*
@@ -187,9 +178,9 @@ - (void)rollbackPackage
187178
// Write the current package's hash to the "failed list"
188179
[self saveFailedUpdate:packageHash];
189180

190-
// Do the actual rollback and then
191-
// refresh the app with the previous package
181+
// Rollback to the previous version and de-register the new update
192182
[CodePushPackage rollbackPackage];
183+
[self removePendingUpdate];
193184
[self loadBundle];
194185
}
195186

@@ -215,39 +206,36 @@ - (void)saveFailedUpdate:(NSString *)packageHash
215206
[preferences synchronize];
216207
}
217208

209+
/*
210+
* This method is used to register the fact that a pending
211+
* update succeeded and therefore can be removed.
212+
*/
213+
- (void)removePendingUpdate
214+
{
215+
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
216+
[preferences removeObjectForKey:PendingUpdateKey];
217+
[preferences synchronize];
218+
}
219+
218220
/*
219221
* When an update is installed whose mode isn't IMMEDIATE, this method
220-
* can be called to store the pending update's metadata (e.g. rollbackTimeout)
222+
* can be called to store the pending update's metadata (e.g. packageHash)
221223
* so that it can be used when the actual update application occurs at a later point.
222224
*/
223225
- (void)savePendingUpdate:(NSString *)packageHash
224-
rollbackTimeout:(int)rollbackTimeout
226+
isLoading:(BOOL)isLoading
225227
{
226228
// Since we're not restarting, we need to store the fact that the update
227229
// was installed, but hasn't yet become "active".
228230
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
229231
NSDictionary *pendingUpdate = [[NSDictionary alloc] initWithObjectsAndKeys:
230232
packageHash,PendingUpdateHashKey,
231-
[NSNumber numberWithInt:rollbackTimeout],PendingUpdateRollbackTimeoutKey, nil];
233+
[NSNumber numberWithBool:isLoading],PendingUpdateIsLoadingKey, nil];
232234

233235
[preferences setObject:pendingUpdate forKey:PendingUpdateKey];
234236
[preferences synchronize];
235237
}
236238

237-
/*
238-
* This method handles starting the actual rollback timer
239-
* after an update has been installed.
240-
*/
241-
- (void)startRollbackTimer:(int)rollbackTimeout
242-
{
243-
double timeoutInSeconds = rollbackTimeout / 1000;
244-
_timer = [NSTimer scheduledTimerWithTimeInterval:timeoutInSeconds
245-
target:self
246-
selector:@selector(rollbackPackage)
247-
userInfo:nil
248-
repeats:NO];
249-
}
250-
251239
// JavaScript-exported module methods
252240

253241
/*
@@ -318,7 +306,6 @@ - (void)startRollbackTimer:(int)rollbackTimeout
318306
* This method is the native side of the LocalPackage.install method.
319307
*/
320308
RCT_EXPORT_METHOD(installUpdate:(NSDictionary*)updatePackage
321-
rollbackTimeout:(int)rollbackTimeout
322309
installMode:(CodePushInstallMode)installMode
323310
resolver:(RCTPromiseResolveBlock)resolve
324311
rejecter:(RCTPromiseRejectBlock)reject)
@@ -332,7 +319,7 @@ - (void)startRollbackTimer:(int)rollbackTimeout
332319
reject(error);
333320
} else {
334321
[self savePendingUpdate:updatePackage[@"packageHash"]
335-
rollbackTimeout:rollbackTimeout];
322+
isLoading:NO];
336323

337324
if (installMode == CodePushInstallModeImmediate) {
338325
[self loadBundle];
@@ -389,7 +376,7 @@ - (void)startRollbackTimer:(int)rollbackTimeout
389376
RCT_EXPORT_METHOD(notifyApplicationReady:(RCTPromiseResolveBlock)resolve
390377
rejecter:(RCTPromiseRejectBlock)reject)
391378
{
392-
[self cancelRollbackTimer];
379+
[self removePendingUpdate];
393380
resolve([NSNull null]);
394381
}
395382

0 commit comments

Comments
 (0)