Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// Copyright 2019 Google
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
 
15
#import "Crashlytics/Crashlytics/Models/FIRCLSSettings.h"
16
 
17
#if __has_include(<FBLPromises/FBLPromises.h>)
18
#import <FBLPromises/FBLPromises.h>
19
#else
20
#import "FBLPromises.h"
21
#endif
22
 
23
#import "Crashlytics/Crashlytics/Helpers/FIRCLSLogger.h"
24
#import "Crashlytics/Crashlytics/Models/FIRCLSFileManager.h"
25
#import "Crashlytics/Crashlytics/Settings/Models/FIRCLSApplicationIdentifierModel.h"
26
#import "Crashlytics/Shared/FIRCLSConstants.h"
27
#import "Crashlytics/Shared/FIRCLSNetworking/FIRCLSURLBuilder.h"
28
 
29
NSString *const CreatedAtKey = @"created_at";
30
NSString *const GoogleAppIDKey = @"google_app_id";
31
NSString *const BuildInstanceID = @"build_instance_id";
32
NSString *const AppVersion = @"app_version";
33
 
34
@interface FIRCLSSettings ()
35
 
36
@property(nonatomic, strong) FIRCLSFileManager *fileManager;
37
@property(nonatomic, strong) FIRCLSApplicationIdentifierModel *appIDModel;
38
 
39
@property(nonatomic, strong) NSDictionary<NSString *, id> *settingsDictionary;
40
 
41
@property(nonatomic) BOOL isCacheKeyExpired;
42
 
43
@end
44
 
45
@implementation FIRCLSSettings
46
 
47
- (instancetype)initWithFileManager:(FIRCLSFileManager *)fileManager
48
                         appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel {
49
  self = [super init];
50
  if (!self) {
51
    return nil;
52
  }
53
 
54
  _fileManager = fileManager;
55
  _appIDModel = appIDModel;
56
 
57
  _settingsDictionary = nil;
58
  _isCacheKeyExpired = NO;
59
 
60
  return self;
61
}
62
 
63
#pragma mark - Public Methods
64
 
65
- (void)reloadFromCacheWithGoogleAppID:(NSString *)googleAppID
66
                      currentTimestamp:(NSTimeInterval)currentTimestamp {
67
  NSString *settingsFilePath = self.fileManager.settingsFilePath;
68
 
69
  NSData *data = [self.fileManager dataWithContentsOfFile:settingsFilePath];
70
 
71
  if (!data) {
72
    FIRCLSDebugLog(@"[Crashlytics:Settings] No settings were cached");
73
 
74
    return;
75
  }
76
 
77
  NSError *error = nil;
78
  @synchronized(self) {
79
    _settingsDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
80
  }
81
 
82
  if (!_settingsDictionary) {
83
    FIRCLSErrorLog(@"Could not load settings file data with error: %@", error.localizedDescription);
84
 
85
    // Attempt to remove it, in case it's messed up
86
    [self deleteCachedSettings];
87
    return;
88
  }
89
 
90
  NSDictionary<NSString *, id> *cacheKey = [self loadCacheKey];
91
  if (!cacheKey) {
92
    FIRCLSErrorLog(@"Could not load settings cache key");
93
 
94
    [self deleteCachedSettings];
95
    return;
96
  }
97
 
98
  NSString *cachedGoogleAppID = cacheKey[GoogleAppIDKey];
99
  if (![cachedGoogleAppID isEqualToString:googleAppID]) {
100
    FIRCLSDebugLog(
101
        @"[Crashlytics:Settings] Invalidating settings cache because Google App ID changed");
102
 
103
    [self deleteCachedSettings];
104
    return;
105
  }
106
 
107
  NSTimeInterval cacheCreatedAt = [cacheKey[CreatedAtKey] unsignedIntValue];
108
  NSTimeInterval cacheDurationSeconds = self.cacheDurationSeconds;
109
  if (currentTimestamp > (cacheCreatedAt + cacheDurationSeconds)) {
110
    FIRCLSDebugLog(@"[Crashlytics:Settings] Settings TTL expired");
111
 
112
    @synchronized(self) {
113
      self.isCacheKeyExpired = YES;
114
    }
115
  }
116
 
117
  NSString *cacheBuildInstanceID = cacheKey[BuildInstanceID];
118
  if (![cacheBuildInstanceID isEqualToString:self.appIDModel.buildInstanceID]) {
119
    FIRCLSDebugLog(@"[Crashlytics:Settings] Settings expired because build instance changed");
120
 
121
    @synchronized(self) {
122
      self.isCacheKeyExpired = YES;
123
    }
124
  }
125
 
126
  NSString *cacheAppVersion = cacheKey[AppVersion];
127
  if (![cacheAppVersion isEqualToString:self.appIDModel.synthesizedVersion]) {
128
    FIRCLSDebugLog(@"[Crashlytics:Settings] Settings expired because app version changed");
129
 
130
    @synchronized(self) {
131
      self.isCacheKeyExpired = YES;
132
    }
133
  }
134
}
135
 
