added iOS source code
[wl-app.git] / iOS / Pods / FirebaseCore / Firebase / Core / FIRComponentContainer.m
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/FIRComponentContainer.m b/iOS/Pods/FirebaseCore/Firebase/Core/FIRComponentContainer.m
new file mode 100644 (file)
index 0000000..381c95c
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "Private/FIRComponentContainer.h"
+
+#import "Private/FIRAppInternal.h"
+#import "Private/FIRComponent.h"
+#import "Private/FIRComponentRegistrant.h"
+#import "Private/FIRLogger.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRComponentContainer ()
+
+/// The dictionary of components that are registered for a particular app. The key is an NSString
+/// of the protocol.
+@property(nonatomic, strong) NSMutableDictionary<NSString *, FIRComponentCreationBlock> *components;
+
+/// Cached instances of components that requested to be cached.
+@property(nonatomic, strong) NSMutableDictionary<NSString *, id> *cachedInstances;
+
+@end
+
+@implementation FIRComponentContainer
+
+// Collection of all classes that register to provide components.
+static NSMutableSet<Class> *gFIRComponentRegistrants;
+
+#pragma mark - Public Registration
+
++ (void)registerAsComponentRegistrant:(Class)klass {
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    gFIRComponentRegistrants = [[NSMutableSet<Class> alloc] init];
+  });
+
+  [self registerAsComponentRegistrant:klass inSet:gFIRComponentRegistrants];
+}
+
++ (void)registerAsComponentRegistrant:(Class)klass inSet:(NSMutableSet<Class> *)allRegistrants {
+  // Validate the array to store the components is initialized.
+  if (!allRegistrants) {
+    FIRLogWarning(kFIRLoggerCore, @"I-COR000025",
+                  @"Attempted to store registered components in an empty set.");
+    return;
+  }
+
+  // Ensure the class given conforms to the proper protocol.
+  if (![klass conformsToProtocol:@protocol(FIRComponentRegistrant)] ||
+      ![klass respondsToSelector:@selector(componentsToRegister)]) {
+    [NSException raise:NSInvalidArgumentException
+                format:
+                    @"Class %@ attempted to register components, but it does not conform to "
+                    @"`FIRComponentRegistrant` or provide a `componentsToRegister:` method.",
+                    klass];
+  }
+
+  [allRegistrants addObject:klass];
+}
+
+#pragma mark - Internal Initialization
+
+- (instancetype)initWithApp:(FIRApp *)app {
+  return [self initWithApp:app registrants:gFIRComponentRegistrants];
+}
+
+- (instancetype)initWithApp:(FIRApp *)app registrants:(NSMutableSet<Class> *)allRegistrants {
+  self = [super init];
+  if (self) {
+    _app = app;
+    _cachedInstances = [NSMutableDictionary<NSString *, id> dictionary];
+    _components = [NSMutableDictionary<NSString *, FIRComponentCreationBlock> dictionary];
+
+    [self populateComponentsFromRegisteredClasses:allRegistrants forApp:app];
+  }
+  return self;
+}
+
+- (void)populateComponentsFromRegisteredClasses:(NSSet<Class> *)classes forApp:(FIRApp *)app {
+  // Loop through the verified component registrants and populate the components array.
+  for (Class<FIRComponentRegistrant> klass in classes) {
+    // Loop through all the components being registered and store them as appropriate.
+    // Classes which do not provide functionality should use a dummy FIRComponentRegistrant
+    // protocol.
+    for (FIRComponent *component in [klass componentsToRegister]) {
+      // Check if the component has been registered before, and error out if so.
+      NSString *protocolName = NSStringFromProtocol(component.protocol);
+      if (self.components[protocolName]) {
+        FIRLogError(kFIRLoggerCore, @"I-COR000029",
+                    @"Attempted to register protocol %@, but it already has an implementation.",
+                    protocolName);
+        continue;
+      }
+
+      // Store the creation block for later usage.
+      self.components[protocolName] = component.creationBlock;
+
+      // Instantiate the
+      BOOL shouldInstantiateEager =
+          (component.instantiationTiming == FIRInstantiationTimingAlwaysEager);
+      BOOL shouldInstantiateDefaultEager =
+          (component.instantiationTiming == FIRInstantiationTimingEagerInDefaultApp &&
+           [app isDefaultApp]);
+      if (shouldInstantiateEager || shouldInstantiateDefaultEager) {
+        [self instantiateInstanceForProtocol:component.protocol withBlock:component.creationBlock];
+      }
+    }
+  }
+}
+
+#pragma mark - Instance Creation
+
+/// Instantiate an instance of a class that conforms to the specified protocol.
+/// This will:
+///   - Call the block to create an instance if possible,
+///   - Validate that the instance returned conforms to the protocol it claims to,
+///   - Cache the instance if the block requests it
+- (nullable id)instantiateInstanceForProtocol:(Protocol *)protocol
+                                    withBlock:(FIRComponentCreationBlock)creationBlock {
+  if (!creationBlock) {
+    return nil;
+  }
+
+  // Create an instance using the creation block.
+  BOOL shouldCache = NO;
+  id instance = creationBlock(self, &shouldCache);
+  if (!instance) {
+    return nil;
+  }
+
+  // An instance was created, validate that it conforms to the protocol it claims to.
+  NSString *protocolName = NSStringFromProtocol(protocol);
+  if (![instance conformsToProtocol:protocol]) {
+    FIRLogError(kFIRLoggerCore, @"I-COR000030",
+                @"An instance conforming to %@ was requested, but the instance provided does not "
+                @"conform to the protocol",
+                protocolName);
+  }
+
+  // The instance is ready to be returned, but check if it should be cached first before returning.
+  if (shouldCache) {
+    self.cachedInstances[protocolName] = instance;
+  }
+
+  return instance;
+}
+
+#pragma mark - Internal Retrieval
+
+- (nullable id)instanceForProtocol:(Protocol *)protocol {
+  // Check if there is a cached instance, and return it if so.
+  NSString *protocolName = NSStringFromProtocol(protocol);
+  id cachedInstance = self.cachedInstances[protocolName];
+  if (cachedInstance) {
+    return cachedInstance;
+  }
+
+  // Use the creation block to instantiate an instance and return it.
+  FIRComponentCreationBlock creationBlock = self.components[protocolName];
+  return [self instantiateInstanceForProtocol:protocol withBlock:creationBlock];
+}
+
+#pragma mark - Lifecycle
+
+- (void)removeAllCachedInstances {
+  // Loop through the cache and notify each instance that is a maintainer to clean up after itself.
+  for (id instance in self.cachedInstances.allValues) {
+    if ([instance conformsToProtocol:@protocol(FIRComponentLifecycleMaintainer)] &&
+        [instance respondsToSelector:@selector(appWillBeDeleted:)]) {
+      [instance appWillBeDeleted:self.app];
+    }
+  }
+
+  [self.cachedInstances removeAllObjects];
+}
+
+#pragma mark - Testing Initializers
+
+// TODO(wilsonryan): Set up a testing flag so this only is compiled in with unit tests.
+/// Initialize an instance with an app and existing components.
+- (instancetype)initWithApp:(FIRApp *)app
+                 components:(NSDictionary<NSString *, FIRComponentCreationBlock> *)components {
+  self = [self initWithApp:app registrants:[[NSMutableSet alloc] init]];
+  if (self) {
+    _components = [components mutableCopy];
+  }
+  return self;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END