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