diff --git a/CodePush.js b/CodePush.js index 0ae086d..ff52fce 100644 --- a/CodePush.js +++ b/CodePush.js @@ -1,7 +1,7 @@ import { AcquisitionManager as Sdk } from "code-push/script/acquisition-sdk"; import { Alert } from "./AlertAdapter"; import requestFetchAdapter from "./request-fetch-adapter"; -import { Platform } from "react-native"; +import { AppState, Platform } from "react-native"; let NativeCodePush = require("react-native").NativeModules.CodePush; const PackageMixins = require("./package-mixins")(NativeCodePush); @@ -174,19 +174,42 @@ const notifyApplicationReady = (() => { async function notifyApplicationReadyInternal() { await NativeCodePush.notifyApplicationReady(); + tryReportStatus(); +} + +async function tryReportStatus(resumeListener) { const statusReport = await NativeCodePush.getNewStatusReport(); if (statusReport) { const config = await getConfiguration(); const previousLabelOrAppVersion = statusReport.previousLabelOrAppVersion; const previousDeploymentKey = statusReport.previousDeploymentKey || config.deploymentKey; - if (statusReport.appVersion) { - const sdk = getPromisifiedSdk(requestFetchAdapter, config); - sdk.reportStatusDeploy(/* deployedPackage */ null, /* status */ null, previousLabelOrAppVersion, previousDeploymentKey); - } else { - config.deploymentKey = statusReport.package.deploymentKey; - const sdk = getPromisifiedSdk(requestFetchAdapter, config); - sdk.reportStatusDeploy(statusReport.package, statusReport.status, previousLabelOrAppVersion, previousDeploymentKey); + try { + if (statusReport.appVersion) { + const sdk = getPromisifiedSdk(requestFetchAdapter, config); + await sdk.reportStatusDeploy(/* deployedPackage */ null, /* status */ null, previousLabelOrAppVersion, previousDeploymentKey); + } else { + config.deploymentKey = statusReport.package.deploymentKey; + const sdk = getPromisifiedSdk(requestFetchAdapter, config); + await sdk.reportStatusDeploy(statusReport.package, statusReport.status, previousLabelOrAppVersion, previousDeploymentKey); + } + + log(`Reported status: ${JSON.stringify(statusReport)}`); + NativeCodePush.recordStatusReported(statusReport); + resumeListener && AppState.removeEventListener("change", resumeListener); + } catch (e) { + log(`Report status failed: ${JSON.stringify(statusReport)}`); + NativeCodePush.saveStatusReportForRetry(statusReport); + // Try again when the app resumes + if (!resumeListener) { + resumeListener = (newState) => { + newState === "active" && tryReportStatus(resumeListener); + }; + + AppState.addEventListener("change", resumeListener); + } } + } else { + resumeListener && AppState.removeEventListener("change", resumeListener); } } diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java index ee50edc..7bbd414 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java @@ -626,6 +626,12 @@ public class CodePush implements ReactPackage { promise.resolve(newAppVersionStatusReport); return null; } + } else { + WritableMap retryStatusReport = codePushTelemetryManager.getRetryStatusReport(); + if (retryStatusReport != null) { + promise.resolve(retryStatusReport); + return null; + } } promise.resolve(""); @@ -720,6 +726,11 @@ public class CodePush implements ReactPackage { promise.resolve(""); } + @ReactMethod + public void recordStatusReported(ReadableMap statusReport) { + codePushTelemetryManager.recordStatusReported(statusReport); + } + @ReactMethod public void restartApp(boolean onlyIfUpdateIsPending) { // If this is an unconditional restart request, or there @@ -729,6 +740,11 @@ public class CodePush implements ReactPackage { } } + @ReactMethod + public void saveStatusReportForRetry(ReadableMap statusReport) { + codePushTelemetryManager.saveStatusReportForRetry(statusReport); + } + @ReactMethod // Replaces the current bundle with the one downloaded from removeBundleUrl. // It is only to be used during tests. No-ops if the test configuration flag is not set. diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePushTelemetryManager.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePushTelemetryManager.java index 86ae472..0c70a9a 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePushTelemetryManager.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePushTelemetryManager.java @@ -3,18 +3,28 @@ package com.microsoft.codepush.react; import android.content.Context; import android.content.SharedPreferences; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; +import org.json.JSONException; +import org.json.JSONObject; + public class CodePushTelemetryManager { private Context applicationContext; + private final String APP_VERSION_KEY = "appVersion"; private final String CODE_PUSH_PREFERENCES; private final String DEPLOYMENT_FAILED_STATUS = "DeploymentFailed"; private final String DEPLOYMENT_KEY_KEY = "deploymentKey"; private final String DEPLOYMENT_SUCCEEDED_STATUS = "DeploymentSucceeded"; private final String LABEL_KEY = "label"; private final String LAST_DEPLOYMENT_REPORT_KEY = "CODE_PUSH_LAST_DEPLOYMENT_REPORT"; + private final String PACKAGE_KEY = "package"; + private final String PREVIOUS_DEPLOYMENT_KEY_KEY = "previousDeploymentKey"; + private final String PREVIOUS_LABEL_OR_APP_VERSION_KEY = "previousLabelOrAppVersion"; + private final String RETRY_DEPLOYMENT_REPORT_KEY = "CODE_PUSH_RETRY_DEPLOYMENT_REPORT"; + private final String STATUS_KEY = "status"; public CodePushTelemetryManager(Context applicationContext, String codePushPreferencesKey) { this.applicationContext = applicationContext; @@ -23,26 +33,41 @@ public class CodePushTelemetryManager { public WritableMap getBinaryUpdateReport(String appVersion) { String previousStatusReportIdentifier = this.getPreviousStatusReportIdentifier(); + WritableNativeMap reportMap = null; if (previousStatusReportIdentifier == null) { - this.recordDeploymentStatusReported(appVersion); - WritableNativeMap reportMap = new WritableNativeMap(); - reportMap.putString("appVersion", appVersion); - return reportMap; + this.clearRetryStatusReport(); + reportMap = new WritableNativeMap(); + reportMap.putString(APP_VERSION_KEY, appVersion); } else if (!previousStatusReportIdentifier.equals(appVersion)) { - this.recordDeploymentStatusReported(appVersion); - WritableNativeMap reportMap = new WritableNativeMap(); + this.clearRetryStatusReport(); + reportMap = new WritableNativeMap(); if (this.isStatusReportIdentifierCodePushLabel(previousStatusReportIdentifier)) { String previousDeploymentKey = this.getDeploymentKeyFromStatusReportIdentifier(previousStatusReportIdentifier); String previousLabel = this.getVersionLabelFromStatusReportIdentifier(previousStatusReportIdentifier); - reportMap.putString("appVersion", appVersion); - reportMap.putString("previousDeploymentKey", previousDeploymentKey); - reportMap.putString("previousLabelOrAppVersion", previousLabel); + reportMap.putString(APP_VERSION_KEY, appVersion); + reportMap.putString(PREVIOUS_DEPLOYMENT_KEY_KEY, previousDeploymentKey); + reportMap.putString(PREVIOUS_LABEL_OR_APP_VERSION_KEY, previousLabel); } else { // Previous status report was with a binary app version. - reportMap.putString("appVersion", appVersion); - reportMap.putString("previousLabelOrAppVersion", previousStatusReportIdentifier); + reportMap.putString(APP_VERSION_KEY, appVersion); + reportMap.putString(PREVIOUS_LABEL_OR_APP_VERSION_KEY, previousStatusReportIdentifier); + } + } + + return reportMap; + } + + public WritableMap getRetryStatusReport() { + SharedPreferences settings = applicationContext.getSharedPreferences(CODE_PUSH_PREFERENCES, 0); + String retryStatusReportString = settings.getString(RETRY_DEPLOYMENT_REPORT_KEY, null); + if (retryStatusReportString != null) { + clearRetryStatusReport(); + try { + JSONObject retryStatusReport = new JSONObject(retryStatusReportString); + return CodePushUtils.convertJsonObjectToWritable(retryStatusReport); + } catch (JSONException e) { + e.printStackTrace(); } - return reportMap; } return null; @@ -50,44 +75,61 @@ public class CodePushTelemetryManager { public WritableMap getRollbackReport(WritableMap lastFailedPackage) { WritableNativeMap reportMap = new WritableNativeMap(); - reportMap.putMap("package", lastFailedPackage); - reportMap.putString("status", DEPLOYMENT_FAILED_STATUS); + reportMap.putMap(PACKAGE_KEY, lastFailedPackage); + reportMap.putString(STATUS_KEY, DEPLOYMENT_FAILED_STATUS); return reportMap; } public WritableMap getUpdateReport(WritableMap currentPackage) { String currentPackageIdentifier = this.getPackageStatusReportIdentifier(currentPackage); String previousStatusReportIdentifier = this.getPreviousStatusReportIdentifier(); + WritableNativeMap reportMap = null; if (currentPackageIdentifier != null) { if (previousStatusReportIdentifier == null) { - this.recordDeploymentStatusReported(currentPackageIdentifier); - WritableNativeMap reportMap = new WritableNativeMap(); - reportMap.putMap("package", currentPackage); - reportMap.putString("status", DEPLOYMENT_SUCCEEDED_STATUS); - return reportMap; + this.clearRetryStatusReport(); + reportMap = new WritableNativeMap(); + reportMap.putMap(PACKAGE_KEY, currentPackage); + reportMap.putString(STATUS_KEY, DEPLOYMENT_SUCCEEDED_STATUS); } else if (!previousStatusReportIdentifier.equals(currentPackageIdentifier)) { - this.recordDeploymentStatusReported(currentPackageIdentifier); + this.clearRetryStatusReport(); + reportMap = new WritableNativeMap(); if (this.isStatusReportIdentifierCodePushLabel(previousStatusReportIdentifier)) { String previousDeploymentKey = this.getDeploymentKeyFromStatusReportIdentifier(previousStatusReportIdentifier); String previousLabel = this.getVersionLabelFromStatusReportIdentifier(previousStatusReportIdentifier); - WritableNativeMap reportMap = new WritableNativeMap(); - reportMap.putMap("package", currentPackage); - reportMap.putString("status", DEPLOYMENT_SUCCEEDED_STATUS); - reportMap.putString("previousDeploymentKey", previousDeploymentKey); - reportMap.putString("previousLabelOrAppVersion", previousLabel); - return reportMap; + reportMap.putMap(PACKAGE_KEY, currentPackage); + reportMap.putString(STATUS_KEY, DEPLOYMENT_SUCCEEDED_STATUS); + reportMap.putString(PREVIOUS_DEPLOYMENT_KEY_KEY, previousDeploymentKey); + reportMap.putString(PREVIOUS_LABEL_OR_APP_VERSION_KEY, previousLabel); } else { // Previous status report was with a binary app version. - WritableNativeMap reportMap = new WritableNativeMap(); - reportMap.putMap("package", currentPackage); - reportMap.putString("status", DEPLOYMENT_SUCCEEDED_STATUS); - reportMap.putString("previousLabelOrAppVersion", previousStatusReportIdentifier); - return reportMap; + reportMap.putMap(PACKAGE_KEY, currentPackage); + reportMap.putString(STATUS_KEY, DEPLOYMENT_SUCCEEDED_STATUS); + reportMap.putString(PREVIOUS_LABEL_OR_APP_VERSION_KEY, previousStatusReportIdentifier); } } } - return null; + return reportMap; + } + + public void recordStatusReported(ReadableMap statusReport) { + if (statusReport.hasKey(APP_VERSION_KEY)) { + saveStatusReportedForIdentifier(statusReport.getString(APP_VERSION_KEY)); + } else if (statusReport.hasKey(PACKAGE_KEY)) { + String packageIdentifier = getPackageStatusReportIdentifier(statusReport.getMap(PACKAGE_KEY)); + saveStatusReportedForIdentifier(packageIdentifier); + } + } + + public void saveStatusReportForRetry(ReadableMap statusReport) { + SharedPreferences settings = applicationContext.getSharedPreferences(CODE_PUSH_PREFERENCES, 0); + JSONObject statusReportJSON = CodePushUtils.convertReadableToJsonObject(statusReport); + settings.edit().putString(RETRY_DEPLOYMENT_REPORT_KEY, statusReportJSON.toString()).commit(); + } + + private void clearRetryStatusReport() { + SharedPreferences settings = applicationContext.getSharedPreferences(CODE_PUSH_PREFERENCES, 0); + settings.edit().remove(RETRY_DEPLOYMENT_REPORT_KEY).commit(); } private String getDeploymentKeyFromStatusReportIdentifier(String statusReportIdentifier) { @@ -99,7 +141,7 @@ public class CodePushTelemetryManager { } } - private String getPackageStatusReportIdentifier(WritableMap updatePackage) { + private String getPackageStatusReportIdentifier(ReadableMap updatePackage) { // Because deploymentKeys can be dynamically switched, we use a // combination of the deploymentKey and label as the packageIdentifier. String deploymentKey = CodePushUtils.tryGetString(updatePackage, DEPLOYMENT_KEY_KEY); @@ -129,7 +171,7 @@ public class CodePushTelemetryManager { return statusReportIdentifier != null && statusReportIdentifier.contains(":"); } - private void recordDeploymentStatusReported(String appVersionOrPackageIdentifier) { + private void saveStatusReportedForIdentifier(String appVersionOrPackageIdentifier) { SharedPreferences settings = applicationContext.getSharedPreferences(CODE_PUSH_PREFERENCES, 0); settings.edit().putString(LAST_DEPLOYMENT_REPORT_KEY, appVersionOrPackageIdentifier).commit(); } diff --git a/ios/CodePush/CodePush.h b/ios/CodePush/CodePush.h index 06ac51b..782b5b9 100644 --- a/ios/CodePush/CodePush.h +++ b/ios/CodePush/CodePush.h @@ -112,8 +112,11 @@ failCallback:(void (^)(NSError *err))failCallback; @interface CodePushTelemetryManager : NSObject + (NSDictionary *)getBinaryUpdateReport:(NSString *)appVersion; ++ (NSDictionary *)getRetryStatusReport; + (NSDictionary *)getRollbackReport:(NSDictionary *)lastFailedPackage; + (NSDictionary *)getUpdateReport:(NSDictionary *)currentPackage; ++ (void)recordStatusReported:(NSDictionary *)statusReport; ++ (void)saveStatusReportForRetry:(NSDictionary *)statusReport; @end diff --git a/ios/CodePush/CodePush.m b/ios/CodePush/CodePush.m index 10ece53..25c4aa3 100644 --- a/ios/CodePush/CodePush.m +++ b/ios/CodePush/CodePush.m @@ -15,7 +15,7 @@ BOOL _isFirstRunAfterUpdate; int _minimumBackgroundDuration; NSDate *_lastResignedDate; - + // Used to coordinate the dispatching of download progress events to JS. long long _latestExpectedContentLength; long long _latestReceivedConentLength; @@ -507,14 +507,14 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage [mutableUpdatePackage setValue:[CodePushUpdateUtils modifiedDateStringOfFileAtURL:binaryBundleURL] forKey:BinaryBundleDateKey]; } - + if (notifyProgress) { // Set up and unpause the frame observer so that it can emit // progress events every frame if the progress is updated. _didUpdateProgress = NO; _paused = NO; } - + [CodePushPackage downloadPackage:mutableUpdatePackage expectedBundleFileName:[bundleResourceName stringByAppendingPathExtension:bundleResourceExtension] @@ -525,7 +525,7 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage _latestExpectedContentLength = expectedContentLength; _latestReceivedConentLength = receivedContentLength; _didUpdateProgress = YES; - + // If the download is completed, stop observing frame // updates and synchronously send the last event. if (expectedContentLength == receivedContentLength) { @@ -549,7 +549,7 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage if ([CodePushErrorUtils isCodePushError:err]) { [self saveFailedUpdate:mutableUpdatePackage]; } - + // Stop observing frame updates if the download fails. _didUpdateProgress = NO; _paused = YES; @@ -783,11 +783,27 @@ RCT_EXPORT_METHOD(getNewStatusReport:(RCTPromiseResolveBlock)resolve NSString *appVersion = [[CodePushConfig current] appVersion]; resolve([CodePushTelemetryManager getBinaryUpdateReport:appVersion]); return; + } else { + NSDictionary *retryStatusReport = [CodePushTelemetryManager getRetryStatusReport]; + if (retryStatusReport) { + resolve(retryStatusReport); + return; + } } resolve(nil); } +RCT_EXPORT_METHOD(recordStatusReported:(NSDictionary *)statusReport) +{ + [CodePushTelemetryManager recordStatusReported:statusReport]; +} + +RCT_EXPORT_METHOD(saveStatusReportForRetry:(NSDictionary *)statusReport) +{ + [CodePushTelemetryManager saveStatusReportForRetry:statusReport]; +} + #pragma mark - RCTFrameUpdateObserver Methods - (void)didUpdateFrame:(RCTFrameUpdate *)update @@ -795,7 +811,7 @@ RCT_EXPORT_METHOD(getNewStatusReport:(RCTPromiseResolveBlock)resolve if (!_didUpdateProgress) { return; } - + [self dispatchDownloadProgressEvent]; _didUpdateProgress = NO; } diff --git a/ios/CodePush/CodePushDownloadHandler.m b/ios/CodePush/CodePushDownloadHandler.m index e352116..dd4c17c 100644 --- a/ios/CodePush/CodePushDownloadHandler.m +++ b/ios/CodePush/CodePushDownloadHandler.m @@ -20,7 +20,7 @@ failCallback:(void (^)(NSError *err))failCallback { return self; } --(void)download:(NSString*)url { +- (void)download:(NSString*)url { NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; @@ -47,12 +47,12 @@ failCallback:(void (^)(NSError *err))failCallback { return nil; } --(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { self.expectedContentLength = response.expectedContentLength; [self.outputFileStream open]; } --(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { if (self.receivedContentLength < 4) { for (int i = 0; i < [data length]; i++) { int headerOffset = (int)self.receivedContentLength + i; @@ -97,7 +97,7 @@ failCallback:(void (^)(NSError *err))failCallback { self.failCallback(error); } --(void)connectionDidFinishLoading:(NSURLConnection *)connection { +- (void)connectionDidFinishLoading:(NSURLConnection *)connection { // expectedContentLength might be -1 when NSURLConnection don't know the length(e.g. response encode with gzip) if (self.expectedContentLength > 0) { // We should have received all of the bytes if this is called. diff --git a/ios/CodePush/CodePushTelemetryManager.m b/ios/CodePush/CodePushTelemetryManager.m index 4c13f10..21988fa 100644 --- a/ios/CodePush/CodePushTelemetryManager.m +++ b/ios/CodePush/CodePushTelemetryManager.m @@ -1,10 +1,16 @@ #import "CodePush.h" +static NSString *const AppVersionKey = @"appVersion"; static NSString *const DeploymentFailed = @"DeploymentFailed"; static NSString *const DeploymentKeyKey = @"deploymentKey"; static NSString *const DeploymentSucceeded = @"DeploymentSucceeded"; static NSString *const LabelKey = @"label"; static NSString *const LastDeploymentReportKey = @"CODE_PUSH_LAST_DEPLOYMENT_REPORT"; +static NSString *const PackageKey = @"package"; +static NSString *const PreviousDeploymentKeyKey = @"previousDeploymentKey"; +static NSString *const PreviousLabelOrAppVersionKey = @"previousLabelOrAppVersion"; +static NSString *const RetryDeploymentReportKey = @"CODE_PUSH_RETRY_DEPLOYMENT_REPORT"; +static NSString *const StatusKey = @"status"; @implementation CodePushTelemetryManager @@ -12,35 +18,48 @@ static NSString *const LastDeploymentReportKey = @"CODE_PUSH_LAST_DEPLOYMENT_REP { NSString *previousStatusReportIdentifier = [self getPreviousStatusReportIdentifier]; if (previousStatusReportIdentifier == nil) { - [self recordDeploymentStatusReported:appVersion]; - return @{ @"appVersion": appVersion }; + [self clearRetryStatusReport]; + return @{ AppVersionKey: appVersion }; } else if (![previousStatusReportIdentifier isEqualToString:appVersion]) { - [self recordDeploymentStatusReported:appVersion]; if ([self isStatusReportIdentifierCodePushLabel:previousStatusReportIdentifier]) { NSString *previousDeploymentKey = [self getDeploymentKeyFromStatusReportIdentifier:previousStatusReportIdentifier]; NSString *previousLabel = [self getVersionLabelFromStatusReportIdentifier:previousStatusReportIdentifier]; + [self clearRetryStatusReport]; return @{ - @"appVersion": appVersion, - @"previousDeploymentKey": previousDeploymentKey, - @"previousLabelOrAppVersion": previousLabel + AppVersionKey: appVersion, + PreviousDeploymentKeyKey: previousDeploymentKey, + PreviousLabelOrAppVersionKey: previousLabel }; } else { + [self clearRetryStatusReport]; // Previous status report was with a binary app version. return @{ - @"appVersion": appVersion, - @"previousLabelOrAppVersion": previousStatusReportIdentifier + AppVersionKey: appVersion, + PreviousLabelOrAppVersionKey: previousStatusReportIdentifier }; } } - + return nil; } ++ (NSDictionary *)getRetryStatusReport +{ + NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; + NSDictionary *retryStatusReport = [preferences objectForKey:RetryDeploymentReportKey]; + if (retryStatusReport) { + [self clearRetryStatusReport]; + return retryStatusReport; + } else { + return nil; + } +} + + (NSDictionary *)getRollbackReport:(NSDictionary *)lastFailedPackage { return @{ - @"package": lastFailedPackage, - @"status": DeploymentFailed + PackageKey: lastFailedPackage, + StatusKey: DeploymentFailed }; } @@ -50,38 +69,62 @@ static NSString *const LastDeploymentReportKey = @"CODE_PUSH_LAST_DEPLOYMENT_REP NSString *previousStatusReportIdentifier = [self getPreviousStatusReportIdentifier]; if (currentPackageIdentifier) { if (previousStatusReportIdentifier == nil) { - [self recordDeploymentStatusReported:currentPackageIdentifier]; + [self clearRetryStatusReport]; return @{ - @"package": currentPackage, - @"status": DeploymentSucceeded + PackageKey: currentPackage, + StatusKey: DeploymentSucceeded }; } else if (![previousStatusReportIdentifier isEqualToString:currentPackageIdentifier]) { - [self recordDeploymentStatusReported:currentPackageIdentifier]; + [self clearRetryStatusReport]; if ([self isStatusReportIdentifierCodePushLabel:previousStatusReportIdentifier]) { NSString *previousDeploymentKey = [self getDeploymentKeyFromStatusReportIdentifier:previousStatusReportIdentifier]; NSString *previousLabel = [self getVersionLabelFromStatusReportIdentifier:previousStatusReportIdentifier]; return @{ - @"package": currentPackage, - @"status": DeploymentSucceeded, - @"previousDeploymentKey": previousDeploymentKey, - @"previousLabelOrAppVersion": previousLabel + PackageKey: currentPackage, + StatusKey: DeploymentSucceeded, + PreviousDeploymentKeyKey: previousDeploymentKey, + PreviousLabelOrAppVersionKey: previousLabel }; } else { // Previous status report was with a binary app version. return @{ - @"package": currentPackage, - @"status": DeploymentSucceeded, - @"previousLabelOrAppVersion": previousStatusReportIdentifier + PackageKey: currentPackage, + StatusKey: DeploymentSucceeded, + PreviousLabelOrAppVersionKey: previousStatusReportIdentifier }; } } } - + return nil; } ++ (void)recordStatusReported:(NSDictionary *)statusReport +{ + if (statusReport[AppVersionKey]) { + [self saveStatusReportedForIdentifier:statusReport[AppVersionKey]]; + } else if (statusReport[PackageKey]) { + NSString *packageIdentifier = [self getPackageStatusReportIdentifier:statusReport[PackageKey]]; + [self saveStatusReportedForIdentifier:packageIdentifier]; + } +} + ++ (void)saveStatusReportForRetry:(NSDictionary *)statusReport +{ + NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; + [preferences setValue:statusReport forKey:RetryDeploymentReportKey]; + [preferences synchronize]; +} + #pragma mark - private methods ++ (void)clearRetryStatusReport +{ + NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; + [preferences setValue:nil forKey:RetryDeploymentReportKey]; + [preferences synchronize]; +} + + (NSString *)getDeploymentKeyFromStatusReportIdentifier:(NSString *)statusReportIdentifier { return [[statusReportIdentifier componentsSeparatedByString:@":"] firstObject]; @@ -117,11 +160,11 @@ static NSString *const LastDeploymentReportKey = @"CODE_PUSH_LAST_DEPLOYMENT_REP return statusReportIdentifier != nil && [statusReportIdentifier rangeOfString:@":"].location != NSNotFound; } -+ (void)recordDeploymentStatusReported:(NSString *)appVersionOrPackageIdentifier ++ (void)saveStatusReportedForIdentifier:(NSString *)appVersionOrPackageIdentifier { NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; [preferences setValue:appVersionOrPackageIdentifier forKey:LastDeploymentReportKey]; [preferences synchronize]; } -@end \ No newline at end of file +@end