Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// Copyright 2017 Google
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
 
15
#import "FirebaseCore/Sources/FIRBundleUtil.h"
16
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
17
#import "FirebaseCore/Sources/Private/FIRLogger.h"
18
#import "FirebaseCore/Sources/Private/FIROptionsInternal.h"
19
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRVersion.h"
20
 
21
// Keys for the strings in the plist file.
22
NSString *const kFIRAPIKey = @"API_KEY";
23
NSString *const kFIRTrackingID = @"TRACKING_ID";
24
NSString *const kFIRGoogleAppID = @"GOOGLE_APP_ID";
25
NSString *const kFIRClientID = @"CLIENT_ID";
26
NSString *const kFIRGCMSenderID = @"GCM_SENDER_ID";
27
NSString *const kFIRAndroidClientID = @"ANDROID_CLIENT_ID";
28
NSString *const kFIRDatabaseURL = @"DATABASE_URL";
29
NSString *const kFIRStorageBucket = @"STORAGE_BUCKET";
30
// The key to locate the expected bundle identifier in the plist file.
31
NSString *const kFIRBundleID = @"BUNDLE_ID";
32
// The key to locate the project identifier in the plist file.
33
NSString *const kFIRProjectID = @"PROJECT_ID";
34
 
35
NSString *const kFIRIsMeasurementEnabled = @"IS_MEASUREMENT_ENABLED";
36
NSString *const kFIRIsAnalyticsCollectionEnabled = @"FIREBASE_ANALYTICS_COLLECTION_ENABLED";
37
NSString *const kFIRIsAnalyticsCollectionDeactivated = @"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED";
38
 
39
NSString *const kFIRIsAnalyticsEnabled = @"IS_ANALYTICS_ENABLED";
40
NSString *const kFIRIsSignInEnabled = @"IS_SIGNIN_ENABLED";
41
 
42
// Library version ID formatted like:
43
// @"5"     // Major version (one or more digits)
44
// @"04"    // Minor version (exactly 2 digits)
45
// @"01"    // Build number (exactly 2 digits)
46
// @"000";  // Fixed "000"
47
NSString *kFIRLibraryVersionID;
48
 
49
// Plist file name.
50
NSString *const kServiceInfoFileName = @"GoogleService-Info";
51
// Plist file type.
52
NSString *const kServiceInfoFileType = @"plist";
53
 
54
// Exception raised from attempting to modify a FIROptions after it's been copied to a FIRApp.
55
NSString *const kFIRExceptionBadModification =
56
    @"Attempted to modify options after it's set on FIRApp. Please modify all properties before "
57
    @"initializing FIRApp.";
58
 
59
@interface FIROptions ()
60
 
61
/**
62
 * This property maintains the actual configuration key-value pairs.
63
 */
64
@property(nonatomic, readwrite) NSMutableDictionary *optionsDictionary;
65
 
66
/**
67
 * Calls `analyticsOptionsDictionaryWithInfoDictionary:` using [NSBundle mainBundle].infoDictionary.
68
 * It combines analytics options from both the infoDictionary and the GoogleService-Info.plist.
69
 * Values which are present in the main plist override values from the GoogleService-Info.plist.
70
 */
71
@property(nonatomic, readonly) NSDictionary *analyticsOptionsDictionary;
72
 
73
/**
74
 * Combination of analytics options from both the infoDictionary and the GoogleService-Info.plist.
75
 * Values which are present in the infoDictionary override values from the GoogleService-Info.plist.
76
 */
