FIRStorage.m 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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 "FIRStorage.h"
  15. #import "FIRStorageConstants_Private.h"
  16. #import "FIRStoragePath.h"
  17. #import "FIRStorageReference.h"
  18. #import "FIRStorageReference_Private.h"
  19. #import "FIRStorageTokenAuthorizer.h"
  20. #import "FIRStorageUtils.h"
  21. #import "FIRStorage_Private.h"
  22. #import <FirebaseCore/FIRApp.h>
  23. #import <FirebaseCore/FIROptions.h>
  24. #import <GTMSessionFetcher/GTMSessionFetcher.h>
  25. #import <GTMSessionFetcher/GTMSessionFetcherLogging.h>
  26. static NSMutableDictionary<
  27. NSString * /* app name */,
  28. NSMutableDictionary<NSString * /* bucket */, GTMSessionFetcherService *> *> *_fetcherServiceMap;
  29. static GTMSessionFetcherRetryBlock _retryWhenOffline;
  30. @implementation FIRStorage
  31. + (void)initialize {
  32. static dispatch_once_t onceToken;
  33. dispatch_once(&onceToken, ^{
  34. _retryWhenOffline = ^(BOOL suggestedWillRetry, NSError *GTM_NULLABLE_TYPE error,
  35. GTMSessionFetcherRetryResponse response) {
  36. bool shouldRetry = suggestedWillRetry;
  37. // GTMSessionFetcher does not consider being offline a retryable error, but we do, so we
  38. // special-case it here.
  39. if (!shouldRetry && error) {
  40. shouldRetry = error.code == NSURLErrorNotConnectedToInternet;
  41. }
  42. response(shouldRetry);
  43. };
  44. _fetcherServiceMap = [[NSMutableDictionary alloc] init];
  45. });
  46. }
  47. + (GTMSessionFetcherService *)fetcherServiceForApp:(FIRApp *)app bucket:(NSString *)bucket {
  48. @synchronized(_fetcherServiceMap) {
  49. NSMutableDictionary *bucketMap = _fetcherServiceMap[app.name];
  50. if (!bucketMap) {
  51. bucketMap = [[NSMutableDictionary alloc] init];
  52. _fetcherServiceMap[app.name] = bucketMap;
  53. }
  54. GTMSessionFetcherService *fetcherService = bucketMap[bucket];
  55. if (!fetcherService) {
  56. fetcherService = [[GTMSessionFetcherService alloc] init];
  57. [fetcherService setRetryEnabled:YES];
  58. [fetcherService setRetryBlock:_retryWhenOffline];
  59. FIRStorageTokenAuthorizer *authorizer =
  60. [[FIRStorageTokenAuthorizer alloc] initWithApp:app fetcherService:fetcherService];
  61. [fetcherService setAuthorizer:authorizer];
  62. bucketMap[bucket] = fetcherService;
  63. }
  64. return fetcherService;
  65. }
  66. }
  67. + (void)setGTMSessionFetcherLoggingEnabled:(BOOL)isLoggingEnabled {
  68. [GTMSessionFetcher setLoggingEnabled:isLoggingEnabled];
  69. }
  70. + (instancetype)storage {
  71. return [self storageForApp:[FIRApp defaultApp]];
  72. }
  73. + (instancetype)storageForApp:(FIRApp *)app {
  74. NSString *url;
  75. if (app.options.storageBucket) {
  76. url = [app.options.storageBucket isEqualToString:@""]
  77. ? @""
  78. : [@"gs://" stringByAppendingString:app.options.storageBucket];
  79. }
  80. return [self storageForApp:app URL:url];
  81. }
  82. + (instancetype)storageWithURL:(NSString *)url {
  83. return [self storageForApp:[FIRApp defaultApp] URL:url];
  84. }
  85. + (instancetype)storageForApp:(FIRApp *)app URL:(NSString *)url {
  86. if (!url) {
  87. NSString *const kAppNotConfiguredMessage =
  88. @"No default Storage bucket found. Did you configure Firebase Storage properly?";
  89. [NSException raise:NSInvalidArgumentException format:kAppNotConfiguredMessage];
  90. }
  91. NSString *bucket;
  92. if ([url isEqualToString:@""]) {
  93. bucket = @"";
  94. } else {
  95. FIRStoragePath *path;
  96. @try {
  97. path = [FIRStoragePath pathFromGSURI:url];
  98. } @catch (NSException *e) {
  99. [NSException raise:NSInternalInconsistencyException
  100. format:@"URI must be in the form of gs://<bucket>/"];
  101. }
  102. if (path.object != nil && ![path.object isEqualToString:@""]) {
  103. [NSException raise:NSInternalInconsistencyException
  104. format:@"Storage bucket cannot be initialized with a path"];
  105. }
  106. bucket = path.bucket;
  107. }
  108. return [[self alloc] initWithApp:app bucket:bucket];
  109. }
  110. - (instancetype)initWithApp:(FIRApp *)app bucket:(NSString *)bucket {
  111. self = [super init];
  112. if (self) {
  113. _app = app;
  114. _storageBucket = bucket;
  115. _fetcherServiceForApp = [FIRStorage fetcherServiceForApp:_app bucket:bucket];
  116. _maxDownloadRetryTime = 600.0;
  117. _maxOperationRetryTime = 120.0;
  118. _maxUploadRetryTime = 600.0;
  119. }
  120. return self;
  121. }
  122. #pragma mark - NSObject overrides
  123. - (instancetype)copyWithZone:(NSZone *)zone {
  124. FIRStorage *storage = [[[self class] allocWithZone:zone] initWithApp:_app bucket:_storageBucket];
  125. storage.callbackQueue = _callbackQueue;
  126. return storage;
  127. }
  128. // Two FIRStorage objects are equal if they use the same app
  129. - (BOOL)isEqual:(id)object {
  130. if (self == object) {
  131. return YES;
  132. }
  133. if (![object isKindOfClass:[FIRStorage class]]) {
  134. return NO;
  135. }
  136. BOOL isEqualObject = [self isEqualToFIRStorage:(FIRStorage *)object];
  137. return isEqualObject;
  138. }
  139. - (BOOL)isEqualToFIRStorage:(FIRStorage *)storage {
  140. BOOL isEqual = [_app isEqual:storage->_app];
  141. return isEqual;
  142. }
  143. - (NSUInteger)hash {
  144. NSUInteger hash = [_app hash] ^ [_callbackQueue hash];
  145. return hash;
  146. }
  147. - (NSString *)description {
  148. return [NSString stringWithFormat:@"%@ %p: %@", [self class], self, _app];
  149. }
  150. #pragma mark - Public methods
  151. - (FIRStorageReference *)reference {
  152. FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:_storageBucket object:nil];
  153. return [[FIRStorageReference alloc] initWithStorage:self path:path];
  154. }
  155. - (FIRStorageReference *)referenceForURL:(NSString *)string {
  156. FIRStoragePath *path = [FIRStoragePath pathFromString:string];
  157. // If no default bucket exists (empty string), accept anything.
  158. if ([_storageBucket isEqual:@""]) {
  159. FIRStorageReference *reference = [[FIRStorageReference alloc] initWithStorage:self path:path];
  160. return reference;
  161. }
  162. // If there exists a default bucket, throw if provided a different bucket.
  163. if (![path.bucket isEqual:_storageBucket]) {
  164. NSString *const kInvalidBucketFormat =
  165. @"Provided bucket: %@ does not match the Storage bucket of the current instance: %@";
  166. [NSException raise:NSInvalidArgumentException
  167. format:kInvalidBucketFormat, path.bucket, _storageBucket];
  168. }
  169. FIRStorageReference *reference = [[FIRStorageReference alloc] initWithStorage:self path:path];
  170. return reference;
  171. }
  172. - (FIRStorageReference *)referenceWithPath:(NSString *)string {
  173. FIRStorageReference *reference = [[self reference] child:string];
  174. return reference;
  175. }
  176. - (void)setCallbackQueue:(dispatch_queue_t)callbackQueue {
  177. _fetcherServiceForApp.callbackQueue = callbackQueue;
  178. }
  179. #pragma mark - Background tasks
  180. + (void)enableBackgroundTasks:(BOOL)isEnabled {
  181. [NSException raise:NSGenericException format:@"enableBackgroundTasks not implemented"];
  182. }
  183. - (NSArray<FIRStorageUploadTask *> *)uploadTasks {
  184. [NSException raise:NSGenericException format:@"getUploadTasks not implemented"];
  185. return nil;
  186. }
  187. - (NSArray<FIRStorageDownloadTask *> *)downloadTasks {
  188. [NSException raise:NSGenericException format:@"getDownloadTasks not implemented"];
  189. return nil;
  190. }
  191. @end