@algocare/react-native-code-push 9.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/AlertAdapter.js +24 -0
  2. package/CONTRIBUTING.md +134 -0
  3. package/CodePush.js +778 -0
  4. package/CodePush.podspec +27 -0
  5. package/LICENSE.md +13 -0
  6. package/README.md +491 -0
  7. package/SECURITY.md +41 -0
  8. package/android/app/build.gradle +31 -0
  9. package/android/app/proguard-rules.pro +25 -0
  10. package/android/app/src/main/AndroidManifest.xml +6 -0
  11. package/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java +441 -0
  12. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushBuilder.java +37 -0
  13. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushConstants.java +35 -0
  14. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushDialog.java +102 -0
  15. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushInstallMode.java +16 -0
  16. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPublicKeyException.java +12 -0
  17. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidUpdateException.java +7 -0
  18. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushMalformedDataException.java +12 -0
  19. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java +714 -0
  20. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushNotInitializedException.java +12 -0
  21. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushTelemetryManager.java +175 -0
  22. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUnknownException.java +12 -0
  23. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateManager.java +383 -0
  24. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateState.java +15 -0
  25. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateUtils.java +268 -0
  26. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java +238 -0
  27. package/android/app/src/main/java/com/microsoft/codepush/react/DownloadProgress.java +30 -0
  28. package/android/app/src/main/java/com/microsoft/codepush/react/DownloadProgressCallback.java +5 -0
  29. package/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java +203 -0
  30. package/android/app/src/main/java/com/microsoft/codepush/react/ReactInstanceHolder.java +17 -0
  31. package/android/app/src/main/java/com/microsoft/codepush/react/SettingsManager.java +173 -0
  32. package/android/app/src/main/java/com/microsoft/codepush/react/TLSSocketFactory.java +72 -0
  33. package/android/build.gradle +21 -0
  34. package/android/codepush.gradle +162 -0
  35. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  36. package/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  37. package/android/gradle.properties +20 -0
  38. package/android/gradlew +164 -0
  39. package/android/gradlew.bat +90 -0
  40. package/android/settings.gradle +1 -0
  41. package/babel.config.js +3 -0
  42. package/cli/commands/bundleCommand/bundleCodePush.js +57 -0
  43. package/cli/commands/bundleCommand/index.js +29 -0
  44. package/cli/commands/createHistoryCommand/createReleaseHistory.js +53 -0
  45. package/cli/commands/createHistoryCommand/index.js +29 -0
  46. package/cli/commands/releaseCommand/addToReleaseHistory.js +75 -0
  47. package/cli/commands/releaseCommand/index.js +66 -0
  48. package/cli/commands/releaseCommand/release.js +109 -0
  49. package/cli/commands/showHistoryCommand/index.js +29 -0
  50. package/cli/commands/updateHistoryCommand/index.js +50 -0
  51. package/cli/commands/updateHistoryCommand/updateReleaseHistory.js +62 -0
  52. package/cli/constant.js +6 -0
  53. package/cli/functions/getReactTempDir.js +16 -0
  54. package/cli/functions/makeCodePushBundle.js +28 -0
  55. package/cli/functions/prepareToBundleJS.js +12 -0
  56. package/cli/functions/runHermesEmitBinaryCommand.js +213 -0
  57. package/cli/functions/runReactNativeBundleCommand.js +59 -0
  58. package/cli/index.js +43 -0
  59. package/cli/utils/file-utils.js +42 -0
  60. package/cli/utils/fsUtils.js +49 -0
  61. package/cli/utils/hash-utils.js +227 -0
  62. package/cli/utils/promisfied-fs.js +29 -0
  63. package/cli/utils/showLogo.js +23 -0
  64. package/cli/utils/zip.js +89 -0
  65. package/code-push.config.example.supabase.ts +114 -0
  66. package/docs/api-android.md +83 -0
  67. package/docs/api-ios.md +31 -0
  68. package/docs/api-js.md +592 -0
  69. package/docs/multi-deployment-testing-android.md +148 -0
  70. package/docs/multi-deployment-testing-ios.md +59 -0
  71. package/docs/setup-android.md +482 -0
  72. package/docs/setup-ios.md +280 -0
  73. package/eslint.config.mjs +32 -0
  74. package/ios/CodePush/Base64/Base64/MF_Base64Additions.h +34 -0
  75. package/ios/CodePush/Base64/Base64/MF_Base64Additions.m +252 -0
  76. package/ios/CodePush/Base64/README.md +47 -0
  77. package/ios/CodePush/CodePush.h +235 -0
  78. package/ios/CodePush/CodePush.m +1121 -0
  79. package/ios/CodePush/CodePushConfig.m +116 -0
  80. package/ios/CodePush/CodePushDownloadHandler.m +130 -0
  81. package/ios/CodePush/CodePushErrorUtils.m +20 -0
  82. package/ios/CodePush/CodePushPackage.m +602 -0
  83. package/ios/CodePush/CodePushTelemetryManager.m +175 -0
  84. package/ios/CodePush/CodePushUpdateUtils.m +376 -0
  85. package/ios/CodePush/CodePushUtils.m +9 -0
  86. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithm.h +69 -0
  87. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmFactory.h +16 -0
  88. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmFactory.m +51 -0
  89. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmNone.h +15 -0
  90. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmNone.m +55 -0
  91. package/ios/CodePush/JWT/Core/Algorithms/ESFamily/JWTAlgorithmESBase.h +24 -0
  92. package/ios/CodePush/JWT/Core/Algorithms/ESFamily/JWTAlgorithmESBase.m +41 -0
  93. package/ios/CodePush/JWT/Core/Algorithms/HSFamily/JWTAlgorithmHSBase.h +28 -0
  94. package/ios/CodePush/JWT/Core/Algorithms/HSFamily/JWTAlgorithmHSBase.m +205 -0
  95. package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolder.h +103 -0
  96. package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolder.m +322 -0
  97. package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolderChain.h +37 -0
  98. package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolderChain.m +145 -0
  99. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/JWTAlgorithmRSBase.h +35 -0
  100. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/JWTAlgorithmRSBase.m +551 -0
  101. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/JWTRSAlgorithm.h +23 -0
  102. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKey.h +43 -0
  103. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKey.m +230 -0
  104. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKeyExtractor.h +31 -0
  105. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKeyExtractor.m +113 -0
  106. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoSecurity.h +38 -0
  107. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoSecurity.m +500 -0
  108. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaim.h +18 -0
  109. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaim.m +214 -0
  110. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSet.h +23 -0
  111. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSet.m +29 -0
  112. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetSerializer.h +19 -0
  113. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetSerializer.m +68 -0
  114. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetVerifier.h +18 -0
  115. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetVerifier.m +72 -0
  116. package/ios/CodePush/JWT/Core/Coding/JWTCoding+ResultTypes.h +67 -0
  117. package/ios/CodePush/JWT/Core/Coding/JWTCoding+ResultTypes.m +111 -0
  118. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionOne.h +119 -0
  119. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionOne.m +307 -0
  120. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionThree.h +94 -0
  121. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionThree.m +619 -0
  122. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionTwo.h +164 -0
  123. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionTwo.m +514 -0
  124. package/ios/CodePush/JWT/Core/Coding/JWTCoding.h +24 -0
  125. package/ios/CodePush/JWT/Core/Coding/JWTCoding.m +11 -0
  126. package/ios/CodePush/JWT/Core/FrameworkSupplement/JWT.h +52 -0
  127. package/ios/CodePush/JWT/Core/FrameworkSupplement/Map.modulemap +5 -0
  128. package/ios/CodePush/JWT/Core/Supplement/JWTBase64Coder.h +28 -0
  129. package/ios/CodePush/JWT/Core/Supplement/JWTBase64Coder.m +70 -0
  130. package/ios/CodePush/JWT/Core/Supplement/JWTDeprecations.h +22 -0
  131. package/ios/CodePush/JWT/Core/Supplement/JWTErrorDescription.h +34 -0
  132. package/ios/CodePush/JWT/Core/Supplement/JWTErrorDescription.m +73 -0
  133. package/ios/CodePush/JWT/LICENSE +19 -0
  134. package/ios/CodePush/JWT/README.md +489 -0
  135. package/ios/CodePush/RCTConvert+CodePushInstallMode.m +20 -0
  136. package/ios/CodePush/RCTConvert+CodePushUpdateState.m +20 -0
  137. package/ios/CodePush/SSZipArchive/Common.h +81 -0
  138. package/ios/CodePush/SSZipArchive/README.md +1 -0
  139. package/ios/CodePush/SSZipArchive/SSZipArchive.h +76 -0
  140. package/ios/CodePush/SSZipArchive/SSZipArchive.m +691 -0
  141. package/ios/CodePush/SSZipArchive/aes/aes.h +198 -0
  142. package/ios/CodePush/SSZipArchive/aes/aes_via_ace.h +541 -0
  143. package/ios/CodePush/SSZipArchive/aes/aescrypt.c +294 -0
  144. package/ios/CodePush/SSZipArchive/aes/aeskey.c +548 -0
  145. package/ios/CodePush/SSZipArchive/aes/aesopt.h +739 -0
  146. package/ios/CodePush/SSZipArchive/aes/aestab.c +391 -0
  147. package/ios/CodePush/SSZipArchive/aes/aestab.h +173 -0
  148. package/ios/CodePush/SSZipArchive/aes/brg_endian.h +126 -0
  149. package/ios/CodePush/SSZipArchive/aes/brg_types.h +219 -0
  150. package/ios/CodePush/SSZipArchive/aes/entropy.c +54 -0
  151. package/ios/CodePush/SSZipArchive/aes/entropy.h +16 -0
  152. package/ios/CodePush/SSZipArchive/aes/fileenc.c +144 -0
  153. package/ios/CodePush/SSZipArchive/aes/fileenc.h +121 -0
  154. package/ios/CodePush/SSZipArchive/aes/hmac.c +145 -0
  155. package/ios/CodePush/SSZipArchive/aes/hmac.h +103 -0
  156. package/ios/CodePush/SSZipArchive/aes/prng.c +155 -0
  157. package/ios/CodePush/SSZipArchive/aes/prng.h +82 -0
  158. package/ios/CodePush/SSZipArchive/aes/pwd2key.c +103 -0
  159. package/ios/CodePush/SSZipArchive/aes/pwd2key.h +57 -0
  160. package/ios/CodePush/SSZipArchive/aes/sha1.c +258 -0
  161. package/ios/CodePush/SSZipArchive/aes/sha1.h +73 -0
  162. package/ios/CodePush/SSZipArchive/minizip/crypt.h +130 -0
  163. package/ios/CodePush/SSZipArchive/minizip/ioapi.c +369 -0
  164. package/ios/CodePush/SSZipArchive/minizip/ioapi.h +175 -0
  165. package/ios/CodePush/SSZipArchive/minizip/mztools.c +284 -0
  166. package/ios/CodePush/SSZipArchive/minizip/mztools.h +31 -0
  167. package/ios/CodePush/SSZipArchive/minizip/unzip.c +1839 -0
  168. package/ios/CodePush/SSZipArchive/minizip/unzip.h +248 -0
  169. package/ios/CodePush/SSZipArchive/minizip/zip.c +1910 -0
  170. package/ios/CodePush/SSZipArchive/minizip/zip.h +202 -0
  171. package/ios/CodePush.xcodeproj/project.pbxproj +937 -0
  172. package/ios/CodePush.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  173. package/ios/CodePush.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  174. package/logging.js +6 -0
  175. package/package-mixins.js +61 -0
  176. package/package.json +84 -0
  177. package/react-native.config.js +12 -0
  178. package/scripts/generateBundledResourcesHash.js +125 -0
  179. package/scripts/getFilesInFolder.js +19 -0
  180. package/scripts/recordFilesBeforeBundleCommand.js +41 -0
  181. package/tsconfig.json +14 -0
  182. package/tslint.json +32 -0
  183. package/typings/react-native-code-push.d.ts +589 -0
  184. package/versioning/BaseVersioning.js +126 -0
  185. package/versioning/BaseVersioning.test.js +15 -0
  186. package/versioning/IncrementalVersioning.js +9 -0
  187. package/versioning/IncrementalVersioning.test.js +186 -0
  188. package/versioning/SemverVersioning.js +10 -0
  189. package/versioning/SemverVersioning.test.js +205 -0
  190. package/versioning/index.js +7 -0
