@react-native-firebase/database 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 (77) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/RNFBDatabase.podspec +10 -2
  3. package/android/build.gradle +7 -0
  4. package/android/src/main/java/io/invertase/firebase/database/DatabaseAbortable.java +22 -0
  5. package/android/src/main/java/io/invertase/firebase/database/DatabaseQueryHandle.java +24 -0
  6. package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseKeepSyncedRegistry.java +106 -0
  7. package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseListenerRegistry.kt +133 -0
  8. package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseQueryRegistry.kt +76 -0
  9. package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseTransactionRegistry.kt +67 -0
  10. package/android/src/main/java/io/invertase/firebase/database/UniversalFirebaseDatabaseOnDisconnectModule.java +102 -9
  11. package/android/src/reactnative/java/io/invertase/firebase/database/NativeRNFBTurboDatabaseOnDisconnect.java +6 -11
  12. package/android/src/reactnative/java/io/invertase/firebase/database/NativeRNFBTurboDatabaseQuery.java +111 -24
  13. package/android/src/reactnative/java/io/invertase/firebase/database/NativeRNFBTurboDatabaseTransaction.java +12 -25
  14. package/android/src/reactnative/java/io/invertase/firebase/database/ReactNativeFirebaseDatabaseQuery.java +30 -33
  15. package/android/src/reactnative/java/io/invertase/firebase/database/ReactNativeFirebaseDatabaseTransactionHandler.java +3 -3
  16. package/android/src/reactnative/java/io/invertase/firebase/database/generated/java/com/facebook/fbreact/specs/NativeRNFBTurboDatabaseQuerySpec.java +4 -0
  17. package/android/src/reactnative/java/io/invertase/firebase/database/generated/jni/RNFBDatabaseTurboModules-generated.cpp +6 -0
  18. package/android/src/reactnative/java/io/invertase/firebase/database/generated/jni/react/renderer/components/RNFBDatabaseTurboModules/RNFBDatabaseTurboModulesJSI.h +12 -0
  19. package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseKeepSyncedRegistryTest.java +174 -0
  20. package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseListenerRegistryTest.java +191 -0
  21. package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseQueryRegistryTest.java +489 -0
  22. package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseTransactionRegistryTest.java +199 -0
  23. package/android/src/test/java/io/invertase/firebase/database/UniversalFirebaseDatabaseOnDisconnectCancelErrorTest.java +137 -0
  24. package/dist/module/DatabaseQuery.js +52 -1
  25. package/dist/module/DatabaseQuery.js.map +1 -1
  26. package/dist/module/DatabaseSyncTree.js +1 -1
  27. package/dist/module/DatabaseSyncTree.js.map +1 -1
  28. package/dist/module/index.js +1 -2
  29. package/dist/module/index.js.map +1 -1
  30. package/dist/module/modular/query.js +1 -1
  31. package/dist/module/modular/query.js.map +1 -1
  32. package/dist/module/version.js +1 -1
  33. package/dist/module/web/RNFBDatabaseModule.js +7 -0
  34. package/dist/module/web/RNFBDatabaseModule.js.map +1 -1
  35. package/dist/typescript/lib/DatabaseQuery.d.ts +25 -0
  36. package/dist/typescript/lib/DatabaseQuery.d.ts.map +1 -1
  37. package/dist/typescript/lib/index.d.ts +1 -1
  38. package/dist/typescript/lib/types/internal.d.ts +2 -0
  39. package/dist/typescript/lib/types/internal.d.ts.map +1 -1
  40. package/dist/typescript/lib/version.d.ts +1 -1
  41. package/dist/typescript/lib/web/RNFBDatabaseModule.d.ts.map +1 -1
  42. package/ios/RNFBDatabase/RNFBDatabaseListenerRegistry.h +37 -0
  43. package/ios/RNFBDatabase/RNFBDatabaseListenerRegistry.m +91 -0
  44. package/ios/RNFBDatabase/RNFBDatabaseListenerRegistryStorage.swift +81 -0
  45. package/ios/RNFBDatabase/RNFBDatabaseNullSentinelDecoder.h +36 -0
  46. package/ios/RNFBDatabase/RNFBDatabaseNullSentinelDecoder.m +48 -0
  47. package/ios/RNFBDatabase/RNFBDatabaseOnDisconnectHelper.m +29 -26
  48. package/ios/RNFBDatabase/RNFBDatabaseQuery.h +3 -2
  49. package/ios/RNFBDatabase/RNFBDatabaseQuery.m +14 -13
  50. package/ios/RNFBDatabase/RNFBDatabaseQueryHelper.h +7 -0
  51. package/ios/RNFBDatabase/RNFBDatabaseQueryHelper.m +44 -21
  52. package/ios/RNFBDatabase/RNFBDatabaseQueryModule.mm +14 -0
  53. package/ios/RNFBDatabase/RNFBDatabaseQueryRegistry.h +37 -0
  54. package/ios/RNFBDatabase/RNFBDatabaseQueryRegistry.m +79 -0
  55. package/ios/RNFBDatabase/RNFBDatabaseQueryRegistryStorage.swift +104 -0
  56. package/ios/RNFBDatabase/RNFBDatabaseReferenceHelper.m +14 -12
  57. package/ios/RNFBDatabase/RNFBDatabaseTransactionHelper.m +3 -1
  58. package/ios/RNFBDatabase.xcodeproj/project.pbxproj +12 -0
  59. package/ios/RNFBDatabaseUnitTests/RNFBDatabaseListenerRegistryStorageTests.swift +86 -0
  60. package/ios/RNFBDatabaseUnitTests/RNFBDatabaseListenerRegistryTests.m +90 -0
  61. package/ios/RNFBDatabaseUnitTests/RNFBDatabaseNullSentinelDecodeTests.m +118 -0
  62. package/ios/RNFBDatabaseUnitTests/RNFBDatabaseQueryRegistryStorageTests.swift +181 -0
  63. package/ios/RNFBDatabaseUnitTests/RNFBDatabaseQueryRegistryTests.m +390 -0
  64. package/ios/RNFBDatabaseUnitTests/RNFBDatabaseUnitTests.xcodeproj/project.pbxproj +303 -0
  65. package/ios/RNFBDatabaseUnitTests/RNFBDatabaseUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBDatabaseUnitTests.xcscheme +69 -0
  66. package/ios/generated/RNFBDatabaseTurboModules/RNFBDatabaseTurboModules-generated.mm +7 -0
  67. package/ios/generated/RNFBDatabaseTurboModules/RNFBDatabaseTurboModules.h +6 -0
  68. package/ios/generated/RNFBDatabaseTurboModulesJSI.h +12 -0
  69. package/lib/DatabaseQuery.ts +56 -0
  70. package/lib/DatabaseSyncTree.ts +1 -1
  71. package/lib/index.ts +1 -1
  72. package/lib/modular/query.ts +1 -1
  73. package/lib/types/internal.ts +2 -0
  74. package/lib/version.ts +1 -1
  75. package/lib/web/RNFBDatabaseModule.ts +13 -0
  76. package/package.json +5 -4
  77. package/specs/NativeRNFBTurboDatabaseQuery.ts +1 -0
