@react-native-firebase/database 26.4.0 → 27.1.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 (67) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/RNFBDatabase.podspec +8 -2
  3. package/android/build.gradle +5 -0
  4. package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseKeepSyncedRegistry.java +106 -0
  5. package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseListenerRegistry.kt +133 -0
  6. package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseQueryRegistry.kt +76 -0
  7. package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseTransactionRegistry.kt +67 -0
  8. package/android/src/main/java/io/invertase/firebase/database/UniversalFirebaseDatabaseOnDisconnectModule.java +102 -9
  9. package/android/src/reactnative/java/io/invertase/firebase/database/NativeRNFBTurboDatabaseOnDisconnect.java +6 -11
  10. package/android/src/reactnative/java/io/invertase/firebase/database/NativeRNFBTurboDatabaseQuery.java +93 -2
  11. package/android/src/reactnative/java/io/invertase/firebase/database/generated/java/com/facebook/fbreact/specs/NativeRNFBTurboDatabaseQuerySpec.java +4 -0
  12. package/android/src/reactnative/java/io/invertase/firebase/database/generated/jni/RNFBDatabaseTurboModules-generated.cpp +6 -0
  13. package/android/src/reactnative/java/io/invertase/firebase/database/generated/jni/react/renderer/components/RNFBDatabaseTurboModules/RNFBDatabaseTurboModulesJSI.h +12 -0
  14. package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseKeepSyncedRegistryTest.java +174 -0
  15. package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseListenerRegistryTest.java +31 -0
  16. package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseQueryRegistryTest.java +55 -0
  17. package/android/src/test/java/io/invertase/firebase/database/UniversalFirebaseDatabaseOnDisconnectCancelErrorTest.java +137 -0
  18. package/dist/module/DatabaseQuery.js +52 -1
  19. package/dist/module/DatabaseQuery.js.map +1 -1
  20. package/dist/module/DatabaseSyncTree.js +1 -1
  21. package/dist/module/DatabaseSyncTree.js.map +1 -1
  22. package/dist/module/index.js +1 -2
  23. package/dist/module/index.js.map +1 -1
  24. package/dist/module/modular/query.js +1 -1
  25. package/dist/module/modular/query.js.map +1 -1
  26. package/dist/module/version.js +1 -1
  27. package/dist/module/web/RNFBDatabaseModule.js +7 -0
  28. package/dist/module/web/RNFBDatabaseModule.js.map +1 -1
  29. package/dist/typescript/lib/DatabaseQuery.d.ts +25 -0
  30. package/dist/typescript/lib/DatabaseQuery.d.ts.map +1 -1
  31. package/dist/typescript/lib/index.d.ts +1 -1
  32. package/dist/typescript/lib/types/internal.d.ts +2 -0
  33. package/dist/typescript/lib/types/internal.d.ts.map +1 -1
  34. package/dist/typescript/lib/version.d.ts +1 -1
  35. package/dist/typescript/lib/web/RNFBDatabaseModule.d.ts.map +1 -1
  36. package/ios/RNFBDatabase/RNFBDatabaseListenerRegistry.m +24 -17
  37. package/ios/RNFBDatabase/RNFBDatabaseListenerRegistryStorage.swift +81 -0
  38. package/ios/RNFBDatabase/RNFBDatabaseNullSentinelDecoder.h +36 -0
  39. package/ios/RNFBDatabase/RNFBDatabaseNullSentinelDecoder.m +48 -0
  40. package/ios/RNFBDatabase/RNFBDatabaseOnDisconnectHelper.m +29 -26
  41. package/ios/RNFBDatabase/RNFBDatabaseQueryHelper.h +7 -0
  42. package/ios/RNFBDatabase/RNFBDatabaseQueryHelper.m +20 -0
  43. package/ios/RNFBDatabase/RNFBDatabaseQueryModule.mm +14 -0
  44. package/ios/RNFBDatabase/RNFBDatabaseQueryRegistry.m +25 -51
  45. package/ios/RNFBDatabase/RNFBDatabaseQueryRegistryStorage.swift +104 -0
  46. package/ios/RNFBDatabase/RNFBDatabaseReferenceHelper.m +14 -12
  47. package/ios/RNFBDatabase/RNFBDatabaseTransactionHelper.m +3 -1
  48. package/ios/RNFBDatabase.xcodeproj/project.pbxproj +12 -0
  49. package/ios/RNFBDatabaseUnitTests/RNFBDatabaseListenerRegistryStorageTests.swift +86 -0
  50. package/ios/RNFBDatabaseUnitTests/RNFBDatabaseNullSentinelDecodeTests.m +118 -0
  51. package/ios/RNFBDatabaseUnitTests/RNFBDatabaseQueryRegistryStorageTests.swift +181 -0
  52. package/ios/RNFBDatabaseUnitTests/RNFBDatabaseUnitTests.xcodeproj/project.pbxproj +42 -0
  53. package/ios/generated/RNFBDatabaseTurboModules/RNFBDatabaseTurboModules-generated.mm +7 -0
  54. package/ios/generated/RNFBDatabaseTurboModules/RNFBDatabaseTurboModules.h +6 -0
  55. package/ios/generated/RNFBDatabaseTurboModulesJSI.h +12 -0
  56. package/lib/DatabaseQuery.ts +56 -0
  57. package/lib/DatabaseSyncTree.ts +1 -1
  58. package/lib/index.ts +1 -1
  59. package/lib/modular/query.ts +1 -1
  60. package/lib/types/internal.ts +2 -0
  61. package/lib/version.ts +1 -1
  62. package/lib/web/RNFBDatabaseModule.ts +13 -0
  63. package/package.json +4 -4
  64. package/specs/NativeRNFBTurboDatabaseQuery.ts +1 -0
  65. package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseListenerRegistry.java +0 -117
  66. package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseQueryRegistry.java +0 -78
  67. package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseTransactionRegistry.java +0 -71