136
- (void)cacheSettingsWithGoogleAppID:(NSString *)googleAppID
137
                    currentTimestamp:(NSTimeInterval)currentTimestamp {
138
  NSNumber *createdAtTimestamp = [NSNumber numberWithDouble:currentTimestamp];
139
  NSDictionary *cacheKey = @{
140
    CreatedAtKey : createdAtTimestamp,
141
    GoogleAppIDKey : googleAppID,
142
    BuildInstanceID : self.appIDModel.buildInstanceID,
143
    AppVersion : self.appIDModel.synthesizedVersion,
144
  };
145
 
146
  NSError *error = nil;
147
  NSData *jsonData = [NSJSONSerialization dataWithJSONObject:cacheKey
148
                                                     options:kNilOptions
149
                                                       error:&error];
150
 
151
  if (!jsonData) {
152
    FIRCLSErrorLog(@"Could not create settings cache key with error: %@",
153
                   error.localizedDescription);
154
 
155
    return;
156
  }
157
 
158
  if ([self.fileManager fileExistsAtPath:self.fileManager.settingsCacheKeyPath]) {
159
    [self.fileManager removeItemAtPath:self.fileManager.settingsCacheKeyPath];
160
  }
161
  [self.fileManager createFileAtPath:self.fileManager.settingsCacheKeyPath
162
                            contents:jsonData
163
                          attributes:nil];
164
 
165
  // If Settings were expired before, they should no longer be expired after this.
166
  // This may be set back to YES if reloading from the cache fails
167
  @synchronized(self) {
168
    self.isCacheKeyExpired = NO;
169
  }
170
 
171
  [self reloadFromCacheWithGoogleAppID:googleAppID currentTimestamp:currentTimestamp];
172
}
173
 
174
#pragma mark - Convenience Methods
175
 
176
- (NSDictionary *)loadCacheKey {
177
  NSData *cacheKeyData =
178
      [self.fileManager dataWithContentsOfFile:self.fileManager.settingsCacheKeyPath];
179
 
180
  if (!cacheKeyData) {
181
    return nil;
182
  }
183
 
184
  NSError *error = nil;
185
  NSDictionary *cacheKey = [NSJSONSerialization JSONObjectWithData:cacheKeyData
186
                                                           options:NSJSONReadingAllowFragments
187
                                                             error:&error];
188
  return cacheKey;
189
}
190
 
191
- (void)deleteCachedSettings {
192
  __weak FIRCLSSettings *weakSelf = self;
193
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
194
    __strong FIRCLSSettings *strongSelf = weakSelf;
195
    if ([strongSelf.fileManager fileExistsAtPath:strongSelf.fileManager.settingsFilePath]) {
196
      [strongSelf.fileManager removeItemAtPath:strongSelf.fileManager.settingsFilePath];
197
    }
198
    if ([strongSelf.fileManager fileExistsAtPath:strongSelf.fileManager.settingsCacheKeyPath]) {
199
      [strongSelf.fileManager removeItemAtPath:strongSelf.fileManager.settingsCacheKeyPath];
200
    }
201
  });
202
 
203
  @synchronized(self) {
204
    self.isCacheKeyExpired = YES;
205
    _settingsDictionary = nil;
206
  }
207
}
208
 
209
- (NSDictionary<NSString *, id> *)settingsDictionary {
210
  @synchronized(self) {
211
    return _settingsDictionary;
212
  }
213
}
214
 
215
#pragma mark - Settings Groups
216
 
