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

Commit 51c40c9

Browse files
committed
report acquisition status
1 parent 4b7e624 commit 51c40c9

File tree

8 files changed

+346
-44
lines changed

8 files changed

+346
-44
lines changed

CodePush.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ async function checkForUpdate(deploymentKey = null) {
6262
} else {
6363
const remotePackage = { ...update, ...PackageMixins.remote };
6464
remotePackage.failedInstall = await NativeCodePush.isFailedUpdate(remotePackage.packageHash);
65+
remotePackage.deploymentKey = deploymentKey || nativeConfig.deploymentKey;
6566
return remotePackage;
6667
}
6768
}
@@ -102,6 +103,18 @@ function getPromisifiedSdk(requestFetchAdapter, config) {
102103
});
103104
};
104105

106+
sdk.reportStatus = (package, status) => {
107+
return new Promise((resolve, reject) => {
108+
module.exports.AcquisitionSdk.prototype.reportStatusDeploy.call(sdk, package, status, (err) => {
109+
if (err) {
110+
reject(err);
111+
} else {
112+
resolve();
113+
}
114+
});
115+
});
116+
};
117+
105118
return sdk;
106119
}
107120

@@ -110,6 +123,23 @@ function log(message) {
110123
console.log(`[CodePush] ${message}`)
111124
}
112125