@@ -0,0 +1,36 @@
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
+ /**
21
+ * Decodes New Architecture null sentinels before Realtime Database writes. Used by the
22
+ * reference, onDisconnect and transaction helpers. Foundation only so unit tests can compile it
23
+ * without the Firebase SDK.
24
+ */
25
+ @interface RNFBDatabaseNullSentinelDecoder : NSObject
26
+
27
+ /** Top-level set(null) arrives as props.value = { __rnfbNull: true }. */
28
+ + (id)decodedValueFromProps:(NSDictionary *)props;
29
+
30
+ + (id)decodedValuesFromProps:(NSDictionary *)props;
31
+
32
+ + (id)decodedPriorityFromProps:(NSDictionary *)props;
33
+
34
+ + (id)decodedTransactionValue:(id)value;
35
+
36
+ @end
@@ -0,0 +1,48 @@
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 "RNFBDatabaseNullSentinelDecoder.h"
19
+
20
+ #if __has_include(<RNFBApp/RNFBApp-Swift.h>)
21
+ #import <RNFBApp/RNFBApp-Swift.h>
22
+ #elif __has_include("RNFBApp-Swift.h")
23
+ #import "RNFBApp-Swift.h"
24
+ #elif __has_include("RNFBHandleMapStorage-Swift.inc")
25
+ #import "RNFBHandleMapStorage-Swift.inc"
26
+ #else
27
+ #error "RNFBApp Swift interface not found"
28
+ #endif
29
+
30
+ @implementation RNFBDatabaseNullSentinelDecoder
31
+
32
+ + (id)decodedValueFromProps:(NSDictionary *)props {
33
+ return [RNFBNullSentinelDecoder decodeNullSentinels:[props valueForKey:@"value"]];
34
+ }
35
+
36
+ + (id)decodedValuesFromProps:(NSDictionary *)props {
37
+ return [RNFBNullSentinelDecoder decodeNullSentinels:[props valueForKey:@"values"]];
38
+ }
39
+
40
+ + (id)decodedPriorityFromProps:(NSDictionary *)props {
41
+ return [RNFBNullSentinelDecoder decodeNullSentinels:[props valueForKey:@"priority"]];
42
+ }
43
+
44
+ + (id)decodedTransactionValue:(id)value {
45
+ return [RNFBNullSentinelDecoder decodeNullSentinels:value];
46
+ }
47
+
48
+ @end
@@ -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 "RNFBDatabaseOnDisconnectHelper.h"
28
29
 
29
30
  @implementation RNFBDatabaseOnDisconnectHelper
@@ -79,14 +80,15 @@
79
80
  FIRDatabaseReference *firDatabaseReference =
80
81
  [RNFBDatabaseCommon getReferenceForDatabase:firDatabase path:path];