217
- (NSDictionary<NSString *, id> *)appSettings {
218
  return self.settingsDictionary[@"app"];
219
}
220
 
221
- (NSDictionary<NSString *, id> *)sessionSettings {
222
  return self.settingsDictionary[@"session"];
223
}
224
 
225
- (NSDictionary<NSString *, id> *)featuresSettings {
226
  return self.settingsDictionary[@"features"];
227
}
228
 
229
- (NSDictionary<NSString *, id> *)fabricSettings {
230
  return self.settingsDictionary[@"fabric"];
231
}
232
 
233
#pragma mark - Caching
234
 
235
- (BOOL)isCacheExpired {
236
  if (!self.settingsDictionary) {
237
    return YES;
238
  }
239
 
240
  @synchronized(self) {
241
    return self.isCacheKeyExpired;
242
  }
243
}
244
 
245
- (uint32_t)cacheDurationSeconds {
246
  id fetchedCacheDuration = self.settingsDictionary[@"cache_duration"];
247
  if (fetchedCacheDuration) {
248
    return [fetchedCacheDuration unsignedIntValue];
249
  }
250
 
251
  return 60 * 60;
252
}
253
 
254
#pragma mark - On / Off Switches
255
 
256
- (BOOL)errorReportingEnabled {
257
  NSNumber *value = [self featuresSettings][@"collect_logged_exceptions"];
258
 
259
  if (value != nil) {
260
    return [value boolValue];
261
  }
262
 
263
  return YES;
264
}
265
 
266
- (BOOL)customExceptionsEnabled {
267
  // Right now, recording custom exceptions from the API and
268
  // automatically capturing non-fatal errors go hand in hand
269
  return [self errorReportingEnabled];
270
}
271
 
272
- (BOOL)collectReportsEnabled {
273
  NSNumber *value = [self featuresSettings][@"collect_reports"];
274
 
275
  if (value != nil) {
276
    return value.boolValue;
277
  }
278
 
279
  return YES;
280
}
281
 
282
- (BOOL)metricKitCollectionEnabled {
283
  NSNumber *value = [self featuresSettings][@"collect_metric_kit"];
284
 
285
  if (value != nil) {
286
    return value.boolValue;
287
  }
288
 
289
  return NO;
290
}
291
 
292
#pragma mark - Optional Limit Overrides
293
 
294
- (uint32_t)errorLogBufferSize {
295
  return [self logBufferSize];
296
}
297
 
298
- (uint32_t)logBufferSize {
299
  NSNumber *value = [self sessionSettings][@"log_buffer_size"];
300
 
301
  if (value != nil) {
302
    return value.unsignedIntValue;
303
  }
304
 
305
  return 64 * 1000;
306
}
307
 
308
- (uint32_t)maxCustomExceptions {
309
  NSNumber *value = [self sessionSettings][@"max_custom_exception_events"];
310
 
311
  if (value != nil) {
312
    return value.unsignedIntValue;
313
  }
314
 
315
  return 8;
316
}
317
 
318
- (uint32_t)maxCustomKeys {
319
  NSNumber *value = [self sessionSettings][@"max_custom_key_value_pairs"];
320
 
321
  if (value != nil) {
322
    return value.unsignedIntValue;
323
  }
324
 
325
  return 64;
326
}
327
 
328
#pragma mark - On Demand Reporting Parameters
329
 
330
- (double)onDemandUploadRate {
331
  NSNumber *value = self.settingsDictionary[@"on_demand_upload_rate_per_minute"];
332
 
333
  if (value != nil) {
334
    return value.doubleValue;
335
  }
336
 
337
  return 10;  // on-demand uploads allowed per minute
338
}
339
 
340
- (double)onDemandBackoffBase {
341
  NSNumber *value = self.settingsDictionary[@"on_demand_backoff_base"];
342
 
343
  if (value != nil) {
344
    return [value doubleValue];
345
  }
346
 
347
  return 1.5;  // base of exponent for exponential backoff
348
}
349
 
350
- (uint32_t)onDemandBackoffStepDuration {
351
  NSNumber *value = self.settingsDictionary[@"on_demand_backoff_step_duration_seconds"];
352
 
353
  if (value != nil) {
354
    return value.unsignedIntValue;
355
  }
356
 
357
  return 6;  // step duration for exponential backoff
358
}
359
 
360
@end