GULHeartbeatDateStorage.m 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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. #import <GoogleUtilities/GULHeartbeatDateStorage.h>
  17. #import <GoogleUtilities/GULSecureCoding.h>
  18. @interface GULHeartbeatDateStorage ()
  19. /** The storage to store the date of the last sent heartbeat. */
  20. @property(nonatomic, readonly) NSFileCoordinator *fileCoordinator;
  21. @end
  22. @implementation GULHeartbeatDateStorage
  23. - (instancetype)initWithFileName:(NSString *)fileName {
  24. if (fileName == nil) {
  25. return nil;
  26. }
  27. self = [super init];
  28. if (self) {
  29. _fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
  30. NSURL *directoryURL = [[self class] directoryPathURL];
  31. [[self class] checkAndCreateDirectory:directoryURL fileCoordinator:_fileCoordinator];
  32. _fileURL = [directoryURL URLByAppendingPathComponent:fileName];
  33. }
  34. return self;
  35. }
  36. /** Returns the URL path of the Application Support folder.
  37. * @return the URL path of Application Support.
  38. */
  39. + (NSURL *)directoryPathURL {
  40. NSArray<NSString *> *paths =
  41. NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
  42. NSArray<NSString *> *components = @[ paths.lastObject, @"Google/FIRApp" ];
  43. NSString *directoryString = [NSString pathWithComponents:components];
  44. NSURL *directoryURL = [NSURL fileURLWithPath:directoryString];
  45. return directoryURL;
  46. }
  47. /** Checks and creates a directory for the directory specified by the
  48. * directory url
  49. * @param directoryPathURL The path to the directory which needs to be created.
  50. * @param fileCoordinator The fileCoordinator object to coordinate writes to the directory.
  51. */
  52. + (void)checkAndCreateDirectory:(NSURL *)directoryPathURL
  53. fileCoordinator:(NSFileCoordinator *)fileCoordinator {
  54. NSError *fileCoordinatorError = nil;
  55. [fileCoordinator
  56. coordinateWritingItemAtURL:directoryPathURL
  57. options:0
  58. error:&fileCoordinatorError
  59. byAccessor:^(NSURL *writingDirectoryURL) {
  60. NSError *error;
  61. if (![writingDirectoryURL checkResourceIsReachableAndReturnError:&error]) {
  62. // If fail creating the Application Support directory, log warning.
  63. NSError *error;
  64. [[NSFileManager defaultManager] createDirectoryAtURL:writingDirectoryURL
  65. withIntermediateDirectories:YES
  66. attributes:nil
  67. error:&error];
  68. }
  69. }];
  70. }
  71. - (nullable NSMutableDictionary *)heartbeatDictionaryWithFileURL:(NSURL *)readingFileURL {
  72. NSError *error;
  73. NSMutableDictionary *dict;
  74. NSData *objectData = [NSData dataWithContentsOfURL:readingFileURL options:0 error:&error];
  75. if (objectData == nil || error != nil) {
  76. dict = [NSMutableDictionary dictionary];
  77. } else {
  78. dict = [GULSecureCoding
  79. unarchivedObjectOfClasses:[NSSet setWithArray:@[ NSDictionary.class, NSDate.class ]]
  80. fromData:objectData
  81. error:&error];
  82. if (dict == nil || error != nil) {
  83. dict = [NSMutableDictionary dictionary];
  84. }
  85. }
  86. return dict;
  87. }
  88. - (nullable NSDate *)heartbeatDateForTag:(NSString *)tag {
  89. __block NSMutableDictionary *dict;
  90. NSError *error;
  91. [self.fileCoordinator coordinateReadingItemAtURL:self.fileURL
  92. options:0
  93. error:&error
  94. byAccessor:^(NSURL *readingURL) {
  95. dict = [self heartbeatDictionaryWithFileURL:readingURL];
  96. }];
  97. return dict[tag];
  98. }
  99. - (BOOL)setHearbeatDate:(NSDate *)date forTag:(NSString *)tag {
  100. NSError *error;
  101. __block BOOL isSuccess = false;
  102. [self.fileCoordinator coordinateReadingItemAtURL:self.fileURL
  103. options:0
  104. writingItemAtURL:self.fileURL
  105. options:0
  106. error:&error
  107. byAccessor:^(NSURL *readingURL, NSURL *writingURL) {
  108. NSMutableDictionary *dictionary =
  109. [self heartbeatDictionaryWithFileURL:readingURL];
  110. dictionary[tag] = date;
  111. NSError *error;
  112. isSuccess = [self writeDictionary:dictionary
  113. forWritingURL:writingURL
  114. error:&error];
  115. }];
  116. return isSuccess;
  117. }
  118. - (BOOL)writeDictionary:(NSMutableDictionary *)dictionary
  119. forWritingURL:(NSURL *)writingFileURL
  120. error:(NSError **)outError {
  121. NSData *data = [GULSecureCoding archivedDataWithRootObject:dictionary error:outError];
  122. if (*outError != nil) {
  123. return false;
  124. } else {
  125. return [data writeToURL:writingFileURL atomically:YES];
  126. }
  127. }
  128. @end