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 *appLoader; 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:(EXCachedResourceBehavior)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} 89 90- (void)updatesModule:(id)scopedModule 91didRequestBundleWithManifest:(NSDictionary *)manifest 92 progress:(void (^)(NSDictionary * _Nonnull))progressBlock 93 success:(void (^)(NSData * _Nonnull))success 94 failure:(void (^)(NSError * _Nonnull))failure 95{ 96 if ([EXEnvironment sharedEnvironment].isDetached && ![EXEnvironment sharedEnvironment].areRemoteUpdatesEnabled) { 97 failure(RCTErrorWithMessage(@"Remote updates are disabled in app.json")); 98 return; 99 } 100 void (^progressDictBlock)(EXLoadingProgress * _Nonnull) = ^void(EXLoadingProgress * _Nonnull progress) { 101 progressBlock(@{ 102 @"status": progress.status, 103 @"done": progress.done, 104 @"total": progress.total 105 }); 106 }; 107 EXAppLoader *appLoader = [self _appLoaderWithScopedModule:scopedModule]; 108 [appLoader fetchJSBundleWithManifest:manifest 109 cacheBehavior:EXCachedResourceWriteToCache 110 timeoutInterval:kEXJSBundleTimeout 111 progress:progressDictBlock 112 success:success 113 error:failure]; 114} 115 116- (BOOL)_doesBridgeSupportUpdatesModule:(RCTBridge *)bridge 117{ 118 // sdk versions prior to 26 didn't include this module. 119 return ([bridge.scopedModules respondsToSelector:@selector(updates)]); 120} 121 122@end 123