77
- (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary;
78
 
79
/**
80
 * Throw exception if editing is locked when attempting to modify an option.
81
 */
82
- (void)checkEditingLocked;
83
 
84
@end
85
 
86
@implementation FIROptions {
87
  /// Backing variable for self.analyticsOptionsDictionary.
88
  NSDictionary *_analyticsOptionsDictionary;
89
}
90
 
91
static FIROptions *sDefaultOptions = nil;
92
static NSDictionary *sDefaultOptionsDictionary = nil;
93
static dispatch_once_t sDefaultOptionsOnceToken;
94
static dispatch_once_t sDefaultOptionsDictionaryOnceToken;
95
 
96
#pragma mark - Public only for internal class methods
97
 
98
+ (FIROptions *)defaultOptions {
99
  dispatch_once(&sDefaultOptionsOnceToken, ^{
100
    NSDictionary *defaultOptionsDictionary = [self defaultOptionsDictionary];
101
    if (defaultOptionsDictionary != nil) {
102
      sDefaultOptions =
103
          [[FIROptions alloc] initInternalWithOptionsDictionary:defaultOptionsDictionary];
104
    }
105
  });
106
 
107
  return sDefaultOptions;
108
}
109
 
110
#pragma mark - Private class methods
111
 
112
+ (NSDictionary *)defaultOptionsDictionary {
113
  dispatch_once(&sDefaultOptionsDictionaryOnceToken, ^{
114
    NSString *plistFilePath = [FIROptions plistFilePathWithName:kServiceInfoFileName];
115
    if (plistFilePath == nil) {
116
      return;
117
    }
118
    sDefaultOptionsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistFilePath];
119
    if (sDefaultOptionsDictionary == nil) {
120
      FIRLogError(kFIRLoggerCore, @"I-COR000011",
121
                  @"The configuration file is not a dictionary: "
122
                  @"'%@.%@'.",
123
                  kServiceInfoFileName, kServiceInfoFileType);
124
    }
125
  });
126
 
127
  return sDefaultOptionsDictionary;
128
}
129
 
130
// Returns the path of the plist file with a given file name.
131
+ (NSString *)plistFilePathWithName:(NSString *)fileName {
132
  NSArray *bundles = [FIRBundleUtil relevantBundles];
133
  NSString *plistFilePath =
134
      [FIRBundleUtil optionsDictionaryPathWithResourceName:fileName
135
                                               andFileType:kServiceInfoFileType
136
                                                 inBundles:bundles];
137
  if (plistFilePath == nil) {
138
    FIRLogError(kFIRLoggerCore, @"I-COR000012", @"Could not locate configuration file: '%@.%@'.",
139
                fileName, kServiceInfoFileType);
140
  }
141
  return plistFilePath;
142
}
143
 
144
+ (void)resetDefaultOptions {
145
  sDefaultOptions = nil;
146
  sDefaultOptionsDictionary = nil;
147
  sDefaultOptionsOnceToken = 0;
148
  sDefaultOptionsDictionaryOnceToken = 0;
149
}
150
 
151
#pragma mark - Private instance methods
152
 
153
- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)optionsDictionary {
154
  self = [super init];
155
  if (self) {
156
    _optionsDictionary = [optionsDictionary mutableCopy];
157
    _usingOptionsFromDefaultPlist = YES;
158
  }
159
  return self;
160
}
161
 
162
- (id)copyWithZone:(NSZone *)zone {
163
  FIROptions *newOptions = [(FIROptions *)[[self class] allocWithZone:zone]
164
      initInternalWithOptionsDictionary:self.optionsDictionary];
165
  if (newOptions) {
166
    newOptions.deepLinkURLScheme = self.deepLinkURLScheme;
167
    newOptions.appGroupID = self.appGroupID;
168
    newOptions.editingLocked = self.isEditingLocked;
169
    newOptions.usingOptionsFromDefaultPlist = self.usingOptionsFromDefaultPlist;
170
  }
171
  return newOptions;
172
}
173
 
174
#pragma mark - Public instance methods
175
 
176
- (instancetype)init {
177
  // Unavailable.
178
  [self doesNotRecognizeSelector:_cmd];
179
  return nil;
180
}
181
 
182
- (instancetype)initWithContentsOfFile:(NSString *)plistPath {
183
  self = [super init];
184
  if (self) {
185
    if (plistPath == nil) {
186
      FIRLogError(kFIRLoggerCore, @"I-COR000013", @"The plist file path is nil.");
187
      return nil;
188
    }
189
    _optionsDictionary = [[NSDictionary dictionaryWithContentsOfFile:plistPath] mutableCopy];
190
    if (_optionsDictionary == nil) {
191
      FIRLogError(kFIRLoggerCore, @"I-COR000014",
192
                  @"The configuration file at %@ does not exist or "
193
                  @"is not a well-formed plist file.",
194
                  plistPath);
195
      return nil;
196
    }
197
    // TODO: Do we want to validate the dictionary here? It says we do that already in
198
    // the public header.
199
  }
200
  return self;
201
}
202
 
