1// Copyright 2015-present 650 Industries. All rights reserved. 2 3#import "EXAppLoader+Updates.h" 4#import "EXEnvironment.h" 5#import "EXKernel.h" 6#import "EXKernelAppRecord.h" 7#import "EXReactAppManager.h" 8#import "EXScopedModuleRegistry.h" 9#import "EXUpdates.h" 10#import "EXUpdatesManager.h" 11 12#import <React/RCTBridge.h> 13#import <React/RCTUtils.h> 14 15@interface EXUpdatesManager () 16 17@property (nonatomic, strong) EXAppLoader *manifestAppLoader; 18 19@end 20 21@implementation EXUpdatesManager 22 23- (void)notifyApp:(EXKernelAppRecord *)appRecord 24ofDownloadWithManifest:(NSDictionary * _Nullable)manifest 25 isNew:(BOOL)isBundleNew 26 error:(NSError * _Nullable)error; 27{ 28 NSDictionary *body; 29 if (error) { 30 body = @{ 31 @"type": EXUpdatesErrorEventType, 32 @"message": error.localizedDescription 33 }; 34 } else if (isBundleNew) { 35 if (!manifest) { 36 // prevent a crash, but this shouldn't ever happen 37 manifest = @{}; 38 } 39 body = @{ 40 @"type": EXUpdatesDownloadFinishedEventType, 41 @"manifest": manifest 42 }; 43 } else { 44 body = @{ 45 @"type": EXUpdatesNotAvailableEventType 46 }; 47 } 48 RCTBridge *bridge = appRecord.appManager.reactBridge; 49 if (appRecord.status == kEXKernelAppRecordStatusRunning && [self _doesBridgeSupportUpdatesModule:bridge]) { 50 [bridge.scopedModules.updates sendEventWithBody:body]; 51 } 52} 53 54# pragma mark - internal 55 56- (EXAppLoader *)_appLoaderWithScopedModule:(id)scopedModule 57{ 58 NSString *experienceId = ((EXScopedBridgeModule *)scopedModule).experienceId; 59 EXKernelAppRecord *appRecord = [[EXKernel sharedInstance].appRegistry newestRecordWithExperienceId:experienceId]; 60 return appRecord.appLoader; 61} 62 63# pragma mark - EXUpdatesScopedModuleDelegate 64 65- (void)updatesModuleDidSelectReload:(id)scopedModule 66{ 67 NSString *experienceId = ((EXScopedBridgeModule *)scopedModule).experienceId; 68 [[EXKernel sharedInstance] reloadAppWithExperienceId:experienceId]; 69} 70 71- (void)updatesModuleDidSelectReloadFromCache:(id)scopedModule 72{ 73 NSString *experienceId = ((EXScopedBridgeModule *)scopedModule).experienceId; 74 [[EXKernel sharedInstance] reloadAppFromCacheWithExperienceId:experienceId]; 75} 76 77- (void)updatesModule:(id)scopedModule 78didRequestManifestWithCacheBehavior:(EXManifestCacheBehavior)cacheBehavior 79 success:(void (^)(NSDictionary * _Nonnull))success 80 failure:(void (^)(NSError * _Nonnull))failure 81{ 82 if ([EXEnvironment sharedEnvironment].isDetached && ![EXEnvironment sharedEnvironment].areRemoteUpdatesEnabled) { 83 failure(RCTErrorWithMessage(@"Remote updates are disabled in app.json")); 84 return; 85 } 86 EXAppLoader *appLoader = [self _appLoaderWithScopedModule:scopedModule]; 87 [appLoader fetchManifestWithCacheBehavior:cacheBehavior success:success failure:failure]; 88 if (cacheBehavior == EXManifestPrepareToCache) { 89 _manifestAppLoader = appLoader; 90 } 91} 92 93- (void)updatesModule:(id)scopedModule 94didRequestBundleWithManifest:(NSDictionary *)manifest 95 progress:(void (^)(NSDictionary * _Nonnull))progressBlock 96 success:(void (^)(NSData * _Nonnull))success 97 failure:(void (^)(NSError * _Nonnull))failure 98{ 99 if ([EXEnvironment sharedEnvironment].isDetached && ![EXEnvironment sharedEnvironment].areRemoteUpdatesEnabled) { 100 failure(RCTErrorWithMessage(@"Remote updates are disabled in app.json")); 101 return; 102 } 103 void (^progressDictBlock)(EXLoadingProgress * _Nonnull) = ^void(EXLoadingProgress * _Nonnull progress) { 104 progressBlock(@{ 105 @"status": progress.status, 106 @"done": progress.done, 107 @"total": progress.total 108 }); 109 }; 110 EXAppLoader *appLoader = _manifestAppLoader ?: [self _appLoaderWithScopedModule:scopedModule]; 111 [appLoader fetchJSBundleWithManifest:manifest 112 cacheBehavior:EXCachedResourceWriteToCache 113 timeoutInterval:kEXJSBundleTimeout 114 progress:progressDictBlock 115 success:^(NSData * _Nonnull data) { 116 if (self->_manifestAppLoader) { 117 [self->_manifestAppLoader writeManifestToCache]; 118 } 119 self->_manifestAppLoader = nil; 120 success(data); 121 } 122 error:^(NSError * _Nonnull error) { 123 self->_manifestAppLoader = nil; 124 failure(error); 125 }]; 126} 127 128- (BOOL)_doesBridgeSupportUpdatesModule:(RCTBridge *)bridge 129{ 130 // sdk versions prior to 26 didn't include this module. 131 return ([bridge.scopedModules respondsToSelector:@selector(updates)]); 132} 133 134@end 135