开源 企业版 高校版 私有云 模力方舟 AI 队友
代码拉取完成,页面将自动刷新
捐赠
捐赠前请先登录
扫描微信二维码支付
取消
支付完成
支付提示
将跳转至支付宝完成支付
确定
取消
1 Star 0 Fork 1

碳猫科技/react-native-code-push

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
已有帐号? 立即登录
文件
master
分支 (3)
标签 (75)
master
brownfield-apps-dev
brownfield-apps-archive
v5.4.2
v5.4.1
v5.4.0
v5.3.5
v5.3.4
v5.3.3
v5.3.2
v5.3.1
v5.3.0
v5.2.2
v5.2.1
v5.2.0-beta
v5.1.3-beta
v5.1.2-beta
v5.1.1-beta
v5.1.0-beta
v5.0.0-beta
v4.1.0-beta
v4.0.0-beta
v3.0.1-beta
master
分支 (3)
标签 (75)
master
brownfield-apps-dev
brownfield-apps-archive
v5.4.2
v5.4.1
v5.4.0
v5.3.5
v5.3.4
v5.3.3
v5.3.2
v5.3.1
v5.3.0
v5.2.2
v5.2.1
v5.2.0-beta
v5.1.3-beta
v5.1.2-beta
v5.1.1-beta
v5.1.0-beta
v5.0.0-beta
v4.1.0-beta
v4.0.0-beta
v3.0.1-beta
克隆/下载
克隆/下载
提示
下载代码请复制以下命令到终端执行
为确保你提交的代码身份被 Gitee 正确识别,请执行以下命令完成配置
初次使用 SSH 协议进行代码克隆、推送等操作时,需按下述提示完成 SSH 配置
1 生成 RSA 密钥
2 获取 RSA 公钥内容,并配置到 SSH公钥
在 Gitee 上使用 SVN,请访问 使用指南
使用 HTTPS 协议时,命令行会出现如下账号密码验证步骤。基于安全考虑,Gitee 建议 配置并使用私人令牌 替代登录密码进行克隆、推送等操作
Username for 'https://gitee.com': userName
Password for 'https://userName@gitee.com': # 私人令牌
master
分支 (3)
标签 (75)
master
brownfield-apps-dev
brownfield-apps-archive
v5.4.2
v5.4.1
v5.4.0
v5.3.5
v5.3.4
v5.3.3
v5.3.2
v5.3.1
v5.3.0
v5.2.2
v5.2.1
v5.2.0-beta
v5.1.3-beta
v5.1.2-beta
v5.1.1-beta
v5.1.0-beta
v5.0.0-beta
v4.1.0-beta
v4.0.0-beta
v3.0.1-beta
CodePush.m 35.18 KB
一键复制 编辑 原始数据 按行查看 历史
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
#if __has_include(<React/RCTAssert.h>)
#import <React/RCTAssert.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTConvert.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTRootView.h>
#import <React/RCTUtils.h>
#else // back compatibility for RN version < 0.40
#import "RCTAssert.h"
#import "RCTBridgeModule.h"
#import "RCTConvert.h"
#import "RCTEventDispatcher.h"
#import "RCTRootView.h"
#import "RCTUtils.h"
#endif
#import "CodePush.h"
@interface CodePush () <RCTBridgeModule, RCTFrameUpdateObserver>
@end
@implementation CodePush {
BOOL _hasResumeListener;
BOOL _isFirstRunAfterUpdate;
int _minimumBackgroundDuration;
NSDate *_lastResignedDate;
CodePushInstallMode _installMode;
NSTimer *_appSuspendTimer;
// Used to coordinate the dispatching of download progress events to JS.
long long _latestExpectedContentLength;
long long _latestReceivedConentLength;
BOOL _didUpdateProgress;
}
RCT_EXPORT_MODULE()
#pragma mark - Private constants
// These constants represent emitted events
static NSString *const DownloadProgressEvent = @"CodePushDownloadProgress";
// These constants represent valid deployment statuses
static NSString *const DeploymentFailed = @"DeploymentFailed";
static NSString *const DeploymentSucceeded = @"DeploymentSucceeded";
// These keys represent the names we use to store data in NSUserDefaults
static NSString *const FailedUpdatesKey = @"CODE_PUSH_FAILED_UPDATES";
static NSString *const PendingUpdateKey = @"CODE_PUSH_PENDING_UPDATE";
// These keys are already "namespaced" by the PendingUpdateKey, so
// their values don't need to be obfuscated to prevent collision with app data
static NSString *const PendingUpdateHashKey = @"hash";
static NSString *const PendingUpdateIsLoadingKey = @"isLoading";
// These keys are used to inspect/augment the metadata
// that is associated with an update's package.
static NSString *const AppVersionKey = @"appVersion";
static NSString *const BinaryBundleDateKey = @"binaryDate";
static NSString *const PackageHashKey = @"packageHash";
static NSString *const PackageIsPendingKey = @"isPending";
#pragma mark - Static variables
static BOOL isRunningBinaryVersion = NO;
static BOOL needToReportRollback = NO;
static BOOL testConfigurationFlag = NO;
// These values are used to save the NS bundle, name, extension and subdirectory
// for the JS bundle in the binary.
static NSBundle *bundleResourceBundle = nil;
static NSString *bundleResourceExtension = @"jsbundle";
static NSString *bundleResourceName = @"main";
static NSString *bundleResourceSubdirectory = nil;
+ (void)initialize
{
[super initialize];
if (self == [CodePush class]) {
// Use the mainBundle by default.
bundleResourceBundle = [NSBundle mainBundle];
}
}
#pragma mark - Public Obj-C API
+ (NSURL *)binaryBundleURL
{
return [bundleResourceBundle URLForResource:bundleResourceName
withExtension:bundleResourceExtension
subdirectory:bundleResourceSubdirectory];
}
+ (NSString *)bundleAssetsPath
{
NSString *resourcePath = [bundleResourceBundle resourcePath];
if (bundleResourceSubdirectory) {
resourcePath = [resourcePath stringByAppendingPathComponent:bundleResourceSubdirectory];
}
return [resourcePath stringByAppendingPathComponent:[CodePushUpdateUtils assetsFolderName]];
}
+ (NSURL *)bundleURL
{
return [self bundleURLForResource:bundleResourceName
withExtension:bundleResourceExtension
subdirectory:bundleResourceSubdirectory
bundle:bundleResourceBundle];
}
+ (NSURL *)bundleURLForResource:(NSString *)resourceName
{
return [self bundleURLForResource:resourceName
withExtension:bundleResourceExtension
subdirectory:bundleResourceSubdirectory
bundle:bundleResourceBundle];
}
+ (NSURL *)bundleURLForResource:(NSString *)resourceName
withExtension:(NSString *)resourceExtension
{
return [self bundleURLForResource:resourceName
withExtension:resourceExtension
subdirectory:bundleResourceSubdirectory
bundle:bundleResourceBundle];
}
+ (NSURL *)bundleURLForResource:(NSString *)resourceName
withExtension:(NSString *)resourceExtension
subdirectory:(NSString *)resourceSubdirectory
{
return [self bundleURLForResource:resourceName
withExtension:resourceExtension
subdirectory:resourceSubdirectory
bundle:bundleResourceBundle];
}
+ (NSURL *)bundleURLForResource:(NSString *)resourceName
withExtension:(NSString *)resourceExtension
subdirectory:(NSString *)resourceSubdirectory
bundle:(NSBundle *)resourceBundle
{
bundleResourceName = resourceName;
bundleResourceExtension = resourceExtension;
bundleResourceSubdirectory = resourceSubdirectory;
bundleResourceBundle = resourceBundle;
[self ensureBinaryBundleExists];
NSString *logMessageFormat = @"Loading JS bundle from %@";
NSError *error;
NSString *packageFile = [CodePushPackage getCurrentPackageBundlePath:&error];
NSURL *binaryBundleURL = [self binaryBundleURL];
if (error || !packageFile) {
CPLog(logMessageFormat, binaryBundleURL);
isRunningBinaryVersion = YES;
return binaryBundleURL;
}
NSString *binaryAppVersion = [[CodePushConfig current] appVersion];
NSDictionary *currentPackageMetadata = [CodePushPackage getCurrentPackage:&error];
if (error || !currentPackageMetadata) {
CPLog(logMessageFormat, binaryBundleURL);
isRunningBinaryVersion = YES;
return binaryBundleURL;
}
NSString *packageDate = [currentPackageMetadata objectForKey:BinaryBundleDateKey];
NSString *packageAppVersion = [currentPackageMetadata objectForKey:AppVersionKey];
if ([[CodePushUpdateUtils modifiedDateStringOfFileAtURL:binaryBundleURL] isEqualToString:packageDate] && ([CodePush isUsingTestConfiguration] ||[binaryAppVersion isEqualToString:packageAppVersion])) {
// Return package file because it is newer than the app store binary's JS bundle
NSURL *packageUrl = [[NSURL alloc] initFileURLWithPath:packageFile];
CPLog(logMessageFormat, packageUrl);
isRunningBinaryVersion = NO;
return packageUrl;
} else {
BOOL isRelease = NO;
#ifndef DEBUG
isRelease = YES;
#endif
if (isRelease || ![binaryAppVersion isEqualToString:packageAppVersion]) {
[CodePush clearUpdates];
}
CPLog(logMessageFormat, binaryBundleURL);
isRunningBinaryVersion = YES;
return binaryBundleURL;
}
}
+ (NSString *)getApplicationSupportDirectory
{
NSString *applicationSupportDirectory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0];
return applicationSupportDirectory;
}
+ (void)overrideAppVersion:(NSString *)appVersion
{
[CodePushConfig current].appVersion = appVersion;
}
+ (void)setDeploymentKey:(NSString *)deploymentKey
{
[CodePushConfig current].deploymentKey = deploymentKey;
}
/*
* WARNING: This cleans up all downloaded and pending updates.
*/
+ (void)clearUpdates
{
[CodePushPackage clearUpdates];
[self removePendingUpdate];
[self removeFailedUpdates];
}
#pragma mark - Test-only methods
/*
* This returns a boolean value indicating whether CodePush has
* been set to run under a test configuration.
*/
+ (BOOL)isUsingTestConfiguration
{
return testConfigurationFlag;
}
/*
* This is used to enable an environment in which tests can be run.
* Specifically, it flips a boolean flag that causes bundles to be
* saved to a test folder and enables the ability to modify
* installed bundles on the fly from JavaScript.
*/
+ (void)setUsingTestConfiguration:(BOOL)shouldUseTestConfiguration
{
testConfigurationFlag = shouldUseTestConfiguration;
}
#pragma mark - Private API methods
@synthesize methodQueue = _methodQueue;
@synthesize pauseCallback = _pauseCallback;
@synthesize paused = _paused;
- (void)setPaused:(BOOL)paused
{
if (_paused != paused) {
_paused = paused;
if (_pauseCallback) {
_pauseCallback();
}
}
}
/*
* This method is used to clear updates that are installed
* under a different app version and hence don't apply anymore,
* during a debug run configuration and when the bridge is
* running the JS bundle from the dev server.
*/
- (void)clearDebugUpdates
{
dispatch_async(dispatch_get_main_queue(), ^{
if ([super.bridge.bundleURL.scheme hasPrefix:@"http"]) {
NSError *error;
NSString *binaryAppVersion = [[CodePushConfig current] appVersion];
NSDictionary *currentPackageMetadata = [CodePushPackage getCurrentPackage:&error];
if (currentPackageMetadata) {
NSString *packageAppVersion = [currentPackageMetadata objectForKey:AppVersionKey];
if (![binaryAppVersion isEqualToString:packageAppVersion]) {
[CodePush clearUpdates];
}
}
}
});
}
/*
* This method is used by the React Native bridge to allow
* our plugin to expose constants to the JS-side. In our case
* we're simply exporting enum values so that the JS and Native
* sides of the plugin can be in sync.
*/
- (NSDictionary *)constantsToExport
{
// Export the values of the CodePushInstallMode and CodePushUpdateState
// enums so that the script-side can easily stay in sync
return @{
@"codePushInstallModeOnNextRestart":@(CodePushInstallModeOnNextRestart),
@"codePushInstallModeImmediate": @(CodePushInstallModeImmediate),
@"codePushInstallModeOnNextResume": @(CodePushInstallModeOnNextResume),
@"codePushInstallModeOnNextSuspend": @(CodePushInstallModeOnNextSuspend),
@"codePushUpdateStateRunning": @(CodePushUpdateStateRunning),
@"codePushUpdateStatePending": @(CodePushUpdateStatePending),
@"codePushUpdateStateLatest": @(CodePushUpdateStateLatest)
};
};
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
- (void)dealloc
{
// Ensure the global resume handler is cleared, so that
// this object isn't kept alive unnecessarily
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)dispatchDownloadProgressEvent {
// Notify the script-side about the progress
[self sendEventWithName:DownloadProgressEvent
body:@{
@"totalBytes" : [NSNumber
numberWithLongLong:_latestExpectedContentLength],
@"receivedBytes" : [NSNumber
numberWithLongLong:_latestReceivedConentLength]
}];
}
/*
* This method ensures that the app was packaged with a JS bundle
* file, and if not, it throws the appropriate exception.
*/
+ (void)ensureBinaryBundleExists
{
if (![self binaryBundleURL]) {
NSString *errorMessage;
#ifdef DEBUG
#if TARGET_IPHONE_SIMULATOR
errorMessage = @"React Native doesn't generate your app's JS bundle by default when deploying to the simulator. "
"If you'd like to test CodePush using the simulator, you can do one of the following depending on your "
"React Native version and/or preferred workflow:\n\n"
"1. Update your AppDelegate.m file to load the JS bundle from the packager instead of from CodePush. "
"You can still test your CodePush update experience using this workflow (Debug builds only).\n\n"
"2. Force the JS bundle to be generated in simulator builds by adding 'export FORCE_BUNDLING=true' to the script under "
"\"Build Phases\" > \"Bundle React Native code and images\" (React Native >=0.48 only).\n\n"
"3. Force the JS bundle to be generated in simulator builds by removing the if block that echoes "
"\"Skipping bundling for Simulator platform\" in the \"node_modules/react-native/packager/react-native-xcode.sh\" file (React Native <=0.47 only)\n\n"
"4. Deploy a Release build to the simulator, which unlike Debug builds, will generate the JS bundle (React Native >=0.22.0 only).";
#else
errorMessage = [NSString stringWithFormat:@"The specified JS bundle file wasn't found within the app's binary. Is \"%@\" the correct file name?", [bundleResourceName stringByAppendingPathExtension:bundleResourceExtension]];
#endif
#else
errorMessage = @"Something went wrong. Please verify if generated JS bundle is correct. ";
#endif
RCTFatal([CodePushErrorUtils errorWithMessage:errorMessage]);
}
}
- (instancetype)init
{
self = [super init];
if (self) {
[self initializeUpdateAfterRestart];
}
return self;
}
/*
* This method is used when the app is started to either
* initialize a pending update or rollback a faulty update
* to the previous version.
*/
- (void)initializeUpdateAfterRestart
{
#ifdef DEBUG
[self clearDebugUpdates];
#endif
self.paused = YES;
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
NSDictionary *pendingUpdate = [preferences objectForKey:PendingUpdateKey];
if (pendingUpdate) {
_isFirstRunAfterUpdate = YES;
BOOL updateIsLoading = [pendingUpdate[PendingUpdateIsLoadingKey] boolValue];
if (updateIsLoading) {
// Pending update was initialized, but notifyApplicationReady was not called.
// Therefore, deduce that it is a broken update and rollback.
CPLog(@"Update did not finish loading the last time, rolling back to a previous version.");
needToReportRollback = YES;
[self rollbackPackage];
} else {
// Mark that we tried to initialize the new update, so that if it crashes,
// we will know that we need to rollback when the app next starts.
[self savePendingUpdate:pendingUpdate[PendingUpdateHashKey]
isLoading:YES];
}
}
}
/*
* This method checks to see whether a specific package hash
* has previously failed installation.
*/
+ (BOOL)isFailedHash:(NSString*)packageHash
{
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
NSMutableArray *failedUpdates = [preferences objectForKey:FailedUpdatesKey];
if (failedUpdates == nil || packageHash == nil) {
return NO;
} else {
for (NSDictionary *failedPackage in failedUpdates)
{
// Type check is needed for backwards compatibility, where we used to just store
// the failed package hash instead of the metadata. This only impacts "dev"
// scenarios, since in production we clear out old information whenever a new
// binary is applied.
if ([failedPackage isKindOfClass:[NSDictionary class]]) {
NSString *failedPackageHash = [failedPackage objectForKey:PackageHashKey];
if ([packageHash isEqualToString:failedPackageHash]) {
return YES;
}
}
}
return NO;
}
}
/*
* This method checks to see whether a specific package hash
* represents a downloaded and installed update, that hasn't
* been applied yet via an app restart.
*/
+ (BOOL)isPendingUpdate:(NSString*)packageHash
{
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
NSDictionary *pendingUpdate = [preferences objectForKey:PendingUpdateKey];
// If there is a pending update whose "state" isn't loading, then we consider it "pending".
// Additionally, if a specific hash was provided, we ensure it matches that of the pending update.
BOOL updateIsPending = pendingUpdate &&
[pendingUpdate[PendingUpdateIsLoadingKey] boolValue] == NO &&
(!packageHash || [pendingUpdate[PendingUpdateHashKey] isEqualToString:packageHash]);
return updateIsPending;
}
/*
* This method updates the React Native bridge's bundle URL
* to point at the latest CodePush update, and then restarts
* the bridge. This isn't meant to be called directly.
*/
- (void)loadBundle
{
// This needs to be async dispatched because the bridge is not set on init
// when the app first starts, therefore rollbacks will not take effect.
dispatch_async(dispatch_get_main_queue(), ^{
// If the current bundle URL is using http(s), then assume the dev
// is debugging and therefore, shouldn't be redirected to a local
// file (since Chrome wouldn't support it). Otherwise, update
// the current bundle URL to point at the latest update
if ([CodePush isUsingTestConfiguration] || ![super.bridge.bundleURL.scheme hasPrefix:@"http"]) {
[super.bridge setValue:[CodePush bundleURL] forKey:@"bundleURL"];
}
[super.bridge reload];
});
}
/*
* This method is used when an update has failed installation
* and the app needs to be rolled back to the previous bundle.
* This method is automatically called when the rollback timer
* expires without the app indicating whether the update succeeded,
* and therefore, it shouldn't be called directly.
*/
- (void)rollbackPackage
{
NSError *error;
NSDictionary *failedPackage = [CodePushPackage getCurrentPackage:&error];
if (!failedPackage) {
if (error) {
CPLog(@"Error getting current update metadata during rollback: %@", error);
} else {
CPLog(@"Attempted to perform a rollback when there is no current update");
}
} else {
// Write the current package's metadata to the "failed list"
[self saveFailedUpdate:failedPackage];
}
// Rollback to the previous version and de-register the new update
[CodePushPackage rollbackPackage];
[CodePush removePendingUpdate];
[self loadBundle];
}
/*
* When an update failed to apply, this method can be called
* to store its hash so that it can be ignored on future
* attempts to check the server for an update.
*/
- (void)saveFailedUpdate:(NSDictionary *)failedPackage
{
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
NSMutableArray *failedUpdates = [preferences objectForKey:FailedUpdatesKey];
if (failedUpdates == nil) {
failedUpdates = [[NSMutableArray alloc] init];
} else {
// The NSUserDefaults sytem always returns immutable
// objects, regardless if you stored something mutable.
failedUpdates = [failedUpdates mutableCopy];
}
[failedUpdates addObject:failedPackage];
[preferences setObject:failedUpdates forKey:FailedUpdatesKey];
[preferences synchronize];
}
/*
* This method is used to clear away failed updates in the event that
* a new app store binary is installed.
*/
+ (void)removeFailedUpdates
{
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
[preferences removeObjectForKey:FailedUpdatesKey];
[preferences synchronize];
}
/*
* This method is used to register the fact that a pending
* update succeeded and therefore can be removed.
*/
+ (void)removePendingUpdate
{
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
[preferences removeObjectForKey:PendingUpdateKey];
[preferences synchronize];
}
/*
* When an update is installed whose mode isn't IMMEDIATE, this method
* can be called to store the pending update's metadata (e.g. packageHash)
* so that it can be used when the actual update application occurs at a later point.
*/
- (void)savePendingUpdate:(NSString *)packageHash
isLoading:(BOOL)isLoading
{
// Since we're not restarting, we need to store the fact that the update
// was installed, but hasn't yet become "active".
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
NSDictionary *pendingUpdate = [[NSDictionary alloc] initWithObjectsAndKeys:
packageHash,PendingUpdateHashKey,
[NSNumber numberWithBool:isLoading],PendingUpdateIsLoadingKey, nil];
[preferences setObject:pendingUpdate forKey:PendingUpdateKey];
[preferences synchronize];
}
- (NSArray<NSString *> *)supportedEvents {
return @[DownloadProgressEvent];
}
#pragma mark - Application lifecycle event handlers
// These two handlers will only be registered when there is
// a resume-based update still pending installation.
- (void)applicationWillEnterForeground
{
if (_appSuspendTimer) {
[_appSuspendTimer invalidate];
_appSuspendTimer = nil;
}
// Determine how long the app was in the background and ensure
// that it meets the minimum duration amount of time.
int durationInBackground = 0;
if (_lastResignedDate) {
durationInBackground = [[NSDate date] timeIntervalSinceDate:_lastResignedDate];
}
if (durationInBackground >= _minimumBackgroundDuration) {
[self loadBundle];
}
}
- (void)applicationWillResignActive
{
// Save the current time so that when the app is later
// resumed, we can detect how long it was in the background.
_lastResignedDate = [NSDate date];
if (_installMode == CodePushInstallModeOnNextSuspend && [[self class] isPendingUpdate:nil]) {
_appSuspendTimer = [NSTimer scheduledTimerWithTimeInterval:_minimumBackgroundDuration
target:self
selector:@selector(loadBundleOnTick:)
userInfo:nil
repeats:NO];
}
}
-(void)loadBundleOnTick:(NSTimer *)timer {
[self loadBundle];
}
#pragma mark - JavaScript-exported module methods (Public)
/*
* This is native-side of the RemotePackage.download method
*/
RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
notifyProgress:(BOOL)notifyProgress
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSDictionary *mutableUpdatePackage = [updatePackage mutableCopy];
NSURL *binaryBundleURL = [CodePush binaryBundleURL];
if (binaryBundleURL != nil) {
[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;
self.paused = NO;
}
NSString * publicKey = [[CodePushConfig current] publicKey];
[CodePushPackage
downloadPackage:mutableUpdatePackage
expectedBundleFileName:[bundleResourceName stringByAppendingPathExtension:bundleResourceExtension]
publicKey:publicKey
operationQueue:_methodQueue
// The download is progressing forward
progressCallback:^(long long expectedContentLength, long long receivedContentLength) {
// Update the download progress so that the frame observer can notify the JS side
_latestExpectedContentLength = expectedContentLength;
_latestReceivedConentLength = receivedContentLength;
_didUpdateProgress = YES;
// If the download is completed, stop observing frame
// updates and synchronously send the last event.
if (expectedContentLength == receivedContentLength) {
_didUpdateProgress = NO;
self.paused = YES;
[self dispatchDownloadProgressEvent];
}
}
// The download completed
doneCallback:^{
NSError *err;
NSDictionary *newPackage = [CodePushPackage getPackage:mutableUpdatePackage[PackageHashKey] error:&err];
if (err) {
return reject([NSString stringWithFormat: @"%lu", (long)err.code], err.localizedDescription, err);
}
resolve(newPackage);
}
// The download failed
failCallback:^(NSError *err) {
if ([CodePushErrorUtils isCodePushError:err]) {
[self saveFailedUpdate:mutableUpdatePackage];
}
// Stop observing frame updates if the download fails.
_didUpdateProgress = NO;
self.paused = YES;
reject([NSString stringWithFormat: @"%lu", (long)err.code], err.localizedDescription, err);
}];
}
/*
* This is the native side of the CodePush.getConfiguration method. It isn't
* currently exposed via the "react-native-code-push" module, and is used
* internally only by the CodePush.checkForUpdate method in order to get the
* app version, as well as the deployment key that was configured in the Info.plist file.
*/
RCT_EXPORT_METHOD(getConfiguration:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSDictionary *configuration = [[CodePushConfig current] configuration];
NSError *error;
if (isRunningBinaryVersion) {
// isRunningBinaryVersion will not get set to "YES" if running against the packager.
NSString *binaryHash = [CodePushUpdateUtils getHashForBinaryContents:[CodePush binaryBundleURL] error:&error];
if (error) {
CPLog(@"Error obtaining hash for binary contents: %@", error);
resolve(configuration);
return;
}
if (binaryHash == nil) {
// The hash was not generated either due to a previous unknown error or the fact that
// the React Native assets were not bundled in the binary (e.g. during dev/simulator)
// builds.
resolve(configuration);
return;
}
NSMutableDictionary *mutableConfiguration = [configuration mutableCopy];
[mutableConfiguration setObject:binaryHash forKey:PackageHashKey];
resolve(mutableConfiguration);
return;
}
resolve(configuration);
}
/*
* This method is the native side of the CodePush.getUpdateMetadata method.
*/
RCT_EXPORT_METHOD(getUpdateMetadata:(CodePushUpdateState)updateState
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSError *error;
NSMutableDictionary *package = [[CodePushPackage getCurrentPackage:&error] mutableCopy];
if (error) {
return reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error);
} else if (package == nil) {
// The app hasn't downloaded any CodePush updates yet,
// so we simply return nil regardless if the user
// wanted to retrieve the pending or running update.
return resolve(nil);
}
// We have a CodePush update, so let's see if it's currently in a pending state.
BOOL currentUpdateIsPending = [[self class] isPendingUpdate:[package objectForKey:PackageHashKey]];
if (updateState == CodePushUpdateStatePending && !currentUpdateIsPending) {
// The caller wanted a pending update
// but there isn't currently one.
resolve(nil);
} else if (updateState == CodePushUpdateStateRunning && currentUpdateIsPending) {
// The caller wants the running update, but the current
// one is pending, so we need to grab the previous.
resolve([CodePushPackage getPreviousPackage:&error]);
} else {
// The current package satisfies the request:
// 1) Caller wanted a pending, and there is a pending update
// 2) Caller wanted the running update, and there isn't a pending
// 3) Caller wants the latest update, regardless if it's pending or not
if (isRunningBinaryVersion) {
// This only matters in Debug builds. Since we do not clear "outdated" updates,
// we need to indicate to the JS side that somehow we have a current update on
// disk that is not actually running.
[package setObject:@(YES) forKey:@"_isDebugOnly"];
}
// Enable differentiating pending vs. non-pending updates
[package setObject:@(currentUpdateIsPending) forKey:PackageIsPendingKey];
resolve(package);
}
}
/*
* This method is the native side of the LocalPackage.install method.
*/
RCT_EXPORT_METHOD(installUpdate:(NSDictionary*)updatePackage
installMode:(CodePushInstallMode)installMode
minimumBackgroundDuration:(int)minimumBackgroundDuration
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSError *error;
[CodePushPackage installPackage:updatePackage
removePendingUpdate:[[self class] isPendingUpdate:nil]
error:&error];
if (error) {
reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error);
} else {
[self savePendingUpdate:updatePackage[PackageHashKey]
isLoading:NO];
_installMode = installMode;
if (_installMode == CodePushInstallModeOnNextResume || _installMode == CodePushInstallModeOnNextSuspend) {
_minimumBackgroundDuration = minimumBackgroundDuration;
if (!_hasResumeListener) {
// Ensure we do not add the listener twice.
// Register for app resume notifications so that we
// can check for pending updates which support "restart on resume"
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillEnterForeground)
name:UIApplicationWillEnterForegroundNotification
object:RCTSharedApplication()];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillResignActive)
name:UIApplicationWillResignActiveNotification
object:RCTSharedApplication()];
_hasResumeListener = YES;
}
}
// Signal to JS that the update has been applied.
resolve(nil);
}
}
/*
* This method isn't publicly exposed via the "react-native-code-push"
* module, and is only used internally to populate the RemotePackage.failedInstall property.
*/
RCT_EXPORT_METHOD(isFailedUpdate:(NSString *)packageHash
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
{
BOOL isFailedHash = [[self class] isFailedHash:packageHash];
resolve(@(isFailedHash));
}
/*
* This method isn't publicly exposed via the "react-native-code-push"
* module, and is only used internally to populate the LocalPackage.isFirstRun property.
*/
RCT_EXPORT_METHOD(isFirstRun:(NSString *)packageHash
resolve:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSError *error;
BOOL isFirstRun = _isFirstRunAfterUpdate
&& nil != packageHash
&& [packageHash length] > 0
&& [packageHash isEqualToString:[CodePushPackage getCurrentPackageHash:&error]];
resolve(@(isFirstRun));
}
/*
* This method is the native side of the CodePush.notifyApplicationReady() method.
*/
RCT_EXPORT_METHOD(notifyApplicationReady:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
[CodePush removePendingUpdate];
resolve(nil);
}
/*
* This method is the native side of the CodePush.restartApp() method.
*/
RCT_EXPORT_METHOD(restartApp:(BOOL)onlyIfUpdateIsPending
resolve:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
// If this is an unconditional restart request, or there
// is current pending update, then reload the app.
if (!onlyIfUpdateIsPending || [[self class] isPendingUpdate:nil]) {
[self loadBundle];
resolve(@(YES));
return;
}
resolve(@(NO));
}
/*
* This method clears CodePush's downloaded updates.
* It is needed to switch to a different deployment if the current deployment is more recent.
* Note: we don’t recommend to use this method in scenarios other than that (CodePush will call this method
* automatically when needed in other cases) as it could lead to unpredictable behavior.
*/
RCT_EXPORT_METHOD(clearUpdates) {
CPLog(@"Clearing updates.");
[CodePush clearUpdates];
}
#pragma mark - JavaScript-exported module methods (Private)
/*
* This method is the native side of the CodePush.downloadAndReplaceCurrentBundle()
* method, which replaces the current bundle with the one downloaded from
* removeBundleUrl. It is only to be used during tests and no-ops if the test
* configuration flag is not set.
*/
RCT_EXPORT_METHOD(downloadAndReplaceCurrentBundle:(NSString *)remoteBundleUrl)
{
if ([CodePush isUsingTestConfiguration]) {
[CodePushPackage downloadAndReplaceCurrentBundle:remoteBundleUrl];
}
}
/*
* This method is checks if a new status update exists (new version was installed,
* or an update failed) and return its details (version label, status).
*/
RCT_EXPORT_METHOD(getNewStatusReport:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
if (needToReportRollback) {
needToReportRollback = NO;
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
NSMutableArray *failedUpdates = [preferences objectForKey:FailedUpdatesKey];
if (failedUpdates) {
NSDictionary *lastFailedPackage = [failedUpdates lastObject];
if (lastFailedPackage) {
resolve([CodePushTelemetryManager getRollbackReport:lastFailedPackage]);
return;
}
}
} else if (_isFirstRunAfterUpdate) {
NSError *error;
NSDictionary *currentPackage = [CodePushPackage getCurrentPackage:&error];
if (!error && currentPackage) {
resolve([CodePushTelemetryManager getUpdateReport:currentPackage]);
return;
}
} else if (isRunningBinaryVersion) {
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
{
if (!_didUpdateProgress) {
return;
}
[self dispatchDownloadProgressEvent];
_didUpdateProgress = NO;
}
@end
Loading...
举报
举报成功
我们将于2个工作日内通过站内信反馈结果给你!
请认真填写举报原因,尽可能描述详细。
请选择举报类型
取消
发送
误判申诉

此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。

如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。

取消
提交

简介

React Native module for CodePush
取消

发行版

暂无发行版

贡献者

全部

近期动态

不能加载更多了
编辑仓库简介
简介内容
主页
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Objective-C
1
https://gitee.com/hackcat/react-native-code-push.git
git@gitee.com:hackcat/react-native-code-push.git
hackcat
react-native-code-push
react-native-code-push
master
点此查找更多帮助

搜索帮助

评论
仓库举报
回到顶部
登录提示
该操作需登录 Gitee 帐号,请先登录后再操作。
立即登录
没有帐号,去注册

AltStyle によって変換されたページ (->オリジナル) /