203
- (instancetype)initWithGoogleAppID:(NSString *)googleAppID GCMSenderID:(NSString *)GCMSenderID {
204
  self = [super init];
205
  if (self) {
206
    NSMutableDictionary *mutableOptionsDict = [NSMutableDictionary dictionary];
207
    [mutableOptionsDict setValue:googleAppID forKey:kFIRGoogleAppID];
208
    [mutableOptionsDict setValue:GCMSenderID forKey:kFIRGCMSenderID];
209
    [mutableOptionsDict setValue:[[NSBundle mainBundle] bundleIdentifier] forKey:kFIRBundleID];
210
    self.optionsDictionary = mutableOptionsDict;
211
  }
212
  return self;
213
}
214
 
215
- (NSString *)APIKey {
216
  return self.optionsDictionary[kFIRAPIKey];
217
}
218
 
219
- (void)checkEditingLocked {
220
  if (self.isEditingLocked) {
221
    [NSException raise:kFirebaseCoreErrorDomain format:kFIRExceptionBadModification];
222
  }
223
}
224
 
225
- (void)setAPIKey:(NSString *)APIKey {
226
  [self checkEditingLocked];
227
  _optionsDictionary[kFIRAPIKey] = [APIKey copy];
228
}
229
 
230
- (NSString *)clientID {
231
  return self.optionsDictionary[kFIRClientID];
232
}
233
 
234
- (void)setClientID:(NSString *)clientID {
235
  [self checkEditingLocked];
236
  _optionsDictionary[kFIRClientID] = [clientID copy];
237
}
238
 
239
- (NSString *)trackingID {
240
  return self.optionsDictionary[kFIRTrackingID];
241
}
242
 
243
- (void)setTrackingID:(NSString *)trackingID {
244
  [self checkEditingLocked];
245
  _optionsDictionary[kFIRTrackingID] = [trackingID copy];
246
}
247
 
248
- (NSString *)GCMSenderID {
249
  return self.optionsDictionary[kFIRGCMSenderID];
250
}
251
 
252
- (void)setGCMSenderID:(NSString *)GCMSenderID {
253
  [self checkEditingLocked];
254
  _optionsDictionary[kFIRGCMSenderID] = [GCMSenderID copy];
255
}
256
 
257
- (NSString *)projectID {
258
  return self.optionsDictionary[kFIRProjectID];
259
}
260
 
261
- (void)setProjectID:(NSString *)projectID {
262
  [self checkEditingLocked];
263
  _optionsDictionary[kFIRProjectID] = [projectID copy];
264
}
265
 
266
- (NSString *)androidClientID {
267
  return self.optionsDictionary[kFIRAndroidClientID];
268
}
269
 
270
- (void)setAndroidClientID:(NSString *)androidClientID {
271
  [self checkEditingLocked];
272
  _optionsDictionary[kFIRAndroidClientID] = [androidClientID copy];
273
}
274
 
275
- (NSString *)googleAppID {
276
  return self.optionsDictionary[kFIRGoogleAppID];
277
}
278
 
279
- (void)setGoogleAppID:(NSString *)googleAppID {
280
  [self checkEditingLocked];
281
  _optionsDictionary[kFIRGoogleAppID] = [googleAppID copy];
282
}
283
 
284
- (NSString *)libraryVersionID {
285
  static dispatch_once_t onceToken;
286
  dispatch_once(&onceToken, ^{
287
    // The unit tests are set up to catch anything that does not properly convert.
288
    NSString *version = FIRFirebaseVersion();
289
    NSArray *components = [version componentsSeparatedByString:@"."];
290
    NSString *major = [components objectAtIndex:0];
291
    NSString *minor = [NSString stringWithFormat:@"%02d", [[components objectAtIndex:1] intValue]];
292
    NSString *patch = [NSString stringWithFormat:@"%02d", [[components objectAtIndex:2] intValue]];
293
    kFIRLibraryVersionID = [NSString stringWithFormat:@"%@%@%@000", major, minor, patch];
294
  });
295
  return kFIRLibraryVersionID;
296
}
297
 
