@react-native-firebase/storage 26.3.3 → 27.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/RNFBStorage.podspec +9 -4
  3. package/android/build.gradle +6 -0
  4. package/android/src/main/java/io/invertase/firebase/storage/NativeRNFBTurboStorage.java +24 -4
  5. package/android/src/main/java/io/invertase/firebase/storage/RNFBStorageTaskRegistry.kt +88 -0
  6. package/android/src/main/java/io/invertase/firebase/storage/ReactNativeFirebaseStorageTask.java +25 -46
  7. package/android/src/main/java/io/invertase/firebase/storage/StoragePendingHandle.java +30 -0
  8. package/android/src/test/java/io/invertase/firebase/storage/RNFBStorageTaskRegistryTest.java +237 -0
  9. package/android/src/test/java/io/invertase/firebase/storage/ReactNativeFirebaseStorageTaskTest.java +285 -0
  10. package/dist/module/index.js +1 -2
  11. package/dist/module/index.js.map +1 -1
  12. package/dist/module/version.js +1 -1
  13. package/dist/typescript/lib/index.d.ts +1 -1
  14. package/dist/typescript/lib/types/internal.d.ts +2 -3
  15. package/dist/typescript/lib/types/internal.d.ts.map +1 -1
  16. package/dist/typescript/lib/version.d.ts +1 -1
  17. package/ios/RNFBStorage/RNFBStorageCommon.m +17 -98
  18. package/ios/RNFBStorage/RNFBStorageErrorCodeMapper.swift +122 -0
  19. package/ios/RNFBStorage/RNFBStorageErrorSnapshotDictBuilder.swift +70 -0
  20. package/ios/RNFBStorage/RNFBStorageHelper.m +38 -13
  21. package/ios/RNFBStorage/RNFBStorageTaskRegistry.h +42 -0
  22. package/ios/RNFBStorage/RNFBStorageTaskRegistry.m +87 -0
  23. package/ios/RNFBStorage/RNFBStorageTaskRegistryStorage.swift +87 -0
  24. package/ios/RNFBStorage/RNFBStorageTaskStatusMapper.swift +57 -0
  25. package/ios/RNFBStorage.xcodeproj/project.pbxproj +26 -0
  26. package/ios/RNFBStorageUnitTests/RNFBStorageErrorCodeMapperFacadeTests.m +91 -0
  27. package/ios/RNFBStorageUnitTests/RNFBStorageErrorCodeMapperTests.swift +211 -0
  28. package/ios/RNFBStorageUnitTests/RNFBStorageErrorSnapshotDictBuilderFacadeTests.m +125 -0
  29. package/ios/RNFBStorageUnitTests/RNFBStorageTaskRegistryStorageTests.swift +154 -0
  30. package/ios/RNFBStorageUnitTests/RNFBStorageTaskRegistryTests.m +225 -0
  31. package/ios/RNFBStorageUnitTests/RNFBStorageTaskStatusMapperFacadeTests.m +52 -0
  32. package/ios/RNFBStorageUnitTests/RNFBStorageTaskStatusMapperTests.swift +58 -0
  33. package/ios/RNFBStorageUnitTests/RNFBStorageUnitTests.xcodeproj/project.pbxproj +301 -0
  34. package/ios/RNFBStorageUnitTests/RNFBStorageUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBStorageUnitTests.xcscheme +69 -0
  35. package/lib/index.ts +1 -1
  36. package/lib/types/internal.ts +5 -4
  37. package/lib/version.ts +1 -1
  38. package/package.json +5 -4
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library 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 Foundation
18
+
19
+ /**
20
+ * `FIRStorageErrorCode` raw value → JS error-code / message mapping previously
21
+ * inline in `+[RNFBStorageCommon getErrorCodeMessage:]`.
22
+ *
23
+ * Raw ints from Firebase Storage `StorageErrorCode`
24
+ * (`@objc(FIRStorageErrorCode)` in `StorageError.swift`):
25
+ * - unknown = -13000, objectNotFound = -13010, …
26
+ *
27
+ * Hardcoded so the Storage unit target does not need FirebaseStorage.
28
+ */
29
+ @objc(RNFBStorageErrorCodeMapper)
30
+ public final class RNFBStorageErrorCodeMapper: NSObject {
31
+ /// `FIRStorageErrorCodeUnknown`
32
+ private static let codeUnknown = -13000
33
+ /// `FIRStorageErrorCodeObjectNotFound`
34
+ private static let codeObjectNotFound = -13010
35
+ /// `FIRStorageErrorCodeBucketNotFound`
36
+ private static let codeBucketNotFound = -13011
37
+ /// `FIRStorageErrorCodeProjectNotFound`
38
+ private static let codeProjectNotFound = -13012
39
+ /// `FIRStorageErrorCodeQuotaExceeded`
40
+ private static let codeQuotaExceeded = -13013
41
+ /// `FIRStorageErrorCodeUnauthenticated`
42
+ private static let codeUnauthenticated = -13020
43
+ /// `FIRStorageErrorCodeUnauthorized`
44
+ private static let codeUnauthorized = -13021
45
+ /// `FIRStorageErrorCodeRetryLimitExceeded`
46
+ private static let codeRetryLimitExceeded = -13030
47
+ /// `FIRStorageErrorCodeNonMatchingChecksum`
48
+ private static let codeNonMatchingChecksum = -13031
49
+ /// `FIRStorageErrorCodeDownloadSizeExceeded`
50
+ private static let codeDownloadSizeExceeded = -13032
51
+ /// `FIRStorageErrorCodeCancelled`
52
+ private static let codeCancelled = -13040
53
+
54
+ /// Exact pre-port string (U+2019 apostrophe in "couldn't").
55
+ private static let permissionDeniedUnderlying =
56
+ "The operation couldn’t be completed. Permission denied"
57
+
58
+ @objc(codeAndMessageForError:)
59
+ public static func codeAndMessage(for error: NSError?) -> [String] {
60
+ var code = "unknown"
61
+
62
+ guard let error else {
63
+ return [code, "An unknown error has occurred."]
64
+ }
65
+
66
+ var message = error.localizedDescription
67
+ let userInfo = error.userInfo
68
+ let underlyingError = userInfo[NSUnderlyingErrorKey] as? NSError
69
+ let underlyingErrorDescription = underlyingError?.localizedDescription
70
+
71
+ switch error.code {
72
+ case codeUnknown:
73
+ if underlyingErrorDescription == permissionDeniedUnderlying {
74
+ code = "invalid-device-file-path"
75
+ message = "The specified device file path is invalid or is restricted."
76
+ } else if let responseBody = userInfo["ResponseBody"] {
77
+ message =
78
+ "An unknown error has occurred. (underlying reason '\(responseBody)')"
79
+ } else {
80
+ message = "An unknown error has occurred."
81
+ }
82
+ case codeObjectNotFound:
83
+ code = "object-not-found"
84
+ message = "No object exists at the desired reference."
85
+ case codeBucketNotFound:
86
+ code = "bucket-not-found"
87
+ message = "No bucket is configured for Firebase Storage."
88
+ case codeProjectNotFound:
89
+ code = "project-not-found"
90
+ message = "No project is configured for Firebase Storage."
91
+ case codeQuotaExceeded:
92
+ code = "quota-exceeded"
93
+ message = "Quota on your Firebase Storage bucket has been exceeded."
94
+ case codeUnauthenticated:
95
+ code = "unauthenticated"
96
+ message = "User is unauthenticated. Authenticate and try again."
97
+ case codeUnauthorized:
98
+ code = "unauthorized"
99
+ message = "User is not authorized to perform the desired action."
100
+ case codeRetryLimitExceeded:
101
+ code = "retry-limit-exceeded"
102
+ message =
103
+ "The maximum time limit on an operation (upload, download, delete, etc.) has been "
104
+ + "exceeded."
105
+ case codeNonMatchingChecksum:
106
+ code = "non-matching-checksum"
107
+ message =
108
+ "File on the client does not match the checksum of the file received by the server."
109
+ case codeDownloadSizeExceeded:
110
+ code = "download-size-exceeded"
111
+ message =
112
+ "Size of the downloaded file exceeds the amount of memory allocated for the download."
113
+ case codeCancelled:
114
+ code = "cancelled"
115
+ message = "User cancelled the operation."
116
+ default:
117
+ break
118
+ }
119
+
120
+ return [code, message]
121
+ }
122
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library 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 Foundation
18
+
19
+ /**
20
+ * Error payload attachment previously inline in
21
+ * `+[RNFBStorageCommon buildErrorSnapshotDict:taskSnapshotDict:]` and
22
+ * `+[RNFBStorageCommon buildErrorSnapshotDictFromCodeAndMessage:taskSnapshotDict:]`.
23
+ *
24
+ * Mutates `taskSnapshotDict` in place and returns the same instance (or nil when
25
+ * the snapshot dict is nil — matching ObjC nil-messaging).
26
+ */
27
+ @objc(RNFBStorageErrorSnapshotDictBuilder)
28
+ public final class RNFBStorageErrorSnapshotDictBuilder: NSObject {
29
+ @objc(buildErrorSnapshotDict:taskSnapshotDict:)
30
+ public static func buildErrorSnapshotDict(
31
+ _ error: NSError?,
32
+ taskSnapshotDict: NSMutableDictionary?
33
+ ) -> NSDictionary? {
34
+ guard let taskSnapshotDict else {
35
+ return nil
36
+ }
37
+
38
+ let codeAndMessage = RNFBStorageErrorCodeMapper.codeAndMessage(for: error)
39
+ var errorPayload: [String: Any] = [
40
+ "code": codeAndMessage[0],
41
+ "message": codeAndMessage[1],
42
+ ]
43
+ // Pre-port ObjC always set this key from `[error localizedDescription]`.
44
+ if let error {
45
+ errorPayload["nativeErrorMessage"] = error.localizedDescription
46
+ } else {
47
+ errorPayload["nativeErrorMessage"] = NSNull()
48
+ }
49
+ taskSnapshotDict["error"] = errorPayload
50
+ return taskSnapshotDict
51
+ }
52
+
53
+ @objc(buildErrorSnapshotDictFromCodeAndMessage:taskSnapshotDict:)
54
+ public static func buildErrorSnapshotDictFromCodeAndMessage(
55
+ _ codeAndMessage: [Any]?,
56
+ taskSnapshotDict: NSMutableDictionary?
57
+ ) -> NSDictionary? {
58
+ guard let taskSnapshotDict else {
59
+ return nil
60
+ }
61
+
62
+ // Pre-port ObjC indexed without bounds checks; callers always pass [code, message].
63
+ let pair = codeAndMessage!
64
+ taskSnapshotDict["error"] = [
65
+ "code": pair[0],
66
+ "message": pair[1],
67
+ ]
68
+ return taskSnapshotDict
69
+ }
70
+ }
@@ -28,6 +28,7 @@
28
28
  #import "RNFBRCTEventEmitter.h"
29
29
  #import "RNFBStorageCommon.h"
30
30
  #import "RNFBStorageHelper.h"
31
+ #import "RNFBStorageTaskRegistry.h"
31
32
 
32
33
  static NSString *const RNFB_STORAGE_EVENT = @"storage_event";
33
34
  static NSString *const RNFB_STORAGE_STATE_CHANGED = @"state_changed";
@@ -36,7 +37,7 @@ static NSString *const RNFB_STORAGE_UPLOAD_FAILURE = @"upload_failure";
36
37
  static NSString *const RNFB_STORAGE_DOWNLOAD_SUCCESS = @"download_success";
37
38
  static NSString *const RNFB_STORAGE_DOWNLOAD_FAILURE = @"download_failure";
38
39
 
39
- static NSMutableDictionary *PENDING_TASKS;
40
+ static RNFBStorageTaskRegistry *pendingTasks;
40
41
 
41
42
  // The iOS SDK has a short memory on settings, store these globally and set them in each time
42
43
  static NSString *emulatorHost = nil;
@@ -54,16 +55,14 @@ static NSTimeInterval maxOperationRetryTime = 120;
54
55
  + (void)initializeSharedStateOnce {
55
56
  static dispatch_once_t onceToken;
56
57
  dispatch_once(&onceToken, ^{
57
- PENDING_TASKS = [[NSMutableDictionary alloc] init];
58
+ pendingTasks = [[RNFBStorageTaskRegistry alloc] init];
58
59
  emulatorConfigs = [[NSMutableDictionary alloc] init];
59
60
  });
60
61
  }
61
62
 
62
63
  + (void)invalidate {
63
64
  [self initializeSharedStateOnce];
64
- for (NSString *key in [PENDING_TASKS allKeys]) {
65
- [PENDING_TASKS removeObjectForKey:key];
66
- }
65
+ [pendingTasks cancelAll];
67
66
  }
68
67
 
69
68
  #pragma mark -
@@ -283,7 +282,10 @@ static NSTimeInterval maxOperationRetryTime = 120;
283
282
  downloadTask = [storageReference writeToFile:localFile];
284
283
  });