126+
async function notifyApplicationReady() {
127+
await NativeCodePush.notifyApplicationReady();
128+
const statusReport = await NativeCodePush.getNewStatusReport();
129+
if (statusReport) {
130+
const config = await getConfiguration();
131+
if (statusReport.appVersion) {
132+
config.appVersion = statusReport.appVersion;
133+
const sdk = getPromisifiedSdk(requestFetchAdapter, config);
134+
sdk.reportStatus();
135+
} else {
136+
config.deploymentKey = statusReport.package.deploymentKey;
137+
const sdk = getPromisifiedSdk(requestFetchAdapter, config);
138+
sdk.reportStatus(statusReport.package, statusReport.status);
139+
}
140+
}
141+
}
142+
113143
function restartApp(onlyIfUpdateIsPending = false) {
114144
NativeCodePush.restartApp(onlyIfUpdateIsPending);
115145
}
@@ -269,7 +299,7 @@ const CodePush = {
269299
getConfiguration,
270300
getCurrentPackage,
271301
log,
272-
notifyApplicationReady: NativeCodePush.notifyApplicationReady,
302+
notifyApplicationReady,
273303
restartApp,
274304
setUpTestDependencies,
275305
sync,

CodePush.m

Lines changed: 117 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@ @implementation CodePush {
1515

1616
static BOOL testConfigurationFlag = NO;
1717

18+
// These constants represent valid deployment statuses
19+
static NSString *const DeploymentSucceeded = @"DeploymentSucceeded";
20+
static NSString *const DeploymentFailed = @"DeploymentFailed";
21+
1822
// These keys represent the names we use to store data in NSUserDefaults
1923
static NSString *const FailedUpdatesKey = @"CODE_PUSH_FAILED_UPDATES";
2024
static NSString *const PendingUpdateKey = @"CODE_PUSH_PENDING_UPDATE";
25+
static NSString *const StatusReportsKey = @"CODE_PUSH_STATUS_REPORTS";
2126

2227
// These keys are already "namespaced" by the PendingUpdateKey, so
2328
// their values don't need to be obfuscated to prevent collision with app data
@@ -26,6 +31,8 @@ @implementation CodePush {
2631

2732
// These keys are used to inspect/augment the metadata
2833
// that is associated with an update's package.
34+
static NSString *const DeploymentKeyKey = @"deploymentKey";
35+
static NSString *const LabelKey = @"label";
2936
static NSString *const PackageHashKey = @"packageHash";
3037
static NSString *const PackageIsPendingKey = @"isPending";
3138

@@ -70,7 +77,7 @@ + (NSURL *)bundleURLForResource:(NSString *)resourceName
7077

7178
NSString *packageAppVersion = [currentPackageMetadata objectForKey:@"appVersion"];
7279

73-
if ([binaryDate compare:packageDate] == NSOrderedAscending && [binaryAppVersion isEqualToString:packageAppVersion]) {
80+
if ([binaryDate compare:packageDate] == NSOrderedAscending && ([CodePush isUsingTestConfiguration] ||[binaryAppVersion isEqualToString:packageAppVersion])) {
7481
// Return package file because it is newer than the app store binary's JS bundle
7582
NSURL *packageUrl = [[NSURL alloc] initFileURLWithPath:packageFile];
7683
NSLog(logMessageFormat, packageUrl);
@@ -148,6 +155,19 @@ - (void)dealloc
148155
[[NSNotificationCenter defaultCenter] removeObserver:self];
149156
}
150157

158+
- (NSString *)getPackageStatusReportIdentifier:(NSDictionary *)package
159+
{
160+
// Because deploymentKeys can be dynamically switched, we use a
161+
// combination of the deploymentKey and label as the packageIdentifier.
162+
NSString *deploymentKey = [package objectForKey:DeploymentKeyKey];
163+
NSString *label = [package objectForKey:LabelKey];
164+
if (deploymentKey && label) {
165+
return [[deploymentKey stringByAppendingString:@":"] stringByAppendingString:label];
166+
} else {
167+
return nil;
168+
}
169+
}
170+
151171
- (instancetype)init
152172
{
153173
self = [super init];
@@ -185,6 +205,13 @@ - (void)initializeUpdateAfterRestart
185205
}
186206
}
187207

208+
- (BOOL)isDeploymentStatusNotYetReported:(NSString *)appVersionOrPackageIdentifier
209+
{
210+
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
211+
NSDictionary *sentStatusReports = [preferences objectForKey:StatusReportsKey];
212+
return sentStatusReports == nil || [sentStatusReports objectForKey:appVersionOrPackageIdentifier] == nil;
213+
}
214+
188215
/*
189216
* This method checks to see whether a specific package hash
190217
* has previously failed installation.
@@ -193,7 +220,25 @@ - (BOOL)isFailedHash:(NSString*)packageHash
193220
{
194221
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
195222
NSMutableArray *failedUpdates = [preferences objectForKey:FailedUpdatesKey];
196-
return (failedUpdates != nil && [failedUpdates containsObject:packageHash]);
223+
if (failedUpdates == nil) {
224+
return NO;
225+
} else {
226+
for (NSDictionary *failedPackage in failedUpdates)
227+
{
228+
// Type check is needed for backwards compatibility, where we used to just store
229+
// the failed package hash instead of the metadata. This only impacts "dev"
230+
// scenarios, since in production we clear out old information whenever a new
231+
// binary is applied.
232+
if ([failedPackage isKindOfClass:[NSDictionary class]]) {
233+
NSString *failedPackageHash = [failedPackage objectForKey:PackageHashKey];
234+
if ([packageHash isEqualToString:failedPackageHash]) {
235+
return YES;
236+
}
237+
}
238+
}
239+
240+
return NO;
241+
}
197242
}
198243

199244
/*
@@ -237,6 +282,22 @@ - (void)loadBundle
237282
});
238283
}
239284

285+
- (void)recordDeploymentStatusReported:(NSString *)appVersionOrPackageIdentifier
286+
status:(NSString *)status
287+
{
288+
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
289+
NSMutableDictionary *sentStatusReports = [preferences objectForKey:StatusReportsKey];
290+
if (sentStatusReports == nil) {
291+
sentStatusReports = [NSMutableDictionary dictionary];
292+
} else {
293+
sentStatusReports = [sentStatusReports mutableCopy];
294+
}
295+
296+
[sentStatusReports setValue:status forKey:appVersionOrPackageIdentifier];
297+
[preferences setValue:sentStatusReports forKey:StatusReportsKey];
298+
[preferences synchronize];
299+
}
300+
240301
/*
241302
* This method is used when an update has failed installation
242303
* and the app needs to be rolled back to the previous bundle.
@@ -247,10 +308,10 @@ - (void)loadBundle
247308
- (void)rollbackPackage
248309
{
249310
NSError *error;
250-
NSString *packageHash = [CodePushPackage getCurrentPackageHash:&error];
311+
NSDictionary *failedPackage = [CodePushPackage getCurrentPackage:&error];
251312

252-
// Write the current package's hash to the "failed list"
253-
[self saveFailedUpdate:packageHash];
313+
// Write the current package's metadata to the "failed list"
314+
[self saveFailedUpdate:failedPackage];
254315

255316
// Rollback to the previous version and de-register the new update
256317
[CodePushPackage rollbackPackage];
@@ -263,7 +324,7 @@ - (void)rollbackPackage
263324
* to store its hash so that it can be ignored on future
264325
* attempts to check the server for an update.
265326
*/
266-
- (void)saveFailedUpdate:(NSString *)packageHash
327+
- (void)saveFailedUpdate:(NSDictionary *)failedPackage
267328
{
268329
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
269330
NSMutableArray *failedUpdates = [preferences objectForKey:FailedUpdatesKey];
@@ -275,7 +336,7 @@ - (void)saveFailedUpdate:(NSString *)packageHash
275336
failedUpdates = [failedUpdates mutableCopy];
276337
}
277338

278-
[failedUpdates addObject:packageHash];
339+
[failedUpdates addObject:failedPackage];
279340
[preferences setObject:failedUpdates forKey:FailedUpdatesKey];
280341
[preferences synchronize];
281342
}
@@ -467,6 +528,55 @@ - (void)savePendingUpdate:(NSString *)packageHash
467528
resolve([NSNull null]);
468529
}
469530

531+
/*
532+
* This method is checks if a new status update exists (new version was installed,
533+
* or an update failed) and return its details (version label, status).
534+
*/
535+
RCT_EXPORT_METHOD(getNewStatusReport:(RCTPromiseResolveBlock)resolve
536+
rejecter:(RCTPromiseRejectBlock)reject)
537+
{
538+
// Check if the current appVersion has been reported.
539+
NSString *appVersion = [[CodePushConfig current] appVersion];
540+
if ([self isDeploymentStatusNotYetReported:appVersion]) {
541+
[self recordDeploymentStatusReported:appVersion
542+
status:DeploymentSucceeded];
543+
resolve(@{ @"appVersion": appVersion });
544+
return;
545+
}
546+
547+
// Check if there was a rollback that was not yet reported
548+
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
549+
NSMutableArray *failedUpdates = [preferences objectForKey:FailedUpdatesKey];
550+
if (failedUpdates) {
551+
NSDictionary* lastFailedPackage = [failedUpdates lastObject];
552+
if (lastFailedPackage) {
553+
NSString* lastFailedPackageIdentifier = [self getPackageStatusReportIdentifier:lastFailedPackage];
554+
if (lastFailedPackageIdentifier && [self isDeploymentStatusNotYetReported:lastFailedPackageIdentifier]) {
555+
[self recordDeploymentStatusReported:lastFailedPackageIdentifier
556+
status:DeploymentFailed];
557+
resolve(@{ @"package": lastFailedPackage, @"status": DeploymentFailed });
558+
return;
559+
}
560+
}
561+
}
562+
563+
// Check if the current CodePush package has been reported
564+
NSError *error;
565+
NSDictionary* currentPackage = [CodePushPackage getCurrentPackage:&error];
566+
if (currentPackage) {
567+
NSString* currentPackageIdentifier = [self getPackageStatusReportIdentifier:currentPackage];
568+
if (currentPackageIdentifier && [self isDeploymentStatusNotYetReported:currentPackageIdentifier]) {
569+
[self recordDeploymentStatusReported:currentPackageIdentifier
570+
status:DeploymentSucceeded];
571+
resolve(@{ @"package": currentPackage, @"status": DeploymentSucceeded });
572+
return;
573+
}
574+
}
575+
576+
resolve([NSNull null]);
577+
return;
578+
}
579+
470580
/*
471581
* This method is the native side of the CodePush.restartApp() method.
472582
*/

CodePushConfig.m

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#import "CodePush.h"
2+
#import <UIKit/UIKit.h>
23

34
@implementation CodePushConfig {
45
NSMutableDictionary *_configDictionary;
@@ -10,6 +11,7 @@ @implementation CodePushConfig {
1011
static NSString * const BuildVdersionConfigKey = @"buildVersion";
1112
static NSString * const DeploymentKeyConfigKey = @"deploymentKey";
1213
static NSString * const ServerURLConfigKey = @"serverUrl";
14+
static NSString * const ClientUniqueIDConfigKey = @"clientUniqueId";
1315

1416
+ (instancetype)current
1517
{
@@ -31,6 +33,14 @@ - (instancetype)init
3133
NSString *deploymentKey = [infoDictionary objectForKey:@"CodePushDeploymentKey"];
3234
NSString *serverURL = [infoDictionary objectForKey:@"CodePushServerURL"];
3335

36+
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
37+
NSString* clientUniqueId = [userDefaults stringForKey:ClientUniqueIDConfigKey];
38+
if (clientUniqueId == nil) {
39+
clientUniqueId = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
40+
[userDefaults setObject:clientUniqueId forKey:ClientUniqueIDConfigKey];
41+
[userDefaults synchronize];
42+
}
43+
3444
if (!serverURL) {
3545
serverURL = @"https://codepush.azurewebsites.net/";
3646
}
@@ -40,6 +50,7 @@ - (instancetype)init
4050
buildVersion,BuildVdersionConfigKey,
4151
serverURL,ServerURLConfigKey,
4252
deploymentKey,DeploymentKeyConfigKey,
53+
clientUniqueId,ClientUniqueIDConfigKey,
4354
nil];
4455

4556
return self;
@@ -70,6 +81,11 @@ - (NSString *)serverURL
7081
return [_configDictionary objectForKey:ServerURLConfigKey];
7182
}
7283

84+
- (NSString *)clientUniqueId
85+
{
86+
return [_configDictionary objectForKey:ClientUniqueIDConfigKey];
87+
}
88+
7389
- (void)setDeploymentKey:(NSString *)deploymentKey
7490
{
7591
[_configDictionary setValue:deploymentKey forKey:DeploymentKeyConfigKey];

Examples/CodePushDemoApp/CodePushDemoAppTests/CheckForUpdateTests/testcases/SwitchDeploymentKeyTest.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ let SwitchDeploymentKeyTest = createTestCaseComponent(
2525
},
2626
async () => {
2727
let update = await CodePush.checkForUpdate(deploymentKey);
28-
assert.equal(JSON.stringify(update), JSON.stringify({ ...serverPackage, ...PackageMixins.remote, failedInstall: false }), "checkForUpdate did not return the update from the server");
28+
assert.equal(JSON.stringify(update), JSON.stringify({ ...serverPackage, ...PackageMixins.remote, failedInstall: false, deploymentKey }), "checkForUpdate did not return the update from the server");
2929
}
3030
);
3131

Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/resources/remotePackage.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export default {
2+
deploymentKey: "myKey123",
23
description: "Angry flappy birds",
34
appVersion: "1.5.0",
45
label: "2.4.0",

Examples/CodePushDemoApp/CodePushDemoAppTests/utils/mockAcquisitionSdk.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ function createMockAcquisitionSdk(serverPackage, localPackage, expectedDeploymen
1313
callback(/*err:*/ null, serverPackage);
1414
};
1515

16+
AcquisitionManager.prototype.reportStatus = (status, message, callback) => {
17+
// No-op and return success.
18+
callback(null, null);
19+
};
20+
1621
return AcquisitionManager;
1722
}
1823

0 commit comments

Comments
 (0)