Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
/*
2
 * Copyright 2017 Google
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
 
17
#import <TargetConditionals.h>
18
#if TARGET_OS_IOS || TARGET_OS_TV
19
 
20
#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
21
 
22
#import "FirebaseInAppMessaging/Sources/Analytics/FIRIAMAnalyticsEventLoggerImpl.h"
23
#import "FirebaseInAppMessaging/Sources/Analytics/FIRIAMClearcutHttpRequestSender.h"
24
#import "FirebaseInAppMessaging/Sources/Analytics/FIRIAMClearcutLogStorage.h"
25
#import "FirebaseInAppMessaging/Sources/FIRCore+InAppMessaging.h"
26
#import "FirebaseInAppMessaging/Sources/FIRInAppMessagingPrivate.h"
27
#import "FirebaseInAppMessaging/Sources/Flows/FIRIAMDisplayCheckOnFetchDoneNotificationFlow.h"
28
#import "FirebaseInAppMessaging/Sources/Private/Analytics/FIRIAMClearcutLogger.h"
29
#import "FirebaseInAppMessaging/Sources/Private/Analytics/FIRIAMClearcutUploader.h"
30
#import "FirebaseInAppMessaging/Sources/Private/Analytics/FIRIAMClientInfoFetcher.h"
31
#import "FirebaseInAppMessaging/Sources/Private/Data/FIRIAMFetchResponseParser.h"
32
#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMActivityLogger.h"
33
#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMBookKeeper.h"
34
#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMDisplayCheckOnAnalyticEventsFlow.h"
35
#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMDisplayCheckOnAppForegroundFlow.h"
36
#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMDisplayExecutor.h"
37
#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMFetchOnAppForegroundFlow.h"
38
#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMMessageClientCache.h"
39
#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMMsgFetcherUsingRestful.h"
40
#import "FirebaseInAppMessaging/Sources/Private/Runtime/FIRIAMRuntimeManager.h"
41
#import "FirebaseInAppMessaging/Sources/Private/Runtime/FIRIAMSDKModeManager.h"
42
#import "FirebaseInAppMessaging/Sources/Public/FirebaseInAppMessaging/FIRInAppMessaging.h"
43
 
44
// A enum indicating 3 different possiblities of a setting about auto data collection.
45
typedef NS_ENUM(NSInteger, FIRIAMAutoDataCollectionSetting) {
46
  // This indicates that the config is not explicitly set.
47
  FIRIAMAutoDataCollectionSettingNone = 0,
48
 
49
  // This indicates that the setting explicitly enables the auto data collection.
50
  FIRIAMAutoDataCollectionSettingEnabled = 1,
51
 
52
  // This indicates that the setting explicitly disables the auto data collection.
53
  FIRIAMAutoDataCollectionSettingDisabled = 2,
54
};
55
 
56
@interface FIRIAMRuntimeManager () <FIRIAMTestingModeListener>
57
@property(nonatomic, nonnull) FIRIAMMsgFetcherUsingRestful *restfulFetcher;
58
@property(nonatomic, nonnull) FIRIAMDisplayCheckOnAppForegroundFlow *displayOnAppForegroundFlow;
59
@property(nonatomic, nonnull) FIRIAMDisplayCheckOnFetchDoneNotificationFlow *displayOnFetchDoneFlow;
60
@property(nonatomic, nonnull)
61
    FIRIAMDisplayCheckOnAnalyticEventsFlow *displayOnFIRAnalyticEventsFlow;
62
 
63
@property(nonatomic, nonnull) FIRIAMFetchOnAppForegroundFlow *fetchOnAppForegroundFlow;
64
@property(nonatomic, nonnull) FIRIAMClientInfoFetcher *clientInfoFetcher;
65
@property(nonatomic, nonnull) FIRIAMFetchResponseParser *responseParser;
66
@end
67
 
68
static NSString *const _userDefaultsKeyForFIAMProgammaticAutoDataCollectionSetting =
69
    @"firebase-iam-sdk-auto-data-collection";
70
 
71
@implementation FIRIAMRuntimeManager {
72
  // since we allow the SDK feature to be disabled/enabled at runtime, we need a field to track
73
  // its state on this
74
  BOOL _running;
75
}
76
+ (FIRIAMRuntimeManager *)getSDKRuntimeInstance {
77
  static FIRIAMRuntimeManager *managerInstance = nil;
78
  static dispatch_once_t onceToken;
79
 
80
  dispatch_once(&onceToken, ^{
81
    managerInstance = [[FIRIAMRuntimeManager alloc] init];
82
  });
83
 
84
  return managerInstance;
85
}
86
 
87
// For protocol FIRIAMTestingModeListener.
88
- (void)testingModeSwitchedOn {
89
  FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180015",
90
              @"Dynamically switch to the display flow for testing mode instance.");
91
 
92
  [self.displayOnAppForegroundFlow stop];
93
  [self.displayOnFetchDoneFlow start];
94
}
95
 
96
- (FIRIAMAutoDataCollectionSetting)FIAMProgrammaticAutoDataCollectionSetting {
97
  id settingEntry = [[NSUserDefaults standardUserDefaults]
98
      objectForKey:_userDefaultsKeyForFIAMProgammaticAutoDataCollectionSetting];
99
 
100
  if (![settingEntry isKindOfClass:[NSNumber class]]) {
101
    FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180014",
102
                @"No auto data collection enable setting entry detected."
103
                 "So no FIAM programmatic setting from the app.");
104
    return FIRIAMAutoDataCollectionSettingNone;
105
  } else {
106
    if ([(NSNumber *)settingEntry boolValue]) {
107
      return FIRIAMAutoDataCollectionSettingEnabled;
108
    } else {
109
      return FIRIAMAutoDataCollectionSettingDisabled;
110
    }
111
  }
112
}
113
 
114
// the key for the plist entry to suppress auto start
115
static NSString *const kFirebaseInAppMessagingAutoDataCollectionKey =
116
    @"FirebaseInAppMessagingAutomaticDataCollectionEnabled";
117
 
118
- (FIRIAMAutoDataCollectionSetting)FIAMPlistAutoDataCollectionSetting {
119
  id fiamAutoDataCollectionPlistEntry = [[NSBundle mainBundle]
120
      objectForInfoDictionaryKey:kFirebaseInAppMessagingAutoDataCollectionKey];
121
 
122
  if ([fiamAutoDataCollectionPlistEntry isKindOfClass:[NSNumber class]]) {
123
    BOOL fiamDataCollectionEnabledPlistSetting =
124
        [(NSNumber *)fiamAutoDataCollectionPlistEntry boolValue];
125
 
126
    if (fiamDataCollectionEnabledPlistSetting) {
127
      FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180011",
128
                  @"Auto data collection is explicitly enabled in FIAM plist entry.");
129
      return FIRIAMAutoDataCollectionSettingEnabled;
130
    } else {
131
      FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180012",
132
                  @"Auto data collection is explicitly disabled in FIAM plist entry.");
133
      return FIRIAMAutoDataCollectionSettingDisabled;
134
    }
135
  } else {
136
    return FIRIAMAutoDataCollectionSettingNone;
137
  }
138
}
139
 
140
// Whether data collection is enabled by FIAM programmatic flag.
141
- (BOOL)automaticDataCollectionEnabled {
142
  return
143
      [self FIAMProgrammaticAutoDataCollectionSetting] != FIRIAMAutoDataCollectionSettingDisabled;
144
}
145
 
146
// Sets FIAM's programmatic flag for auto data collection.
147
- (void)setAutomaticDataCollectionEnabled:(BOOL)automaticDataCollectionEnabled {
148
  if (automaticDataCollectionEnabled) {
149
    [self resume];
150
  } else {
151
    [self pause];
152
  }
153
}
154
 
155
- (BOOL)shouldRunSDKFlowsOnStartup {
156
  // This can be controlled at 3 different levels in decsending priority. If a higher-priority
157
  // setting exists, the lower level settings are ignored.
158
  //   1. Setting made by the app by setting FIAM SDK's automaticDataCollectionEnabled flag.
159
  //   2. FIAM specific data collection setting in plist file.
160
  //   3. Global Firebase auto data collecting setting (carried over by currentSetting property).
161
 
162
  FIRIAMAutoDataCollectionSetting programmaticSetting =
163
      [self FIAMProgrammaticAutoDataCollectionSetting];
164
 
165
  if (programmaticSetting == FIRIAMAutoDataCollectionSettingEnabled) {
166
    FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180010",
167
                @"FIAM auto data-collection is explicitly enabled, start SDK flows.");
168
    return true;
169
  } else if (programmaticSetting == FIRIAMAutoDataCollectionSettingDisabled) {
170
    FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180013",
171
                @"FIAM auto data-collection is explicitly disabled, do not start SDK flows.");
172
    return false;
173
  } else {
174
    // No explicit setting from fiam's programmatic setting. Checking next level down.
175
    FIRIAMAutoDataCollectionSetting fiamPlistDataCollectionSetting =
176
        [self FIAMPlistAutoDataCollectionSetting];
177
 
178
    if (fiamPlistDataCollectionSetting == FIRIAMAutoDataCollectionSettingNone) {
179
      FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180018",
180
                  @"No programmatic or plist setting at FIAM level. Fallback to global Firebase "
181
                   "level setting.");
182
      return self.currentSetting.isFirebaseAutoDataCollectionEnabled;
183
    } else {
184
      return fiamPlistDataCollectionSetting == FIRIAMAutoDataCollectionSettingEnabled;
185
    }
186
  }
187
}
188
 
189
- (void)resume {
190
  // persist the setting
191
  [[NSUserDefaults standardUserDefaults]
192
      setObject:@(YES)
193
         forKey:_userDefaultsKeyForFIAMProgammaticAutoDataCollectionSetting];
194
 
195
  @synchronized(self) {
196
    if (!_running) {
197
      [self.fetchOnAppForegroundFlow start];
198
      [self.displayOnAppForegroundFlow start];
199
      [self.displayOnFIRAnalyticEventsFlow start];
200
      FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180019",
201
                  @"Start Firebase In-App Messaging flows from inactive.");
202
      _running = YES;
203
    } else {
204
      FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM180004",
205
                    @"Runtime is already active, resume is just a no-op");
206
    }
207
  }
208
}
209
 
210
- (void)pause {
211
  // persist the setting
212
  [[NSUserDefaults standardUserDefaults]
213
      setObject:@(NO)
214
         forKey:_userDefaultsKeyForFIAMProgammaticAutoDataCollectionSetting];
215
 
216
  @synchronized(self) {
217
    if (_running) {
218
      [self.fetchOnAppForegroundFlow stop];
219
      [self.displayOnAppForegroundFlow stop];
220
      [self.displayOnFIRAnalyticEventsFlow stop];
221
      FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180006",
222
                  @"Shutdown Firebase In-App Messaging flows.");
223
      _running = NO;
224
    } else {
225
      FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM180005",
226
                    @"No runtime active yet, pause is just a no-op");
227
    }
228
  }
229
}
230
 
231
- (void)setShouldSuppressMessageDisplay:(BOOL)shouldSuppress {
232
  FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180003", @"Message display suppress set to %@",
233
              @(shouldSuppress));
234
  self.displayExecutor.suppressMessageDisplay = shouldSuppress;
235
}
236
 
237
- (void)startRuntimeWithSDKSettings:(FIRIAMSDKSettings *)settings {
238
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^{
239
    [self internalStartRuntimeWithSDKSettings:settings];
240
  });
241
}
242
 
243
- (void)internalStartRuntimeWithSDKSettings:(FIRIAMSDKSettings *)settings {
244
  if (_running) {
245
    // Runtime has been started previously. Stop all the flows first.
246
    [self.fetchOnAppForegroundFlow stop];
247
    [self.displayOnAppForegroundFlow stop];
248
    [self.displayOnFIRAnalyticEventsFlow stop];
249
  }
250
 
251
  self.currentSetting = settings;
252
 
253
  FIRIAMTimerWithNSDate *timeFetcher = [[FIRIAMTimerWithNSDate alloc] init];
254
  NSTimeInterval start = [timeFetcher currentTimestampInSeconds];
255
 
256
  self.activityLogger =
257
      [[FIRIAMActivityLogger alloc] initWithMaxCountBeforeReduce:settings.loggerMaxCountBeforeReduce
258
                                             withSizeAfterReduce:settings.loggerSizeAfterReduce
259
                                                     verboseMode:settings.loggerInVerboseMode
260
                                                   loadFromCache:YES];
261
 
262
  self.responseParser = [[FIRIAMFetchResponseParser alloc] initWithTimeFetcher:timeFetcher];
263
 
264
  self.bookKeeper = [[FIRIAMBookKeeperViaUserDefaults alloc]
265
      initWithUserDefaults:[NSUserDefaults standardUserDefaults]];
266
 
267
  self.messageCache = [[FIRIAMMessageClientCache alloc] initWithBookkeeper:self.bookKeeper
268
                                                       usingResponseParser:self.responseParser];
269
  self.fetchResultStorage = [[FIRIAMServerMsgFetchStorage alloc] init];
270
 
271
  self.clientInfoFetcher = [[FIRIAMClientInfoFetcher alloc]
272
      initWithFirebaseInstallations:[FIRInAppMessaging inAppMessaging].installations];
273
 
274
  self.restfulFetcher =
275
      [[FIRIAMMsgFetcherUsingRestful alloc] initWithHost:settings.apiServerHost
276
                                            HTTPProtocol:settings.apiHttpProtocol
277
                                                 project:settings.firebaseProjectNumber
278
                                             firebaseApp:settings.firebaseAppId
279
                                                  APIKey:settings.apiKey
280
                                            fetchStorage:self.fetchResultStorage
281
                                       instanceIDFetcher:self.clientInfoFetcher
282
                                         usingURLSession:nil
283
                                          responseParser:self.responseParser];
284
 
285
  // start fetch on app foreground flow
286
  FIRIAMFetchSetting *fetchSetting = [[FIRIAMFetchSetting alloc] init];
287
  fetchSetting.fetchMinIntervalInMinutes = settings.fetchMinIntervalInMinutes;
288
 
289
  // start render on app foreground flow
290
  FIRIAMDisplaySetting *appForegroundDisplaysetting = [[FIRIAMDisplaySetting alloc] init];
291
  appForegroundDisplaysetting.displayMinIntervalInMinutes =
292
      settings.appFGRenderMinIntervalInMinutes;
293
 
294
  // clearcut log expires after 14 days: give up on attempting to deliver them any more
295
  NSInteger ctLogExpiresInSeconds = 14 * 24 * 60 * 60;
296
 
297
  FIRIAMClearcutLogStorage *ctLogStorage =
298
      [[FIRIAMClearcutLogStorage alloc] initWithExpireAfterInSeconds:ctLogExpiresInSeconds
299
                                                     withTimeFetcher:timeFetcher];
300
 
301
  FIRIAMClearcutHttpRequestSender *clearcutRequestSender = [[FIRIAMClearcutHttpRequestSender alloc]
302
      initWithClearcutHost:settings.clearcutServerHost
303
          usingTimeFetcher:timeFetcher
304
        withOSMajorVersion:[self.clientInfoFetcher getOSMajorVersion]];
305
 
306
  FIRIAMClearcutUploader *ctUploader =
307
      [[FIRIAMClearcutUploader alloc] initWithRequestSender:clearcutRequestSender
308
                                                timeFetcher:timeFetcher
309
                                                 logStorage:ctLogStorage
310
                                              usingStrategy:settings.clearcutStrategy
311
                                          usingUserDefaults:nil];
312
 
313
  FIRIAMClearcutLogger *clearcutLogger =
314
      [[FIRIAMClearcutLogger alloc] initWithFBProjectNumber:settings.firebaseProjectNumber
315
                                                    fbAppId:settings.firebaseAppId
316
                                          clientInfoFetcher:self.clientInfoFetcher
317
                                           usingTimeFetcher:timeFetcher
318
                                              usingUploader:ctUploader];
319
 
320
  FIRIAMAnalyticsEventLoggerImpl *analyticsEventLogger = [[FIRIAMAnalyticsEventLoggerImpl alloc]
321
      initWithClearcutLogger:clearcutLogger
322
            usingTimeFetcher:timeFetcher
323
           usingUserDefaults:nil
324
                   analytics:[FIRInAppMessaging inAppMessaging].analytics];
325
 
326
  FIRIAMSDKModeManager *sdkModeManager =
327
      [[FIRIAMSDKModeManager alloc] initWithUserDefaults:NSUserDefaults.standardUserDefaults
328
                                     testingModeListener:self];
329
 
330
  FIRIAMActionURLFollower *actionFollower = [FIRIAMActionURLFollower actionURLFollower];
331
 
332
  self.displayExecutor =
333
      [[FIRIAMDisplayExecutor alloc] initWithInAppMessaging:[FIRInAppMessaging inAppMessaging]
334
                                                    setting:appForegroundDisplaysetting
335
                                               messageCache:self.messageCache
336
                                                timeFetcher:timeFetcher
337
                                                 bookKeeper:self.bookKeeper
338
                                          actionURLFollower:actionFollower
339
                                             activityLogger:self.activityLogger
340
                                       analyticsEventLogger:analyticsEventLogger];
341
 
342
  self.fetchOnAppForegroundFlow =
343
      [[FIRIAMFetchOnAppForegroundFlow alloc] initWithSetting:fetchSetting
344
                                                 messageCache:self.messageCache
345
                                               messageFetcher:self.restfulFetcher
346
                                                  timeFetcher:timeFetcher
347
                                                   bookKeeper:self.bookKeeper
348
                                               activityLogger:self.activityLogger
349
                                         analyticsEventLogger:analyticsEventLogger
350
                                         FIRIAMSDKModeManager:sdkModeManager
351
                                              displayExecutor:self.displayExecutor];
352
 
353
  // Setting the message display component and suppression. It's needed in case
354
  // headless SDK is initialized after the these properties are already set on FIRInAppMessaging.
355
  self.displayExecutor.messageDisplayComponent =
356
      FIRInAppMessaging.inAppMessaging.messageDisplayComponent;
357
  self.displayExecutor.suppressMessageDisplay =
358
      FIRInAppMessaging.inAppMessaging.messageDisplaySuppressed;
359
 
360
  // Both display flows are created on startup. But they would only be turned on (started) based on
361
  // the sdk mode for the current instance
362
  self.displayOnFetchDoneFlow = [[FIRIAMDisplayCheckOnFetchDoneNotificationFlow alloc]
363
      initWithDisplayFlow:self.displayExecutor];
364
  self.displayOnAppForegroundFlow =
365
      [[FIRIAMDisplayCheckOnAppForegroundFlow alloc] initWithDisplayFlow:self.displayExecutor];
366
 
367
  self.displayOnFIRAnalyticEventsFlow =
368
      [[FIRIAMDisplayCheckOnAnalyticEventsFlow alloc] initWithDisplayFlow:self.displayExecutor];
369
 
370
  self.messageCache.analycisEventDislayCheckFlow = self.displayOnFIRAnalyticEventsFlow;
371
  [self.messageCache
372
      loadMessageDataFromServerFetchStorage:self.fetchResultStorage
373
                             withCompletion:^(BOOL success) {
374
                               // start flows regardless whether we can load messages from fetch
375
                               // storage successfully
376
                               FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180001",
377
                                           @"Message loading from fetch storage was done.");
378
 
379
                               if ([self shouldRunSDKFlowsOnStartup]) {
380
                                 FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180008",
381
                                             @"Start SDK runtime components.");
382
 
383
                                 [self.clientInfoFetcher
384
                                     fetchFirebaseInstallationDataWithProjectNumber:
385
                                         self.currentSetting.firebaseProjectNumber
386
                                                                     withCompletion:^(
387
                                                                         NSString *_Nullable FID,
388
                                                                         NSString
389
                                                                             *_Nullable FISToken,
390
                                                                         NSError *_Nullable error) {
391
                                                                       // Always dump the
392
                                                                       // installation ID into log
393
                                                                       // on startup to help
394
                                                                       // developers to find it for
395
                                                                       // their app instance.
396
                                                                       FIRLogDebug(
397
                                                                           kFIRLoggerInAppMessaging,
398
                                                                           @"I-IAM180017",
399
                                                                           @"Starting "
400
                                                                           @"InAppMessaging "
401
                                                                           @"runtime "
402
                                                                           @"with "
403
                                                                            "Firebase Installation "
404
                                                                            "ID %@",
405
                                                                           FID);
406
                                                                     }];
407
 
408
                                 [self.fetchOnAppForegroundFlow start];
409
                                 [self.displayOnFIRAnalyticEventsFlow start];
410
 
411
                                 self->_running = YES;
412
 
413
                                 if (sdkModeManager.currentMode == FIRIAMSDKModeTesting) {
414
                                   FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180007",
415
                                               @"InAppMessaging testing mode enabled. App "
416
                                                "foreground messages will be displayed following "
417
                                                "fetch");
418
                                   [self.displayOnFetchDoneFlow start];
419
                                 } else {
420
                                   FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180020",
421
                                               @"Start regular display flow for non-testing "
422
                                                "instance mode");
423
                                   [self.displayOnAppForegroundFlow start];
424
 
425
                                   // Simulate app going into foreground on startup
426
                                   [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
427
                                 }
428
 
429
                                 // One-time triggering of checks for both fetch flow
430
                                 // upon SDK/app startup.
431
                                 [self.fetchOnAppForegroundFlow
432
                                     checkAndFetchForInitialAppLaunch:YES];
433
                               } else {
434
                                 FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180009",
435
                                             @"No FIAM SDK startup due to settings.");
436
                               }
437
                             }];
438
 
439
  FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180002",
440
              @"Firebase In-App Messaging SDK version %@ finished startup in %lf seconds "
441
               "with these settings: %@",
442
              [self.clientInfoFetcher getIAMSDKVersion],
443
              (double)([timeFetcher currentTimestampInSeconds] - start), settings);
444
}
445
@end
446
 
447
#endif  // TARGET_OS_IOS || TARGET_OS_TV