Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
/*
2
 * Copyright 2019 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 "GoogleUtilities/Environment/Public/GoogleUtilities/GULHeartbeatDateStorage.h"
18
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULSecureCoding.h"
19
 
20
NSString *const kGULHeartbeatStorageDirectory = @"Google/FIRApp";
21
 
22
@interface GULHeartbeatDateStorage ()
23
 
24
/** The name of the file that stores heartbeat information. */
25
@property(nonatomic, readonly) NSString *fileName;
26
@end
27
 
28
@implementation GULHeartbeatDateStorage
29
 
30
@synthesize fileURL = _fileURL;
31
 
32
- (instancetype)initWithFileName:(NSString *)fileName {
33
  if (fileName == nil) return nil;
34
 
35
  self = [super init];
36
  if (self) {
37
    _fileName = fileName;
38
  }
39
  return self;
40
}
41
 
42
/** Lazy getter for fileURL.
43
 * @return fileURL where heartbeat information is stored.
44
 */
45
- (NSURL *)fileURL {
46
  if (!_fileURL) {
47
    NSURL *directoryURL = [self directoryPathURL];
48
    [self checkAndCreateDirectory:directoryURL];
49
    _fileURL = [directoryURL URLByAppendingPathComponent:_fileName];
50
  }
51
  return _fileURL;
52
}
53
 
54
/** Returns the URL path of the directory for heartbeat storage data.
55
 * @return the URL path of the directory for heartbeat storage data.
56
 */
57
- (NSURL *)directoryPathURL {
58
  NSArray<NSString *> *paths;
59
#if TARGET_OS_TV
60
  paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
61
#else
62
  paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
63
#endif  // TARGET_OS_TV
64
  NSString *rootPath = [paths lastObject];
65
  NSURL *rootURL = [NSURL fileURLWithPath:rootPath];
66
  NSURL *directoryURL = [rootURL URLByAppendingPathComponent:kGULHeartbeatStorageDirectory];
67
  return directoryURL;
68
}
69
 
70
/** Check for the existence of the directory specified by the URL, and create it if it does not
71
 * exist.
72
 * @param directoryPathURL The path to the directory that needs to exist.
73
 */
74
- (void)checkAndCreateDirectory:(NSURL *)directoryPathURL {
75
  NSError *error;
76
  if (![directoryPathURL checkResourceIsReachableAndReturnError:&error]) {
77
    NSError *error;
78
    [[NSFileManager defaultManager] createDirectoryAtURL:directoryPathURL
79
                             withIntermediateDirectories:YES
80
                                              attributes:nil
81
                                                   error:&error];
82
  }
83
}
84
 
85
- (nullable NSDate *)heartbeatDateForTag:(NSString *)tag {
86
  @synchronized(self.class) {
87
    NSDictionary *heartbeatDictionary = [self heartbeatDictionaryWithFileURL:self.fileURL];
88
    NSDate *heartbeatDate = heartbeatDictionary[tag];
89
 
90
    // Validate the value type. If the storage file was corrupted or updated with a different format
91
    // by a newer SDK version the value type may be different.
92
    if (![heartbeatDate isKindOfClass:[NSDate class]]) {
93
      heartbeatDate = nil;
94
    }
95
 
96
    return heartbeatDate;
97
  }
98
}
99
 
100
- (BOOL)setHearbeatDate:(NSDate *)date forTag:(NSString *)tag {
101
  // Synchronize on the class to ensure that the different instances of the class will not access
102
  // the same file concurrently.
103
  // TODO: Consider a different synchronization strategy here and in `-heartbeatDateForTag:` method.
104
  // Currently no heartbeats can be read/written concurrently even if they are in different files.
105
  @synchronized(self.class) {
106
    NSMutableDictionary *heartbeatDictionary =
107
        [[self heartbeatDictionaryWithFileURL:self.fileURL] mutableCopy];
108
    heartbeatDictionary[tag] = date;
109
    NSError *error;
110
    BOOL isSuccess = [self writeDictionary:[heartbeatDictionary copy]
111
                             forWritingURL:self.fileURL
112
                                     error:&error];
113
    return isSuccess;
114
  }
115
}
116
 
117
- (NSDictionary *)heartbeatDictionaryWithFileURL:(NSURL *)readingFileURL {
118
  NSDictionary *heartbeatDictionary;
119
 
120
  NSError *error;
121
  NSData *objectData = [NSData dataWithContentsOfURL:readingFileURL options:0 error:&error];
122
 
123
  if (objectData.length > 0 && error == nil) {
124
    NSSet<Class> *objectClasses =
125
        [NSSet setWithArray:@[ NSDictionary.class, NSDate.class, NSString.class ]];
126
    heartbeatDictionary = [GULSecureCoding unarchivedObjectOfClasses:objectClasses
127
                                                            fromData:objectData
128
                                                               error:&error];
129
  }
130
 
131
  if (heartbeatDictionary.count == 0 || error != nil) {
132
    heartbeatDictionary = [NSDictionary dictionary];
133
  }
134
 
135
  return heartbeatDictionary;
136
}
137
 
138
- (BOOL)writeDictionary:(NSDictionary *)dictionary
139
          forWritingURL:(NSURL *)writingFileURL
140
                  error:(NSError **)outError {
141
  // Archive a mutable copy `dictionary` for writing to disk. This is done for
142
  // backwards compatibility. See Google Utilities issue #36 for more context.
143
  // TODO: Remove usage of mutable copy in a future version of Google Utilities.
144
  NSData *data = [GULSecureCoding archivedDataWithRootObject:[dictionary mutableCopy]
145
                                                       error:outError];
146
  if (data.length == 0) {
147
    return NO;
148
  }
149
 
150
  return [data writeToURL:writingFileURL atomically:YES];
151
}
152
 
153
@end