@@ -76,6 +76,20 @@ RCT_EXPORT_MODULE(NativeRNFBTurboDatabaseQuery);
76
76
  reject:reject];
77
77
  }
78
78
 
79
+ - (void)get:(NSString *)app
80
+ dbURL:(NSString *)dbURL
81
+ path:(NSString *)path
82
+ modifiers:(NSArray *)modifiers
83
+ resolve:(RCTPromiseResolveBlock)resolve
84
+ reject:(RCTPromiseRejectBlock)reject {
85
+ [RNFBDatabaseQueryHelper get:app
86
+ dbURL:dbURL
87
+ path:path
88
+ modifiers:modifiers
89
+ resolve:resolve
90
+ reject:reject];
91
+ }
92
+
79
93
  - (void)on:(NSString *)app dbURL:(NSString *)dbURL props:(NSDictionary *)props {
80
94
  [RNFBDatabaseQueryHelper on:app dbURL:dbURL props:props];
81
95
  }
@@ -0,0 +1,37 @@
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
+ * Cached query map. Unique `put`; callers `take`, `takeIfIdle`, or `removeAll` then
24
+ * `removeAllEventListeners` outside the HandleMap lock. Stored values are expected to respond to
25
+ * `removeAllEventListeners` and `hasListeners`.
26
+ */
27
+ @interface RNFBDatabaseQueryRegistry : NSObject
28
+
29
+ - (BOOL)put:(id)key value:(id)value error:(NSError *_Nullable *_Nullable)error;
30
+ - (nullable id)get:(id)key;
31
+ - (nullable id)take:(id)key;
32
+ - (nullable id)takeIfIdle:(id)key;
33
+ - (void)removeAll;
34
+
35
+ @end
36
+
37
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,79 @@
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 "RNFBDatabaseQueryRegistry.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(<RNFBDatabase/RNFBDatabase-Swift.h>)
27
+ #import <RNFBDatabase/RNFBDatabase-Swift.h>
28
+ #elif __has_include("RNFBDatabase-Swift.h")
29
+ #import "RNFBDatabase-Swift.h"
30
+ #elif __has_include("RNFBHandleMapStorage-Swift.inc")
31
+ #import "RNFBHandleMapStorage-Swift.inc"
32
+ #else
33
+ #error "RNFBDatabaseQueryRegistryStorage Swift interface not found"
34
+ #endif
35
+
36
+ @interface RNFBDatabaseQueryRegistry ()
37
+ @property(nonatomic, strong) RNFBDatabaseQueryRegistryStorage *storage;
38
+ @end
39
+
40
+ @implementation RNFBDatabaseQueryRegistry
41
+
42
+ - (instancetype)init {
43
+ self = [super init];
44
+ if (self) {
45
+ _storage = [[RNFBDatabaseQueryRegistryStorage 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
+ return YES;
53
+ }
54
+ if (error != nil) {
55
+ NSString *message = [NSString stringWithFormat:@"Handle id already registered: %@", key];
56
+ *error = [NSError errorWithDomain:RNFBHandleMapErrorDomain
57
+ code:RNFBHandleMapErrorCollision
58
+ userInfo:@{NSLocalizedDescriptionKey : message}];
59
+ }
60
+ return NO;
61
+ }
62
+
63
+ - (id)get:(id)key {
64
+ return [self.storage get:key];
65
+ }
66
+
67
+ - (id)take:(id)key {
68
+ return [self.storage take:key];
69
+ }
70
+
71
+ - (id)takeIfIdle:(id)key {
72
+ return [self.storage takeIfIdle:key];
73
+ }
74
+
75
+ - (void)removeAll {
76
+ [self.storage removeAll];
77
+ }
78
+
79
+ @end
@@ -0,0 +1,104 @@
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
+ /// Cached query map storage. Composes `RNFBHandleMapStorage`.
24
+ /// Unique `put` collision errors stay on the ObjC façade.
25
+ /// `takeIfIdle` reads `hasListeners` outside the HandleMap lock (avoids nesting).
26
+ @objc(RNFBDatabaseQueryRegistryStorage)
27
+ public final class RNFBDatabaseQueryRegistryStorage: NSObject {
28
+ private static let hasListenersSelector = Selector(("hasListeners"))
29
+ private static let removeAllEventListenersSelector = Selector(("removeAllEventListeners"))
30
+
31
+ private let storage = RNFBHandleMapStorage()
32
+
33
+ private func removeAllEventListeners(_ query: AnyObject?) {
34
+ guard let query = query, query.responds(to: Self.removeAllEventListenersSelector) else {
35
+ return
36
+ }
37
+ _ = query.perform(Self.removeAllEventListenersSelector)
38
+ }
39
+
40
+ /// Calls `-hasListeners` via IMP (avoids Swift protocol cast requiring formal conformance).
41
+ /// Returns `nil` when the selector is absent.
42
+ private func queryHasListeners(_ query: AnyObject) -> Bool? {
43
+ typealias HasListenersIMP = @convention(c) (AnyObject, Selector) -> Bool
44
+ let sel = Self.hasListenersSelector
45
+ guard query.responds(to: sel), let method = class_getInstanceMethod(type(of: query), sel)
46
+ else {
47
+ return nil
48
+ }
49
+ let fn = unsafeBitCast(method_getImplementation(method), to: HasListenersIMP.self)
50
+ return fn(query, sel)
51
+ }
52
+
53
+ /// First-wins store. Used by façade `put`.
54
+ @objc(putIfAbsent:value:)
55
+ public func putIfAbsent(_ key: AnyObject, value: AnyObject) -> Bool {
56
+ storage.putIfAbsent(key, value: value)
57
+ }
58
+
59
+ @objc(get:)
60
+ public func get(_ key: AnyObject) -> AnyObject? {
61
+ storage.get(key)
62
+ }
63
+
64
+ @objc(take:)
65
+ public func take(_ key: AnyObject) -> AnyObject? {
66
+ storage.take(key)
67
+ }
68
+
69
+ /// Idle-only take. `hasListeners` is read outside the HandleMap lock; identity-take then
70
+ /// put-back (or orphan cleanup) if listeners appear after the outside check.
71
+ @objc(takeIfIdle:)
72
+ public func takeIfIdle(_ key: AnyObject) -> AnyObject? {
73
+ guard let query = storage.get(key) else {
74
+ return nil
75
+ }
76
+ guard let hasListeners = queryHasListeners(query) else {
77
+ return nil
78
+ }
79
+ if hasListeners {
80
+ return nil
81
+ }
82
+ guard let taken = storage.takeIf(key, when: { $0 === query }) else {
83
+ return nil
84
+ }
85
+ if queryHasListeners(taken) == true {
86
+ // Prefer put-back so an active query stays registered. If a concurrent put claimed the
87
+ // slot, putIfAbsent fails and this taken query would be an orphan with listeners — clear
88
+ // them so SDK callbacks are not left attached outside the registry.
89
+ if !storage.putIfAbsent(key, value: taken) {
90
+ removeAllEventListeners(taken)
91
+ }
92
+ return nil
93
+ }
94
+ return taken
95
+ }
96
+
97
+ /// Snapshot then clear; `removeAllEventListeners` on each value that responds.
98
+ @objc
99
+ public func removeAll() {
100
+ for query in storage.takeAll() {
101
+ removeAllEventListeners(query)
102
+ }
103
+ }
104
+ }
@@ -24,6 +24,7 @@
24
24
 
25
25
  #import "RNFBApp/RCTConvert+FIRApp.h"
26
26
  #import "RNFBDatabaseCommon.h"
27
+ #import "RNFBDatabaseNullSentinelDecoder.h"
27
28
  #import "RNFBDatabaseReferenceHelper.h"
28
29
 
29
30
  @implementation RNFBDatabaseReferenceHelper
@@ -39,7 +40,7 @@
39
40
  FIRDatabaseReference *firDatabaseReference =
40
41
  [RNFBDatabaseCommon getReferenceForDatabase:firDatabase path:path];
41
42
 
42
- [firDatabaseReference setValue:[props valueForKey:@"value"]
43
+ [firDatabaseReference setValue:[RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props]
43
44
  withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
44
45
  if (error != nil) {
45
46
  [RNFBDatabaseCommon promiseRejectDatabaseException:reject error:error];
@@ -60,14 +61,15 @@
60
61
  FIRDatabaseReference *firDatabaseReference =
61
62
  [RNFBDatabaseCommon getReferenceForDatabase:firDatabase path:path];
62
63
 
63
- [firDatabaseReference updateChildValues:[props valueForKey:@"values"]
64
- withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
65
- if (error != nil) {
66
- [RNFBDatabaseCommon promiseRejectDatabaseException:reject error:error];
67
- } else {
68
- resolve([NSNull null]);
69
- }
70
- }];
64
+ [firDatabaseReference
65
+ updateChildValues:[RNFBDatabaseNullSentinelDecoder decodedValuesFromProps:props]
66
+ withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
67
+ if (error != nil) {
68
+ [RNFBDatabaseCommon promiseRejectDatabaseException:reject error:error];
69
+ } else {
70
+ resolve([NSNull null]);
71
+ }
72
+ }];
71
73
  }
72
74
 
73
75
  + (void)setWithPriority:(NSString *)app
@@ -81,8 +83,8 @@
81
83
  FIRDatabaseReference *firDatabaseReference =
82
84
  [RNFBDatabaseCommon getReferenceForDatabase:firDatabase path:path];
83
85
 
84
- [firDatabaseReference setValue:[props valueForKey:@"value"]
85
- andPriority:[props valueForKey:@"priority"]
86
+ [firDatabaseReference setValue:[RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props]
87
+ andPriority:[RNFBDatabaseNullSentinelDecoder decodedPriorityFromProps:props]
86
88
  withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
87
89
  if (error != nil) {
88
90
  [RNFBDatabaseCommon promiseRejectDatabaseException:reject error:error];
@@ -123,7 +125,7 @@
123
125
  FIRDatabaseReference *firDatabaseReference =
124
126
  [RNFBDatabaseCommon getReferenceForDatabase:firDatabase path:path];
125
127
 
126
- [firDatabaseReference setPriority:[props valueForKey:@"priority"]
128
+ [firDatabaseReference setPriority:[RNFBDatabaseNullSentinelDecoder decodedPriorityFromProps:props]
127
129
  withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
128
130
  if (error != nil) {
129
131
  [RNFBDatabaseCommon promiseRejectDatabaseException:reject error:error];
@@ -25,6 +25,7 @@
25
25
  #import "RNFBApp/RCTConvert+FIRApp.h"
26
26
  #import "RNFBApp/RNFBSharedUtils.h"
27
27
  #import "RNFBDatabaseCommon.h"
28
+ #import "RNFBDatabaseNullSentinelDecoder.h"
28
29
  #import "RNFBDatabaseTransactionHelper.h"
29
30
  #import "RNFBRCTEventEmitter.h"
30
31
 
@@ -163,7 +164,8 @@ static NSString *const RNFB_DATABASE_TRANSACTION_EVENT = @"database_transaction_
163
164
  if (abort) {
164
165
  [transactionState setValue:@true forKey:@"abort"];
165
166
  } else {
166
- id newValue = [updates valueForKey:@"value"];
167
+ id newValue =
168
+ [RNFBDatabaseNullSentinelDecoder decodedTransactionValue:[updates valueForKey:@"value"]];
167
169
  [transactionState setValue:newValue forKey:@"value"];
168
170
  }
169
171
 
@@ -14,6 +14,8 @@
14
14
  8B9DD3C622A11F4F00A16945 /* RNFBDatabaseReferenceModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B9DD3C522A11F4F00A16945 /* RNFBDatabaseReferenceModule.mm */; };
15
15
  8B9DD3C922A1258B00A16945 /* RNFBDatabaseQueryModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B9DD3C822A1258B00A16945 /* RNFBDatabaseQueryModule.mm */; };
16
16
  8B9DD3CE22A43D1400A16945 /* RNFBDatabaseTransactionModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B9DD3CD22A43D1400A16945 /* RNFBDatabaseTransactionModule.mm */; };
17
+ RNFBDB000000000000000001 /* RNFBDatabaseListenerRegistryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = RNFBDB000000000000000002 /* RNFBDatabaseListenerRegistryStorage.swift */; };
18
+ RNFBDB000000000000000003 /* RNFBDatabaseQueryRegistryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = RNFBDB000000000000000004 /* RNFBDatabaseQueryRegistryStorage.swift */; };
17
19
  /* End PBXBuildFile section */
18
20
 
19
21
  /* Begin PBXCopyFilesBuildPhase section */
@@ -44,6 +46,8 @@
44
46
  8B9DD3CA22A1259F00A16945 /* RNFBDatabaseQueryModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFBDatabaseQueryModule.h; sourceTree = "<group>"; };
45
47
  8B9DD3CC22A43D0300A16945 /* RNFBDatabaseTransactionModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFBDatabaseTransactionModule.h; sourceTree = "<group>"; };
46
48
  8B9DD3CD22A43D1400A16945 /* RNFBDatabaseTransactionModule.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNFBDatabaseTransactionModule.mm; sourceTree = "<group>"; };
49
+ RNFBDB000000000000000002 /* RNFBDatabaseListenerRegistryStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RNFBDatabaseListenerRegistryStorage.swift; path = RNFBDatabase/RNFBDatabaseListenerRegistryStorage.swift; sourceTree = SOURCE_ROOT; };
50
+ RNFBDB000000000000000004 /* RNFBDatabaseQueryRegistryStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RNFBDatabaseQueryRegistryStorage.swift; path = RNFBDatabase/RNFBDatabaseQueryRegistryStorage.swift; sourceTree = SOURCE_ROOT; };
47
51
  /* End PBXFileReference section */
48
52
 
49
53
  /* Begin PBXFrameworksBuildPhase section */
@@ -70,6 +74,8 @@
70
74
  children = (
71
75
  2744B98421F45429004F8E3F /* RNFBDatabaseModule.h */,
72
76
  2744B98521F45429004F8E3F /* RNFBDatabaseModule.mm */,
77
+ RNFBDB000000000000000002 /* RNFBDatabaseListenerRegistryStorage.swift */,
78
+ RNFBDB000000000000000004 /* RNFBDatabaseQueryRegistryStorage.swift */,
73
79
  8B9DD3B822A03C6700A16945 /* RNFBDatabaseQuery.h */,
74
80
  8B9DD3B922A03C7900A16945 /* RNFBDatabaseQuery.m */,
75
81
  8B9DD3BC22A05CEB00A16945 /* RNFBDatabaseOnDisconnectModule.mm */,
@@ -153,6 +159,8 @@
153
159
  isa = PBXSourcesBuildPhase;
154
160
  buildActionMask = 2147483647;
155
161
  files = (
162
+ RNFBDB000000000000000001 /* RNFBDatabaseListenerRegistryStorage.swift in Sources */,
163
+ RNFBDB000000000000000003 /* RNFBDatabaseQueryRegistryStorage.swift in Sources */,
156
164
  8B9DD3BD22A05CEB00A16945 /* RNFBDatabaseOnDisconnectModule.mm in Sources */,
157
165
  8B9DD3C922A1258B00A16945 /* RNFBDatabaseQueryModule.mm in Sources */,
158
166
  8B9DD3CE22A43D1400A16945 /* RNFBDatabaseTransactionModule.mm in Sources */,
@@ -185,6 +193,7 @@
185
193
  CODE_SIGN_STYLE = Automatic;
186
194
  COPY_PHASE_STRIP = NO;
187
195
  DEBUG_INFORMATION_FORMAT = dwarf;
196
+ DEFINES_MODULE = YES;
188
197
  GCC_C_LANGUAGE_STANDARD = gnu11;
189
198
  GCC_DYNAMIC_NO_PIC = NO;
190
199
  GCC_OPTIMIZATION_LEVEL = 0;
@@ -201,6 +210,7 @@
201
210
  PRODUCT_NAME = "$(TARGET_NAME)";
202
211
  SDKROOT = iphoneos;
203
212
  SKIP_INSTALL = YES;
213
+ SWIFT_VERSION = 5.10;
204
214
  TARGETED_DEVICE_FAMILY = "1,2";
205
215
  };
206
216
  name = Debug;
@@ -224,6 +234,7 @@
224
234
  CODE_SIGN_STYLE = Automatic;
225
235
  COPY_PHASE_STRIP = NO;
226
236
  DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
237
+ DEFINES_MODULE = YES;
227
238
  ENABLE_NS_ASSERTIONS = NO;
228
239
  GCC_C_LANGUAGE_STANDARD = gnu11;
229
240
  GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
@@ -235,6 +246,7 @@
235
246
  PRODUCT_NAME = "$(TARGET_NAME)";
236
247
  SDKROOT = iphoneos;
237
248
  SKIP_INSTALL = YES;
249
+ SWIFT_VERSION = 5.10;
238
250
  TARGETED_DEVICE_FAMILY = "1,2";
239
251
  VALIDATE_PRODUCT = YES;
240
252
  };
@@ -0,0 +1,86 @@
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 XCTest
18
+
19
+ final class RNFBDatabaseListenerRegistryStorageTests: XCTestCase {
20
+ private var storage: RNFBDatabaseListenerRegistryStorage!
21
+
22
+ override func setUp() {
23
+ super.setUp()
24
+ storage = RNFBDatabaseListenerRegistryStorage()
25
+ }
26
+
27
+ func testPutIfAbsent_storesWhenFree_andIncrementsOccupancy() {
28
+ let handle = 7 as NSNumber
29
+ XCTAssertTrue(storage.putIfAbsent("k" as NSString, value: handle))
30
+ XCTAssertTrue(storage.get("k" as NSString) === handle)
31
+ XCTAssertTrue(storage.hasListeners)
32
+ XCTAssertTrue(storage.hasEventListener("k" as NSString))
33
+ }
34
+
35
+ func testPutIfAbsent_collision_keepsExistingWithoutOccupancyBump() {
36
+ let first = 1 as NSNumber
37
+ let second = 2 as NSNumber
38
+ XCTAssertTrue(storage.putIfAbsent("k" as NSString, value: first))
39
+ XCTAssertFalse(storage.putIfAbsent("k" as NSString, value: second))
40
+ XCTAssertTrue(storage.get("k" as NSString) === first)
41
+ XCTAssertTrue(storage.hasListeners)
42
+ XCTAssertEqual(storage.takeAll().count, 1)
43
+ XCTAssertFalse(storage.hasListeners)
44
+ }
45
+
46
+ func testGet_whenFree_isNil() {
47
+ XCTAssertNil(storage.get("missing" as NSString))
48
+ XCTAssertFalse(storage.hasEventListener("missing" as NSString))
49
+ XCTAssertFalse(storage.hasListeners)
50
+ }
51
+
52
+ func testTake_removesAndDecrementsOccupancy() {
53
+ let handle = 7 as NSNumber
54
+ XCTAssertTrue(storage.putIfAbsent("k" as NSString, value: handle))
55
+ XCTAssertTrue(storage.take("k" as NSString) === handle)
56
+ XCTAssertNil(storage.get("k" as NSString))
57
+ XCTAssertFalse(storage.hasListeners)
58
+ }
59
+
60
+ func testTake_whenFree_isNoOp() {
61
+ XCTAssertNil(storage.take("missing" as NSString))
62
+ XCTAssertFalse(storage.hasListeners)
63
+ }
64
+
65
+ func testTakeAll_snapshotAndLeavesEmpty() {
66
+ XCTAssertTrue(storage.putIfAbsent("a" as NSString, value: 1 as NSNumber))
67
+ XCTAssertTrue(storage.putIfAbsent("b" as NSString, value: 2 as NSNumber))
68
+ let remaining = storage.takeAll()
69
+ XCTAssertEqual(remaining.count, 2)
70
+ XCTAssertFalse(storage.hasListeners)
71
+ XCTAssertNil(storage.get("a" as NSString))
72
+ }
73
+
74
+ func testTakeAll_empty_isNoOp() {
75
+ XCTAssertEqual(storage.takeAll().count, 0)
76
+ XCTAssertFalse(storage.hasListeners)
77
+ }
78
+
79
+ func testPut_afterTake_allowsReuse() {
80
+ XCTAssertTrue(storage.putIfAbsent("k" as NSString, value: 1 as NSNumber))
81
+ XCTAssertEqual(storage.take("k" as NSString) as? NSNumber, 1)
82
+ XCTAssertTrue(storage.putIfAbsent("k" as NSString, value: 2 as NSNumber))
83
+ XCTAssertEqual(storage.get("k" as NSString) as? NSNumber, 2)
84
+ XCTAssertTrue(storage.hasListeners)
85
+ }
86
+ }
@@ -0,0 +1,90 @@
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 <XCTest/XCTest.h>
19
+
20
+ #import "RNFBDatabaseListenerRegistry.h"
21
+ #import "RNFBHandleMap.h"
22
+
23
+ @interface RNFBDatabaseListenerRegistryTests : XCTestCase
24
+ @property(nonatomic, strong) RNFBDatabaseListenerRegistry *registry;
25
+ @end
26
+
27
+ @implementation RNFBDatabaseListenerRegistryTests
28
+
29
+ - (void)setUp {
30
+ [super setUp];
31
+ self.registry = [[RNFBDatabaseListenerRegistry alloc] init];
32
+ }
33
+
34
+ - (void)testPutGetTake_happyPath {
35
+ NSNumber *handle = @7;
36
+ NSError *error = nil;
37
+ XCTAssertTrue([self.registry put:@"k" value:handle error:&error]);
38
+ XCTAssertNil(error);
39
+ XCTAssertEqual(handle, [self.registry get:@"k"]);
40
+ XCTAssertEqual(handle, [self.registry take:@"k"]);
41
+ XCTAssertNil([self.registry get:@"k"]);
42
+ XCTAssertFalse([self.registry hasListeners]);
43
+ }
44
+
45
+ - (void)testPut_occupiedId_returnsCollision {
46
+ XCTAssertTrue([self.registry put:@"k" value:@1 error:nil]);
47
+ NSError *error = nil;
48
+ XCTAssertFalse([self.registry put:@"k" value:@2 error:&error]);
49
+ XCTAssertEqual(error.code, RNFBHandleMapErrorCollision);
50
+ XCTAssertEqualObjects([self.registry get:@"k"], @1);
51
+ XCTAssertTrue([self.registry hasListeners]);
52
+ }
53
+
54
+ - (void)testPut_occupiedId_nilErrorOut_returnsNo {
55
+ XCTAssertTrue([self.registry put:@"k" value:@1 error:nil]);
56
+ XCTAssertFalse([self.registry put:@"k" value:@2 error:nil]);
57
+ }
58
+
59
+ - (void)testTake_missingKey_isNoOp {
60
+ XCTAssertNil([self.registry take:@"missing"]);
61
+ XCTAssertFalse([self.registry hasListeners]);
62
+ }
63
+
64
+ - (void)testTakeAll_snapshotAndLeavesEmpty {
65
+ XCTAssertTrue([self.registry put:@"a" value:@1 error:nil]);
66
+ XCTAssertTrue([self.registry put:@"b" value:@2 error:nil]);
67
+ NSArray *remaining = [self.registry takeAll];
68
+ XCTAssertEqual(remaining.count, 2);
69
+ XCTAssertFalse([self.registry hasListeners]);
70
+ XCTAssertNil([self.registry get:@"a"]);
71
+ }
72
+
73
+ - (void)testTakeAll_empty_isNoOp {
74
+ XCTAssertEqual([self.registry takeAll].count, 0);
75
+ }
76
+
77
+ - (void)testHasEventListener {
78
+ XCTAssertFalse([self.registry hasEventListener:@"k"]);
79
+ XCTAssertTrue([self.registry put:@"k" value:@1 error:nil]);
80
+ XCTAssertTrue([self.registry hasEventListener:@"k"]);
81
+ }
82
+
83
+ - (void)testPut_afterTake_allowsReuse {
84
+ XCTAssertTrue([self.registry put:@"k" value:@1 error:nil]);
85
+ XCTAssertEqualObjects([self.registry take:@"k"], @1);
86
+ XCTAssertTrue([self.registry put:@"k" value:@2 error:nil]);
87
+ XCTAssertEqualObjects([self.registry get:@"k"], @2);
88
+ }
89
+
90
+ @end
@@ -0,0 +1,118 @@
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 <XCTest/XCTest.h>
19
+
20
+ #import "RNFBDatabaseNullSentinelDecoder.h"
21
+
22
+ @interface RNFBDatabaseNullSentinelDecodeTests : XCTestCase
23
+ @end
24
+
25
+ @implementation RNFBDatabaseNullSentinelDecodeTests
26
+
27
+ - (NSDictionary *)nullSentinel {
28
+ return @{@"__rnfbNull" : @YES};
29
+ }
30
+
31
+ - (NSDictionary *)serverTimestamp {
32
+ return @{@".sv" : @"timestamp"};
33
+ }
34
+
35
+ #pragma mark - Reference helper seams
36
+
37
+ - (void)testReferenceSet_topLevelNullSentinel_becomesNSNull {
38
+ NSDictionary *props = @{@"value" : [self nullSentinel]};
39
+ XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props],
40
+ [NSNull null]);
41
+ }
42
+
43
+ - (void)testReferenceUpdate_childNullSentinel_becomesNSNull {
44
+ NSDictionary *props = @{@"values" : @{@"a" : [self nullSentinel], @"b" : @1}};
45
+ NSDictionary *decoded = [RNFBDatabaseNullSentinelDecoder decodedValuesFromProps:props];
46
+ XCTAssertEqualObjects(decoded[@"a"], [NSNull null]);
47
+ XCTAssertEqualObjects(decoded[@"b"], @1);
48
+ }
49
+
50
+ - (void)testReferenceSetWithPriority_valueAndPrioritySentinels_becomeNSNull {
51
+ NSDictionary *props = @{
52
+ @"value" : [self nullSentinel],
53
+ @"priority" : [self nullSentinel],
54
+ };
55
+ XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props],
56
+ [NSNull null]);
57
+ XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedPriorityFromProps:props],
58
+ [NSNull null]);
59
+ }
60
+
61
+ - (void)testReferenceSetPriority_sentinel_becomesNSNull {
62
+ NSDictionary *props = @{@"priority" : [self nullSentinel]};
63
+ XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedPriorityFromProps:props],
64
+ [NSNull null]);
65
+ }
66
+
67
+ - (void)testReference_serverTimestampDictionary_unchanged {
68
+ NSDictionary *props = @{@"value" : [self serverTimestamp]};
69
+ XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props],
70
+ [self serverTimestamp]);
71
+ }
72
+
73
+ #pragma mark - OnDisconnect helper seams
74
+
75
+ - (void)testOnDisconnectSet_topLevelNullSentinel_becomesNSNull {
76
+ NSDictionary *props = @{@"value" : [self nullSentinel]};
77
+ XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props],
78
+ [NSNull null]);
79
+ }
80
+
81
+ - (void)testOnDisconnectSetWithPriority_valueAndPrioritySentinels_becomeNSNull {
82
+ NSDictionary *props = @{
83
+ @"value" : [self nullSentinel],
84
+ @"priority" : [self nullSentinel],
85
+ };
86
+ XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props],
87
+ [NSNull null]);
88
+ XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedPriorityFromProps:props],
89
+ [NSNull null]);
90
+ }
91
+
92
+ - (void)testOnDisconnectUpdate_childNullSentinel_becomesNSNull {
93
+ NSDictionary *props = @{@"values" : @{@"gone" : [self nullSentinel]}};
94
+ NSDictionary *decoded = [RNFBDatabaseNullSentinelDecoder decodedValuesFromProps:props];
95
+ XCTAssertEqualObjects(decoded[@"gone"], [NSNull null]);
96
+ }
97
+
98
+ - (void)testOnDisconnect_serverTimestampDictionary_unchanged {
99
+ NSDictionary *props = @{@"value" : [self serverTimestamp]};
100
+ XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props],
101
+ [self serverTimestamp]);
102
+ }
103
+
104
+ #pragma mark - Transaction helper seam
105
+
106
+ - (void)testTransactionTryCommit_valueSentinel_becomesNSNull {
107
+ id decoded = [RNFBDatabaseNullSentinelDecoder decodedTransactionValue:[self nullSentinel]];
108
+ XCTAssertEqualObjects(decoded, [NSNull null]);
109
+ XCTAssertFalse([decoded isKindOfClass:[NSDictionary class]]);
110
+ }
111
+
112
+ - (void)testTransactionTryCommit_serverTimestamp_unchanged {
113
+ XCTAssertEqualObjects(
114
+ [RNFBDatabaseNullSentinelDecoder decodedTransactionValue:[self serverTimestamp]],
115
+ [self serverTimestamp]);
116
+ }
117
+
118
+ @end