1// Copyright 2015-present 650 Industries. All rights reserved. 2 3#import "EXKernelAppRegistry.h" 4#import "EXKernelAppLoader.h" 5#import "EXReactAppManager.h" 6#import "EXKernel.h" 7#import "EXShellManager.h" 8 9#import <React/RCTBridge.h> 10 11@interface EXKernelAppRegistry () 12 13@property (nonatomic, strong) NSMutableDictionary *appRegistry; 14@property (nonatomic, strong) EXKernelAppRecord *homeAppRecord; 15 16@end 17 18@implementation EXKernelAppRegistry 19 20- (instancetype)init 21{ 22 if (self = [super init]) { 23 _appRegistry = [[NSMutableDictionary alloc] init]; 24 } 25 return self; 26} 27 28- (NSString *)registerAppWithManifestUrl:(NSURL *)manifestUrl initialProps:(NSDictionary *)initialProps 29{ 30 NSAssert(manifestUrl, @"Cannot register an app with no manifest URL"); 31 // not enforcing uniqueness yet - we will do this once we download the manifest & have the experienceId 32 EXKernelAppRecord *newRecord = [[EXKernelAppRecord alloc] initWithManifestUrl:manifestUrl initialProps:initialProps]; 33 NSString *recordId = [[NSUUID UUID] UUIDString]; 34 [_appRegistry setObject:newRecord forKey:recordId]; 35 36 if (_delegate) { 37 [_delegate appRegistry:self didRegisterAppRecord:newRecord]; 38 } 39 40 return recordId; 41} 42 43- (void)unregisterAppWithRecordId:(NSString *)recordId 44{ 45 EXKernelAppRecord *record = [_appRegistry objectForKey:recordId]; 46 if (record) { 47 if (_delegate) { 48 [_delegate appRegistry:self willUnregisterAppRecord:record]; 49 } 50 [record.appManager invalidate]; 51 [_appRegistry removeObjectForKey:recordId]; 52 } 53} 54 55- (void)registerHomeAppRecord:(EXKernelAppRecord *)homeRecord 56{ 57 _homeAppRecord = homeRecord; 58} 59 60- (void)unregisterHomeAppRecord 61{ 62 _homeAppRecord = nil; 63} 64 65- (EXKernelAppRecord *)homeAppRecord 66{ 67 return _homeAppRecord; 68} 69 70- (EXKernelAppRecord *)standaloneAppRecord 71{ 72 if ([EXShellManager sharedInstance].isShell) { 73 for (NSString *recordId in self.appEnumerator) { 74 EXKernelAppRecord *record = [self recordForId:recordId]; 75 if (record.appLoader.manifestUrl 76 && [record.appLoader.manifestUrl.absoluteString isEqualToString:[EXShellManager sharedInstance].shellManifestUrl]) { 77 return record; 78 } 79 } 80 } 81 return nil; 82} 83 84- (EXKernelAppRecord *)recordForId:(NSString *)recordId 85{ 86 return [_appRegistry objectForKey:recordId]; 87} 88 89// when reloading, for a brief period of time there are two records with the same experienceId in the registry 90- (EXKernelAppRecord * _Nullable)newestRecordWithExperienceId:(NSString *)experienceId 91{ 92 EXKernelAppRecord *recordToReturn; 93 for (NSString *recordId in self.appEnumerator) { 94 EXKernelAppRecord *record = [self recordForId:recordId]; 95 if (record && record.experienceId && [record.experienceId isEqualToString:experienceId]) { 96 if (recordToReturn && [recordToReturn.timeCreated compare:record.timeCreated] == NSOrderedDescending) { 97 continue; 98 } 99 recordToReturn = record; 100 } 101 } 102 return recordToReturn; 103} 104 105- (NSEnumerator<id> *)appEnumerator 106{ 107 // TODO: use mutexes to control access to _appRegistry rather than just copying it here 108 return [(NSDictionary *)[_appRegistry copy] keyEnumerator]; 109} 110 111- (NSString *)description 112{ 113 if (_appRegistry.count > 0) { 114 NSMutableString *results = [NSMutableString string]; 115 for (NSString *recordId in self.appEnumerator) { 116 EXKernelAppRecord *record = [self recordForId:recordId]; 117 [results appendString:[NSString stringWithFormat:@" %@: %@\n", recordId, record]]; 118 } 119 return [NSString stringWithFormat:@"EXKernelAppRegistry with apps: {\n%@}", results]; 120 } 121 return @"EXKernelAppRegistry (empty)"; 122} 123 124- (BOOL)isExperienceIdUnique:(NSString *)experienceId 125{ 126 int count = 0; 127 for (NSString *recordId in self.appEnumerator) { 128 EXKernelAppRecord *appRecord = [self recordForId:recordId]; 129 if (appRecord.experienceId && [appRecord.experienceId isEqualToString:experienceId]) { 130 count++; 131 if (count > 1) { 132 return NO; 133 } 134 } 135 } 136 return YES; 137} 138 139@end 140