81
82
 
82
- [firDatabaseReference onDisconnectSetValue:[props valueForKey:@"value"]
83
- withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
84
- if (error != nil) {
85
- [RNFBDatabaseCommon promiseRejectDatabaseException:reject error:error];
86
- } else {
87
- resolve([NSNull null]);
88
- }
89
- }];
83
+ [firDatabaseReference
84
+ onDisconnectSetValue:[RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props]
85
+ withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
86
+ if (error != nil) {
87
+ [RNFBDatabaseCommon promiseRejectDatabaseException:reject error:error];
88
+ } else {
89
+ resolve([NSNull null]);
90
+ }
91
+ }];
90
92
  }
91
93
 
92
94
  + (void)onDisconnectSetWithPriority:(NSString *)app
@@ -100,15 +102,16 @@
100
102
  FIRDatabaseReference *firDatabaseReference =
101
103
  [RNFBDatabaseCommon getReferenceForDatabase:firDatabase path:path];
102
104
 
103
- [firDatabaseReference onDisconnectSetValue:[props valueForKey:@"value"]
104
- andPriority:[props valueForKey:@"priority"]
105
- withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
106
- if (error != nil) {
107
- [RNFBDatabaseCommon promiseRejectDatabaseException:reject error:error];
108
- } else {
109
- resolve([NSNull null]);
110
- }
111
- }];
105
+ [firDatabaseReference
106
+ onDisconnectSetValue:[RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props]
107
+ andPriority:[RNFBDatabaseNullSentinelDecoder decodedPriorityFromProps:props]
108
+ withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
109
+ if (error != nil) {
110
+ [RNFBDatabaseCommon promiseRejectDatabaseException:reject error:error];
111
+ } else {
112
+ resolve([NSNull null]);
113
+ }
114
+ }];
112
115
  }
113
116
 
114
117
  + (void)onDisconnectUpdate:(NSString *)app
@@ -122,15 +125,15 @@
122
125
  FIRDatabaseReference *firDatabaseReference =
123
126
  [RNFBDatabaseCommon getReferenceForDatabase:firDatabase path:path];
124
127
 
125
- [firDatabaseReference onDisconnectUpdateChildValues:[props valueForKey:@"values"]
126
- withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
127
- if (error != nil) {
128
- [RNFBDatabaseCommon promiseRejectDatabaseException:reject
129
- error:error];
130
- } else {
131
- resolve([NSNull null]);
132
- }
133
- }];
128
+ [firDatabaseReference
129
+ onDisconnectUpdateChildValues:[RNFBDatabaseNullSentinelDecoder decodedValuesFromProps:props]
130
+ withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
131
+ if (error != nil) {
132
+ [RNFBDatabaseCommon promiseRejectDatabaseException:reject error:error];
133
+ } else {
134
+ resolve([NSNull null]);
135
+ }
136
+ }];
134
137
  }
135
138
 
136
139
  @end
@@ -31,6 +31,13 @@
31
31
  resolve:(RCTPromiseResolveBlock)resolve
32
32
  reject:(RCTPromiseRejectBlock)reject;
33
33
 
34
+ + (void)get:(NSString *)app
35
+ dbURL:(NSString *)dbURL
36
+ path:(NSString *)path
37
+ modifiers:(NSArray *)modifiers
38
+ resolve:(RCTPromiseResolveBlock)resolve
39
+ reject:(RCTPromiseRejectBlock)reject;
40
+
34
41
  + (void)on:(NSString *)app dbURL:(NSString *)dbURL props:(NSDictionary *)props;
35
42
 
36
43
  + (void)off:(NSString *)queryKey eventRegistrationKey:(NSString *)eventRegistrationKey;
@@ -191,6 +191,26 @@ static NSString *const RNFB_DATABASE_SYNC = @"database_sync_event";
191
191
  [self addOnceEventListener:databaseQuery eventType:eventType resolve:resolve reject:reject];
192
192
  }
193
193
 