285
284
 
286
- PENDING_TASKS[taskIdNumber] = downloadTask;
285
+ if (![pendingTasks putOrDiscard:taskIdNumber value:downloadTask]) {
286
+ reject(@"internal", @"Handle id already registered", nil);
287
+ return;
288
+ }
287
289
 
288
290
  [downloadTask
289
291
  observeStatus:FIRStorageTaskStatusResume
@@ -324,7 +326,10 @@ static NSTimeInterval maxOperationRetryTime = 120;
324
326
  [downloadTask
325
327
  observeStatus:FIRStorageTaskStatusSuccess
326
328
  handler:^(FIRStorageTaskSnapshot *snapshot) {
327
- [PENDING_TASKS removeObjectForKey:taskIdNumber];
329
+ [pendingTasks takeIf:taskIdNumber
330
+ when:^BOOL(id value) {
331
+ return value == downloadTask;
332
+ }];
328
333
 
329
334
  NSDictionary *stateChangedEventBody =
330
335
  [RNFBStorageCommon getDownloadTaskAsDictionary:snapshot];
@@ -349,7 +354,10 @@ static NSTimeInterval maxOperationRetryTime = 120;
349
354
  [downloadTask
350
355
  observeStatus:FIRStorageTaskStatusFailure
351
356
  handler:^(FIRStorageTaskSnapshot *snapshot) {
352
- [PENDING_TASKS removeObjectForKey:taskIdNumber];
357
+ [pendingTasks takeIf:taskIdNumber
358
+ when:^BOOL(id value) {
359
+ return value == downloadTask;
360
+ }];
353
361
 
354
362
  NSDictionary *stateChangedEventBody =
355
363
  [RNFBStorageCommon getDownloadTaskAsDictionary:snapshot];
@@ -501,7 +509,7 @@ static NSTimeInterval maxOperationRetryTime = 120;
501
509
  + (NSNumber *)setTaskStatus:(NSString *)appName taskId:(double)taskId status:(double)status {
502
510
  [self initializeSharedStateOnce];
503
511
  NSNumber *taskIdNumber = @(taskId);
504
- id task = PENDING_TASKS[taskIdNumber];
512
+ id task = [pendingTasks get:taskIdNumber];
505
513
  if (task == nil) {
506
514
  return @NO;
507
515
  }
@@ -546,7 +554,15 @@ static NSTimeInterval maxOperationRetryTime = 120;
546
554
  (currentStatus == FIRStorageTaskStatusResume ||
547
555
  currentStatus == FIRStorageTaskStatusProgress ||
548
556
  currentStatus == FIRStorageTaskStatusPause)) {
549
- [PENDING_TASKS removeObjectForKey:taskIdNumber];
557
+ // Identity take then cancel so a replacement at taskId is not stolen.
558
+ // Ordering differs from registry takeAndCancel: (takeIf → cancel) vs Android
559
+ // (get → cancel → takeIf). Both are identity-safe; FIRStorage cancel is void.
560
+ if ([pendingTasks takeIf:taskIdNumber
561
+ when:^BOOL(id value) {
562
+ return value == task;
563
+ }] == nil) {
564
+ return @NO;
565
+ }
550
566
  if ([task isKindOfClass:[FIRStorageDownloadTask class]]) {
551
567
  [(FIRStorageDownloadTask *)task cancel];
552
568
  } else {
@@ -595,7 +611,10 @@ static NSTimeInterval maxOperationRetryTime = 120;
595
611
  resolver:(RCTPromiseResolveBlock)resolve
596
612
  rejecter:(RCTPromiseRejectBlock)reject {
597
613
  [self initializeSharedStateOnce];
598
- PENDING_TASKS[taskId] = uploadTask;
614
+ if (![pendingTasks putOrDiscard:taskId value:uploadTask]) {
615
+ reject(@"internal", @"Handle id already registered", nil);
616
+ return;
617
+ }
599
618
 
600
619
  [uploadTask
601
620
  observeStatus:FIRStorageTaskStatusResume
@@ -635,7 +654,10 @@ static NSTimeInterval maxOperationRetryTime = 120;
635
654
 
636
655
  [uploadTask observeStatus:FIRStorageTaskStatusSuccess
637
656
  handler:^(FIRStorageTaskSnapshot *snapshot) {
638
- [PENDING_TASKS removeObjectForKey:taskId];
657
+ [pendingTasks takeIf:taskId
658
+ when:^BOOL(id value) {
659
+ return value == uploadTask;
660
+ }];
639
661
 
640
662
  NSDictionary *eventBody =
641
663
  [RNFBStorageCommon getUploadTaskAsDictionary:snapshot];
@@ -661,7 +683,10 @@ static NSTimeInterval maxOperationRetryTime = 120;
661
683
  [uploadTask
662
684
  observeStatus:FIRStorageTaskStatusFailure
663
685
  handler:^(FIRStorageTaskSnapshot *snapshot) {
664
- [PENDING_TASKS removeObjectForKey:taskId];
686
+ [pendingTasks takeIf:taskId
687
+ when:^BOOL(id value) {
688
+ return value == uploadTask;
689
+ }];
665
690
 
666
691
  NSMutableDictionary *taskSnapshotDict =
667
692
  [RNFBStorageCommon getUploadTaskAsDictionary:snapshot];
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library 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
+
18
+ #import <Foundation/Foundation.h>
19
+
20
+ NS_ASSUME_NONNULL_BEGIN
21
+
22
+ /**
23
+ * Pending upload/download task map. Unique `put`; callers `take` or `cancelAll` then cancel
24
+ * outside the HandleMap lock. Stored values are expected to respond to `cancel`.
25
+ */
26
+ @interface RNFBStorageTaskRegistry : NSObject
27
+
28
+ - (BOOL)put:(id)key value:(id)value error:(NSError *_Nullable *_Nullable)error;
29
+ /// Unique put. On collision, `cancel`s the incoming value and leaves the existing mapping.
30
+ - (BOOL)putOrDiscard:(id)key value:(id)value;
31
+ - (nullable id)get:(id)key;
32
+ - (nullable id)take:(id)key;
33
+ /// Identity-gated take; see RNFBHandleMap `takeIf:when:`.
34
+ - (nullable id)takeIf:(id)key when:(BOOL (^)(id value))condition;
35
+ /// get → cancel → identity take (Android shape). FIRStorage cancel is void (no keep-on-false).
36
+ /// Production setTaskStatus cancel uses takeIf then cancel; see RNFBStorageHelper.
37
+ - (BOOL)takeAndCancel:(id)key;
38
+ - (void)cancelAll;
39
+
40
+ @end
41
+
42
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library 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
+
18
+ #import "RNFBStorageTaskRegistry.h"
19
+
20
+ #if __has_include("RNFBHandleMap.h")
21
+ #import "RNFBHandleMap.h"
22
+ #else
23
+ #import "RNFBApp/RNFBHandleMap.h"
24
+ #endif
25
+
26
+ #if __has_include(<RNFBStorage/RNFBStorage-Swift.h>)
27
+ #import <RNFBStorage/RNFBStorage-Swift.h>
28
+ #elif __has_include("RNFBStorage-Swift.h")
29
+ #import "RNFBStorage-Swift.h"
30
+ #elif __has_include("RNFBHandleMapStorage-Swift.inc")
31
+ #import "RNFBHandleMapStorage-Swift.inc"
32
+ #else
33
+ #error "RNFBStorageTaskRegistryStorage Swift interface not found"
34
+ #endif
35
+
36
+ @interface RNFBStorageTaskRegistry ()
37
+ @property(nonatomic, strong) RNFBStorageTaskRegistryStorage *storage;
38
+ @end
39
+
40
+ @implementation RNFBStorageTaskRegistry
41
+
42
+ - (instancetype)init {
43
+ self = [super init];
44
+ if (self) {
45
+ _storage = [[RNFBStorageTaskRegistryStorage alloc] init];
46
+ }
47
+ return self;
48
+ }
49
+
50
+ - (BOOL)put:(id)key value:(id)value error:(NSError **)error {
51
+ if (![self.storage putIfAbsent:key value:value]) {
52
+ if (error != nil) {
53
+ NSString *message = [NSString stringWithFormat:@"Handle id already registered: %@", key];
54
+ *error = [NSError errorWithDomain:RNFBHandleMapErrorDomain
55
+ code:RNFBHandleMapErrorCollision
56
+ userInfo:@{NSLocalizedDescriptionKey : message}];
57
+ }
58
+ return NO;
59
+ }
60
+ return YES;
61
+ }
62
+
63
+ - (BOOL)putOrDiscard:(id)key value:(id)value {
64
+ return [self.storage putOrDiscard:key value:value];
65
+ }
66
+
67
+ - (id)get:(id)key {
68
+ return [self.storage get:key];
69
+ }
70
+
71
+ - (id)take:(id)key {
72
+ return [self.storage take:key];
73
+ }
74
+
75
+ - (id)takeIf:(id)key when:(BOOL (^)(id))condition {
76
+ return [self.storage takeIf:key when:condition];
77
+ }
78
+
79
+ - (BOOL)takeAndCancel:(id)key {
80
+ return [self.storage takeAndCancel:key];
81
+ }
82
+
83
+ - (void)cancelAll {
84
+ [self.storage cancelAll];
85
+ }
86
+
87
+ @end
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library 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 Foundation
18
+
19
+ #if canImport(RNFBApp)
20
+ import RNFBApp
21
+ #endif
22
+
23
+ /// Pending upload/download task map storage. Composes `RNFBHandleMapStorage`.
24
+ /// `putOrDiscard` is first-wins and calls `cancel` on the incoming value on collision.
25
+ /// Unique `put` collision errors stay on the ObjC façade.
26
+ @objc(RNFBStorageTaskRegistryStorage)
27
+ public final class RNFBStorageTaskRegistryStorage: NSObject {
28
+ private static let cancelSelector = Selector(("cancel"))
29
+
30
+ private let storage = RNFBHandleMapStorage()
31
+
32
+ private func cancelHandle(_ handle: AnyObject?) {
33
+ guard let handle = handle, handle.responds(to: Self.cancelSelector) else {
34
+ return
35
+ }
36
+ _ = handle.perform(Self.cancelSelector)
37
+ }
38
+
39
+ /// First-wins store without discarding the incoming value. Used by façade `put`.
40
+ @objc(putIfAbsent:value:)
41
+ public func putIfAbsent(_ key: AnyObject, value: AnyObject) -> Bool {
42
+ storage.putIfAbsent(key, value: value)
43
+ }
44
+
45
+ /// Unique put. On collision, `cancel`s the incoming value and leaves the existing mapping.
46
+ @objc(putOrDiscard:value:)
47
+ public func putOrDiscard(_ key: AnyObject, value: AnyObject) -> Bool {
48
+ if storage.putIfAbsent(key, value: value) {
49
+ return true
50
+ }
51
+ cancelHandle(value)
52
+ return false
53
+ }
54
+
55
+ @objc(get:)
56
+ public func get(_ key: AnyObject) -> AnyObject? {
57
+ storage.get(key)
58
+ }
59
+
60
+ @objc(take:)
61
+ public func take(_ key: AnyObject) -> AnyObject? {
62
+ storage.take(key)
63
+ }
64
+
65
+ @objc(takeIf:when:)
66
+ public func takeIf(_ key: AnyObject, when condition: (AnyObject) -> Bool) -> AnyObject? {
67
+ storage.takeIf(key, when: condition)
68
+ }
69
+
70
+ /// get → cancel → identity take (Android shape). FIRStorage cancel is void (no keep-on-false).
71
+ @objc(takeAndCancel:)
72
+ public func takeAndCancel(_ key: AnyObject) -> Bool {
73
+ guard let handle = storage.get(key) else {
74
+ return false
75
+ }
76
+ cancelHandle(handle)
77
+ _ = storage.takeIf(key, when: { $0 === handle })
78
+ return true
79
+ }
80
+
81
+ @objc
82
+ public func cancelAll() {
83
+ for handle in storage.takeAll() {
84
+ cancelHandle(handle)
85
+ }
86
+ }
87
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library 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 Foundation
18
+
19
+ /**
20
+ * `FIRStorageTaskStatus` raw value → JS task-state string mapping previously
21
+ * inline in `+[RNFBStorageCommon getTaskStatus:]`.
22
+ *
23
+ * Raw ints from Firebase Storage `StorageTaskStatus`
24
+ * (`@objc(FIRStorageTaskStatus)` in `StorageConstants.swift`):
25
+ * - unknown = 0, resume = 1, progress = 2, pause = 3, success = 4, failure = 5
26
+ *
27
+ * Hardcoded so the Storage unit target does not need FirebaseStorage.
28
+ */
29
+ @objc(RNFBStorageTaskStatusMapper)
30
+ public final class RNFBStorageTaskStatusMapper: NSObject {
31
+ /// `FIRStorageTaskStatusResume`
32
+ private static let statusResume = 1
33
+ /// `FIRStorageTaskStatusProgress`
34
+ private static let statusProgress = 2
35
+ /// `FIRStorageTaskStatusPause`
36
+ private static let statusPause = 3
37
+ /// `FIRStorageTaskStatusSuccess`
38
+ private static let statusSuccess = 4
39
+ /// `FIRStorageTaskStatusFailure`
40
+ private static let statusFailure = 5
41
+
42
+ @objc(stringForTaskStatus:)
43
+ public static func string(forTaskStatus status: Int) -> String {
44
+ if status == statusResume || status == statusProgress {
45
+ return "running"
46
+ } else if status == statusPause {
47
+ return "paused"
48
+ } else if status == statusSuccess {
49
+ return "success"
50
+ } else if status == statusFailure {
51
+ return "error"
52
+ } else {
53
+ // `FIRStorageTaskStatusUnknown` (0) and any other raw value
54
+ return "unknown"
55
+ }
56
+ }
57
+ }
@@ -12,6 +12,11 @@
12
12
  27A35B652268E118005BE5A8 /* RNFBStorageDownloadTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A35B642268E118005BE5A8 /* RNFBStorageDownloadTask.m */; };
13
13
  27A35B682268E138005BE5A8 /* RNFBStorageUploadTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A35B672268E138005BE5A8 /* RNFBStorageUploadTask.m */; };
14
14
  27A35B6B2268E1BE005BE5A8 /* RNFBStorageCommon.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A35B6A2268E1BE005BE5A8 /* RNFBStorageCommon.m */; };
15
+ RNFBSTOR0000000000000001 /* RNFBStorageTaskRegistry.m in Sources */ = {isa = PBXBuildFile; fileRef = RNFBSTOR0000000000000002 /* RNFBStorageTaskRegistry.m */; };
16
+ RNFBSTOR0000000000000003 /* RNFBStorageTaskRegistryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = RNFBSTOR0000000000000004 /* RNFBStorageTaskRegistryStorage.swift */; };
17
+ RNFBSTOR0000000000000006 /* RNFBStorageTaskStatusMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = RNFBSTOR0000000000000007 /* RNFBStorageTaskStatusMapper.swift */; };
18
+ RNFBSTOR0000000000000008 /* RNFBStorageErrorCodeMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = RNFBSTOR0000000000000009 /* RNFBStorageErrorCodeMapper.swift */; };
19
+ RNFBSTOR000000000000000A /* RNFBStorageErrorSnapshotDictBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = RNFBSTOR000000000000000B /* RNFBStorageErrorSnapshotDictBuilder.swift */; };
15
20
  /* End PBXBuildFile section */
16
21
 
17
22
  /* Begin PBXCopyFilesBuildPhase section */
@@ -38,6 +43,12 @@
38
43
  27A35B692268E143005BE5A8 /* RNFBStorageUploadTask.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFBStorageUploadTask.h; sourceTree = "<group>"; };
39
44
  27A35B6A2268E1BE005BE5A8 /* RNFBStorageCommon.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNFBStorageCommon.m; sourceTree = "<group>"; };
40
45
  27A35B6C2268E1C8005BE5A8 /* RNFBStorageCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFBStorageCommon.h; sourceTree = "<group>"; };
46
+ RNFBSTOR0000000000000005 /* RNFBStorageTaskRegistry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNFBStorageTaskRegistry.h; path = RNFBStorage/RNFBStorageTaskRegistry.h; sourceTree = SOURCE_ROOT; };
47
+ RNFBSTOR0000000000000002 /* RNFBStorageTaskRegistry.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RNFBStorageTaskRegistry.m; path = RNFBStorage/RNFBStorageTaskRegistry.m; sourceTree = SOURCE_ROOT; };
48
+ RNFBSTOR0000000000000004 /* RNFBStorageTaskRegistryStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RNFBStorageTaskRegistryStorage.swift; path = RNFBStorage/RNFBStorageTaskRegistryStorage.swift; sourceTree = SOURCE_ROOT; };
49
+ RNFBSTOR0000000000000007 /* RNFBStorageTaskStatusMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RNFBStorageTaskStatusMapper.swift; path = RNFBStorage/RNFBStorageTaskStatusMapper.swift; sourceTree = SOURCE_ROOT; };
50
+ RNFBSTOR0000000000000009 /* RNFBStorageErrorCodeMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RNFBStorageErrorCodeMapper.swift; path = RNFBStorage/RNFBStorageErrorCodeMapper.swift; sourceTree = SOURCE_ROOT; };
51
+ RNFBSTOR000000000000000B /* RNFBStorageErrorSnapshotDictBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RNFBStorageErrorSnapshotDictBuilder.swift; path = RNFBStorage/RNFBStorageErrorSnapshotDictBuilder.swift; sourceTree = SOURCE_ROOT; };
41
52
  /* End PBXFileReference section */
42
53
 
43
54
  /* Begin PBXFrameworksBuildPhase section */
@@ -64,6 +75,12 @@
64
75
  children = (
65
76
  2744B98421F45429004F8E3F /* RNFBStorageModule.h */,
66
77
  2744B98521F45429004F8E3F /* RNFBStorageModule.mm */,
78
+ RNFBSTOR0000000000000005 /* RNFBStorageTaskRegistry.h */,
79
+ RNFBSTOR0000000000000002 /* RNFBStorageTaskRegistry.m */,
80
+ RNFBSTOR0000000000000004 /* RNFBStorageTaskRegistryStorage.swift */,
81
+ RNFBSTOR0000000000000007 /* RNFBStorageTaskStatusMapper.swift */,
82
+ RNFBSTOR0000000000000009 /* RNFBStorageErrorCodeMapper.swift */,
83
+ RNFBSTOR000000000000000B /* RNFBStorageErrorSnapshotDictBuilder.swift */,
67
84
  27A35B612268E0AB005BE5A8 /* RNFBStorageTask.m */,
68
85
  27A35B632268E0CD005BE5A8 /* RNFBStorageTask.h */,
69
86
  27A35B642268E118005BE5A8 /* RNFBStorageDownloadTask.m */,
@@ -142,6 +159,11 @@
142
159
  isa = PBXSourcesBuildPhase;
143
160
  buildActionMask = 2147483647;
144
161
  files = (
162
+ RNFBSTOR0000000000000003 /* RNFBStorageTaskRegistryStorage.swift in Sources */,
163
+ RNFBSTOR0000000000000006 /* RNFBStorageTaskStatusMapper.swift in Sources */,
164
+ RNFBSTOR0000000000000008 /* RNFBStorageErrorCodeMapper.swift in Sources */,
165
+ RNFBSTOR000000000000000A /* RNFBStorageErrorSnapshotDictBuilder.swift in Sources */,
166
+ RNFBSTOR0000000000000001 /* RNFBStorageTaskRegistry.m in Sources */,
145
167
  27A35B622268E0AB005BE5A8 /* RNFBStorageTask.m in Sources */,
146
168
  27A35B6B2268E1BE005BE5A8 /* RNFBStorageCommon.m in Sources */,
147
169
  2744B98621F45429004F8E3F /* RNFBStorageModule.mm in Sources */,
@@ -172,6 +194,7 @@
172
194
  CODE_SIGN_STYLE = Automatic;
173
195
  COPY_PHASE_STRIP = NO;
174
196
  DEBUG_INFORMATION_FORMAT = dwarf;
197
+ DEFINES_MODULE = YES;
175
198
  GCC_C_LANGUAGE_STANDARD = gnu11;
176
199
  GCC_DYNAMIC_NO_PIC = NO;
177
200
  GCC_OPTIMIZATION_LEVEL = 0;
@@ -188,6 +211,7 @@
188
211
  PRODUCT_NAME = "$(TARGET_NAME)";
189
212
  SDKROOT = iphoneos;
190
213
  SKIP_INSTALL = YES;
214
+ SWIFT_VERSION = 5.10;
191
215
  TARGETED_DEVICE_FAMILY = "1,2";
192
216
  };
193
217
  name = Debug;
@@ -211,6 +235,7 @@
211
235
  CODE_SIGN_STYLE = Automatic;
212
236
  COPY_PHASE_STRIP = NO;
213
237
  DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
238
+ DEFINES_MODULE = YES;
214
239
  ENABLE_NS_ASSERTIONS = NO;
215
240
  GCC_C_LANGUAGE_STANDARD = gnu11;
216
241
  GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
@@ -222,6 +247,7 @@
222
247
  PRODUCT_NAME = "$(TARGET_NAME)";
223
248
  SDKROOT = iphoneos;
224
249
  SKIP_INSTALL = YES;
250
+ SWIFT_VERSION = 5.10;
225
251
  TARGETED_DEVICE_FAMILY = "1,2";
226
252
  VALIDATE_PRODUCT = YES;
227
253
  };