1ea3f1d02STomasz Sapeta// Copyright © 2018 650 Industries. All rights reserved.
2ea3f1d02STomasz Sapeta
3ea3f1d02STomasz Sapeta#import <Foundation/Foundation.h>
4ea3f1d02STomasz Sapeta#import <ExpoModulesCore/EXSingletonModule.h>
5ea3f1d02STomasz Sapeta#import <ExpoModulesCore/EXModuleRegistryProvider.h>
6ea3f1d02STomasz Sapeta
7ea3f1d02STomasz Sapetastatic dispatch_once_t onceToken;
8ea3f1d02STomasz Sapetastatic NSMutableSet<Class> *EXModuleClasses;
9ea3f1d02STomasz Sapetastatic NSMutableSet<Class> *EXSingletonModuleClasses;
10ea3f1d02STomasz Sapeta
11ea3f1d02STomasz Sapetavoid (^EXinitializeGlobalModulesRegistry)(void) = ^{
12ea3f1d02STomasz Sapeta  EXModuleClasses = [NSMutableSet set];
13ea3f1d02STomasz Sapeta  EXSingletonModuleClasses = [NSMutableSet set];
14ea3f1d02STomasz Sapeta};
15ea3f1d02STomasz Sapeta
16ea3f1d02STomasz Sapetaextern void EXRegisterModule(Class);
17ea3f1d02STomasz Sapetaextern void EXRegisterModule(Class moduleClass)
18ea3f1d02STomasz Sapeta{
19ea3f1d02STomasz Sapeta  dispatch_once(&onceToken, EXinitializeGlobalModulesRegistry);
20ea3f1d02STomasz Sapeta  [EXModuleClasses addObject:moduleClass];
21ea3f1d02STomasz Sapeta}
22ea3f1d02STomasz Sapeta
23ea3f1d02STomasz Sapetaextern void EXRegisterSingletonModule(Class);
24ea3f1d02STomasz Sapetaextern void EXRegisterSingletonModule(Class singletonModuleClass)
25ea3f1d02STomasz Sapeta{
26ea3f1d02STomasz Sapeta  dispatch_once(&onceToken, EXinitializeGlobalModulesRegistry);
27ea3f1d02STomasz Sapeta
28ea3f1d02STomasz Sapeta  // A heuristic solution to "multiple singletons registering
29ea3f1d02STomasz Sapeta  // to the same name" problem. Usually it happens when we want to
30ea3f1d02STomasz Sapeta  // override module singleton with an ExpoKit one. This solution
31ea3f1d02STomasz Sapeta  // gives preference to subclasses.
32ea3f1d02STomasz Sapeta
33ea3f1d02STomasz Sapeta  // If a superclass of a registering singleton is already registered
34ea3f1d02STomasz Sapeta  // we want to remove it in favor of the registering singleton.
35ea3f1d02STomasz Sapeta  Class superClass = [singletonModuleClass superclass];
36ea3f1d02STomasz Sapeta  while (superClass != [NSObject class]) {
37ea3f1d02STomasz Sapeta    [EXSingletonModuleClasses removeObject:superClass];
38ea3f1d02STomasz Sapeta    superClass = [superClass superclass];
39ea3f1d02STomasz Sapeta  }
40ea3f1d02STomasz Sapeta
41ea3f1d02STomasz Sapeta  // If a registering singleton is a superclass of an already registered
42ea3f1d02STomasz Sapeta  // singleton, we don't register it.
43ea3f1d02STomasz Sapeta  for (Class registeredClass in EXSingletonModuleClasses) {
44ea3f1d02STomasz Sapeta    if ([singletonModuleClass isSubclassOfClass:registeredClass]) {
45ea3f1d02STomasz Sapeta      return;
46ea3f1d02STomasz Sapeta    }
47ea3f1d02STomasz Sapeta  }
48ea3f1d02STomasz Sapeta
49ea3f1d02STomasz Sapeta  [EXSingletonModuleClasses addObject:singletonModuleClass];
50ea3f1d02STomasz Sapeta}
51ea3f1d02STomasz Sapeta
52ea3f1d02STomasz Sapeta// Singleton modules classes register in EXSingletonModuleClasses
53ea3f1d02STomasz Sapeta// with EXRegisterSingletonModule function. Then they should be
54ea3f1d02STomasz Sapeta// initialized exactly once (onceSingletonModulesToken guards that).
55ea3f1d02STomasz Sapeta
56ea3f1d02STomasz Sapetastatic dispatch_once_t onceSingletonModulesToken;
57ea3f1d02STomasz Sapetastatic NSMutableSet<EXSingletonModule *> *EXSingletonModules;
58ea3f1d02STomasz Sapetavoid (^EXinitializeGlobalSingletonModulesSet)(void) = ^{
59ea3f1d02STomasz Sapeta  EXSingletonModules = [NSMutableSet set];
60ea3f1d02STomasz Sapeta  for (Class singletonModuleClass in EXSingletonModuleClasses) {
61ea3f1d02STomasz Sapeta    [EXSingletonModules addObject:[[singletonModuleClass alloc] init]];
62ea3f1d02STomasz Sapeta  }
63ea3f1d02STomasz Sapeta};
64ea3f1d02STomasz Sapeta
65ea3f1d02STomasz Sapeta@interface EXModuleRegistryProvider ()
66ea3f1d02STomasz Sapeta
67ea3f1d02STomasz Sapeta@property (nonatomic, strong) NSSet *singletonModules;
68ea3f1d02STomasz Sapeta
69ea3f1d02STomasz Sapeta@end
70ea3f1d02STomasz Sapeta
71ea3f1d02STomasz Sapeta@implementation EXModuleRegistryProvider
72ea3f1d02STomasz Sapeta
73ea3f1d02STomasz Sapeta- (instancetype)init
74ea3f1d02STomasz Sapeta{
75ea3f1d02STomasz Sapeta  return [self initWithSingletonModules:[EXModuleRegistryProvider singletonModules]];
76ea3f1d02STomasz Sapeta}
77ea3f1d02STomasz Sapeta
78ea3f1d02STomasz Sapeta- (instancetype)initWithSingletonModules:(NSSet *)modules
79ea3f1d02STomasz Sapeta{
80ea3f1d02STomasz Sapeta  if (self = [super init]) {
81ea3f1d02STomasz Sapeta    _singletonModules = [NSSet setWithSet:modules];
82ea3f1d02STomasz Sapeta  }
83ea3f1d02STomasz Sapeta  return self;
84ea3f1d02STomasz Sapeta}
85ea3f1d02STomasz Sapeta
86*4dda5226STomasz Sapeta+ (NSSet<Class> *)getModulesClasses
87ea3f1d02STomasz Sapeta{
88ea3f1d02STomasz Sapeta  return EXModuleClasses;
89ea3f1d02STomasz Sapeta}
90ea3f1d02STomasz Sapeta
91ea3f1d02STomasz Sapeta+ (NSSet<EXSingletonModule *> *)singletonModules
92ea3f1d02STomasz Sapeta{
93ea3f1d02STomasz Sapeta  dispatch_once(&onceSingletonModulesToken, EXinitializeGlobalSingletonModulesSet);
94ea3f1d02STomasz Sapeta  return EXSingletonModules;
95ea3f1d02STomasz Sapeta}
96ea3f1d02STomasz Sapeta
97ea3f1d02STomasz Sapeta+ (nullable EXSingletonModule *)getSingletonModuleForClass:(Class)singletonClass
98ea3f1d02STomasz Sapeta{
99ea3f1d02STomasz Sapeta  NSSet<EXSingletonModule *> *singletonModules = [self singletonModules];
100ea3f1d02STomasz Sapeta
101ea3f1d02STomasz Sapeta  for (EXSingletonModule *singleton in singletonModules) {
102ea3f1d02STomasz Sapeta    if ([singleton isKindOfClass:singletonClass]) {
103ea3f1d02STomasz Sapeta      return singleton;
104ea3f1d02STomasz Sapeta    }
105ea3f1d02STomasz Sapeta  }
106ea3f1d02STomasz Sapeta  return nil;
107ea3f1d02STomasz Sapeta}
108ea3f1d02STomasz Sapeta
109ea3f1d02STomasz Sapeta- (EXModuleRegistry *)moduleRegistry
110ea3f1d02STomasz Sapeta{
111ea3f1d02STomasz Sapeta  NSMutableSet<id<EXInternalModule>> *internalModules = [NSMutableSet set];
112ea3f1d02STomasz Sapeta  NSMutableSet<EXExportedModule *> *exportedModules = [NSMutableSet set];
113ea3f1d02STomasz Sapeta
114*4dda5226STomasz Sapeta  for (Class klass in [self.class getModulesClasses]) {
115ea3f1d02STomasz Sapeta    if (![klass conformsToProtocol:@protocol(EXInternalModule)]) {
116ea3f1d02STomasz Sapeta      EXLogWarn(@"Registered class `%@` does not conform to the `EXInternalModule` protocol.", [klass description]);
117ea3f1d02STomasz Sapeta      continue;
118ea3f1d02STomasz Sapeta    }
119ea3f1d02STomasz Sapeta
120ea3f1d02STomasz Sapeta    id<EXInternalModule> instance = [self createModuleInstance:klass];
121ea3f1d02STomasz Sapeta
122ea3f1d02STomasz Sapeta    if ([[instance class] exportedInterfaces] != nil && [[[instance class] exportedInterfaces] count] > 0) {
123ea3f1d02STomasz Sapeta      [internalModules addObject:instance];
124ea3f1d02STomasz Sapeta    }
125ea3f1d02STomasz Sapeta
126ea3f1d02STomasz Sapeta    if ([instance isKindOfClass:[EXExportedModule class]]) {
127ea3f1d02STomasz Sapeta      [exportedModules addObject:(EXExportedModule *)instance];
128ea3f1d02STomasz Sapeta    }
129ea3f1d02STomasz Sapeta  }
130ea3f1d02STomasz Sapeta
131ea3f1d02STomasz Sapeta  EXModuleRegistry *moduleRegistry = [[EXModuleRegistry alloc] initWithInternalModules:internalModules
132ea3f1d02STomasz Sapeta                                                                       exportedModules:exportedModules
133ea3f1d02STomasz Sapeta                                                                      singletonModules:_singletonModules];
134ea3f1d02STomasz Sapeta  [moduleRegistry setDelegate:_moduleRegistryDelegate];
135ea3f1d02STomasz Sapeta  return moduleRegistry;
136ea3f1d02STomasz Sapeta}
137ea3f1d02STomasz Sapeta
138ea3f1d02STomasz Sapeta# pragma mark - Utilities
139ea3f1d02STomasz Sapeta
140ea3f1d02STomasz Sapeta- (id<EXInternalModule>)createModuleInstance:(Class)moduleClass
141ea3f1d02STomasz Sapeta{
142ea3f1d02STomasz Sapeta  return [[moduleClass alloc] init];
143ea3f1d02STomasz Sapeta}
144ea3f1d02STomasz Sapeta
145ea3f1d02STomasz Sapeta@end
146