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 <UIKit/UIKit.h>
21
 
22
#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
23
 
24
#import "FirebaseInAppMessaging/Sources/FIRCore+InAppMessaging.h"
25
#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMActivityLogger.h"
26
@implementation FIRIAMActivityRecord
27
 
28
static NSString *const kActiveTypeArchiveKey = @"type";
29
static NSString *const kIsSuccessArchiveKey = @"is_success";
30
static NSString *const kTimeStampArchiveKey = @"timestamp";
31
static NSString *const kDetailArchiveKey = @"detail";
32
 
33
- (id)initWithCoder:(NSCoder *)decoder {
34
  self = [super init];
35
  if (self != nil) {
36
    _activityType = [decoder decodeIntegerForKey:kActiveTypeArchiveKey];
37
    _timestamp = [decoder decodeObjectForKey:kTimeStampArchiveKey];
38
    _success = [decoder decodeBoolForKey:kIsSuccessArchiveKey];
39
    _detail = [decoder decodeObjectForKey:kDetailArchiveKey];
40
  }
41
  return self;
42
}
43
 
44
- (void)encodeWithCoder:(NSCoder *)encoder {
45
  [encoder encodeInteger:self.activityType forKey:kActiveTypeArchiveKey];
46
  [encoder encodeObject:self.timestamp forKey:kTimeStampArchiveKey];
47
  [encoder encodeBool:self.success forKey:kIsSuccessArchiveKey];
48
  [encoder encodeObject:self.detail forKey:kDetailArchiveKey];
49
}
50
 
51
- (instancetype)initWithActivityType:(FIRIAMActivityType)type
52
                        isSuccessful:(BOOL)isSuccessful
53
                          withDetail:(NSString *)detail
54
                           timestamp:(nullable NSDate *)timestamp {
55
  if (self = [super init]) {
56
    _activityType = type;
57
    _success = isSuccessful;
58
    _detail = detail;
59
    _timestamp = timestamp ? timestamp : [[NSDate alloc] init];
60
  }
61
  return self;
62
}
63
 
64
- (NSString *)displayStringForActivityType {
65
  switch (self.activityType) {
66
    case FIRIAMActivityTypeFetchMessage:
67
      return @"Message Fetching";
68
    case FIRIAMActivityTypeRenderMessage:
69
      return @"Message Rendering";
70
    case FIRIAMActivityTypeDismissMessage:
71
      return @"Message Dismiss";
72
    case FIRIAMActivityTypeCheckForOnOpenMessage:
73
      return @"OnOpen Msg Check";
74
    case FIRIAMActivityTypeCheckForAnalyticsEventMessage:
75
      return @"Analytic Msg Check";
76
    case FIRIAMActivityTypeCheckForFetch:
77
      return @"Fetch Check";
78
  }
79
}
80
@end
81
 
82
@interface FIRIAMActivityLogger ()
83
@property(nonatomic) BOOL isDirty;
84
 
85
// always insert at the head of this array so that they are always in anti-chronological order
86
@property(nonatomic, nonnull) NSMutableArray<FIRIAMActivityRecord *> *activityRecords;
87
 
88
// When we see the number of log records goes beyond maxRecordCountBeforeReduce, we would trigger
89
// a reduction action which would bring the array length to be the size as defined by
90
// newSizeAfterReduce
91
@property(nonatomic, readonly) NSInteger maxRecordCountBeforeReduce;
92
@property(nonatomic, readonly) NSInteger newSizeAfterReduce;
93
 
94
@end
95
 
96
@implementation FIRIAMActivityLogger
97
- (instancetype)initWithMaxCountBeforeReduce:(NSInteger)maxBeforeReduce
98
                         withSizeAfterReduce:(NSInteger)sizeAfterReduce
99
                                 verboseMode:(BOOL)verboseMode
100
                               loadFromCache:(BOOL)loadFromCache {
101
  if (self = [super init]) {
102
    _maxRecordCountBeforeReduce = maxBeforeReduce;
103
    _newSizeAfterReduce = sizeAfterReduce;
104
    _activityRecords = [[NSMutableArray alloc] init];
105
    _verboseMode = verboseMode;
106
    _isDirty = NO;
107
 
108
    [[NSNotificationCenter defaultCenter] addObserver:self
109
                                             selector:@selector(appWillBecomeInactive:)
110
                                                 name:UIApplicationWillResignActiveNotification
111
                                               object:nil];
112
#if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
113
    if (@available(iOS 13.0, tvOS 13.0, *)) {
114
      [[NSNotificationCenter defaultCenter] addObserver:self
115
                                               selector:@selector(appWillBecomeInactive:)
116
                                                   name:UISceneWillDeactivateNotification
117
                                                 object:nil];
118
    }
119
#endif  // defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
120
    if (loadFromCache) {
121
      @try {
122
        [self loadFromCachePath:nil];
123
      } @catch (NSException *exception) {
124
        FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM310003",
125
                      @"Non-fatal exception in loading persisted activity log records: %@.",
126
                      exception);
127
      }
128
    }
129
  }