298
- (void)setLibraryVersionID:(NSString *)libraryVersionID {
299
  _optionsDictionary[kFIRLibraryVersionID] = [libraryVersionID copy];
300
}
301
 
302
- (NSString *)databaseURL {
303
  return self.optionsDictionary[kFIRDatabaseURL];
304
}
305
 
306
- (void)setDatabaseURL:(NSString *)databaseURL {
307
  [self checkEditingLocked];
308
 
309
  _optionsDictionary[kFIRDatabaseURL] = [databaseURL copy];
310
}
311
 
312
- (NSString *)storageBucket {
313
  return self.optionsDictionary[kFIRStorageBucket];
314
}
315
 
316
- (void)setStorageBucket:(NSString *)storageBucket {
317
  [self checkEditingLocked];
318
  _optionsDictionary[kFIRStorageBucket] = [storageBucket copy];
319
}
320
 
321
- (void)setDeepLinkURLScheme:(NSString *)deepLinkURLScheme {
322
  [self checkEditingLocked];
323
  _deepLinkURLScheme = [deepLinkURLScheme copy];
324
}
325
 
326
- (NSString *)bundleID {
327
  return self.optionsDictionary[kFIRBundleID];
328
}
329
 
330
- (void)setBundleID:(NSString *)bundleID {
331
  [self checkEditingLocked];
332
  _optionsDictionary[kFIRBundleID] = [bundleID copy];
333
}
334
 
335
- (void)setAppGroupID:(NSString *)appGroupID {
336
  [self checkEditingLocked];
337
  _appGroupID = [appGroupID copy];
338
}
339
 
340
#pragma mark - Equality
341
 
342
- (BOOL)isEqual:(id)object {
343
  if (!object || ![object isKindOfClass:[FIROptions class]]) {
344
    return NO;
345
  }
346
 
347
  return [self isEqualToOptions:(FIROptions *)object];
348
}
349
 
350
- (BOOL)isEqualToOptions:(FIROptions *)options {
351
  // Skip any non-FIROptions classes.
352
  if (![options isKindOfClass:[FIROptions class]]) {
353
    return NO;
354
  }
355
 
356
  // Check the internal dictionary and custom properties for differences.
357
  if (![options.optionsDictionary isEqualToDictionary:self.optionsDictionary]) {
358
    return NO;
359
  }
360
 
361
  // Validate extra properties not contained in the dictionary. Only validate it if one of the
362
  // objects has the property set.
363
  if ((options.deepLinkURLScheme != nil || self.deepLinkURLScheme != nil) &&
364
      ![options.deepLinkURLScheme isEqualToString:self.deepLinkURLScheme]) {
365
    return NO;
366
  }
367
 
368
  if ((options.appGroupID != nil || self.appGroupID != nil) &&
369
      ![options.appGroupID isEqualToString:self.appGroupID]) {
370
    return NO;
371
  }
372
 
373
  // Validate the Analytics options haven't changed with the Info.plist.
374
  if (![options.analyticsOptionsDictionary isEqualToDictionary:self.analyticsOptionsDictionary]) {
375
    return NO;
376
  }
377
 
378
  // We don't care about the `editingLocked` or `usingOptionsFromDefaultPlist` properties since
379
  // those relate to lifecycle and construction, we only care if the contents of the options
380
  // themselves are equal.
381
  return YES;
382
}
383
 
384
- (NSUInteger)hash {
385
  // This is strongly recommended for any object that implements a custom `isEqual:` method to
386
  // ensure that dictionary and set behavior matches other `isEqual:` checks.
387
  // Note: `self.analyticsOptionsDictionary` was left out here since it solely relies on the
388
  // contents of the main bundle's `Info.plist`. We should avoid reading that file and the contents
389
  // should be identical.
390
  return self.optionsDictionary.hash ^ self.deepLinkURLScheme.hash ^ self.appGroupID.hash;
391
}
392
 
393
#pragma mark - Internal instance methods
394
 
