@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.
- package/CHANGELOG.md +25 -0
- package/RNFBDatabase.podspec +8 -2
- package/android/build.gradle +5 -0
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseKeepSyncedRegistry.java +106 -0
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseListenerRegistry.kt +133 -0
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseQueryRegistry.kt +76 -0
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseTransactionRegistry.kt +67 -0
- package/android/src/main/java/io/invertase/firebase/database/UniversalFirebaseDatabaseOnDisconnectModule.java +102 -9
- package/android/src/reactnative/java/io/invertase/firebase/database/NativeRNFBTurboDatabaseOnDisconnect.java +6 -11
- package/android/src/reactnative/java/io/invertase/firebase/database/NativeRNFBTurboDatabaseQuery.java +93 -2
- package/android/src/reactnative/java/io/invertase/firebase/database/generated/java/com/facebook/fbreact/specs/NativeRNFBTurboDatabaseQuerySpec.java +4 -0
- package/android/src/reactnative/java/io/invertase/firebase/database/generated/jni/RNFBDatabaseTurboModules-generated.cpp +6 -0
- package/android/src/reactnative/java/io/invertase/firebase/database/generated/jni/react/renderer/components/RNFBDatabaseTurboModules/RNFBDatabaseTurboModulesJSI.h +12 -0
- package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseKeepSyncedRegistryTest.java +174 -0
- package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseListenerRegistryTest.java +31 -0
- package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseQueryRegistryTest.java +55 -0
- package/android/src/test/java/io/invertase/firebase/database/UniversalFirebaseDatabaseOnDisconnectCancelErrorTest.java +137 -0
- package/dist/module/DatabaseQuery.js +52 -1
- package/dist/module/DatabaseQuery.js.map +1 -1
- package/dist/module/DatabaseSyncTree.js +1 -1
- package/dist/module/DatabaseSyncTree.js.map +1 -1
- package/dist/module/index.js +1 -2
- package/dist/module/index.js.map +1 -1
- package/dist/module/modular/query.js +1 -1
- package/dist/module/modular/query.js.map +1 -1
- package/dist/module/version.js +1 -1
- package/dist/module/web/RNFBDatabaseModule.js +7 -0
- package/dist/module/web/RNFBDatabaseModule.js.map +1 -1
- package/dist/typescript/lib/DatabaseQuery.d.ts +25 -0
- package/dist/typescript/lib/DatabaseQuery.d.ts.map +1 -1
- package/dist/typescript/lib/index.d.ts +1 -1
- package/dist/typescript/lib/types/internal.d.ts +2 -0
- package/dist/typescript/lib/types/internal.d.ts.map +1 -1
- package/dist/typescript/lib/version.d.ts +1 -1
- package/dist/typescript/lib/web/RNFBDatabaseModule.d.ts.map +1 -1
- package/ios/RNFBDatabase/RNFBDatabaseListenerRegistry.m +24 -17
- package/ios/RNFBDatabase/RNFBDatabaseListenerRegistryStorage.swift +81 -0
- package/ios/RNFBDatabase/RNFBDatabaseNullSentinelDecoder.h +36 -0
- package/ios/RNFBDatabase/RNFBDatabaseNullSentinelDecoder.m +48 -0
- package/ios/RNFBDatabase/RNFBDatabaseOnDisconnectHelper.m +29 -26
- package/ios/RNFBDatabase/RNFBDatabaseQueryHelper.h +7 -0
- package/ios/RNFBDatabase/RNFBDatabaseQueryHelper.m +20 -0
- package/ios/RNFBDatabase/RNFBDatabaseQueryModule.mm +14 -0
- package/ios/RNFBDatabase/RNFBDatabaseQueryRegistry.m +25 -51
- package/ios/RNFBDatabase/RNFBDatabaseQueryRegistryStorage.swift +104 -0
- package/ios/RNFBDatabase/RNFBDatabaseReferenceHelper.m +14 -12
- package/ios/RNFBDatabase/RNFBDatabaseTransactionHelper.m +3 -1
- package/ios/RNFBDatabase.xcodeproj/project.pbxproj +12 -0
- package/ios/RNFBDatabaseUnitTests/RNFBDatabaseListenerRegistryStorageTests.swift +86 -0
- package/ios/RNFBDatabaseUnitTests/RNFBDatabaseNullSentinelDecodeTests.m +118 -0
- package/ios/RNFBDatabaseUnitTests/RNFBDatabaseQueryRegistryStorageTests.swift +181 -0
- package/ios/RNFBDatabaseUnitTests/RNFBDatabaseUnitTests.xcodeproj/project.pbxproj +42 -0
- package/ios/generated/RNFBDatabaseTurboModules/RNFBDatabaseTurboModules-generated.mm +7 -0
- package/ios/generated/RNFBDatabaseTurboModules/RNFBDatabaseTurboModules.h +6 -0
- package/ios/generated/RNFBDatabaseTurboModulesJSI.h +12 -0
- package/lib/DatabaseQuery.ts +56 -0
- package/lib/DatabaseSyncTree.ts +1 -1
- package/lib/index.ts +1 -1
- package/lib/modular/query.ts +1 -1
- package/lib/types/internal.ts +2 -0
- package/lib/version.ts +1 -1
- package/lib/web/RNFBDatabaseModule.ts +13 -0
- package/package.json +4 -4
- package/specs/NativeRNFBTurboDatabaseQuery.ts +1 -0
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseListenerRegistry.java +0 -117
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseQueryRegistry.java +0 -78
- 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
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
27
|
-
-
|
|
28
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
64
|
+
return [self.storage get:key];
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
- (id)take:(id)key {
|
|
62
|
-
return [self.
|
|
68
|
+
return [self.storage take:key];
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
- (id)takeIfIdle:(id)key {
|
|
66
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
85
|
-
andPriority:[props
|
|
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
|
|
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 =
|
|
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
|
};
|