| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- /*
- * 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 <UIKit/UIKit.h>
- #import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
- #import "FirebaseInAppMessaging/Sources/FIRCore+InAppMessaging.h"
- #import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMActivityLogger.h"
- @implementation FIRIAMActivityRecord
- static NSString *const kActiveTypeArchiveKey = @"type";
- static NSString *const kIsSuccessArchiveKey = @"is_success";
- static NSString *const kTimeStampArchiveKey = @"timestamp";
- static NSString *const kDetailArchiveKey = @"detail";
- - (id)initWithCoder:(NSCoder *)decoder {
- self = [super init];
- if (self != nil) {
- _activityType = [decoder decodeIntegerForKey:kActiveTypeArchiveKey];
- _timestamp = [decoder decodeObjectForKey:kTimeStampArchiveKey];
- _success = [decoder decodeBoolForKey:kIsSuccessArchiveKey];
- _detail = [decoder decodeObjectForKey:kDetailArchiveKey];
- }
- return self;
- }
- - (void)encodeWithCoder:(NSCoder *)encoder {
- [encoder encodeInteger:self.activityType forKey:kActiveTypeArchiveKey];
- [encoder encodeObject:self.timestamp forKey:kTimeStampArchiveKey];
- [encoder encodeBool:self.success forKey:kIsSuccessArchiveKey];
- [encoder encodeObject:self.detail forKey:kDetailArchiveKey];
- }
- - (instancetype)initWithActivityType:(FIRIAMActivityType)type
- isSuccessful:(BOOL)isSuccessful
- withDetail:(NSString *)detail
- timestamp:(nullable NSDate *)timestamp {
- if (self = [super init]) {
- _activityType = type;
- _success = isSuccessful;
- _detail = detail;
- _timestamp = timestamp ? timestamp : [[NSDate alloc] init];
- }
- return self;
- }
- - (NSString *)displayStringForActivityType {
- switch (self.activityType) {
- case FIRIAMActivityTypeFetchMessage:
- return @"Message Fetching";
- case FIRIAMActivityTypeRenderMessage:
- return @"Message Rendering";
- case FIRIAMActivityTypeDismissMessage:
- return @"Message Dismiss";
- case FIRIAMActivityTypeCheckForOnOpenMessage:
- return @"OnOpen Msg Check";
- case FIRIAMActivityTypeCheckForAnalyticsEventMessage:
- return @"Analytic Msg Check";
- case FIRIAMActivityTypeCheckForFetch:
- return @"Fetch Check";
- }
- }
- @end
- @interface FIRIAMActivityLogger ()
- @property(nonatomic) BOOL isDirty;
- // always insert at the head of this array so that they are always in anti-chronological order
- @property(nonatomic, nonnull) NSMutableArray<FIRIAMActivityRecord *> *activityRecords;
- // When we see the number of log records goes beyond maxRecordCountBeforeReduce, we would trigger
- // a reduction action which would bring the array length to be the size as defined by
- // newSizeAfterReduce
- @property(nonatomic, readonly) NSInteger maxRecordCountBeforeReduce;
- @property(nonatomic, readonly) NSInteger newSizeAfterReduce;
- @end
- @implementation FIRIAMActivityLogger
- - (instancetype)initWithMaxCountBeforeReduce:(NSInteger)maxBeforeReduce
- withSizeAfterReduce:(NSInteger)sizeAfterReduce
- verboseMode:(BOOL)verboseMode
- loadFromCache:(BOOL)loadFromCache {
- if (self = [super init]) {
- _maxRecordCountBeforeReduce = maxBeforeReduce;
- _newSizeAfterReduce = sizeAfterReduce;
- _activityRecords = [[NSMutableArray alloc] init];
- _verboseMode = verboseMode;
- _isDirty = NO;
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(appWillBecomeInactive:)
- name:UIApplicationWillResignActiveNotification
- object:nil];
- #if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
- if (@available(iOS 13.0, tvOS 13.0, *)) {
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(appWillBecomeInactive:)
- name:UISceneWillDeactivateNotification
- object:nil];
- }
- #endif // defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
- if (loadFromCache) {
- @try {
- [self loadFromCachePath:nil];
- } @catch (NSException *exception) {
- FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM310003",
- @"Non-fatal exception in loading persisted activity log records: %@.",
- exception);
- }
- }
- }
- return self;
- }
- - (void)dealloc {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
- + (NSString *)determineCacheFilePath {
- static NSString *logCachePath;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- NSString *cacheDirPath =
- NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
- logCachePath = [NSString stringWithFormat:@"%@/firebase-iam-activity-log-cache", cacheDirPath];
- FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM310001",
- @"Persistent file path for activity log data is %@", logCachePath);
- });
- return logCachePath;
- }
- - (void)loadFromCachePath:(NSString *)cacheFilePath {
- NSString *filePath = cacheFilePath == nil ? [self.class determineCacheFilePath] : cacheFilePath;
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- id fetchedActivityRecords = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
- #pragma clang diagnostic pop
- if (fetchedActivityRecords) {
- @synchronized(self) {
- self.activityRecords = (NSMutableArray<FIRIAMActivityRecord *> *)fetchedActivityRecords;
- self.isDirty = NO;
- }
- }
- }
- - (BOOL)saveIntoCacheWithPath:(NSString *)cacheFilePath {
- NSString *filePath = cacheFilePath == nil ? [self.class determineCacheFilePath] : cacheFilePath;
- @synchronized(self) {
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- BOOL result = [NSKeyedArchiver archiveRootObject:self.activityRecords toFile:filePath];
- #pragma clang diagnostic pop
- if (result) {
- self.isDirty = NO;
- }
- return result;
- }
- }
- - (void)appWillBecomeInactive:(NSNotification *)notification {
- FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM310004",
- @"App will become inactive, save"
- " activity logs");
- if (self.isDirty) {
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^{
- if ([self saveIntoCacheWithPath:nil]) {
- FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM310002",
- @"Persisting activity log data is was successful");
- } else {
- FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM310005",
- @"Persisting activity log data has failed");
- }
- });
- }
- }
- // Helper function to determine if a given activity type should be recorded under
- // non verbose type.
- + (BOOL)isMandatoryType:(FIRIAMActivityType)type {
- switch (type) {
- case FIRIAMActivityTypeFetchMessage:
- case FIRIAMActivityTypeRenderMessage:
- case FIRIAMActivityTypeDismissMessage:
- return YES;
- default:
- return NO;
- }
- }
- - (void)addLogRecord:(FIRIAMActivityRecord *)newRecord {
- if (self.verboseMode || [FIRIAMActivityLogger isMandatoryType:newRecord.activityType]) {
- @synchronized(self) {
- [self.activityRecords insertObject:newRecord atIndex:0];
- if (self.activityRecords.count >= self.maxRecordCountBeforeReduce) {
- NSRange removeRange;
- removeRange.location = self.newSizeAfterReduce;
- removeRange.length = self.maxRecordCountBeforeReduce - self.newSizeAfterReduce;
- [self.activityRecords removeObjectsInRange:removeRange];
- }
- self.isDirty = YES;
- }
- }
- }
- - (NSArray<FIRIAMActivityRecord *> *)readRecords {
- return [self.activityRecords copy];
- }
- @end
- #endif // TARGET_OS_IOS || TARGET_OS_TV
|