194
+ + (void)get:(NSString *)app
195
+ dbURL:(NSString *)dbURL
196
+ path:(NSString *)path
197
+ modifiers:(NSArray *)modifiers
198
+ resolve:(RCTPromiseResolveBlock)resolve
199
+ reject:(RCTPromiseRejectBlock)reject {
200
+ // A single-event observer, not getDataWithCompletionBlock:. FirebaseDatabase's getData
201
+ // returns a covering ancestor's whole node for a child path (firebase/firebase-ios-sdk#12168),
202
+ // never completes a get in flight when the socket drops (firebase/firebase-ios-sdk#16717)
203
+ // and crashes on an error reply without a reason (firebase/firebase-ios-sdk#16718).
204
+ // Switch this to getData once the SDK RNFB depends on fixes all three.
205
+ [self once:app
206
+ dbURL:dbURL
207
+ path:path
208
+ modifiers:modifiers
209
+ eventType:@"value"
210
+ resolve:resolve
211
+ reject:reject];
212
+ }
213
+
194
214
  + (void)on:(NSString *)app dbURL:(NSString *)dbURL props:(NSDictionary *)props {
195
215
  NSString *key = [props valueForKey:@"key"];
196
216
  NSString *path = [props valueForKey:@"path"];
@@ -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
  }
@@ -23,12 +23,18 @@
23
23
  #import "RNFBApp/RNFBHandleMap.h"
24
24
  #endif
25
25
 
26
- @protocol RNFBDatabaseHasListeners
27
- - (BOOL)hasListeners;
28
- @end
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
29
35
 
30
36
  @interface RNFBDatabaseQueryRegistry ()
31
- @property(nonatomic, strong) RNFBHandleMap *map;
37
+ @property(nonatomic, strong) RNFBDatabaseQueryRegistryStorage *storage;
32
38
  @end
33
39
 
34
40
  @implementation RNFBDatabaseQueryRegistry
@@ -36,70 +42,38 @@
36
42
  - (instancetype)init {
37
43
  self = [super init];
38
44
  if (self) {
39
- _map = [[RNFBHandleMap alloc] init];
45
+ _storage = [[RNFBDatabaseQueryRegistryStorage alloc] init];
40
46
  }
41
47
  return self;
42
48
  }
43
49
 
44
- - (void)rnfb_removeAllListeners:(id)query {
45
- if (query && [query respondsToSelector:@selector(removeAllEventListeners)]) {
46
- #pragma clang diagnostic push
47
- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
48
- [query performSelector:@selector(removeAllEventListeners)];
49
- #pragma clang diagnostic pop
50
- }
51
- }
52
-
53
50
  - (BOOL)put:(id)key value:(id)value error:(NSError **)error {
54
- return [self.map put:key value:value error: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;
55
61
  }
56
62
 
57
63
  - (id)get:(id)key {
58
- return [self.map get:key];
64
+ return [self.storage get:key];
59
65
  }
60
66
 
61
67
  - (id)take:(id)key {
62
- return [self.map take:key];
68
+ return [self.storage take:key];
63
69
  }
64
70
 
65
71
  - (id)takeIfIdle:(id)key {
66
- // Read hasListeners outside the HandleMap lock (avoids nesting listener occupancy), then
67
- // identity-take and put-back if listeners appeared.
68
- id query = [self.map get:key];
69
- if (query == nil) {
70
- return nil;
71
- }
72
- if (![query respondsToSelector:@selector(hasListeners)]) {
73
- return nil;
74
- }
75
- if ([(id<RNFBDatabaseHasListeners>)query hasListeners]) {
76
- return nil;
77
- }
78
- id taken = [self.map takeIf:key
79
- when:^BOOL(id value) {
80
- return value == query;
81
- }];
82
- if (taken == nil) {
83
- return nil;
84
- }
85
- if ([taken respondsToSelector:@selector(hasListeners)] &&
86
- [(id<RNFBDatabaseHasListeners>)taken hasListeners]) {
87
- // Prefer put-back so an active query stays registered. If a concurrent put claimed the
88
- // slot, putIfAbsent fails and this taken query would be an orphan with listeners — clear
89
- // them so SDK callbacks are not left attached outside the registry.
90
- if (![self.map putIfAbsent:key value:taken]) {
91
- [self rnfb_removeAllListeners:taken];
92
- }
93
- return nil;
94
- }
95
- return taken;
72
+ return [self.storage takeIfIdle:key];
96
73
  }
97
74
 
98
75
  - (void)removeAll {
99
- NSArray *queries = [self.map takeAll];
100
- for (id query in queries) {
101
- [self rnfb_removeAllListeners:query];
102
- }
76
+ [self.storage removeAll];
103
77
  }
104
78
 
105
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
  };