| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- /*
- * 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/FIRCore+InAppMessaging.h"
- #import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMBookKeeper.h"
- NSString *const FIRIAM_UserDefaultsKeyForImpressions = @"firebase-iam-message-impressions";
- NSString *const FIRIAM_UserDefaultsKeyForLastImpressionTimestamp =
- @"firebase-iam-last-impression-timestamp";
- NSString *FIRIAM_UserDefaultsKeyForLastFetchTimestamp = @"firebase-iam-last-fetch-timestamp";
- // The two keys used to map FIRIAMImpressionRecord object to a NSDictionary object for
- // persistence.
- NSString *const FIRIAM_ImpressionDictKeyForID = @"message_id";
- NSString *const FIRIAM_ImpressionDictKeyForTimestamp = @"impression_time";
- static NSString *const kUserDefaultsKeyForFetchWaitTime = @"firebase-iam-fetch-wait-time";
- // 24 hours
- static NSTimeInterval kDefaultFetchWaitTimeInSeconds = 24 * 60 * 60;
- // 3 days
- static NSTimeInterval kMaxFetchWaitTimeInSeconds = 3 * 24 * 60 * 60;
- @interface FIRIAMBookKeeperViaUserDefaults ()
- @property(nonatomic) double lastDisplayTime;
- @property(nonatomic) double lastFetchTime;
- @property(nonatomic) double nextFetchWaitTime;
- @property(nonatomic, nonnull) NSUserDefaults *defaults;
- @end
- @interface FIRIAMImpressionRecord ()
- - (instancetype)initWithStorageDictionary:(NSDictionary *)dict;
- @end
- @implementation FIRIAMImpressionRecord
- - (instancetype)initWithMessageID:(NSString *)messageID
- impressionTimeInSeconds:(long)impressionTime {
- if (self = [super init]) {
- _messageID = messageID;
- _impressionTimeInSeconds = impressionTime;
- }
- return self;
- }
- - (instancetype)initWithStorageDictionary:(NSDictionary *)dict {
- id timestamp = dict[FIRIAM_ImpressionDictKeyForTimestamp];
- id messageID = dict[FIRIAM_ImpressionDictKeyForID];
- if (![timestamp isKindOfClass:[NSNumber class]] || ![messageID isKindOfClass:[NSString class]]) {
- FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM270003",
- @"Incorrect data in the dictionary object for creating a FIRIAMImpressionRecord"
- " object");
- return nil;
- } else {
- return [self initWithMessageID:messageID
- impressionTimeInSeconds:((NSNumber *)timestamp).longValue];
- }
- }
- - (NSString *)description {
- return [NSString stringWithFormat:@"%@ impressed at %ld in seconds", self.messageID,
- self.impressionTimeInSeconds];
- }
- @end
- @implementation FIRIAMBookKeeperViaUserDefaults
- - (instancetype)initWithUserDefaults:(NSUserDefaults *)userDefaults {
- if (self = [super init]) {
- _defaults = userDefaults;
- // ok if it returns 0 due to the entry being absent
- _lastDisplayTime = [_defaults doubleForKey:FIRIAM_UserDefaultsKeyForLastImpressionTimestamp];
- _lastFetchTime = [_defaults doubleForKey:FIRIAM_UserDefaultsKeyForLastFetchTimestamp];
- id fetchWaitTimeEntry = [_defaults objectForKey:kUserDefaultsKeyForFetchWaitTime];
- if (![fetchWaitTimeEntry isKindOfClass:NSNumber.class]) {
- // This corresponds to the case there is no wait time entry is set in user defaults yet
- _nextFetchWaitTime = kDefaultFetchWaitTimeInSeconds;
- } else {
- _nextFetchWaitTime = ((NSNumber *)fetchWaitTimeEntry).doubleValue;
- FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM270009",
- @"Next fetch wait time loaded from user defaults is %lf", _nextFetchWaitTime);
- }
- }
- return self;
- }
- // A helper function for reading and verifying the stored array data for impressions
- // in UserDefaults. It returns nil if it does not exist or fail to pass the data type
- // checking.
- - (NSArray *)fetchImpressionArrayFromStorage {
- id impressionsData = [self.defaults objectForKey:FIRIAM_UserDefaultsKeyForImpressions];
- if (impressionsData && ![impressionsData isKindOfClass:[NSArray class]]) {
- FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM270007",
- @"Found non-array data from impression userdefaults storage with key %@",
- FIRIAM_UserDefaultsKeyForImpressions);
- return nil;
- }
- return (NSArray *)impressionsData;
- }
- - (void)recordNewImpressionForMessage:(NSString *)messageID
- withStartTimestampInSeconds:(double)timestamp {
- @synchronized(self) {
- NSArray *oldImpressions = [self fetchImpressionArrayFromStorage];
- // oldImpressions could be nil at the first time
- NSMutableArray *newImpressions =
- oldImpressions ? [oldImpressions mutableCopy] : [[NSMutableArray alloc] init];
- // Two cases
- // If a prior impression exists for that messageID, update its impression timestamp
- // If a prior impression for that messageID does not exist, add a new entry for the
- // messageID.
- NSDictionary *newImpressionEntry = @{
- FIRIAM_ImpressionDictKeyForID : messageID,
- FIRIAM_ImpressionDictKeyForTimestamp : [NSNumber numberWithDouble:timestamp]
- };
- BOOL oldImpressionRecordFound = NO;
- for (int i = 0; i < newImpressions.count; i++) {
- if ([newImpressions[i] isKindOfClass:[NSDictionary class]]) {
- NSDictionary *currentItem = (NSDictionary *)newImpressions[i];
- if ([messageID isEqualToString:currentItem[FIRIAM_ImpressionDictKeyForID]]) {
- FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM270001",
- @"Updating timestamp of existing impression record to be %f for "
- "message %@",
- timestamp, messageID);
- [newImpressions replaceObjectAtIndex:i withObject:newImpressionEntry];
- oldImpressionRecordFound = YES;
- break;
- }
- }
- }
- if (!oldImpressionRecordFound) {
- FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM270002",
- @"Insert the first impression record for message %@ with timestamp in seconds "
- "as %f",
- messageID, timestamp);
- [newImpressions addObject:newImpressionEntry];
- }
- [self.defaults setObject:newImpressions forKey:FIRIAM_UserDefaultsKeyForImpressions];
- [self.defaults setDouble:timestamp forKey:FIRIAM_UserDefaultsKeyForLastImpressionTimestamp];
- self.lastDisplayTime = timestamp;
- }
- }
- - (void)clearImpressionsWithMessageList:(NSArray<NSString *> *)messageList {
- @synchronized(self) {
- NSArray *existingImpressions = [self fetchImpressionArrayFromStorage];
- NSSet<NSString *> *messageIDSet = [NSSet setWithArray:messageList];
- NSPredicate *notInMessageListPredicate =
- [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
- if (![evaluatedObject isKindOfClass:[NSDictionary class]]) {
- return NO; // unexpected item. Throw it away
- }
- NSDictionary *impression = (NSDictionary *)evaluatedObject;
- return impression[FIRIAM_ImpressionDictKeyForID] &&
- ![messageIDSet containsObject:impression[FIRIAM_ImpressionDictKeyForID]];
- }];
- NSArray<NSDictionary *> *updatedImpressions =
- [existingImpressions filteredArrayUsingPredicate:notInMessageListPredicate];
- if (existingImpressions.count != updatedImpressions.count) {
- FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM270004",
- @"Updating the impression records after purging %d items based on the "
- "server fetch response",
- (int)(existingImpressions.count - updatedImpressions.count));
- [self.defaults setObject:updatedImpressions forKey:FIRIAM_UserDefaultsKeyForImpressions];
- } else {
- FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM270005",
- @"No impression records update due to no change after applying the server "
- "message list");
- }
- }
- }
- - (NSArray<FIRIAMImpressionRecord *> *)getImpressions {
- NSArray<NSDictionary *> *impressionsFromStorage = [self fetchImpressionArrayFromStorage];
- NSMutableArray<FIRIAMImpressionRecord *> *resultArray = [[NSMutableArray alloc] init];
- for (NSDictionary *next in impressionsFromStorage) {
- FIRIAMImpressionRecord *nextImpression =
- [[FIRIAMImpressionRecord alloc] initWithStorageDictionary:next];
- [resultArray addObject:nextImpression];
- }
- return resultArray;
- }
- - (NSArray<NSString *> *)getMessageIDsFromImpressions {
- NSArray<NSDictionary *> *impressionsFromStorage = [self fetchImpressionArrayFromStorage];
- NSMutableArray<NSString *> *resultArray = [[NSMutableArray alloc] init];
- for (NSDictionary *next in impressionsFromStorage) {
- [resultArray addObject:next[FIRIAM_ImpressionDictKeyForID]];
- }
- return resultArray;
- }
- - (void)recordNewFetchWithFetchCount:(NSInteger)fetchedMsgCount
- withTimestampInSeconds:(double)fetchTimestamp
- nextFetchWaitTime:(nullable NSNumber *)nextFetchWaitTime;
- {
- [self.defaults setDouble:fetchTimestamp forKey:FIRIAM_UserDefaultsKeyForLastFetchTimestamp];
- self.lastFetchTime = fetchTimestamp;
- if (nextFetchWaitTime != nil) {
- if (nextFetchWaitTime.doubleValue > kMaxFetchWaitTimeInSeconds) {
- FIRLogInfo(kFIRLoggerInAppMessaging, @"I-IAM270006",
- @"next fetch wait time %lf is too large. Ignore it.",
- nextFetchWaitTime.doubleValue);
- } else {
- FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM270008",
- @"Setting next fetch wait time as %lf from fetch response.",
- nextFetchWaitTime.doubleValue);
- self.nextFetchWaitTime = nextFetchWaitTime.doubleValue;
- [self.defaults setObject:nextFetchWaitTime forKey:kUserDefaultsKeyForFetchWaitTime];
- }
- }
- }
- - (void)cleanupImpressions {
- [self.defaults setObject:@[] forKey:FIRIAM_UserDefaultsKeyForImpressions];
- }
- - (void)cleanupFetchRecords {
- [self.defaults setDouble:0 forKey:FIRIAM_UserDefaultsKeyForLastFetchTimestamp];
- self.lastFetchTime = 0;
- }
- @end
- #endif // TARGET_OS_IOS || TARGET_OS_TV
|