@@ -0,0 +1,1121 @@
1
+ #if __has_include(<React/RCTAssert.h>)
2
+ #import <React/RCTAssert.h>
3
+ #import <React/RCTBridgeModule.h>
4
+ #import <React/RCTConvert.h>
5
+ #import <React/RCTEventDispatcher.h>
6
+ #import <React/RCTRootView.h>
7
+ #import <React/RCTUtils.h>
8
+ #else // back compatibility for RN version < 0.40
9
+ #import "RCTAssert.h"
10
+ #import "RCTBridgeModule.h"
11
+ #import "RCTConvert.h"
12
+ #import "RCTEventDispatcher.h"
13
+ #import "RCTRootView.h"
14
+ #import "RCTUtils.h"
15
+ #endif
16
+
17
+ #import "CodePush.h"
18
+
19
+ @interface CodePush () <RCTBridgeModule, RCTFrameUpdateObserver>
20
+ @end
21
+
22
+ @implementation CodePush {
23
+ BOOL _hasResumeListener;
24
+ BOOL _isFirstRunAfterUpdate;
25
+ int _minimumBackgroundDuration;
26
+ NSDate *_lastResignedDate;
27
+ CodePushInstallMode _installMode;
28
+ NSTimer *_appSuspendTimer;
29
+
30
+ // Used to coordinate the dispatching of download progress events to JS.
31
+ long long _latestExpectedContentLength;
32
+ long long _latestReceivedConentLength;
33
+ BOOL _didUpdateProgress;
34
+
35
+ BOOL _allowed;
36
+ BOOL _restartInProgress;
37
+ NSMutableArray *_restartQueue;
38
+ }
39
+
40
+ RCT_EXPORT_MODULE()
41
+
42
+ #pragma mark - Private constants
43
+
44
+ // These constants represent emitted events
45
+ static NSString *const DownloadProgressEvent = @"CodePushDownloadProgress";
46
+
47
+ // These constants represent valid deployment statuses
48
+ static NSString *const DeploymentFailed = @"DeploymentFailed";
49
+ static NSString *const DeploymentSucceeded = @"DeploymentSucceeded";
50
+
51
+ // These keys represent the names we use to store data in NSUserDefaults
52
+ static NSString *const FailedUpdatesKey = @"CODE_PUSH_FAILED_UPDATES";
53
+ static NSString *const PendingUpdateKey = @"CODE_PUSH_PENDING_UPDATE";
54
+
55
+ // These keys are already "namespaced" by the PendingUpdateKey, so
56
+ // their values don't need to be obfuscated to prevent collision with app data
57
+ static NSString *const PendingUpdateHashKey = @"hash";
58
+ static NSString *const PendingUpdateIsLoadingKey = @"isLoading";
59
+
60
+ // These keys are used to inspect/augment the metadata
61
+ // that is associated with an update's package.
62
+ static NSString *const AppVersionKey = @"appVersion";
63
+ static NSString *const BinaryBundleDateKey = @"binaryDate";
64
+ static NSString *const PackageHashKey = @"packageHash";
65
+ static NSString *const PackageIsPendingKey = @"isPending";
66
+
67
+ #pragma mark - Static variables
68
+
69
+ static BOOL isRunningBinaryVersion = NO;
70
+ static BOOL needToReportRollback = NO;
71
+ static BOOL testConfigurationFlag = NO;
72
+
73
+ // These values are used to save the NS bundle, name, extension and subdirectory
74
+ // for the JS bundle in the binary.
75
+ static NSBundle *bundleResourceBundle = nil;
76
+ static NSString *bundleResourceExtension = @"jsbundle";
77
+ static NSString *bundleResourceName = @"main";
78
+ static NSString *bundleResourceSubdirectory = nil;
79
+
80
+ // These keys represent the names we use to store information about the latest rollback
81
+ static NSString *const LatestRollbackInfoKey = @"LATEST_ROLLBACK_INFO";
82
+ static NSString *const LatestRollbackPackageHashKey = @"packageHash";
83
+ static NSString *const LatestRollbackTimeKey = @"time";
84
+ static NSString *const LatestRollbackCountKey = @"count";
85
+
86
+ + (void)initialize
87
+ {
88
+ [super initialize];
89
+ if (self == [CodePush class]) {
90
+ // Use the mainBundle by default.
91
+ bundleResourceBundle = [NSBundle mainBundle];
92
+ }
93
+ }
94
+
95
+ #pragma mark - Public Obj-C API
96
+
97
+ + (NSURL *)binaryBundleURL
98
+ {
99
+ return [bundleResourceBundle URLForResource:bundleResourceName
100
+ withExtension:bundleResourceExtension
101
+ subdirectory:bundleResourceSubdirectory];
102
+ }
103
+
104
+ + (NSString *)bundleAssetsPath
105
+ {
106
+ NSString *resourcePath = [bundleResourceBundle resourcePath];
107
+ if (bundleResourceSubdirectory) {
108
+ resourcePath = [resourcePath stringByAppendingPathComponent:bundleResourceSubdirectory];
109
+ }
110
+
111
+ return [resourcePath stringByAppendingPathComponent:[CodePushUpdateUtils assetsFolderName]];
112
+ }
113
+
114
+ + (NSURL *)bundleURL
115
+ {
116
+ return [self bundleURLForResource:bundleResourceName
117
+ withExtension:bundleResourceExtension
118
+ subdirectory:bundleResourceSubdirectory
119
+ bundle:bundleResourceBundle];
120
+ }
121
+
122
+ + (NSURL *)bundleURLForResource:(NSString *)resourceName
123
+ {
124
+ return [self bundleURLForResource:resourceName
125
+ withExtension:bundleResourceExtension
126
+ subdirectory:bundleResourceSubdirectory
127
+ bundle:bundleResourceBundle];
128
+ }
129
+
130
+ + (NSURL *)bundleURLForResource:(NSString *)resourceName
131
+ withExtension:(NSString *)resourceExtension
132
+ {
133
+ return [self bundleURLForResource:resourceName
134
+ withExtension:resourceExtension
135
+ subdirectory:bundleResourceSubdirectory
136
+ bundle:bundleResourceBundle];
137
+ }
138
+
139
+ + (NSURL *)bundleURLForResource:(NSString *)resourceName
140
+ withExtension:(NSString *)resourceExtension
141
+ subdirectory:(NSString *)resourceSubdirectory
142
+ {
143
+ return [self bundleURLForResource:resourceName
144
+ withExtension:resourceExtension
145
+ subdirectory:resourceSubdirectory
146
+ bundle:bundleResourceBundle];
147
+ }
148
+
149
+ + (NSURL *)bundleURLForResource:(NSString *)resourceName
150
+ withExtension:(NSString *)resourceExtension
151
+ subdirectory:(NSString *)resourceSubdirectory
152
+ bundle:(NSBundle *)resourceBundle
153
+ {
154
+ bundleResourceName = resourceName;
155
+ bundleResourceExtension = resourceExtension;
156
+ bundleResourceSubdirectory = resourceSubdirectory;
157
+ bundleResourceBundle = resourceBundle;
158
+
159
+ [self ensureBinaryBundleExists];
160
+
161
+ NSString *logMessageFormat = @"Loading JS bundle from %@";
162
+
163
+ NSError *error;
164
+ NSString *packageFile = [CodePushPackage getCurrentPackageBundlePath:&error];
165
+ NSURL *binaryBundleURL = [self binaryBundleURL];
166
+
167
+ if (error || !packageFile) {
168
+ CPLog(logMessageFormat, binaryBundleURL);
169
+ isRunningBinaryVersion = YES;
170
+ return binaryBundleURL;
171
+ }
172
+
173
+ NSString *binaryAppVersion = [[CodePushConfig current] appVersion];
174
+ NSDictionary *currentPackageMetadata = [CodePushPackage getCurrentPackage:&error];
175
+ if (error || !currentPackageMetadata) {
176
+ CPLog(logMessageFormat, binaryBundleURL);
177
+ isRunningBinaryVersion = YES;
178
+ return binaryBundleURL;
179
+ }
180
+
181
+ NSString *packageDate = [currentPackageMetadata objectForKey:BinaryBundleDateKey];
182
+ NSString *packageAppVersion = [currentPackageMetadata objectForKey:AppVersionKey];
183
+
184
+ if ([[CodePushUpdateUtils modifiedDateStringOfFileAtURL:binaryBundleURL] isEqualToString:packageDate] && ([CodePush isUsingTestConfiguration] ||[binaryAppVersion isEqualToString:packageAppVersion])) {
185
+ // Return package file because it is newer than the app store binary's JS bundle
186
+ NSURL *packageUrl = [[NSURL alloc] initFileURLWithPath:packageFile];
187
+ CPLog(logMessageFormat, packageUrl);
188
+ isRunningBinaryVersion = NO;
189
+ return packageUrl;
190
+ } else {
191
+ BOOL isRelease = NO;
192
+ #ifndef DEBUG
193
+ isRelease = YES;
194
+ #endif
195
+
196
+ if (isRelease || ![binaryAppVersion isEqualToString:packageAppVersion]) {
197
+ [CodePush clearUpdates];
198
+ }
199
+
200
+ CPLog(logMessageFormat, binaryBundleURL);
201
+ isRunningBinaryVersion = YES;
202
+ return binaryBundleURL;
203
+ }
204
+ }
205
+
206
+ + (NSString *)getApplicationSupportDirectory
207
+ {
208
+ NSString *applicationSupportDirectory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0];
209
+ return applicationSupportDirectory;
210
+ }
211
+
212
+ + (void)overrideAppVersion:(NSString *)appVersion
213
+ {
214
+ [CodePushConfig current].appVersion = appVersion;
215
+ }
216
+
217
+ + (void)setDeploymentKey:(NSString *)deploymentKey
218
+ {
219
+ [CodePushConfig current].deploymentKey = deploymentKey;
220
+ }
221
+
222
+ /*
223
+ * WARNING: This cleans up all downloaded and pending updates.
224
+ */
225
+ + (void)clearUpdates
226
+ {
227
+ [CodePushPackage clearUpdates];
228
+ [self removePendingUpdate];
229
+ [self removeFailedUpdates];
230
+ }
231
+
232
+ #pragma mark - Test-only methods
233
+
234
+ /*
235
+ * This returns a boolean value indicating whether CodePush has
236
+ * been set to run under a test configuration.
237
+ */
238
+ + (BOOL)isUsingTestConfiguration
239
+ {
240
+ return testConfigurationFlag;
241
+ }
242
+
243
+ /*
244
+ * This is used to enable an environment in which tests can be run.
245
+ * Specifically, it flips a boolean flag that causes bundles to be
246
+ * saved to a test folder and enables the ability to modify
247
+ * installed bundles on the fly from JavaScript.
248
+ */
249
+ + (void)setUsingTestConfiguration:(BOOL)shouldUseTestConfiguration
250
+ {
251
+ testConfigurationFlag = shouldUseTestConfiguration;
252
+ }
253
+
254
+ #pragma mark - Private API methods
255
+
256
+ @synthesize methodQueue = _methodQueue;
257
+ @synthesize pauseCallback = _pauseCallback;
258
+ @synthesize paused = _paused;
259
+
260
+ - (void)setPaused:(BOOL)paused
261
+ {
262
+ if (_paused != paused) {
263
+ _paused = paused;
264
+ if (_pauseCallback) {
265
+ _pauseCallback();
266
+ }
267
+ }
268
+ }
269
+
270
+ /*
271
+ * This method is used to clear updates that are installed
272
+ * under a different app version and hence don't apply anymore,
273
+ * during a debug run configuration and when the bridge is
274
+ * running the JS bundle from the dev server.
275
+ */
276
+ - (void)clearDebugUpdates
277
+ {
278
+ dispatch_async(dispatch_get_main_queue(), ^{
279
+ if ([super.bridge.bundleURL.scheme hasPrefix:@"http"]) {
280
+ NSError *error;
281
+ NSString *binaryAppVersion = [[CodePushConfig current] appVersion];
282
+ NSDictionary *currentPackageMetadata = [CodePushPackage getCurrentPackage:&error];
283
+ if (currentPackageMetadata) {
284
+ NSString *packageAppVersion = [currentPackageMetadata objectForKey:AppVersionKey];
285
+ if (![binaryAppVersion isEqualToString:packageAppVersion]) {
286
+ [CodePush clearUpdates];
287
+ }
288
+ }
289
+ }
290
+ });
291
+ }
292
+
293
+ /*
294
+ * This method is used by the React Native bridge to allow
295
+ * our plugin to expose constants to the JS-side. In our case
296
+ * we're simply exporting enum values so that the JS and Native
297
+ * sides of the plugin can be in sync.
298
+ */
299
+ - (NSDictionary *)constantsToExport
300
+ {
301
+ // Export the values of the CodePushInstallMode and CodePushUpdateState
302
+ // enums so that the script-side can easily stay in sync
303
+ return @{
304
+ @"codePushInstallModeOnNextRestart":@(CodePushInstallModeOnNextRestart),
305
+ @"codePushInstallModeImmediate": @(CodePushInstallModeImmediate),
306
+ @"codePushInstallModeOnNextResume": @(CodePushInstallModeOnNextResume),
307
+ @"codePushInstallModeOnNextSuspend": @(CodePushInstallModeOnNextSuspend),
308
+
309
+ @"codePushUpdateStateRunning": @(CodePushUpdateStateRunning),
310
+ @"codePushUpdateStatePending": @(CodePushUpdateStatePending),
311
+ @"codePushUpdateStateLatest": @(CodePushUpdateStateLatest)
312
+ };
313
+ };
314
+
315
+ + (BOOL)requiresMainQueueSetup
316
+ {
317
+ return YES;
318
+ }
319
+
320
+ - (void)dealloc
321
+ {
322
+ // Ensure the global resume handler is cleared, so that
323
+ // this object isn't kept alive unnecessarily
324
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
325
+ }
326
+
327
+ - (void)dispatchDownloadProgressEvent {
328
+ // Notify the script-side about the progress
329
+ [self sendEventWithName:DownloadProgressEvent
330
+ body:@{
331
+ @"totalBytes" : [NSNumber
332
+ numberWithLongLong:_latestExpectedContentLength],
333
+ @"receivedBytes" : [NSNumber
334
+ numberWithLongLong:_latestReceivedConentLength]
335
+ }];
336
+ }
337
+
338
+ /*
339
+ * This method ensures that the app was packaged with a JS bundle
340
+ * file, and if not, it throws the appropriate exception.
341
+ */
342
+ + (void)ensureBinaryBundleExists
343
+ {
344
+ if (![self binaryBundleURL]) {
345
+ NSString *errorMessage;
346
+
347
+ #ifdef DEBUG
348
+ #if TARGET_IPHONE_SIMULATOR
349
+ errorMessage = @"React Native doesn't generate your app's JS bundle by default when deploying to the simulator. "
350
+ "If you'd like to test CodePush using the simulator, you can do one of the following depending on your "
351
+ "React Native version and/or preferred workflow:\n\n"
352
+
353
+ "1. Update your AppDelegate.m file to load the JS bundle from the packager instead of from CodePush. "
354
+ "You can still test your CodePush update experience using this workflow (Debug builds only).\n\n"
355
+
356
+ "2. Force the JS bundle to be generated in simulator builds by adding 'export FORCE_BUNDLING=true' to the script under "
357
+ "\"Build Phases\" > \"Bundle React Native code and images\" (React Native >=0.48 only).\n\n"
358
+
359
+ "3. Force the JS bundle to be generated in simulator builds by removing the if block that echoes "
360
+ "\"Skipping bundling for Simulator platform\" in the \"node_modules/react-native/packager/react-native-xcode.sh\" file (React Native <=0.47 only)\n\n"
361
+
362
+ "4. Deploy a Release build to the simulator, which unlike Debug builds, will generate the JS bundle (React Native >=0.22.0 only).";
363
+ #else
364
+ errorMessage = [NSString stringWithFormat:@"The specified JS bundle file wasn't found within the app's binary. Is \"%@\" the correct file name?", [bundleResourceName stringByAppendingPathExtension:bundleResourceExtension]];
365
+ #endif
366
+ #else
367
+ errorMessage = @"Something went wrong. Please verify if generated JS bundle is correct. ";
368
+ #endif
369
+
370
+ RCTFatal([CodePushErrorUtils errorWithMessage:errorMessage]);
371
+ }
372
+ }
373
+
374
+ - (instancetype)init
375
+ {
376
+ _allowed = YES;
377
+ _restartInProgress = NO;
378
+ _restartQueue = [NSMutableArray arrayWithCapacity:1];
379
+
380
+ self = [super init];
381
+ if (self) {
382
+ [self initializeUpdateAfterRestart];
383
+ }
384
+
385
+ return self;
386
+ }
387
+
388
+ /*
389
+ * This method is used when the app is started to either
390
+ * initialize a pending update or rollback a faulty update
391
+ * to the previous version.
392
+ */
393
+ - (void)initializeUpdateAfterRestart
394
+ {
395
+ #ifdef DEBUG
396
+ [self clearDebugUpdates];
397
+ #endif
398
+ self.paused = YES;
399
+ NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
400
+ NSDictionary *pendingUpdate = [preferences objectForKey:PendingUpdateKey];
401
+ if (pendingUpdate) {
402
+ _isFirstRunAfterUpdate = YES;
403
+ BOOL updateIsLoading = [pendingUpdate[PendingUpdateIsLoadingKey] boolValue];
404
+ if (updateIsLoading) {
405
+ // Pending update was initialized, but notifyApplicationReady was not called.
406
+ // Therefore, deduce that it is a broken update and rollback.
407
+ CPLog(@"Update did not finish loading the last time, rolling back to a previous version.");
408
+ needToReportRollback = YES;
409
+ [self rollbackPackage];
410
+ } else {
411
+ // Mark that we tried to initialize the new update, so that if it crashes,
412
+ // we will know that we need to rollback when the app next starts.
413
+ [self savePendingUpdate:pendingUpdate[PendingUpdateHashKey]
414
+ isLoading:YES];
415
+ }
416
+ }
417
+ }
418
+
419
+ /*
420
+ * This method is used to get information about the latest rollback.
421
+ * This information will be used to decide whether the application
422
+ * should ignore the update or not.
423
+ */
424
+ + (NSDictionary *)getLatestRollbackInfo
425
+ {
426
+ NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
427
+ NSDictionary *latestRollbackInfo = [preferences objectForKey:LatestRollbackInfoKey];
428
+ return latestRollbackInfo;
429
+ }
430
+
431
+ /*
432
+ * This method is used to save information about the latest rollback.
433
+ * This information will be used to decide whether the application
434
+ * should ignore the update or not.
435
+ */
436
+ + (void)setLatestRollbackInfo:(NSString*)packageHash
437
+ {
438
+ if (packageHash == nil) {
439
+ return;
440
+ }
441
+
442
+ NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
443
+ NSMutableDictionary *latestRollbackInfo = [preferences objectForKey:LatestRollbackInfoKey];
444
+ if (latestRollbackInfo == nil) {
445
+ latestRollbackInfo = [[NSMutableDictionary alloc] init];
446
+ } else {
447
+ latestRollbackInfo = [latestRollbackInfo mutableCopy];
448
+ }
449
+
450
+ int initialRollbackCount = [self getRollbackCountForPackage: packageHash fromLatestRollbackInfo: latestRollbackInfo];
451
+ NSNumber *count = [NSNumber numberWithInt: initialRollbackCount + 1];
452
+ NSNumber *currentTimeMillis = [NSNumber numberWithDouble: [[NSDate date] timeIntervalSince1970] * 1000];
453
+
454
+ [latestRollbackInfo setValue:count forKey:LatestRollbackCountKey];
455
+ [latestRollbackInfo setValue:currentTimeMillis forKey:LatestRollbackTimeKey];
456
+ [latestRollbackInfo setValue:packageHash forKey:LatestRollbackPackageHashKey];
457
+
458
+ [preferences setObject:latestRollbackInfo forKey:LatestRollbackInfoKey];
459
+ [preferences synchronize];
460
+ }
461
+
462
+ /*
463
+ * This method is used to get the count of rollback for the package
464
+ * using the latest rollback information.
465
+ */
466
+ + (int)getRollbackCountForPackage:(NSString*) packageHash fromLatestRollbackInfo:(NSMutableDictionary*) latestRollbackInfo
467
+ {
468
+ NSString *oldPackageHash = [latestRollbackInfo objectForKey:LatestRollbackPackageHashKey];
469
+ if ([packageHash isEqualToString: oldPackageHash]) {
470
+ NSNumber *oldCount = [latestRollbackInfo objectForKey:LatestRollbackCountKey];
471
+ return [oldCount intValue];
472
+ } else {
473
+ return 0;
474
+ }
475
+ }
476
+
477
+ /*
478
+ * This method checks to see whether a specific package hash
479
+ * has previously failed installation.
480
+ */
481
+ + (BOOL)isFailedHash:(NSString*)packageHash
482
+ {
483
+ NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
484
+ NSMutableArray *failedUpdates = [preferences objectForKey:FailedUpdatesKey];
485
+ if (failedUpdates == nil || packageHash == nil) {
486
+ return NO;
487
+ } else {
488
+ for (NSDictionary *failedPackage in failedUpdates)
489
+ {
490
+ // Type check is needed for backwards compatibility, where we used to just store
491
+ // the failed package hash instead of the metadata. This only impacts "dev"
492
+ // scenarios, since in production we clear out old information whenever a new
493
+ // binary is applied.
494
+ if ([failedPackage isKindOfClass:[NSDictionary class]]) {
495
+ NSString *failedPackageHash = [failedPackage objectForKey:PackageHashKey];
496
+ if ([packageHash isEqualToString:failedPackageHash]) {
497
+ return YES;
498
+ }
499
+ }
500
+ }
501
+
502
+ return NO;
503
+ }
504
+ }
505
+
506
+ /*
507
+ * This method checks to see whether a specific package hash
508
+ * represents a downloaded and installed update, that hasn't
509
+ * been applied yet via an app restart.
510
+ */
511
+ + (BOOL)isPendingUpdate:(NSString*)packageHash
512
+ {
513
+ NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
514
+ NSDictionary *pendingUpdate = [preferences objectForKey:PendingUpdateKey];
515
+
516
+ // If there is a pending update whose "state" isn't loading, then we consider it "pending".
517
+ // Additionally, if a specific hash was provided, we ensure it matches that of the pending update.
518
+ BOOL updateIsPending = pendingUpdate &&
519
+ [pendingUpdate[PendingUpdateIsLoadingKey] boolValue] == NO &&
520
+ (!packageHash || [pendingUpdate[PendingUpdateHashKey] isEqualToString:packageHash]);
521
+
522
+ return updateIsPending;
523
+ }
524
+
525
+ /*
526
+ * This method updates the React Native bridge's bundle URL
527
+ * to point at the latest CodePush update, and then restarts
528
+ * the bridge. This isn't meant to be called directly.
529
+ */
530
+ - (void)loadBundle
531
+ {
532
+ // This needs to be async dispatched because the bridge is not set on init
533
+ // when the app first starts, therefore rollbacks will not take effect.
534
+ dispatch_async(dispatch_get_main_queue(), ^{
535
+ // If the current bundle URL is using http(s), then assume the dev
536
+ // is debugging and therefore, shouldn't be redirected to a local
537
+ // file (since Chrome wouldn't support it). Otherwise, update
538
+ // the current bundle URL to point at the latest update
539
+ if ([CodePush isUsingTestConfiguration] || ![super.bridge.bundleURL.scheme hasPrefix:@"http"]) {
540
+ [super.bridge setValue:[CodePush bundleURL] forKey:@"bundleURL"];
541
+ }
542
+
543
+ [super.bridge reload];
544
+ });
545
+ }
546
+
547
+ /*
548
+ * This method is used when an update has failed installation
549
+ * and the app needs to be rolled back to the previous bundle.
550
+ * This method is automatically called when the rollback timer
551
+ * expires without the app indicating whether the update succeeded,
552
+ * and therefore, it shouldn't be called directly.
553
+ */
554
+ - (void)rollbackPackage
555
+ {
556
+ NSError *error;
557
+ NSDictionary *failedPackage = [CodePushPackage getCurrentPackage:&error];
558
+ if (!failedPackage) {
559
+ if (error) {
560
+ CPLog(@"Error getting current update metadata during rollback: %@", error);
561
+ } else {
562
+ CPLog(@"Attempted to perform a rollback when there is no current update");
563
+ }
564
+ } else {
565
+ // Write the current package's metadata to the "failed list"
566
+ [self saveFailedUpdate:failedPackage];
567
+ }
568
+
569
+ // Rollback to the previous version and de-register the new update
570
+ [CodePushPackage rollbackPackage];
571
+ [CodePush removePendingUpdate];
572
+ [self loadBundle];
573
+ }
574
+
575
+ /*
576
+ * When an update failed to apply, this method can be called
577
+ * to store its hash so that it can be ignored on future
578
+ * attempts to check the server for an update.
579
+ */
580
+ - (void)saveFailedUpdate:(NSDictionary *)failedPackage
581
+ {
582
+ if ([[self class] isFailedHash:[failedPackage objectForKey:PackageHashKey]]) {
583
+ return;
584
+ }
585
+
586
+ NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
587
+ NSMutableArray *failedUpdates = [preferences objectForKey:FailedUpdatesKey];
588
+ if (failedUpdates == nil) {
589
+ failedUpdates = [[NSMutableArray alloc] init];
590
+ } else {
591
+ // The NSUserDefaults sytem always returns immutable
592
+ // objects, regardless if you stored something mutable.
593
+ failedUpdates = [failedUpdates mutableCopy];
594
+ }
595
+
596
+ [failedUpdates addObject:failedPackage];
597
+ [preferences setObject:failedUpdates forKey:FailedUpdatesKey];
598
+ [preferences synchronize];
599
+ }
600
+
601
+ /*
602
+ * This method is used to clear away failed updates in the event that
603
+ * a new app store binary is installed.
604
+ */
605
+ + (void)removeFailedUpdates
606
+ {
607
+ NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
608
+ [preferences removeObjectForKey:FailedUpdatesKey];
609
+ [preferences synchronize];
610
+ }
611
+
612
+ /*
613
+ * This method is used to register the fact that a pending
614
+ * update succeeded and therefore can be removed.
615
+ */
616
+ + (void)removePendingUpdate
617
+ {
618
+ NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
619
+ [preferences removeObjectForKey:PendingUpdateKey];
620
+ [preferences synchronize];
621
+ }
622
+
623
+ /*
624
+ * When an update is installed whose mode isn't IMMEDIATE, this method
625
+ * can be called to store the pending update's metadata (e.g. packageHash)
626
+ * so that it can be used when the actual update application occurs at a later point.
627
+ */
628
+ - (void)savePendingUpdate:(NSString *)packageHash
629
+ isLoading:(BOOL)isLoading
630
+ {
631
+ // Since we're not restarting, we need to store the fact that the update
632
+ // was installed, but hasn't yet become "active".
633
+ NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
634
+ NSDictionary *pendingUpdate = [[NSDictionary alloc] initWithObjectsAndKeys:
635
+ packageHash,PendingUpdateHashKey,
636
+ [NSNumber numberWithBool:isLoading],PendingUpdateIsLoadingKey, nil];
637
+
638
+ [preferences setObject:pendingUpdate forKey:PendingUpdateKey];
639
+ [preferences synchronize];
640
+ }
641
+
642
+ - (NSArray<NSString *> *)supportedEvents {
643
+ return @[DownloadProgressEvent];
644
+ }
645
+
646
+ // Determine how long the app was in the background
647
+ - (int)getDurationInBackground
648
+ {
649
+ int duration = 0;
650
+ if (_lastResignedDate) {
651
+ duration = [[NSDate date] timeIntervalSinceDate:_lastResignedDate];
652
+ }
653
+
654
+ return duration;
655
+ }
656
+
657
+ #pragma mark - Application lifecycle event handlers
658
+
659
+ // These three handlers will only be registered when there is
660
+ // a resume-based update still pending installation.
661
+ - (void)applicationDidBecomeActive
662
+ {
663
+ if (_installMode == CodePushInstallModeOnNextSuspend) {
664
+ int durationInBackground = [self getDurationInBackground];
665
+ // We shouldn't use loadBundle in this case, because _appSuspendTimer will call loadBundleOnTick.
666
+ // We should cancel timer for _appSuspendTimer because otherwise, we would call loadBundle two times.
667
+ if (durationInBackground < _minimumBackgroundDuration) {
668
+ [_appSuspendTimer invalidate];
669
+ _appSuspendTimer = nil;
670
+ }
671
+ }
672
+ }
673
+
674
+ - (void)applicationWillEnterForeground
675
+ {
676
+ if (_installMode == CodePushInstallModeOnNextResume) {
677
+ int durationInBackground = [self getDurationInBackground];
678
+ if (durationInBackground >= _minimumBackgroundDuration) {
679
+ [self restartAppInternal:NO];
680
+ }
681
+ }
682
+ }
683
+
684
+ - (void)applicationWillResignActive
685
+ {
686
+ // Save the current time so that when the app is later
687
+ // resumed, we can detect how long it was in the background.
688
+ _lastResignedDate = [NSDate date];
689
+
690
+ if (_installMode == CodePushInstallModeOnNextSuspend && [[self class] isPendingUpdate:nil]) {
691
+ _appSuspendTimer = [NSTimer scheduledTimerWithTimeInterval:_minimumBackgroundDuration
692
+ target:self
693
+ selector:@selector(loadBundleOnTick:)
694
+ userInfo:nil
695
+ repeats:NO];
696
+ }
697
+ }
698
+
699
+ -(void)loadBundleOnTick:(NSTimer *)timer {
700
+ [self restartAppInternal:NO];
701
+ }
702
+
703
+ #pragma mark - JavaScript-exported module methods (Public)
704
+
705
+ /*
706
+ * This is native-side of the RemotePackage.download method
707
+ */
708
+ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
709
+ notifyProgress:(BOOL)notifyProgress
710
+ resolver:(RCTPromiseResolveBlock)resolve
711
+ rejecter:(RCTPromiseRejectBlock)reject)
712
+ {
713
+ NSDictionary *mutableUpdatePackage = [updatePackage mutableCopy];
714
+ NSURL *binaryBundleURL = [CodePush binaryBundleURL];
715
+ if (binaryBundleURL != nil) {
716
+ [mutableUpdatePackage setValue:[CodePushUpdateUtils modifiedDateStringOfFileAtURL:binaryBundleURL]
717
+ forKey:BinaryBundleDateKey];
718
+ }
719
+
720
+ if (notifyProgress) {
721
+ // Set up and unpause the frame observer so that it can emit
722
+ // progress events every frame if the progress is updated.
723
+ _didUpdateProgress = NO;
724
+ self.paused = NO;
725
+ }
726
+
727
+ NSString * publicKey = [[CodePushConfig current] publicKey];
728
+
729
+ [CodePushPackage
730
+ downloadPackage:mutableUpdatePackage
731
+ expectedBundleFileName:[bundleResourceName stringByAppendingPathExtension:bundleResourceExtension]
732
+ publicKey:publicKey
733
+ operationQueue:_methodQueue
734
+ // The download is progressing forward
735
+ progressCallback:^(long long expectedContentLength, long long receivedContentLength) {
736
+ // Update the download progress so that the frame observer can notify the JS side
737
+ _latestExpectedContentLength = expectedContentLength;
738
+ _latestReceivedConentLength = receivedContentLength;
739
+ _didUpdateProgress = YES;
740
+
741
+ // If the download is completed, stop observing frame
742
+ // updates and synchronously send the last event.
743
+ if (expectedContentLength == receivedContentLength) {
744
+ _didUpdateProgress = NO;
745
+ self.paused = YES;
746
+ [self dispatchDownloadProgressEvent];
747
+ }
748
+ }
749
+ // The download completed
750
+ doneCallback:^{
751
+ NSError *err;
752
+ NSDictionary *newPackage = [CodePushPackage getPackage:mutableUpdatePackage[PackageHashKey] error:&err];
753
+
754
+ if (err) {
755
+ return reject([NSString stringWithFormat: @"%lu", (long)err.code], err.localizedDescription, err);
756
+ }
757
+ resolve(newPackage);
758
+ }
759
+ // The download failed
760
+ failCallback:^(NSError *err) {
761
+ if ([CodePushErrorUtils isCodePushError:err]) {
762
+ [self saveFailedUpdate:mutableUpdatePackage];
763
+ }
764
+
765
+ // Stop observing frame updates if the download fails.
766
+ _didUpdateProgress = NO;
767
+ self.paused = YES;
768
+ reject([NSString stringWithFormat: @"%lu", (long)err.code], err.localizedDescription, err);
769
+ }];
770
+ }
771
+
772
+ - (void)restartAppInternal:(BOOL)onlyIfUpdateIsPending
773
+ {
774
+ if (_restartInProgress) {
775
+ CPLog(@"Restart request queued until the current restart is completed.");
776
+ [_restartQueue addObject:@(onlyIfUpdateIsPending)];
777
+ return;
778
+ } else if (!_allowed) {
779
+ CPLog(@"Restart request queued until restarts are re-allowed.");
780
+ [_restartQueue addObject:@(onlyIfUpdateIsPending)];
781
+ return;
782
+ }
783
+
784
+ _restartInProgress = YES;
785
+ if (!onlyIfUpdateIsPending || [[self class] isPendingUpdate:nil]) {
786
+ [self loadBundle];
787
+ CPLog(@"Restarting app.");
788
+ return;
789
+ }
790
+
791
+ _restartInProgress = NO;
792
+ if ([_restartQueue count] > 0) {
793
+ BOOL buf = [_restartQueue valueForKey: @"@firstObject"];
794
+ [_restartQueue removeObjectAtIndex:0];
795
+ [self restartAppInternal:buf];
796
+ }
797
+ }
798
+
799
+ /*
800
+ * This is the native side of the CodePush.getConfiguration method. It isn't
801
+ * currently exposed via the "react-native-code-push" module, and is used
802
+ * internally only by the CodePush.checkForUpdate method in order to get the
803
+ * app version, as well as the deployment key that was configured in the Info.plist file.
804
+ */
805
+ RCT_EXPORT_METHOD(getConfiguration:(RCTPromiseResolveBlock)resolve
806
+ rejecter:(RCTPromiseRejectBlock)reject)
807
+ {
808
+ NSDictionary *configuration = [[CodePushConfig current] configuration];
809
+ NSError *error;
810
+ if (isRunningBinaryVersion) {
811
+ // isRunningBinaryVersion will not get set to "YES" if running against the packager.
812
+ NSString *binaryHash = [CodePushUpdateUtils getHashForBinaryContents:[CodePush binaryBundleURL] error:&error];
813
+ if (error) {
814
+ CPLog(@"Error obtaining hash for binary contents: %@", error);
815
+ resolve(configuration);
816
+ return;
817
+ }
818
+
819
+ if (binaryHash == nil) {
820
+ // The hash was not generated either due to a previous unknown error or the fact that
821
+ // the React Native assets were not bundled in the binary (e.g. during dev/simulator)
822
+ // builds.
823
+ resolve(configuration);
824
+ return;
825
+ }
826
+
827
+ NSMutableDictionary *mutableConfiguration = [configuration mutableCopy];
828
+ [mutableConfiguration setObject:binaryHash forKey:PackageHashKey];
829
+ resolve(mutableConfiguration);
830
+ return;
831
+ }
832
+
833
+ resolve(configuration);
834
+ }
835
+
836
+ /*
837
+ * This method is the native side of the CodePush.getUpdateMetadata method.
838
+ */
839
+ RCT_EXPORT_METHOD(getUpdateMetadata:(CodePushUpdateState)updateState
840
+ resolver:(RCTPromiseResolveBlock)resolve
841
+ rejecter:(RCTPromiseRejectBlock)reject)
842
+ {
843
+ NSError *error;
844
+ NSMutableDictionary *package = [[CodePushPackage getCurrentPackage:&error] mutableCopy];
845
+
846
+ if (error) {
847
+ return reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error);
848
+ } else if (package == nil) {
849
+ // The app hasn't downloaded any CodePush updates yet,
850
+ // so we simply return nil regardless if the user
851
+ // wanted to retrieve the pending or running update.
852
+ return resolve(nil);
853
+ }
854
+
855
+ // We have a CodePush update, so let's see if it's currently in a pending state.
856
+ BOOL currentUpdateIsPending = [[self class] isPendingUpdate:[package objectForKey:PackageHashKey]];
857
+
858
+ if (updateState == CodePushUpdateStatePending && !currentUpdateIsPending) {
859
+ // The caller wanted a pending update
860
+ // but there isn't currently one.
861
+ resolve(nil);
862
+ } else if (updateState == CodePushUpdateStateRunning && currentUpdateIsPending) {
863
+ // The caller wants the running update, but the current
864
+ // one is pending, so we need to grab the previous.
865
+ resolve([CodePushPackage getPreviousPackage:&error]);
866
+ } else {
867
+ // The current package satisfies the request:
868
+ // 1) Caller wanted a pending, and there is a pending update
869
+ // 2) Caller wanted the running update, and there isn't a pending
870
+ // 3) Caller wants the latest update, regardless if it's pending or not
871
+ if (isRunningBinaryVersion) {
872
+ // This only matters in Debug builds. Since we do not clear "outdated" updates,
873
+ // we need to indicate to the JS side that somehow we have a current update on
874
+ // disk that is not actually running.
875
+ [package setObject:@(YES) forKey:@"_isDebugOnly"];
876
+ }
877
+
878
+ // Enable differentiating pending vs. non-pending updates
879
+ [package setObject:@(currentUpdateIsPending) forKey:PackageIsPendingKey];
880
+ resolve(package);
881
+ }
882
+ }
883
+
884
+ /*
885
+ * This method is the native side of the LocalPackage.install method.
886
+ */
887
+ RCT_EXPORT_METHOD(installUpdate:(NSDictionary*)updatePackage
888
+ installMode:(CodePushInstallMode)installMode
889
+ minimumBackgroundDuration:(int)minimumBackgroundDuration
890
+ resolver:(RCTPromiseResolveBlock)resolve
891
+ rejecter:(RCTPromiseRejectBlock)reject)
892
+ {
893
+ NSError *error;
894
+ [CodePushPackage installPackage:updatePackage
895
+ removePendingUpdate:[[self class] isPendingUpdate:nil]
896
+ error:&error];
897
+
898
+ if (error) {
899
+ reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error);
900
+ } else {
901
+ [self savePendingUpdate:updatePackage[PackageHashKey]
902
+ isLoading:NO];
903
+
904
+ _installMode = installMode;
905
+ if (_installMode == CodePushInstallModeOnNextResume || _installMode == CodePushInstallModeOnNextSuspend) {
906
+ _minimumBackgroundDuration = minimumBackgroundDuration;
907
+
908
+ if (!_hasResumeListener) {
909
+ // Ensure we do not add the listener twice.
910
+ // Register for app resume notifications so that we
911
+ // can check for pending updates which support "restart on resume"
912
+ [[NSNotificationCenter defaultCenter] addObserver:self
913
+ selector:@selector(applicationDidBecomeActive)
914
+ name:UIApplicationDidBecomeActiveNotification
915
+ object:RCTSharedApplication()];
916
+
917
+ [[NSNotificationCenter defaultCenter] addObserver:self
918
+ selector:@selector(applicationWillEnterForeground)
919
+ name:UIApplicationWillEnterForegroundNotification
920
+ object:RCTSharedApplication()];
921
+
922
+ [[NSNotificationCenter defaultCenter] addObserver:self
923
+ selector:@selector(applicationWillResignActive)
924
+ name:UIApplicationWillResignActiveNotification
925
+ object:RCTSharedApplication()];
926
+
927
+ _hasResumeListener = YES;
928
+ }
929
+ }
930
+
931
+ // Signal to JS that the update has been applied.
932
+ resolve(nil);
933
+ }
934
+ }
935
+
936
+ /*
937
+ * This method isn't publicly exposed via the "react-native-code-push"
938
+ * module, and is only used internally to populate the RemotePackage.failedInstall property.
939
+ */
940
+ RCT_EXPORT_METHOD(isFailedUpdate:(NSString *)packageHash
941
+ resolve:(RCTPromiseResolveBlock)resolve
942
+ reject:(RCTPromiseRejectBlock)reject)
943
+ {
944
+ BOOL isFailedHash = [[self class] isFailedHash:packageHash];
945
+ resolve(@(isFailedHash));
946
+ }
947
+
948
+ RCT_EXPORT_METHOD(setLatestRollbackInfo:(NSString *)packageHash
949
+ resolve:(RCTPromiseResolveBlock)resolve
950
+ reject:(RCTPromiseRejectBlock)reject)
951
+ {
952
+ [[self class] setLatestRollbackInfo:packageHash];
953
+ resolve(nil);
954
+ }
955
+
956
+
957
+ RCT_EXPORT_METHOD(getLatestRollbackInfo:(RCTPromiseResolveBlock)resolve
958
+ rejecter:(RCTPromiseRejectBlock)reject)
959
+ {
960
+ NSDictionary *latestRollbackInfo = [[self class] getLatestRollbackInfo];
961
+ resolve(latestRollbackInfo);
962
+ }
963
+
964
+ /*
965
+ * This method isn't publicly exposed via the "react-native-code-push"
966
+ * module, and is only used internally to populate the LocalPackage.isFirstRun property.
967
+ */
968
+ RCT_EXPORT_METHOD(isFirstRun:(NSString *)packageHash
969
+ resolve:(RCTPromiseResolveBlock)resolve
970
+ rejecter:(RCTPromiseRejectBlock)reject)
971
+ {
972
+ NSError *error;
973
+ BOOL isFirstRun = _isFirstRunAfterUpdate
974
+ && nil != packageHash
975
+ && [packageHash length] > 0
976
+ && [packageHash isEqualToString:[CodePushPackage getCurrentPackageHash:&error]];
977
+
978
+ resolve(@(isFirstRun));
979
+ }
980
+
981
+ /*
982
+ * This method is the native side of the CodePush.notifyApplicationReady() method.
983
+ */
984
+ RCT_EXPORT_METHOD(notifyApplicationReady:(RCTPromiseResolveBlock)resolve
985
+ rejecter:(RCTPromiseRejectBlock)reject)
986
+ {
987
+ [CodePush removePendingUpdate];
988
+ resolve(nil);
989
+ }
990
+
991
+ RCT_EXPORT_METHOD(allow:(RCTPromiseResolveBlock)resolve
992
+ rejecter:(RCTPromiseRejectBlock)reject)
993
+ {
994
+ CPLog(@"Re-allowing restarts.");
995
+ _allowed = YES;
996
+
997
+ if ([_restartQueue count] > 0) {
998
+ CPLog(@"Executing pending restart.");
999
+ BOOL buf = [_restartQueue valueForKey: @"@firstObject"];
1000
+ [_restartQueue removeObjectAtIndex:0];
1001
+ [self restartAppInternal:buf];
1002
+ }
1003
+
1004
+ resolve(nil);
1005
+ }
1006
+
1007
+ RCT_EXPORT_METHOD(clearPendingRestart:(RCTPromiseResolveBlock)resolve
1008
+ rejecter:(RCTPromiseRejectBlock)reject)
1009
+ {
1010
+ [_restartQueue removeAllObjects];
1011
+ resolve(nil);
1012
+ }
1013
+
1014
+ RCT_EXPORT_METHOD(disallow:(RCTPromiseResolveBlock)resolve
1015
+ rejecter:(RCTPromiseRejectBlock)reject)
1016
+ {
1017
+ CPLog(@"Disallowing restarts.");
1018
+ _allowed = NO;
1019
+ resolve(nil);
1020
+ }
1021
+
1022
+ /*
1023
+ * This method is the native side of the CodePush.restartApp() method.
1024
+ */
1025
+ RCT_EXPORT_METHOD(restartApp:(BOOL)onlyIfUpdateIsPending
1026
+ resolve:(RCTPromiseResolveBlock)resolve
1027
+ rejecter:(RCTPromiseRejectBlock)reject)
1028
+ {
1029
+ [self restartAppInternal:onlyIfUpdateIsPending];
1030
+ resolve(nil);
1031
+ }
1032
+
1033
+ /*
1034
+ * This method clears CodePush's downloaded updates.
1035
+ * It is needed to switch to a different deployment if the current deployment is more recent.
1036
+ * Note: we don’t recommend to use this method in scenarios other than that (CodePush will call this method
1037
+ * automatically when needed in other cases) as it could lead to unpredictable behavior.
1038
+ */
1039
+ RCT_EXPORT_METHOD(clearUpdates) {
1040
+ CPLog(@"Clearing updates.");
1041
+ [CodePush clearUpdates];
1042
+ }
1043
+
1044
+ #pragma mark - JavaScript-exported module methods (Private)
1045
+
1046
+ /*
1047
+ * This method is the native side of the CodePush.downloadAndReplaceCurrentBundle()
1048
+ * method, which replaces the current bundle with the one downloaded from
1049
+ * removeBundleUrl. It is only to be used during tests and no-ops if the test
1050
+ * configuration flag is not set.
1051
+ */
1052
+ RCT_EXPORT_METHOD(downloadAndReplaceCurrentBundle:(NSString *)remoteBundleUrl)
1053
+ {
1054
+ if ([CodePush isUsingTestConfiguration]) {
1055
+ [CodePushPackage downloadAndReplaceCurrentBundle:remoteBundleUrl];
1056
+ }
1057
+ }
1058
+
1059
+ /*
1060
+ * This method is checks if a new status update exists (new version was installed,
1061
+ * or an update failed) and return its details (version label, status).
1062
+ */
1063
+ RCT_EXPORT_METHOD(getNewStatusReport:(RCTPromiseResolveBlock)resolve
1064
+ rejecter:(RCTPromiseRejectBlock)reject)
1065
+ {
1066
+ if (needToReportRollback) {
1067
+ needToReportRollback = NO;
1068
+ NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
1069
+ NSMutableArray *failedUpdates = [preferences objectForKey:FailedUpdatesKey];
1070
+ if (failedUpdates) {
1071
+ NSDictionary *lastFailedPackage = [failedUpdates lastObject];
1072
+ if (lastFailedPackage) {
1073
+ resolve([CodePushTelemetryManager getRollbackReport:lastFailedPackage]);
1074
+ return;
1075
+ }
1076
+ }
1077
+ } else if (_isFirstRunAfterUpdate) {
1078
+ NSError *error;
1079
+ NSDictionary *currentPackage = [CodePushPackage getCurrentPackage:&error];
1080
+ if (!error && currentPackage) {
1081
+ resolve([CodePushTelemetryManager getUpdateReport:currentPackage]);
1082
+ return;
1083
+ }
1084
+ } else if (isRunningBinaryVersion) {
1085
+ NSString *appVersion = [[CodePushConfig current] appVersion];
1086
+ resolve([CodePushTelemetryManager getBinaryUpdateReport:appVersion]);
1087
+ return;
1088
+ } else {
1089
+ NSDictionary *retryStatusReport = [CodePushTelemetryManager getRetryStatusReport];
1090
+ if (retryStatusReport) {
1091
+ resolve(retryStatusReport);
1092
+ return;
1093
+ }
1094
+ }
1095
+
1096
+ resolve(nil);
1097
+ }
1098
+
1099
+ RCT_EXPORT_METHOD(recordStatusReported:(NSDictionary *)statusReport)
1100
+ {
1101
+ [CodePushTelemetryManager recordStatusReported:statusReport];
1102
+ }
1103
+
1104
+ RCT_EXPORT_METHOD(saveStatusReportForRetry:(NSDictionary *)statusReport)
1105
+ {
1106
+ [CodePushTelemetryManager saveStatusReportForRetry:statusReport];
1107
+ }
1108
+
1109
+ #pragma mark - RCTFrameUpdateObserver Methods
1110
+
1111
+ - (void)didUpdateFrame:(RCTFrameUpdate *)update
1112
+ {
1113
+ if (!_didUpdateProgress) {
1114
+ return;
1115
+ }
1116
+
1117
+ [self dispatchDownloadProgressEvent];
1118
+ _didUpdateProgress = NO;
1119
+ }
1120
+
1121
+ @end