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"
6
7
7
- @implementation CodePush
8
+ @implementation CodePush {
9
+ BOOL _resumablePendingUpdateAvailable;
10
+ }
8
11
9
12
RCT_EXPORT_MODULE ()
10
13
14
+ BOOL didUpdate = NO;
11
15
NSTimer *_timer;
12
16
BOOL usingTestFolder = NO ;
13
- BOOL didUpdate = NO ;
14
17
15
18
NSString * const FailedUpdatesKey = @" CODE_PUSH_FAILED_UPDATES" ;
16
19
NSString * const PendingUpdateKey = @" CODE_PUSH_PENDING_UPDATE" ;
17
20
21
+ // These keys are already "namespaced" by the PendingUpdateKey, so
22
+ // their values don't need to be obfuscated to prevent collision with app data
23
+ NSString * const PendingUpdateHashKey = @" hash" ;
24
+ NSString * const PendingUpdateRollbackTimeoutKey = @" rollbackTimeout" ;
25
+
18
26
@synthesize bridge = _bridge;
19
27
20
28
// Public Obj-C API
@@ -39,7 +47,7 @@ + (NSURL *)getBundleUrl
39
47
NSDictionary *appFileAttribs = [[NSFileManager defaultManager ] attributesOfItemAtPath: packageFile error: nil ];
40
48
NSDate *binaryDate = [binaryFileAttributes objectForKey: NSFileModificationDate ];
41
49
NSDate *packageDate = [appFileAttribs objectForKey: NSFileModificationDate ];
42
-
50
+
43
51
if ([binaryDate compare: packageDate] == NSOrderedAscending) {
44
52
// Return package file because it is newer than the app store binary's JS bundle
45
53
return [[NSURL alloc ] initFileURLWithPath: packageFile];
@@ -56,38 +64,83 @@ - (void)cancelRollbackTimer
56
64
});
57
65
}
58
66
59
- - (CodePush *) init
67
+ - (void ) checkForPendingUpdate : ( BOOL ) needsRestart
60
68
{
61
- self = [super init ];
62
-
63
- if (self) {
69
+ dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
64
70
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults ];
65
71
NSDictionary *pendingUpdate = [preferences objectForKey: PendingUpdateKey];
66
72
67
- if (pendingUpdate)
68
- {
73
+ if (pendingUpdate) {
69
74
NSError *error;
70
- NSString *pendingHash = pendingUpdate[@" hash " ];
75
+ NSString *pendingHash = pendingUpdate[PendingUpdateHashKey ];
71
76
NSString *currentHash = [CodePushPackage getCurrentPackageHash: &error];
72
77
73
78
// If the current hash is equivalent to the pending hash, then the app
74
79
// restart "picked up" the new update, but we need to kick off the
75
80
// rollback timer and ensure that the necessary state is setup.
76
81
if ([pendingHash isEqualToString: currentHash]) {
77
- int rollbackTimeout = [pendingUpdate[@" rollbackTimeout " ] intValue ];
78
- [self initializeUpdateWithRollbackTimeout: rollbackTimeout needsRestart: NO ];
82
+ int rollbackTimeout = [pendingUpdate[PendingUpdateRollbackTimeoutKey ] intValue ];
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
}
90
+ });
91
+ }
92
+
93
+ - (void )checkForPendingUpdateDuringResume
94
+ {
95
+ // In order to ensure that CodePush doesn't impact the app's
96
+ // resume experience, we're using a simple boolean check to
97
+ // check whether we need to restart, before reading the defaults store
98
+ if (_resumablePendingUpdateAvailable) {
99
+ [self checkForPendingUpdate: YES ];
100
+ }
101
+ }
102
+
103
+ - (NSDictionary *)constantsToExport
104
+ {
105
+ // Export the values of the CodePushRestartMode enum
106
+ // so that the script-side can easily stay in sync
107
+ return @{ @" codePushRestartModeNone" : @(CodePushRestartModeNone),
108
+ @" codePushRestartModeImmediate" : @(CodePushRestartModeImmediate),
109
+ @" codePushRestartModeOnNextResume" : @(CodePushRestartModeOnNextResume)
110
+ };
111
+ };
112
+
113
+ - (void )dealloc
114
+ {
115
+ // Ensure the global resume handler is cleared, so that
116
+ // this object isn't kept alive unnecessarily
117
+ [[NSNotificationCenter defaultCenter ] removeObserver: self ];
118
+ }
119
+
120
+ - (CodePush *)init
121
+ {
122
+ self = [super init ];
123
+
124
+ if (self) {
125
+ // Do an async check to see whether
126
+ // we need to start the rollback timer
127
+ // due to a pending update being applied at start
128
+ [self checkForPendingUpdate: NO ];
129
+
130
+ // Register for app resume notifications so that we
131
+ // can check for pending updates which support "restart on resume"
132
+ [[NSNotificationCenter defaultCenter ] addObserver: self
133
+ selector: @selector (checkForPendingUpdateDuringResume )
134
+ name: UIApplicationWillEnterForegroundNotification
135
+ object: [UIApplication sharedApplication ]];
85
136
}
86
137
87
138
return self;
88
139
}
89
140
90
- - (void )initializeUpdateWithRollbackTimeout : (int )rollbackTimeout needsRestart : (BOOL )needsRestart {
141
+ - (void )initializeUpdateWithRollbackTimeout : (int )rollbackTimeout
142
+ needsRestart : (BOOL )needsRestart
143
+ {
91
144
didUpdate = YES ;
92
145
93
146
if (needsRestart) {
@@ -101,7 +154,8 @@ - (void)initializeUpdateWithRollbackTimeout:(int)rollbackTimeout needsRestart:(B
101
154
}
102
155
}
103
156
104
- - (BOOL )isFailedHash : (NSString *)packageHash {
157
+ - (BOOL )isFailedHash : (NSString *)packageHash
158
+ {
105
159
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults ];
106
160
NSMutableArray *failedUpdates = [preferences objectForKey: FailedUpdatesKey];
107
161
return (failedUpdates != nil && [failedUpdates containsObject: packageHash]);
@@ -129,7 +183,8 @@ - (void)rollbackPackage
129
183
[self loadBundle ];
130
184
}
131
185
132
- - (void )saveFailedUpdate : (NSString *)packageHash {
186
+ - (void )saveFailedUpdate : (NSString *)packageHash
187
+ {
133
188
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults ];
134
189
NSMutableArray *failedUpdates = [preferences objectForKey: FailedUpdatesKey];
135
190
if (failedUpdates == nil ) {
@@ -146,13 +201,14 @@ - (void)saveFailedUpdate:(NSString *)packageHash {
146
201
}
147
202
148
203
- (void )savePendingUpdate : (NSString *)packageHash
149
- rollbackTimeout : (int )rollbackTimeout {
204
+ rollbackTimeout : (int )rollbackTimeout
205
+ {
150
206
// Since we're not restarting, we need to store the fact that the update
151
207
// was applied, but hasn't yet become "active".
152
208
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults ];
153
209
NSDictionary *pendingUpdate = [[NSDictionary alloc ] initWithObjectsAndKeys:
154
- packageHash,@" hash " ,
155
- rollbackTimeout, @" rollbackTimeout " , nil ];
210
+ packageHash,PendingUpdateHashKey ,
211
+ [ NSNumber numberWithInt: rollbackTimeout],PendingUpdateRollbackTimeoutKey , nil ];
156
212
157
213
[preferences setObject: pendingUpdate forKey: PendingUpdateKey];
158
214
[preferences synchronize ];
@@ -170,10 +226,10 @@ - (void)startRollbackTimer:(int)rollbackTimeout
170
226
171
227
// JavaScript-exported module methods
172
228
RCT_EXPORT_METHOD (applyUpdate:(NSDictionary *)updatePackage
173
- rollbackTimeout:(int )rollbackTimeout
174
- restartImmediately:( BOOL )restartImmediately
175
- resolver:(RCTPromiseResolveBlock)resolve
176
- rejecter:(RCTPromiseRejectBlock)reject)
229
+ rollbackTimeout:(int )rollbackTimeout
230
+ restartMode:(CodePushRestartMode)restartMode
231
+ resolver:(RCTPromiseResolveBlock)resolve
232
+ rejecter:(RCTPromiseRejectBlock)reject)
177
233
{
178
234
dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
179
235
NSError *error;
@@ -183,10 +239,12 @@ - (void)startRollbackTimer:(int)rollbackTimeout
183
239
if (error) {
184
240
reject (error);
185
241
} else {
186
- if (restartImmediately ) {
242
+ if (restartMode == CodePushRestartModeImmediate ) {
187
243
[self initializeUpdateWithRollbackTimeout: rollbackTimeout needsRestart: YES ];
188
244
} else {
189
- [self savePendingUpdate: updatePackage[@" packageHash" ] rollbackTimeout: rollbackTimeout];
245
+ _resumablePendingUpdateAvailable = (restartMode == CodePushRestartModeOnNextResume);
246
+ [self savePendingUpdate: updatePackage[@" packageHash" ]
247
+ rollbackTimeout: rollbackTimeout];
190
248
}
191
249
}
192
250
});
@@ -256,10 +314,10 @@ - (void)startRollbackTimer:(int)rollbackTimeout
256
314
{
257
315
NSError *error;
258
316
BOOL isFirstRun = didUpdate
259
- && nil != packageHash
260
- && [packageHash length ] > 0
261
- && [packageHash isEqualToString: [CodePushPackage getCurrentPackageHash: &error]];
262
-
317
+ && nil != packageHash
318
+ && [packageHash length ] > 0
319
+ && [packageHash isEqualToString: [CodePushPackage getCurrentPackageHash: &error]];
320
+
263
321
resolve (@(isFirstRun));
264
322
}
265
323
0 commit comments