395
- (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary {
396
  if (_analyticsOptionsDictionary == nil) {
397
    NSMutableDictionary *tempAnalyticsOptions = [[NSMutableDictionary alloc] init];
398
    NSArray *measurementKeys = @[
399
      kFIRIsMeasurementEnabled, kFIRIsAnalyticsCollectionEnabled,
400
      kFIRIsAnalyticsCollectionDeactivated
401
    ];
402
    for (NSString *key in measurementKeys) {
403
      id value = infoDictionary[key] ?: self.optionsDictionary[key] ?: nil;
404
      if (!value) {
405
        continue;
406
      }
407
      tempAnalyticsOptions[key] = value;
408
    }
409
    _analyticsOptionsDictionary = tempAnalyticsOptions;
410
  }
411
  return _analyticsOptionsDictionary;
412
}
413
 
414
- (NSDictionary *)analyticsOptionsDictionary {
415
  return [self analyticsOptionsDictionaryWithInfoDictionary:[NSBundle mainBundle].infoDictionary];
416
}
417
 
418
/**
419
 * Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in
420
 * GoogleService-Info.plist. This uses the old plist flag IS_MEASUREMENT_ENABLED, which should still
421
 * be supported.
422
 */
423
- (BOOL)isMeasurementEnabled {
424
  if (self.isAnalyticsCollectionDeactivated) {
425
    return NO;
426
  }
427
  NSNumber *value = self.analyticsOptionsDictionary[kFIRIsMeasurementEnabled];
428
  if (value == nil) {
429
    // TODO: This could probably be cleaned up since FIROptions shouldn't know about FIRApp or have
430
    //       to check if it's the default app. The FIROptions instance can't be modified after
431
    //       `+configure` is called, so it's not a good place to copy it either in case the flag is
432
    //       changed at runtime.
433
 
434
    // If no values are set for Analytics, fall back to the global collection switch in FIRApp.
435
    // Analytics only supports the default FIRApp, so check that first.
436
    if (![FIRApp isDefaultAppConfigured]) {
437
      return NO;
438
    }
439
 
440
    // Fall back to the default app's collection switch when the key is not in the dictionary.
441
    return [FIRApp defaultApp].isDataCollectionDefaultEnabled;
442
  }
443
  return [value boolValue];
444
}
445
 
446
- (BOOL)isAnalyticsCollectionExplicitlySet {
447
  // If it's de-activated, it classifies as explicity set. If not, it's not a good enough indication
448
  // that the developer wants FirebaseAnalytics enabled so continue checking.
449
  if (self.isAnalyticsCollectionDeactivated) {
450
    return YES;
451
  }
452
 
453
  // Check if the current Analytics flag is set.
454
  id collectionEnabledObject = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionEnabled];
455
  if (collectionEnabledObject && [collectionEnabledObject isKindOfClass:[NSNumber class]]) {
456
    // It doesn't matter what the value is, it's explicitly set.
457
    return YES;
458
  }
459
 
460
  // Check if the old measurement flag is set.
461
  id measurementEnabledObject = self.analyticsOptionsDictionary[kFIRIsMeasurementEnabled];
462
  if (measurementEnabledObject && [measurementEnabledObject isKindOfClass:[NSNumber class]]) {
463
    // It doesn't matter what the value is, it's explicitly set.
464
    return YES;
465
  }
466
 
467
  // No flags are set to explicitly enable or disable FirebaseAnalytics.
468
  return NO;
469
}
470
 
471
- (BOOL)isAnalyticsCollectionEnabled {
472
  if (self.isAnalyticsCollectionDeactivated) {
473
    return NO;
474
  }
475
  NSNumber *value = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionEnabled];
476
  if (value == nil) {
477
    return self.isMeasurementEnabled;  // Fall back to older plist flag.
478
  }
479
  return [value boolValue];
480
}
481
 
482
- (BOOL)isAnalyticsCollectionDeactivated {
483
  NSNumber *value = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionDeactivated];
484
  if (value == nil) {
485
    return NO;  // Analytics Collection is not deactivated when the key is not in the dictionary.
486
  }
487
  return [value boolValue];
488
}
489
 
490
- (BOOL)isAnalyticsEnabled {
491
  return [self.optionsDictionary[kFIRIsAnalyticsEnabled] boolValue];
492
}
493
 
494
- (BOOL)isSignInEnabled {
495
  return [self.optionsDictionary[kFIRIsSignInEnabled] boolValue];
496
}
497
 
498
@end