FIRStorageMetadata.m 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. // Copyright 2017 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. #import "FIRStorageMetadata.h"
  15. #import "FIRStorageConstants.h"
  16. #import "FIRStorageConstants_Private.h"
  17. #import "FIRStorageMetadata_Private.h"
  18. #import "FIRStorageUtils.h"
  19. // TODO: consider rewriting this using GTLR (GTLRStorageObjects.h)
  20. @implementation FIRStorageMetadata
  21. #pragma mark - Initializers
  22. - (instancetype)init {
  23. return [self initWithDictionary:[NSDictionary dictionary]];
  24. }
  25. - (instancetype)initWithDictionary:(NSDictionary *)dictionary {
  26. self = [super init];
  27. if (self) {
  28. _initialMetadata = [dictionary copy];
  29. _bucket = dictionary[kFIRStorageMetadataBucket];
  30. _cacheControl = dictionary[kFIRStorageMetadataCacheControl];
  31. _contentDisposition = dictionary[kFIRStorageMetadataContentDisposition];
  32. _contentEncoding = dictionary[kFIRStorageMetadataContentEncoding];
  33. _contentLanguage = dictionary[kFIRStorageMetadataContentLanguage];
  34. _contentType = dictionary[kFIRStorageMetadataContentType];
  35. _customMetadata = dictionary[kFIRStorageMetadataCustomMetadata];
  36. _size = [dictionary[kFIRStorageMetadataSize] longLongValue];
  37. _downloadURLs = dictionary[kFIRStorageMetadataDownloadURLs];
  38. _generation = [dictionary[kFIRStorageMetadataGeneration] longLongValue];
  39. _metageneration = [dictionary[kFIRStorageMetadataMetageneration] longLongValue];
  40. _timeCreated = [self dateFromRFC3339String:dictionary[kFIRStorageMetadataTimeCreated]];
  41. _updated = [self dateFromRFC3339String:dictionary[kFIRStorageMetadataUpdated]];
  42. // GCS "name" is our path, our "name" is just the last path component of the path
  43. _path = dictionary[kFIRStorageMetadataName];
  44. _name = [_path lastPathComponent];
  45. NSString *downloadTokens = dictionary[kFIRStorageMetadataDownloadTokens];
  46. if (downloadTokens) {
  47. NSArray<NSString *> *downloadStringArray = [downloadTokens componentsSeparatedByString:@","];
  48. NSMutableArray<NSURL *> *downloadURLArray =
  49. [[NSMutableArray alloc] initWithCapacity:[downloadStringArray count]];
  50. [downloadStringArray enumerateObjectsUsingBlock:^(NSString *_Nonnull token, NSUInteger idx,
  51. BOOL *_Nonnull stop) {
  52. NSURLComponents *components = [[NSURLComponents alloc] init];
  53. components.scheme = kFIRStorageScheme;
  54. components.host = kFIRStorageHost;
  55. NSString *path = [FIRStorageUtils GCSEscapedString:_path];
  56. NSString *fullPath = [NSString stringWithFormat:kFIRStorageFullPathFormat, _bucket, path];
  57. components.percentEncodedPath = fullPath;
  58. components.query = [NSString stringWithFormat:@"alt=media&token=%@", token];
  59. [downloadURLArray insertObject:[components URL] atIndex:idx];
  60. }];
  61. _downloadURLs = downloadURLArray;
  62. }
  63. }
  64. return self;
  65. }
  66. #pragma mark - NSObject overrides
  67. - (instancetype)copyWithZone:(NSZone *)zone {
  68. FIRStorageMetadata *clone =
  69. [[[self class] allocWithZone:zone] initWithDictionary:[self dictionaryRepresentation]];
  70. clone.initialMetadata = [self.initialMetadata copy];
  71. return clone;
  72. }
  73. - (BOOL)isEqual:(id)object {
  74. if (self == object) {
  75. return YES;
  76. }
  77. if (![object isKindOfClass:[FIRStorageMetadata class]]) {
  78. return NO;
  79. }
  80. BOOL isEqualObject = [self isEqualToFIRStorageMetadata:(FIRStorageMetadata *)object];
  81. return isEqualObject;
  82. }
  83. - (BOOL)isEqualToFIRStorageMetadata:(FIRStorageMetadata *)metadata {
  84. return [[self dictionaryRepresentation] isEqualToDictionary:[metadata dictionaryRepresentation]];
  85. }
  86. - (NSUInteger)hash {
  87. NSUInteger hash = [[self dictionaryRepresentation] hash];
  88. return hash;
  89. }
  90. - (NSString *)description {
  91. NSDictionary *metadataDictionary = [self dictionaryRepresentation];
  92. return [NSString stringWithFormat:@"%@ %p: %@", [self class], self, metadataDictionary];
  93. }
  94. #pragma mark - Public methods
  95. - (NSDictionary *)dictionaryRepresentation {
  96. NSMutableDictionary *metadataDictionary = [[NSMutableDictionary alloc] initWithCapacity:13];
  97. if (_bucket) {
  98. metadataDictionary[kFIRStorageMetadataBucket] = _bucket;
  99. }
  100. if (_cacheControl) {
  101. metadataDictionary[kFIRStorageMetadataCacheControl] = _cacheControl;
  102. }
  103. if (_contentDisposition) {
  104. metadataDictionary[kFIRStorageMetadataContentDisposition] = _contentDisposition;
  105. }
  106. if (_contentEncoding) {
  107. metadataDictionary[kFIRStorageMetadataContentEncoding] = _contentEncoding;
  108. }
  109. if (_contentLanguage) {
  110. metadataDictionary[kFIRStorageMetadataContentLanguage] = _contentLanguage;
  111. }
  112. if (_contentType) {
  113. metadataDictionary[kFIRStorageMetadataContentType] = _contentType;
  114. }
  115. if (_customMetadata) {
  116. metadataDictionary[kFIRStorageMetadataCustomMetadata] = _customMetadata;
  117. }
  118. if (_downloadURLs) {
  119. NSMutableArray *downloadTokens = [[NSMutableArray alloc] init];
  120. [_downloadURLs
  121. enumerateObjectsUsingBlock:^(NSURL *_Nonnull URL, NSUInteger idx, BOOL *_Nonnull stop) {
  122. NSArray *queryItems = [URL.query componentsSeparatedByString:@"&"];
  123. [queryItems enumerateObjectsUsingBlock:^(NSString *queryString, NSUInteger idx,
  124. BOOL *_Nonnull stop) {
  125. NSString *key;
  126. NSString *value;
  127. NSScanner *scanner = [NSScanner scannerWithString:queryString];
  128. [scanner scanUpToString:@"=" intoString:&key];
  129. [scanner scanString:@"=" intoString:NULL];
  130. [scanner scanUpToString:@"\n" intoString:&value];
  131. if ([key isEqual:@"token"]) {
  132. [downloadTokens addObject:value];
  133. *stop = YES;
  134. }
  135. }];
  136. }];
  137. NSString *downloadTokenString = [downloadTokens componentsJoinedByString:@","];
  138. metadataDictionary[kFIRStorageMetadataDownloadTokens] = downloadTokenString;
  139. }
  140. if (_generation) {
  141. NSString *generationString = [NSString stringWithFormat:@"%lld", _generation];
  142. metadataDictionary[kFIRStorageMetadataGeneration] = generationString;
  143. }
  144. if (_metageneration) {
  145. NSString *metagenerationString = [NSString stringWithFormat:@"%lld", _metageneration];
  146. metadataDictionary[kFIRStorageMetadataMetageneration] = metagenerationString;
  147. }
  148. if (_timeCreated) {
  149. metadataDictionary[kFIRStorageMetadataTimeCreated] = [self RFC3339StringFromDate:_timeCreated];
  150. }
  151. if (_updated) {
  152. metadataDictionary[kFIRStorageMetadataUpdated] = [self RFC3339StringFromDate:_updated];
  153. }
  154. if (_path) {
  155. metadataDictionary[kFIRStorageMetadataName] = _path;
  156. }
  157. if (_size) {
  158. metadataDictionary[kFIRStorageMetadataSize] = [NSNumber numberWithLongLong:_size];
  159. }
  160. return [metadataDictionary copy];
  161. }
  162. - (BOOL)isFile {
  163. return _type == FIRStorageMetadataTypeFile;
  164. }
  165. - (BOOL)isFolder {
  166. return _type == FIRStorageMetadataTypeFolder;
  167. }
  168. - (nullable NSURL *)downloadURL {
  169. return [_downloadURLs firstObject];
  170. }
  171. #pragma mark - Private methods
  172. + (void)removeMatchingMetadata:(NSMutableDictionary *)metadata
  173. oldMetadata:(NSDictionary *)oldMetadata {
  174. for (NSString* metadataKey in [oldMetadata allKeys]) {
  175. id oldValue = [oldMetadata objectForKey:metadataKey];
  176. id newValue = [metadata objectForKey:metadataKey];
  177. if (oldValue && !newValue) {
  178. [metadata setObject:[NSNull null] forKey:metadataKey];
  179. } else if ([oldValue isKindOfClass:[NSString class]] &&
  180. [newValue isKindOfClass:[NSString class]]) {
  181. if ([oldValue isEqualToString:newValue]) {
  182. [metadata removeObjectForKey:metadataKey];
  183. }
  184. } else if ([oldValue isKindOfClass:[NSDictionary class]] &&
  185. [newValue isKindOfClass:[NSDictionary class]]) {
  186. NSMutableDictionary *nestedMetadata = [newValue mutableCopy];
  187. [self removeMatchingMetadata:nestedMetadata oldMetadata:oldValue];
  188. [metadata setObject:[nestedMetadata copy] forKey:metadataKey];
  189. }
  190. }
  191. }
  192. - (NSDictionary *)updatedMetadata {
  193. NSMutableDictionary *metadataUpdate = [[self dictionaryRepresentation] mutableCopy];
  194. [FIRStorageMetadata removeMatchingMetadata:metadataUpdate oldMetadata:_initialMetadata];
  195. return [metadataUpdate copy];
  196. }
  197. #pragma mark - RFC 3339 conversions
  198. static NSDateFormatter *sRFC3339DateFormatter;
  199. static void setupDateFormatterOnce(void) {
  200. static dispatch_once_t onceToken;
  201. dispatch_once(&onceToken, ^{
  202. sRFC3339DateFormatter = [[NSDateFormatter alloc] init];
  203. NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
  204. [sRFC3339DateFormatter setLocale:enUSPOSIXLocale];
  205. [sRFC3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSZZZZZ"];
  206. [sRFC3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
  207. });
  208. }
  209. - (nullable NSDate *)dateFromRFC3339String:(NSString *)dateString {
  210. setupDateFormatterOnce();
  211. NSDate *rfc3339Date = [sRFC3339DateFormatter dateFromString:dateString];
  212. return rfc3339Date;
  213. }
  214. - (nullable NSString *)RFC3339StringFromDate:(NSDate *)date {
  215. setupDateFormatterOnce();
  216. NSString *rfc3339String = [sRFC3339DateFormatter stringFromDate:date];
  217. return rfc3339String;
  218. }
  219. @end