AutorÃa | Ultima modificación | Ver Log |
/** Copyright 2017 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 <TargetConditionals.h>#if TARGET_OS_IOS || TARGET_OS_TV#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"#import "FirebaseInAppMessaging/Sources/Analytics/FIRIAMAnalyticsEventLoggerImpl.h"#import "FirebaseInAppMessaging/Sources/Analytics/FIRIAMClearcutHttpRequestSender.h"#import "FirebaseInAppMessaging/Sources/Analytics/FIRIAMClearcutLogStorage.h"#import "FirebaseInAppMessaging/Sources/FIRCore+InAppMessaging.h"#import "FirebaseInAppMessaging/Sources/FIRInAppMessagingPrivate.h"#import "FirebaseInAppMessaging/Sources/Flows/FIRIAMDisplayCheckOnFetchDoneNotificationFlow.h"#import "FirebaseInAppMessaging/Sources/Private/Analytics/FIRIAMClearcutLogger.h"#import "FirebaseInAppMessaging/Sources/Private/Analytics/FIRIAMClearcutUploader.h"#import "FirebaseInAppMessaging/Sources/Private/Analytics/FIRIAMClientInfoFetcher.h"#import "FirebaseInAppMessaging/Sources/Private/Data/FIRIAMFetchResponseParser.h"#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMActivityLogger.h"#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMBookKeeper.h"#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMDisplayCheckOnAnalyticEventsFlow.h"#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMDisplayCheckOnAppForegroundFlow.h"#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMDisplayExecutor.h"#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMFetchOnAppForegroundFlow.h"#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMMessageClientCache.h"#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMMsgFetcherUsingRestful.h"#import "FirebaseInAppMessaging/Sources/Private/Runtime/FIRIAMRuntimeManager.h"#import "FirebaseInAppMessaging/Sources/Private/Runtime/FIRIAMSDKModeManager.h"#import "FirebaseInAppMessaging/Sources/Public/FirebaseInAppMessaging/FIRInAppMessaging.h"// A enum indicating 3 different possiblities of a setting about auto data collection.typedef NS_ENUM(NSInteger, FIRIAMAutoDataCollectionSetting) {// This indicates that the config is not explicitly set.FIRIAMAutoDataCollectionSettingNone = 0,// This indicates that the setting explicitly enables the auto data collection.FIRIAMAutoDataCollectionSettingEnabled = 1,// This indicates that the setting explicitly disables the auto data collection.FIRIAMAutoDataCollectionSettingDisabled = 2,};@interface FIRIAMRuntimeManager () <FIRIAMTestingModeListener>@property(nonatomic, nonnull) FIRIAMMsgFetcherUsingRestful *restfulFetcher;@property(nonatomic, nonnull) FIRIAMDisplayCheckOnAppForegroundFlow *displayOnAppForegroundFlow;@property(nonatomic, nonnull) FIRIAMDisplayCheckOnFetchDoneNotificationFlow *displayOnFetchDoneFlow;@property(nonatomic, nonnull)FIRIAMDisplayCheckOnAnalyticEventsFlow *displayOnFIRAnalyticEventsFlow;@property(nonatomic, nonnull) FIRIAMFetchOnAppForegroundFlow *fetchOnAppForegroundFlow;@property(nonatomic, nonnull) FIRIAMClientInfoFetcher *clientInfoFetcher;@property(nonatomic, nonnull) FIRIAMFetchResponseParser *responseParser;@endstatic NSString *const _userDefaultsKeyForFIAMProgammaticAutoDataCollectionSetting =@"firebase-iam-sdk-auto-data-collection";@implementation FIRIAMRuntimeManager {// since we allow the SDK feature to be disabled/enabled at runtime, we need a field to track// its state on thisBOOL _running;}+ (FIRIAMRuntimeManager *)getSDKRuntimeInstance {static FIRIAMRuntimeManager *managerInstance = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{managerInstance = [[FIRIAMRuntimeManager alloc] init];});return managerInstance;}// For protocol FIRIAMTestingModeListener.- (void)testingModeSwitchedOn {FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180015",@"Dynamically switch to the display flow for testing mode instance.");[self.displayOnAppForegroundFlow stop];[self.displayOnFetchDoneFlow start];}- (FIRIAMAutoDataCollectionSetting)FIAMProgrammaticAutoDataCollectionSetting {id settingEntry = [[NSUserDefaults standardUserDefaults]objectForKey:_userDefaultsKeyForFIAMProgammaticAutoDataCollectionSetting];if (![settingEntry isKindOfClass:[NSNumber class]]) {FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180014",@"No auto data collection enable setting entry detected.""So no FIAM programmatic setting from the app.");return FIRIAMAutoDataCollectionSettingNone;} else {if ([(NSNumber *)settingEntry boolValue]) {return FIRIAMAutoDataCollectionSettingEnabled;} else {return FIRIAMAutoDataCollectionSettingDisabled;}}}// the key for the plist entry to suppress auto startstatic NSString *const kFirebaseInAppMessagingAutoDataCollectionKey =@"FirebaseInAppMessagingAutomaticDataCollectionEnabled";- (FIRIAMAutoDataCollectionSetting)FIAMPlistAutoDataCollectionSetting {id fiamAutoDataCollectionPlistEntry = [[NSBundle mainBundle]objectForInfoDictionaryKey:kFirebaseInAppMessagingAutoDataCollectionKey];if ([fiamAutoDataCollectionPlistEntry isKindOfClass:[NSNumber class]]) {BOOL fiamDataCollectionEnabledPlistSetting =[(NSNumber *)fiamAutoDataCollectionPlistEntry boolValue];if (fiamDataCollectionEnabledPlistSetting) {FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180011",@"Auto data collection is explicitly enabled in FIAM plist entry.");return FIRIAMAutoDataCollectionSettingEnabled;} else {FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180012",@"Auto data collection is explicitly disabled in FIAM plist entry.");return FIRIAMAutoDataCollectionSettingDisabled;}} else {return FIRIAMAutoDataCollectionSettingNone;}}// Whether data collection is enabled by FIAM programmatic flag.- (BOOL)automaticDataCollectionEnabled {return[self FIAMProgrammaticAutoDataCollectionSetting] != FIRIAMAutoDataCollectionSettingDisabled;}// Sets FIAM's programmatic flag for auto data collection.- (void)setAutomaticDataCollectionEnabled:(BOOL)automaticDataCollectionEnabled {if (automaticDataCollectionEnabled) {[self resume];} else {[self pause];}}- (BOOL)shouldRunSDKFlowsOnStartup {// This can be controlled at 3 different levels in decsending priority. If a higher-priority// setting exists, the lower level settings are ignored.// 1. Setting made by the app by setting FIAM SDK's automaticDataCollectionEnabled flag.// 2. FIAM specific data collection setting in plist file.// 3. Global Firebase auto data collecting setting (carried over by currentSetting property).FIRIAMAutoDataCollectionSetting programmaticSetting =[self FIAMProgrammaticAutoDataCollectionSetting];if (programmaticSetting == FIRIAMAutoDataCollectionSettingEnabled) {FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180010",@"FIAM auto data-collection is explicitly enabled, start SDK flows.");return true;} else if (programmaticSetting == FIRIAMAutoDataCollectionSettingDisabled) {FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180013",@"FIAM auto data-collection is explicitly disabled, do not start SDK flows.");return false;} else {// No explicit setting from fiam's programmatic setting. Checking next level down.FIRIAMAutoDataCollectionSetting fiamPlistDataCollectionSetting =[self FIAMPlistAutoDataCollectionSetting];if (fiamPlistDataCollectionSetting == FIRIAMAutoDataCollectionSettingNone) {FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180018",@"No programmatic or plist setting at FIAM level. Fallback to global Firebase ""level setting.");return self.currentSetting.isFirebaseAutoDataCollectionEnabled;} else {return fiamPlistDataCollectionSetting == FIRIAMAutoDataCollectionSettingEnabled;}}}- (void)resume {// persist the setting[[NSUserDefaults standardUserDefaults]setObject:@(YES)forKey:_userDefaultsKeyForFIAMProgammaticAutoDataCollectionSetting];@synchronized(self) {if (!_running) {[self.fetchOnAppForegroundFlow start];[self.displayOnAppForegroundFlow start];[self.displayOnFIRAnalyticEventsFlow start];FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180019",@"Start Firebase In-App Messaging flows from inactive.");_running = YES;} else {FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM180004",@"Runtime is already active, resume is just a no-op");}}}- (void)pause {// persist the setting[[NSUserDefaults standardUserDefaults]setObject:@(NO)forKey:_userDefaultsKeyForFIAMProgammaticAutoDataCollectionSetting];@synchronized(self) {if (_running) {[self.fetchOnAppForegroundFlow stop];[self.displayOnAppForegroundFlow stop];[self.displayOnFIRAnalyticEventsFlow stop];FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180006",@"Shutdown Firebase In-App Messaging flows.");_running = NO;} else {FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM180005",@"No runtime active yet, pause is just a no-op");}}}- (void)setShouldSuppressMessageDisplay:(BOOL)shouldSuppress {FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180003", @"Message display suppress set to %@",@(shouldSuppress));self.displayExecutor.suppressMessageDisplay = shouldSuppress;}- (void)startRuntimeWithSDKSettings:(FIRIAMSDKSettings *)settings {dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^{[self internalStartRuntimeWithSDKSettings:settings];});}- (void)internalStartRuntimeWithSDKSettings:(FIRIAMSDKSettings *)settings {if (_running) {// Runtime has been started previously. Stop all the flows first.[self.fetchOnAppForegroundFlow stop];[self.displayOnAppForegroundFlow stop];[self.displayOnFIRAnalyticEventsFlow stop];}self.currentSetting = settings;FIRIAMTimerWithNSDate *timeFetcher = [[FIRIAMTimerWithNSDate alloc] init];NSTimeInterval start = [timeFetcher currentTimestampInSeconds];self.activityLogger =[[FIRIAMActivityLogger alloc] initWithMaxCountBeforeReduce:settings.loggerMaxCountBeforeReducewithSizeAfterReduce:settings.loggerSizeAfterReduceverboseMode:settings.loggerInVerboseModeloadFromCache:YES];self.responseParser = [[FIRIAMFetchResponseParser alloc] initWithTimeFetcher:timeFetcher];self.bookKeeper = [[FIRIAMBookKeeperViaUserDefaults alloc]initWithUserDefaults:[NSUserDefaults standardUserDefaults]];self.messageCache = [[FIRIAMMessageClientCache alloc] initWithBookkeeper:self.bookKeeperusingResponseParser:self.responseParser];self.fetchResultStorage = [[FIRIAMServerMsgFetchStorage alloc] init];self.clientInfoFetcher = [[FIRIAMClientInfoFetcher alloc]initWithFirebaseInstallations:[FIRInAppMessaging inAppMessaging].installations];self.restfulFetcher =[[FIRIAMMsgFetcherUsingRestful alloc] initWithHost:settings.apiServerHostHTTPProtocol:settings.apiHttpProtocolproject:settings.firebaseProjectNumberfirebaseApp:settings.firebaseAppIdAPIKey:settings.apiKeyfetchStorage:self.fetchResultStorageinstanceIDFetcher:self.clientInfoFetcherusingURLSession:nilresponseParser:self.responseParser];// start fetch on app foreground flowFIRIAMFetchSetting *fetchSetting = [[FIRIAMFetchSetting alloc] init];fetchSetting.fetchMinIntervalInMinutes = settings.fetchMinIntervalInMinutes;// start render on app foreground flowFIRIAMDisplaySetting *appForegroundDisplaysetting = [[FIRIAMDisplaySetting alloc] init];appForegroundDisplaysetting.displayMinIntervalInMinutes =settings.appFGRenderMinIntervalInMinutes;// clearcut log expires after 14 days: give up on attempting to deliver them any moreNSInteger ctLogExpiresInSeconds = 14 * 24 * 60 * 60;FIRIAMClearcutLogStorage *ctLogStorage =[[FIRIAMClearcutLogStorage alloc] initWithExpireAfterInSeconds:ctLogExpiresInSecondswithTimeFetcher:timeFetcher];FIRIAMClearcutHttpRequestSender *clearcutRequestSender = [[FIRIAMClearcutHttpRequestSender alloc]initWithClearcutHost:settings.clearcutServerHostusingTimeFetcher:timeFetcherwithOSMajorVersion:[self.clientInfoFetcher getOSMajorVersion]];FIRIAMClearcutUploader *ctUploader =[[FIRIAMClearcutUploader alloc] initWithRequestSender:clearcutRequestSendertimeFetcher:timeFetcherlogStorage:ctLogStorageusingStrategy:settings.clearcutStrategyusingUserDefaults:nil];FIRIAMClearcutLogger *clearcutLogger =[[FIRIAMClearcutLogger alloc] initWithFBProjectNumber:settings.firebaseProjectNumberfbAppId:settings.firebaseAppIdclientInfoFetcher:self.clientInfoFetcherusingTimeFetcher:timeFetcherusingUploader:ctUploader];FIRIAMAnalyticsEventLoggerImpl *analyticsEventLogger = [[FIRIAMAnalyticsEventLoggerImpl alloc]initWithClearcutLogger:clearcutLoggerusingTimeFetcher:timeFetcherusingUserDefaults:nilanalytics:[FIRInAppMessaging inAppMessaging].analytics];FIRIAMSDKModeManager *sdkModeManager =[[FIRIAMSDKModeManager alloc] initWithUserDefaults:NSUserDefaults.standardUserDefaultstestingModeListener:self];FIRIAMActionURLFollower *actionFollower = [FIRIAMActionURLFollower actionURLFollower];self.displayExecutor =[[FIRIAMDisplayExecutor alloc] initWithInAppMessaging:[FIRInAppMessaging inAppMessaging]setting:appForegroundDisplaysettingmessageCache:self.messageCachetimeFetcher:timeFetcherbookKeeper:self.bookKeeperactionURLFollower:actionFolloweractivityLogger:self.activityLoggeranalyticsEventLogger:analyticsEventLogger];self.fetchOnAppForegroundFlow =[[FIRIAMFetchOnAppForegroundFlow alloc] initWithSetting:fetchSettingmessageCache:self.messageCachemessageFetcher:self.restfulFetchertimeFetcher:timeFetcherbookKeeper:self.bookKeeperactivityLogger:self.activityLoggeranalyticsEventLogger:analyticsEventLoggerFIRIAMSDKModeManager:sdkModeManagerdisplayExecutor:self.displayExecutor];// Setting the message display component and suppression. It's needed in case// headless SDK is initialized after the these properties are already set on FIRInAppMessaging.self.displayExecutor.messageDisplayComponent =FIRInAppMessaging.inAppMessaging.messageDisplayComponent;self.displayExecutor.suppressMessageDisplay =FIRInAppMessaging.inAppMessaging.messageDisplaySuppressed;// Both display flows are created on startup. But they would only be turned on (started) based on// the sdk mode for the current instanceself.displayOnFetchDoneFlow = [[FIRIAMDisplayCheckOnFetchDoneNotificationFlow alloc]initWithDisplayFlow:self.displayExecutor];self.displayOnAppForegroundFlow =[[FIRIAMDisplayCheckOnAppForegroundFlow alloc] initWithDisplayFlow:self.displayExecutor];self.displayOnFIRAnalyticEventsFlow =[[FIRIAMDisplayCheckOnAnalyticEventsFlow alloc] initWithDisplayFlow:self.displayExecutor];self.messageCache.analycisEventDislayCheckFlow = self.displayOnFIRAnalyticEventsFlow;[self.messageCacheloadMessageDataFromServerFetchStorage:self.fetchResultStoragewithCompletion:^(BOOL success) {// start flows regardless whether we can load messages from fetch// storage successfullyFIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180001",@"Message loading from fetch storage was done.");if ([self shouldRunSDKFlowsOnStartup]) {FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180008",@"Start SDK runtime components.");[self.clientInfoFetcherfetchFirebaseInstallationDataWithProjectNumber:self.currentSetting.firebaseProjectNumberwithCompletion:^(NSString *_Nullable FID,NSString*_Nullable FISToken,NSError *_Nullable error) {// Always dump the// installation ID into log// on startup to help// developers to find it for// their app instance.FIRLogDebug(kFIRLoggerInAppMessaging,@"I-IAM180017",@"Starting "@"InAppMessaging "@"runtime "@"with ""Firebase Installation ""ID %@",FID);}];[self.fetchOnAppForegroundFlow start];[self.displayOnFIRAnalyticEventsFlow start];self->_running = YES;if (sdkModeManager.currentMode == FIRIAMSDKModeTesting) {FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180007",@"InAppMessaging testing mode enabled. App ""foreground messages will be displayed following ""fetch");[self.displayOnFetchDoneFlow start];} else {FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180020",@"Start regular display flow for non-testing ""instance mode");[self.displayOnAppForegroundFlow start];// Simulate app going into foreground on startup[self.displayExecutor checkAndDisplayNextAppForegroundMessage];}// One-time triggering of checks for both fetch flow// upon SDK/app startup.[self.fetchOnAppForegroundFlowcheckAndFetchForInitialAppLaunch:YES];} else {FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180009",@"No FIAM SDK startup due to settings.");}}];FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM180002",@"Firebase In-App Messaging SDK version %@ finished startup in %lf seconds ""with these settings: %@",[self.clientInfoFetcher getIAMSDKVersion],(double)([timeFetcher currentTimestampInSeconds] - start), settings);}@end#endif // TARGET_OS_IOS || TARGET_OS_TV