130
  return self;
131
}
132
 
133
- (void)dealloc {
134
  [[NSNotificationCenter defaultCenter] removeObserver:self];
135
}
136
 
137
+ (NSString *)determineCacheFilePath {
138
  static NSString *logCachePath;
139
  static dispatch_once_t onceToken;
140
 
141
  dispatch_once(&onceToken, ^{
142
    NSString *cacheDirPath =
143
        NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
144
    logCachePath = [NSString stringWithFormat:@"%@/firebase-iam-activity-log-cache", cacheDirPath];
145
    FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM310001",
146
                @"Persistent file path for activity log data is %@", logCachePath);
147
  });
148
  return logCachePath;
149
}
150
 
151
- (void)loadFromCachePath:(NSString *)cacheFilePath {
152
  NSString *filePath = cacheFilePath == nil ? [self.class determineCacheFilePath] : cacheFilePath;
153
#pragma clang diagnostic push
154
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
155
  id fetchedActivityRecords = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
156
#pragma clang diagnostic pop
157
  if (fetchedActivityRecords) {
158
    @synchronized(self) {
159
      self.activityRecords = (NSMutableArray<FIRIAMActivityRecord *> *)fetchedActivityRecords;
160
      self.isDirty = NO;
161
    }
162
  }
163
}
164
 
165
- (BOOL)saveIntoCacheWithPath:(NSString *)cacheFilePath {
166
  NSString *filePath = cacheFilePath == nil ? [self.class determineCacheFilePath] : cacheFilePath;
167
  @synchronized(self) {
168
#pragma clang diagnostic push
169
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
170
    BOOL result = [NSKeyedArchiver archiveRootObject:self.activityRecords toFile:filePath];
171
#pragma clang diagnostic pop
172
    if (result) {
173
      self.isDirty = NO;
174
    }
175
    return result;
176
  }
177
}
178
 
179
- (void)appWillBecomeInactive:(NSNotification *)notification {
180
  FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM310004",
181
              @"App will become inactive, save"
182
               " activity logs");
183
 
184
  if (self.isDirty) {
185
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^{
186
      if ([self saveIntoCacheWithPath:nil]) {
187
        FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM310002",
188
                    @"Persisting activity log data is was successful");
189
      } else {
190
        FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM310005",
191
                      @"Persisting activity log data has failed");
192
      }
193
    });
194
  }
195
}
196
 
197
// Helper function to determine if a given activity type should be recorded under
198
// non verbose type.
199
+ (BOOL)isMandatoryType:(FIRIAMActivityType)type {
200
  switch (type) {
201
    case FIRIAMActivityTypeFetchMessage:
202
    case FIRIAMActivityTypeRenderMessage:
203
    case FIRIAMActivityTypeDismissMessage:
204
      return YES;
205
    default:
206
      return NO;
207
  }
208
}
209
 
210
- (void)addLogRecord:(FIRIAMActivityRecord *)newRecord {
211
  if (self.verboseMode || [FIRIAMActivityLogger isMandatoryType:newRecord.activityType]) {
212
    @synchronized(self) {
213
      [self.activityRecords insertObject:newRecord atIndex:0];
214
 
215
      if (self.activityRecords.count >= self.maxRecordCountBeforeReduce) {
216
        NSRange removeRange;
217
        removeRange.location = self.newSizeAfterReduce;
218
        removeRange.length = self.maxRecordCountBeforeReduce - self.newSizeAfterReduce;
219
        [self.activityRecords removeObjectsInRange:removeRange];
220
      }
221
      self.isDirty = YES;
222
    }
223
  }
224
}
225
 
226
- (NSArray<FIRIAMActivityRecord *> *)readRecords {
227
  return [self.activityRecords copy];
228
}
229
@end
230
 
231
#endif  // TARGET_OS_IOS || TARGET_OS_TV