@react-native-firebase/auth 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.
- package/CHANGELOG.md +27 -0
- package/RNFBAuth.podspec +8 -2
- package/android/build.gradle +5 -0
- package/android/src/main/java/io/invertase/firebase/auth/AuthListenerHandle.java +26 -0
- package/android/src/main/java/io/invertase/firebase/auth/NativeRNFBTurboAuth.java +68 -102
- package/android/src/main/java/io/invertase/firebase/auth/RNFBAuthCacheRegistry.kt +77 -0
- package/android/src/main/java/io/invertase/firebase/auth/RNFBAuthListenerRegistry.kt +76 -0
- package/android/src/test/java/io/invertase/firebase/auth/RNFBAuthCacheRegistryTest.java +184 -0
- package/android/src/test/java/io/invertase/firebase/auth/RNFBAuthListenerRegistryTest.java +171 -0
- package/dist/module/ActionCodeURL.js +1 -1
- package/dist/module/ActionCodeURL.js.map +1 -1
- package/dist/module/index.js +1 -2
- package/dist/module/index.js.map +1 -1
- package/dist/module/version.js +1 -1
- package/dist/typescript/lib/ActionCodeURL.d.ts.map +1 -1
- package/dist/typescript/lib/types/internal.d.ts +2 -3
- package/dist/typescript/lib/types/internal.d.ts.map +1 -1
- package/dist/typescript/lib/version.d.ts +1 -1
- package/ios/RNFBAuth/RNFBAuthCacheRegistry.h +41 -0
- package/ios/RNFBAuth/RNFBAuthCacheRegistry.m +84 -0
- package/ios/RNFBAuth/RNFBAuthCacheRegistryStorage.swift +53 -0
- package/ios/RNFBAuth/RNFBAuthErrorCodeMapper.swift +333 -0
- package/ios/RNFBAuth/RNFBAuthHelper.m +147 -262
- package/ios/RNFBAuth/RNFBAuthListenerRegistry.h +39 -0
- package/ios/RNFBAuth/RNFBAuthListenerRegistry.m +83 -0
- package/ios/RNFBAuth/RNFBAuthListenerRegistryStorage.swift +76 -0
- package/ios/RNFBAuth/RNFBAuthNullSentinelDecoder.h +40 -0
- package/ios/RNFBAuth/RNFBAuthNullSentinelDecoder.m +49 -0
- package/ios/RNFBAuth.xcodeproj/project.pbxproj +22 -0
- package/ios/RNFBAuthUnitTests/RNFBAuthCacheRegistryStorageTests.swift +72 -0
- package/ios/RNFBAuthUnitTests/RNFBAuthCacheRegistryTests.m +131 -0
- package/ios/RNFBAuthUnitTests/RNFBAuthErrorCodeMapperTests.swift +250 -0
- package/ios/RNFBAuthUnitTests/RNFBAuthListenerRegistryStorageTests.swift +122 -0
- package/ios/RNFBAuthUnitTests/RNFBAuthListenerRegistryTests.m +152 -0
- package/ios/RNFBAuthUnitTests/RNFBAuthNullSentinelDecodeTests.m +78 -0
- package/ios/RNFBAuthUnitTests/RNFBAuthUnitTests.xcodeproj/project.pbxproj +311 -0
- package/ios/RNFBAuthUnitTests/RNFBAuthUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBAuthUnitTests.xcscheme +69 -0
- package/lib/ActionCodeURL.ts +1 -2
- package/lib/index.ts +1 -1
- package/lib/types/internal.ts +2 -2
- package/lib/version.ts +1 -1
- package/package.json +5 -4
- package/plugin/build/index.js +4 -0
- package/plugin/build/ios/index.d.ts +2 -1
- package/plugin/build/ios/index.js +3 -1
- package/plugin/build/ios/openUrlFix.js +12 -0
- package/plugin/build/ios/sceneDelegate.d.ts +34 -0
- package/plugin/build/ios/sceneDelegate.js +127 -0
- package/plugin/build/ios/sceneLifecycle.d.ts +32 -0
- package/plugin/build/ios/sceneLifecycle.js +115 -0
- package/plugin/src/index.ts +9 -1
- package/plugin/src/ios/index.ts +2 -1
- package/plugin/src/ios/openUrlFix.ts +15 -0
- package/plugin/src/ios/sceneDelegate.ts +159 -0
- package/plugin/src/ios/sceneLifecycle.ts +134 -0
- package/plugin/tsconfig.tsbuildinfo +1 -1
|
@@ -36,7 +36,35 @@
|
|
|
36
36
|
#import "RNFBApp/RCTConvert+FIRApp.h"
|
|
37
37
|
#import "RNFBApp/RNFBAppModule.h"
|
|
38
38
|
#import "RNFBApp/RNFBSharedUtils.h"
|
|
39
|
+
#import "RNFBAuthCacheRegistry.h"
|
|
39
40
|
#import "RNFBAuthHelper.h"
|
|
41
|
+
#import "RNFBAuthListenerRegistry.h"
|
|
42
|
+
#import "RNFBAuthNullSentinelDecoder.h"
|
|
43
|
+
|
|
44
|
+
#if __has_include(<RNFBAuth/RNFBAuth-Swift.h>)
|
|
45
|
+
#import <RNFBAuth/RNFBAuth-Swift.h>
|
|
46
|
+
#elif __has_include("RNFBAuth-Swift.h")
|
|
47
|
+
#import "RNFBAuth-Swift.h"
|
|
48
|
+
#elif __has_include("RNFBHandleMapStorage-Swift.inc")
|
|
49
|
+
#import "RNFBHandleMapStorage-Swift.inc"
|
|
50
|
+
#else
|
|
51
|
+
#error "RNFBAuthErrorCodeMapper Swift interface not found"
|
|
52
|
+
#endif
|
|
53
|
+
|
|
54
|
+
@interface RNFBAuthListenerRemover : NSObject
|
|
55
|
+
@property(nonatomic, copy, nullable) void (^onRemove)(void);
|
|
56
|
+
- (void)remove;
|
|
57
|
+
@end
|
|
58
|
+
|
|
59
|
+
@implementation RNFBAuthListenerRemover
|
|
60
|
+
- (void)remove {
|
|
61
|
+
if (self.onRemove != nil) {
|
|
62
|
+
void (^block)(void) = self.onRemove;
|
|
63
|
+
self.onRemove = nil;
|
|
64
|
+
block();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
@end
|
|
40
68
|
|
|
41
69
|
static void RNFBAuthThrowSyncErrorWithCode(NSString *code, NSString *message) {
|
|
42
70
|
@throw [NSException exceptionWithName:code
|
|
@@ -71,77 +99,15 @@ static NSString *const AUTH_STATE_CHANGED_EVENT = @"auth_state_changed";
|
|
|
71
99
|
static NSString *const AUTH_ID_TOKEN_CHANGED_EVENT = @"auth_id_token_changed";
|
|
72
100
|
static NSString *const PHONE_AUTH_STATE_CHANGED_EVENT = @"phone_auth_state_changed";
|
|
73
101
|
|
|
74
|
-
static
|
|
75
|
-
|
|
76
|
-
[FIRAuthErrorCodeCustomTokenMismatch] = @"custom-token-mismatch",
|
|
77
|
-
[FIRAuthErrorCodeInvalidCredential] = @"invalid-credential",
|
|
78
|
-
[FIRAuthErrorCodeUserDisabled] = @"user-disabled",
|
|
79
|
-
[FIRAuthErrorCodeOperationNotAllowed] = @"operation-not-allowed",
|
|
80
|
-
[FIRAuthErrorCodeEmailAlreadyInUse] = @"email-already-in-use",
|
|
81
|
-
[FIRAuthErrorCodeInvalidEmail] = @"invalid-email",
|
|
82
|
-
[FIRAuthErrorCodeWrongPassword] = @"wrong-password",
|
|
83
|
-
[FIRAuthErrorCodeTooManyRequests] = @"too-many-requests",
|
|
84
|
-
[FIRAuthErrorCodeUserNotFound] = @"user-not-found",
|
|
85
|
-
[FIRAuthErrorCodeAccountExistsWithDifferentCredential] =
|
|
86
|
-
@"account-exists-with-different-credential",
|
|
87
|
-
[FIRAuthErrorCodeRequiresRecentLogin] = @"requires-recent-login",
|
|
88
|
-
[FIRAuthErrorCodeProviderAlreadyLinked] = @"provider-already-linked",
|
|
89
|
-
[FIRAuthErrorCodeNoSuchProvider] = @"no-such-provider",
|
|
90
|
-
[FIRAuthErrorCodeInvalidUserToken] = @"invalid-user-token",
|
|
91
|
-
[FIRAuthErrorCodeNetworkError] = @"network-request-failed",
|
|
92
|
-
[FIRAuthErrorCodeUserTokenExpired] = @"user-token-expired",
|
|
93
|
-
[FIRAuthErrorCodeInvalidAPIKey] = @"invalid-api-key",
|
|
94
|
-
[FIRAuthErrorCodeUserMismatch] = @"user-mismatch",
|
|
95
|
-
[FIRAuthErrorCodeCredentialAlreadyInUse] = @"credential-already-in-use",
|
|
96
|
-
[FIRAuthErrorCodeWeakPassword] = @"weak-password",
|
|
97
|
-
[FIRAuthErrorCodeAppNotAuthorized] = @"app-not-authorized",
|
|
98
|
-
[FIRAuthErrorCodeExpiredActionCode] = @"expired-action-code",
|
|
99
|
-
[FIRAuthErrorCodeInvalidActionCode] = @"invalid-action-code",
|
|
100
|
-
[FIRAuthErrorCodeInvalidMessagePayload] = @"invalid-message-payload",
|
|
101
|
-
[FIRAuthErrorCodeInvalidSender] = @"invalid-sender",
|
|
102
|
-
[FIRAuthErrorCodeInvalidRecipientEmail] = @"invalid-recipient-email",
|
|
103
|
-
[FIRAuthErrorCodeMissingEmail] = @"invalid-email",
|
|
104
|
-
[FIRAuthErrorCodeMissingIosBundleID] = @"missing-ios-bundle-id",
|
|
105
|
-
[FIRAuthErrorCodeMissingAndroidPackageName] = @"missing-android-pkg-name",
|
|
106
|
-
[FIRAuthErrorCodeUnauthorizedDomain] = @"unauthorized-domain",
|
|
107
|
-
[FIRAuthErrorCodeInvalidContinueURI] = @"invalid-continue-uri",
|
|
108
|
-
[FIRAuthErrorCodeMissingContinueURI] = @"missing-continue-uri",
|
|
109
|
-
[FIRAuthErrorCodeMissingPhoneNumber] = @"missing-phone-number",
|
|
110
|
-
[FIRAuthErrorCodeInvalidPhoneNumber] = @"invalid-phone-number",
|
|
111
|
-
[FIRAuthErrorCodeMissingVerificationCode] = @"missing-verification-code",
|
|
112
|
-
[FIRAuthErrorCodeInvalidVerificationCode] = @"invalid-verification-code",
|
|
113
|
-
[FIRAuthErrorCodeMissingVerificationID] = @"missing-verification-id",
|
|
114
|
-
[FIRAuthErrorCodeInvalidVerificationID] = @"invalid-verification-id",
|
|
115
|
-
[FIRAuthErrorCodeMissingAppCredential] = @"missing-app-credential",
|
|
116
|
-
[FIRAuthErrorCodeInvalidAppCredential] = @"invalid-app-credential",
|
|
117
|
-
[FIRAuthErrorCodeSessionExpired] = @"code-expired",
|
|
118
|
-
[FIRAuthErrorCodeQuotaExceeded] = @"quota-exceeded",
|
|
119
|
-
[FIRAuthErrorCodeMissingAppToken] = @"missing-apns-token",
|
|
120
|
-
[FIRAuthErrorCodeNotificationNotForwarded] = @"notification-not-forwarded",
|
|
121
|
-
[FIRAuthErrorCodeAppNotVerified] = @"app-not-verified",
|
|
122
|
-
[FIRAuthErrorCodeCaptchaCheckFailed] = @"captcha-check-failed",
|
|
123
|
-
[FIRAuthErrorCodeWebContextAlreadyPresented] = @"cancelled-popup-request",
|
|
124
|
-
[FIRAuthErrorCodeWebContextCancelled] = @"popup-closed-by-user",
|
|
125
|
-
[FIRAuthErrorCodeAppVerificationUserInteractionFailure] =
|
|
126
|
-
@"app-verification-user-interaction-failure",
|
|
127
|
-
[FIRAuthErrorCodeInvalidClientID] = @"invalid-oauth-client-id",
|
|
128
|
-
[FIRAuthErrorCodeWebNetworkRequestFailed] = @"network-request-failed",
|
|
129
|
-
[FIRAuthErrorCodeWebInternalError] = @"internal-error",
|
|
130
|
-
[FIRAuthErrorCodeNullUser] = @"null-user",
|
|
131
|
-
[FIRAuthErrorCodeKeychainError] = @"keychain-error",
|
|
132
|
-
[FIRAuthErrorCodeInternalError] = @"internal-error",
|
|
133
|
-
[FIRAuthErrorCodeMalformedJWT] = @"malformed-jwt",
|
|
134
|
-
[FIRAuthErrorCodeSecondFactorRequired] = @"multi-factor-auth-required"};
|
|
135
|
-
|
|
136
|
-
static __strong NSMutableDictionary *authStateHandlers;
|
|
137
|
-
static __strong NSMutableDictionary *idTokenHandlers;
|
|
102
|
+
static __strong RNFBAuthListenerRegistry *authStateHandlers;
|
|
103
|
+
static __strong RNFBAuthListenerRegistry *idTokenHandlers;
|
|
138
104
|
static __strong NSMutableDictionary *emulatorConfigs;
|
|
139
105
|
// Used for caching credentials between method calls.
|
|
140
|
-
static __strong
|
|
106
|
+
static __strong RNFBAuthCacheRegistry *credentials;
|
|
141
107
|
#if TARGET_OS_IOS
|
|
142
|
-
static __strong
|
|
143
|
-
static __strong
|
|
144
|
-
static __strong
|
|
108
|
+
static __strong RNFBAuthCacheRegistry *cachedResolver;
|
|
109
|
+
static __strong RNFBAuthCacheRegistry *cachedSessions;
|
|
110
|
+
static __strong RNFBAuthCacheRegistry *cachedTotpSecrets;
|
|
145
111
|
#endif
|
|
146
112
|
|
|
147
113
|
@interface RNFBAuthHelper ()
|
|
@@ -200,14 +166,14 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
200
166
|
+ (void)initializeSharedStateOnce {
|
|
201
167
|
static dispatch_once_t onceToken;
|
|
202
168
|
dispatch_once(&onceToken, ^{
|
|
203
|
-
authStateHandlers = [[
|
|
204
|
-
idTokenHandlers = [[
|
|
169
|
+
authStateHandlers = [[RNFBAuthListenerRegistry alloc] init];
|
|
170
|
+
idTokenHandlers = [[RNFBAuthListenerRegistry alloc] init];
|
|
205
171
|
emulatorConfigs = [[NSMutableDictionary alloc] init];
|
|
206
|
-
credentials = [[
|
|
172
|
+
credentials = [[RNFBAuthCacheRegistry alloc] init];
|
|
207
173
|
#if TARGET_OS_IOS
|
|
208
|
-
cachedResolver = [[
|
|
209
|
-
cachedSessions = [[
|
|
210
|
-
cachedTotpSecrets = [[
|
|
174
|
+
cachedResolver = [[RNFBAuthCacheRegistry alloc] init];
|
|
175
|
+
cachedSessions = [[RNFBAuthCacheRegistry alloc] init];
|
|
176
|
+
cachedTotpSecrets = [[RNFBAuthCacheRegistry alloc] init];
|
|
211
177
|
#endif
|
|
212
178
|
});
|
|
213
179
|
}
|
|
@@ -215,26 +181,14 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
215
181
|
+ (void)invalidate {
|
|
216
182
|
[self initializeSharedStateOnce];
|
|
217
183
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
[[FIRAuth authWithApp:firebaseApp]
|
|
222
|
-
removeAuthStateDidChangeListener:[authStateHandlers valueForKey:key]];
|
|
223
|
-
}
|
|
224
|
-
[authStateHandlers removeAllObjects];
|
|
225
|
-
|
|
226
|
-
for (NSString *key in idTokenHandlers) {
|
|
227
|
-
FIRApp *firebaseApp = [RCTConvert firAppFromString:key];
|
|
228
|
-
[[FIRAuth authWithApp:firebaseApp]
|
|
229
|
-
removeIDTokenDidChangeListener:[idTokenHandlers valueForKey:key]];
|
|
230
|
-
}
|
|
231
|
-
[idTokenHandlers removeAllObjects];
|
|
184
|
+
[authStateHandlers removeAll];
|
|
185
|
+
[idTokenHandlers removeAll];
|
|
232
186
|
|
|
233
|
-
[credentials
|
|
187
|
+
[credentials clear];
|
|
234
188
|
#if TARGET_OS_IOS
|
|
235
|
-
[cachedResolver
|
|
236
|
-
[cachedSessions
|
|
237
|
-
[cachedTotpSecrets
|
|
189
|
+
[cachedResolver clear];
|
|
190
|
+
[cachedSessions clear];
|
|
191
|
+
[cachedTotpSecrets clear];
|
|
238
192
|
#endif
|
|
239
193
|
}
|
|
240
194
|
|
|
@@ -245,68 +199,75 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
245
199
|
[self initializeSharedStateOnce];
|
|
246
200
|
FIRApp *firebaseApp = [RCTConvert firAppFromString:appName];
|
|
247
201
|
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) {
|
|
251
|
-
if (user != nil) {
|
|
252
|
-
[RNFBSharedUtils sendJSEventForApp:firebaseApp
|
|
253
|
-
name:AUTH_STATE_CHANGED_EVENT
|
|
254
|
-
body:@{keyUser : [self firebaseUserToDict:user]}];
|
|
255
|
-
} else {
|
|
256
|
-
[RNFBSharedUtils sendJSEventForApp:firebaseApp name:AUTH_STATE_CHANGED_EVENT body:@{}];
|
|
257
|
-
}
|
|
258
|
-
}];
|
|
259
|
-
authStateHandlers[firebaseApp.name] = newListenerHandle;
|
|
202
|
+
if ([authStateHandlers get:firebaseApp.name] != nil) {
|
|
203
|
+
return;
|
|
260
204
|
}
|
|
205
|
+
|
|
206
|
+
FIRAuth *auth = [FIRAuth authWithApp:firebaseApp];
|
|
207
|
+
FIRAuthStateDidChangeListenerHandle newListenerHandle = [auth
|
|
208
|
+
addAuthStateDidChangeListener:^(FIRAuth *_Nonnull authInstance, FIRUser *_Nullable user) {
|
|
209
|
+
if (user != nil) {
|
|
210
|
+
[RNFBSharedUtils sendJSEventForApp:firebaseApp
|
|
211
|
+
name:AUTH_STATE_CHANGED_EVENT
|
|
212
|
+
body:@{keyUser : [self firebaseUserToDict:user]}];
|
|
213
|
+
} else {
|
|
214
|
+
[RNFBSharedUtils sendJSEventForApp:firebaseApp name:AUTH_STATE_CHANGED_EVENT body:@{}];
|
|
215
|
+
}
|
|
216
|
+
}];
|
|
217
|
+
|
|
218
|
+
RNFBAuthListenerRemover *remover = [[RNFBAuthListenerRemover alloc] init];
|
|
219
|
+
remover.onRemove = ^{
|
|
220
|
+
[auth removeAuthStateDidChangeListener:newListenerHandle];
|
|
221
|
+
};
|
|
222
|
+
[authStateHandlers putOrDiscard:firebaseApp.name value:remover];
|
|
261
223
|
}
|
|
262
224
|
|
|
263
225
|
+ (void)removeAuthStateListener:(NSString *)appName {
|
|
264
226
|
[self initializeSharedStateOnce];
|
|
265
227
|
FIRApp *firebaseApp = [RCTConvert firAppFromString:appName];
|
|
266
|
-
|
|
267
|
-
if ([authStateHandlers valueForKey:firebaseApp.name]) {
|
|
268
|
-
[[FIRAuth authWithApp:firebaseApp]
|
|
269
|
-
removeAuthStateDidChangeListener:[authStateHandlers valueForKey:firebaseApp.name]];
|
|
270
|
-
[authStateHandlers removeObjectForKey:firebaseApp.name];
|
|
271
|
-
}
|
|
228
|
+
[authStateHandlers takeAndRemove:firebaseApp.name];
|
|
272
229
|
}
|
|
273
230
|
|
|
274
231
|
+ (void)addIdTokenListener:(NSString *)appName {
|
|
275
232
|
[self initializeSharedStateOnce];
|
|
276
233
|
FIRApp *firebaseApp = [RCTConvert firAppFromString:appName];
|
|
277
234
|
|
|
278
|
-
if (
|
|
279
|
-
|
|
280
|
-
addIDTokenDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) {
|
|
281
|
-
if (user != nil) {
|
|
282
|
-
[RNFBSharedUtils sendJSEventForApp:firebaseApp
|
|
283
|
-
name:AUTH_ID_TOKEN_CHANGED_EVENT
|
|
284
|
-
body:@{keyUser : [self firebaseUserToDict:user]}];
|
|
285
|
-
} else {
|
|
286
|
-
[RNFBSharedUtils sendJSEventForApp:firebaseApp
|
|
287
|
-
name:AUTH_ID_TOKEN_CHANGED_EVENT
|
|
288
|
-
body:@{}];
|
|
289
|
-
}
|
|
290
|
-
}];
|
|
291
|
-
idTokenHandlers[firebaseApp.name] = newListenerHandle;
|
|
235
|
+
if ([idTokenHandlers get:firebaseApp.name] != nil) {
|
|
236
|
+
return;
|
|
292
237
|
}
|
|
238
|
+
|
|
239
|
+
FIRAuth *auth = [FIRAuth authWithApp:firebaseApp];
|
|
240
|
+
FIRIDTokenDidChangeListenerHandle newListenerHandle =
|
|
241
|
+
[auth addIDTokenDidChangeListener:^(FIRAuth *_Nonnull authInstance, FIRUser *_Nullable user) {
|
|
242
|
+
if (user != nil) {
|
|
243
|
+
[RNFBSharedUtils sendJSEventForApp:firebaseApp
|
|
244
|
+
name:AUTH_ID_TOKEN_CHANGED_EVENT
|
|
245
|
+
body:@{keyUser : [self firebaseUserToDict:user]}];
|
|
246
|
+
} else {
|
|
247
|
+
[RNFBSharedUtils sendJSEventForApp:firebaseApp name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{}];
|
|
248
|
+
}
|
|
249
|
+
}];
|
|
250
|
+
|
|
251
|
+
RNFBAuthListenerRemover *remover = [[RNFBAuthListenerRemover alloc] init];
|
|
252
|
+
remover.onRemove = ^{
|
|
253
|
+
[auth removeIDTokenDidChangeListener:newListenerHandle];
|
|
254
|
+
};
|
|
255
|
+
[idTokenHandlers putOrDiscard:firebaseApp.name value:remover];
|
|
293
256
|
}
|
|
294
257
|
|
|
295
258
|
+ (void)removeIdTokenListener:(NSString *)appName {
|
|
296
259
|
[self initializeSharedStateOnce];
|
|
297
260
|
FIRApp *firebaseApp = [RCTConvert firAppFromString:appName];
|
|
298
|
-
|
|
299
|
-
if ([idTokenHandlers valueForKey:firebaseApp.name]) {
|
|
300
|
-
[[FIRAuth authWithApp:firebaseApp]
|
|
301
|
-
removeIDTokenDidChangeListener:[idTokenHandlers valueForKey:firebaseApp.name]];
|
|
302
|
-
[idTokenHandlers removeObjectForKey:firebaseApp.name];
|
|
303
|
-
}
|
|
261
|
+
[idTokenHandlers takeAndRemove:firebaseApp.name];
|
|
304
262
|
}
|
|
305
263
|
|
|
306
264
|
+ (void)configureAuthDomain:(NSString *)appName {
|
|
307
265
|
FIRApp *firebaseApp = [RCTConvert firAppFromString:appName];
|
|
308
266
|
|
|
309
|
-
|
|
267
|
+
// customAuthDomains is keyed by the JS app name ([DEFAULT]); FIRApp.name for the default app
|
|
268
|
+
// is __FIRAPP_DEFAULT. Look up with the existing JS-name converter (no parallel key helper).
|
|
269
|
+
NSString *authDomain =
|
|
270
|
+
[RNFBAppModule getCustomDomain:[RNFBSharedUtils getAppJavaScriptName:firebaseApp.name]];
|
|
310
271
|
DLog(@"RNFBAuth app: %@ customAuthDomain: %@", firebaseApp.name, authDomain);
|
|
311
272
|
if (authDomain != nil) {
|
|
312
273
|
[FIRAuth authWithApp:firebaseApp].customAuthDomain = authDomain;
|
|
@@ -640,15 +601,18 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
640
601
|
|
|
641
602
|
if (user) {
|
|
642
603
|
FIRUserProfileChangeRequest *changeRequest = [user profileChangeRequest];
|
|
643
|
-
|
|
604
|
+
// The decoder omits NSNull keys (subscript → nil). Iterate the original props keys so a
|
|
605
|
+
// cleared displayName/photoURL still reaches setValue:forKey: with nil.
|
|
606
|
+
NSDictionary *decodedProps = [RNFBAuthNullSentinelDecoder decodedProfileProps:props];
|
|
644
607
|
|
|
645
|
-
for (NSString *key in
|
|
608
|
+
for (NSString *key in props) {
|
|
646
609
|
@try {
|
|
610
|
+
id value = decodedProps[key];
|
|
647
611
|
if ([key isEqualToString:keyPhotoUrl]) {
|
|
648
|
-
|
|
649
|
-
|
|
612
|
+
[changeRequest setValue:[RNFBAuthNullSentinelDecoder photoURLFromDecodedValue:value]
|
|
613
|
+
forKey:key];
|
|
650
614
|
} else {
|
|
651
|
-
[changeRequest setValue:
|
|
615
|
+
[changeRequest setValue:value forKey:key];
|
|
652
616
|
}
|
|
653
617
|
} @catch (NSException *exception) {
|
|
654
618
|
DLog(@"Exception occurred while configuring: %@", exception);
|
|
@@ -1054,7 +1018,8 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1054
1018
|
[self initializeSharedStateOnce];
|
|
1055
1019
|
FIRApp *firebaseApp = [RCTConvert firAppFromString:appName];
|
|
1056
1020
|
|
|
1057
|
-
|
|
1021
|
+
FIRMultiFactorResolver *resolver = [cachedResolver get:sessionKey];
|
|
1022
|
+
if (resolver == nil) {
|
|
1058
1023
|
[RNFBSharedUtils
|
|
1059
1024
|
rejectPromiseWithUserInfo:reject
|
|
1060
1025
|
userInfo:(NSMutableDictionary *)@{
|
|
@@ -1063,10 +1028,10 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1063
1028
|
}];
|
|
1064
1029
|
return;
|
|
1065
1030
|
}
|
|
1066
|
-
FIRMultiFactorSession *session =
|
|
1031
|
+
FIRMultiFactorSession *session = resolver.session;
|
|
1067
1032
|
NSPredicate *findByUid = [NSPredicate predicateWithFormat:@"UID == %@", hintUid];
|
|
1068
1033
|
FIRMultiFactorInfo *_Nullable hint =
|
|
1069
|
-
[[
|
|
1034
|
+
[[resolver.hints filteredArrayUsingPredicate:findByUid] firstObject];
|
|
1070
1035
|
if (hint == nil) {
|
|
1071
1036
|
[RNFBSharedUtils rejectPromiseWithUserInfo:reject
|
|
1072
1037
|
userInfo:(NSMutableDictionary *)@{
|
|
@@ -1087,8 +1052,7 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1087
1052
|
if (error) {
|
|
1088
1053
|
[self promiseRejectAuthException:reject error:error];
|
|
1089
1054
|
} else {
|
|
1090
|
-
|
|
1091
|
-
resolve(verificationID);
|
|
1055
|
+
resolve(verificationID);
|
|
1092
1056
|
}
|
|
1093
1057
|
}];
|
|
1094
1058
|
}
|
|
@@ -1102,9 +1066,7 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1102
1066
|
FIRApp *firebaseApp = [RCTConvert firAppFromString:appName];
|
|
1103
1067
|
|
|
1104
1068
|
DLog(@"verifyPhoneNumberForMultifactor using app: %@", firebaseApp.name);
|
|
1105
|
-
|
|
1106
|
-
DLog(@"verifyPhoneNumberForMultifactor sessionKey: %@", sessionKey);
|
|
1107
|
-
FIRMultiFactorSession *session = cachedSessions[sessionKey];
|
|
1069
|
+
FIRMultiFactorSession *session = [cachedSessions get:sessionKey];
|
|
1108
1070
|
if (session == nil) {
|
|
1109
1071
|
[RNFBSharedUtils rejectPromiseWithUserInfo:reject
|
|
1110
1072
|
userInfo:(NSMutableDictionary *)@{
|
|
@@ -1142,23 +1104,22 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1142
1104
|
[[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firebaseApp]]
|
|
1143
1105
|
credentialWithVerificationID:verificationId
|
|
1144
1106
|
verificationCode:verificationCode];
|
|
1145
|
-
DLog(@"credential: %@", credential);
|
|
1146
|
-
|
|
1147
1107
|
FIRMultiFactorAssertion *assertion =
|
|
1148
1108
|
[FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
|
|
1149
1109
|
|
|
1150
|
-
[cachedResolver
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1110
|
+
FIRMultiFactorResolver *signInResolver = [cachedResolver get:session];
|
|
1111
|
+
[signInResolver resolveSignInWithAssertion:assertion
|
|
1112
|
+
completion:^(FIRAuthDataResult *_Nullable authResult,
|
|
1113
|
+
NSError *_Nullable error) {
|
|
1114
|
+
DLog(@"authError: %@", error) if (error) {
|
|
1115
|
+
[self promiseRejectAuthException:reject error:error];
|
|
1116
|
+
}
|
|
1117
|
+
else {
|
|
1118
|
+
[self promiseWithAuthResult:resolve
|
|
1119
|
+
rejecter:reject
|
|
1120
|
+
authResult:authResult];
|
|
1121
|
+
}
|
|
1122
|
+
}];
|
|
1162
1123
|
}
|
|
1163
1124
|
|
|
1164
1125
|
+ (void)resolveTotpSignIn:(NSString *)appName
|
|
@@ -1175,19 +1136,19 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1175
1136
|
FIRMultiFactorAssertion *assertion =
|
|
1176
1137
|
[FIRTOTPMultiFactorGenerator assertionForSignInWithEnrollmentID:uid
|
|
1177
1138
|
oneTimePassword:oneTimePassword];
|
|
1178
|
-
[cachedResolver
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1139
|
+
FIRMultiFactorResolver *totpSignInResolver = [cachedResolver get:sessionKey];
|
|
1140
|
+
[totpSignInResolver resolveSignInWithAssertion:assertion
|
|
1141
|
+
completion:^(FIRAuthDataResult *_Nullable authResult,
|
|
1142
|
+
NSError *_Nullable error) {
|
|
1143
|
+
DLog(@"authError: %@", error) if (error) {
|
|
1144
|
+
[self promiseRejectAuthException:reject error:error];
|
|
1145
|
+
}
|
|
1146
|
+
else {
|
|
1147
|
+
[self promiseWithAuthResult:resolve
|
|
1148
|
+
rejecter:reject
|
|
1149
|
+
authResult:authResult];
|
|
1150
|
+
}
|
|
1151
|
+
}];
|
|
1191
1152
|
}
|
|
1192
1153
|
|
|
1193
1154
|
+ (void)generateTotpSecret:(NSString *)appName
|
|
@@ -1199,9 +1160,7 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1199
1160
|
|
|
1200
1161
|
DLog(@"using instance resolve generateTotpSecret: %@", firebaseApp.name);
|
|
1201
1162
|
|
|
1202
|
-
FIRMultiFactorSession *session = cachedSessions
|
|
1203
|
-
DLog(@"using sessionKey: %@", sessionKey);
|
|
1204
|
-
DLog(@"using session: %@", session);
|
|
1163
|
+
FIRMultiFactorSession *session = [cachedSessions get:sessionKey];
|
|
1205
1164
|
[FIRTOTPMultiFactorGenerator
|
|
1206
1165
|
generateSecretWithMultiFactorSession:session
|
|
1207
1166
|
completion:^(FIRTOTPSecret *_Nullable totpSecret,
|
|
@@ -1211,9 +1170,7 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1211
1170
|
}
|
|
1212
1171
|
else {
|
|
1213
1172
|
NSString *secretKey = totpSecret.sharedSecretKey;
|
|
1214
|
-
|
|
1215
|
-
cachedTotpSecrets[secretKey] = totpSecret;
|
|
1216
|
-
DLog(@"cachedSecret: %@", cachedTotpSecrets[secretKey]);
|
|
1173
|
+
[cachedTotpSecrets putReplacing:secretKey value:totpSecret];
|
|
1217
1174
|
resolve(@{
|
|
1218
1175
|
@"secretKey" : secretKey,
|
|
1219
1176
|
});
|
|
@@ -1229,14 +1186,12 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1229
1186
|
FIRApp *firebaseApp = [RCTConvert firAppFromString:appName];
|
|
1230
1187
|
|
|
1231
1188
|
DLog(@"generateQrCodeUrl using instance resolve generateQrCodeUrl: %@", firebaseApp.name);
|
|
1232
|
-
|
|
1233
|
-
FIRTOTPSecret *totpSecret = cachedTotpSecrets[secretKey];
|
|
1189
|
+
FIRTOTPSecret *totpSecret = [cachedTotpSecrets get:secretKey];
|
|
1234
1190
|
if (!totpSecret) {
|
|
1235
1191
|
RNFBAuthThrowSyncErrorWithCode(@"invalid-multi-factor-secret",
|
|
1236
1192
|
@"can't find secret for provided key");
|
|
1237
1193
|
}
|
|
1238
1194
|
NSString *url = [totpSecret generateQRCodeURLWithAccountName:account issuer:issuer];
|
|
1239
|
-
DLog(@"generateQrCodeUrl got QR Code URL %@", url);
|
|
1240
1195
|
return url;
|
|
1241
1196
|
}
|
|
1242
1197
|
|
|
@@ -1245,9 +1200,7 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1245
1200
|
qrCodeUri:(NSString *)qrCodeUri {
|
|
1246
1201
|
[self initializeSharedStateOnce];
|
|
1247
1202
|
|
|
1248
|
-
|
|
1249
|
-
FIRTOTPSecret *totpSecret = cachedTotpSecrets[secretKey];
|
|
1250
|
-
DLog(@"openInOtpApp using qrCodeUri: %@", qrCodeUri);
|
|
1203
|
+
FIRTOTPSecret *totpSecret = [cachedTotpSecrets get:secretKey];
|
|
1251
1204
|
[totpSecret openInOTPAppWithQRCodeURL:qrCodeUri];
|
|
1252
1205
|
}
|
|
1253
1206
|
|
|
@@ -1266,7 +1219,7 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1266
1219
|
}
|
|
1267
1220
|
|
|
1268
1221
|
NSString *sessionId = [NSString stringWithFormat:@"%@", @([session hash])];
|
|
1269
|
-
cachedSessions
|
|
1222
|
+
[cachedSessions putReplacing:sessionId value:session];
|
|
1270
1223
|
resolve(sessionId);
|
|
1271
1224
|
}];
|
|
1272
1225
|
}
|
|
@@ -1333,9 +1286,7 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1333
1286
|
|
|
1334
1287
|
DLog(@"using instance finalizeTotpEnrollment: %@", firebaseApp.name);
|
|
1335
1288
|
|
|
1336
|
-
FIRTOTPSecret *cachedTotpSecret = cachedTotpSecrets
|
|
1337
|
-
DLog(@"using totpSecretKey: %@", totpSecret);
|
|
1338
|
-
DLog(@"using cachedSecret: %@", cachedTotpSecret);
|
|
1289
|
+
FIRTOTPSecret *cachedTotpSecret = [cachedTotpSecrets get:totpSecret];
|
|
1339
1290
|
FIRTOTPMultiFactorAssertion *assertion =
|
|
1340
1291
|
[FIRTOTPMultiFactorGenerator assertionForEnrollmentWithSecret:cachedTotpSecret
|
|
1341
1292
|
oneTimePassword:verificationCode];
|
|
@@ -1789,8 +1740,9 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1789
1740
|
FIRAuthCredential *credential;
|
|
1790
1741
|
|
|
1791
1742
|
// First check if we cached an authToken
|
|
1792
|
-
|
|
1793
|
-
|
|
1743
|
+
FIRAuthCredential *cachedCredential = [credentials get:authToken];
|
|
1744
|
+
if (cachedCredential != nil) {
|
|
1745
|
+
credential = cachedCredential;
|
|
1794
1746
|
} else if ([provider compare:@"twitter.com" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
|
|
1795
1747
|
credential = [FIRTwitterAuthProvider credentialWithToken:authToken secret:authTokenSecret];
|
|
1796
1748
|
} else if ([provider compare:@"facebook.com" options:NSCaseInsensitiveSearch] == NSOrderedSame &&
|
|
@@ -1895,82 +1847,15 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1895
1847
|
|
|
1896
1848
|
+ (NSDictionary *)getJSError:(NSError *)error {
|
|
1897
1849
|
[self initializeSharedStateOnce];
|
|
1898
|
-
NSString *code =
|
|
1850
|
+
NSString *code = [RNFBAuthErrorCodeMapper jsErrorCodeForAuthErrorCode:error.code];
|
|
1899
1851
|
NSString *message = [error localizedDescription];
|
|
1900
1852
|
NSString *nativeErrorMessage = [error localizedDescription];
|
|
1901
1853
|
|
|
1902
1854
|
if (code == nil) code = @"unknown";
|
|
1903
1855
|
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
message = @"The custom token format is incorrect. Please check the documentation.";
|
|
1908
|
-
break;
|
|
1909
|
-
case FIRAuthErrorCodeCustomTokenMismatch:
|
|
1910
|
-
message = @"The custom token corresponds to a different audience.";
|
|
1911
|
-
break;
|
|
1912
|
-
case FIRAuthErrorCodeInvalidCredential:
|
|
1913
|
-
message = @"The supplied auth credential is malformed or has expired.";
|
|
1914
|
-
break;
|
|
1915
|
-
case FIRAuthErrorCodeInvalidEmail:
|
|
1916
|
-
message = @"The email address is badly formatted.";
|
|
1917
|
-
break;
|
|
1918
|
-
case FIRAuthErrorCodeWrongPassword:
|
|
1919
|
-
message = @"The password is invalid or the user does not have a password.";
|
|
1920
|
-
break;
|
|
1921
|
-
case FIRAuthErrorCodeUserMismatch:
|
|
1922
|
-
message = @"The supplied credentials do not correspond to the previously signed in user.";
|
|
1923
|
-
break;
|
|
1924
|
-
case FIRAuthErrorCodeRequiresRecentLogin:
|
|
1925
|
-
message = @"This operation is sensitive and requires recent authentication. Log in again "
|
|
1926
|
-
@"before retrying this request.";
|
|
1927
|
-
break;
|
|
1928
|
-
case FIRAuthErrorCodeSecondFactorRequired:
|
|
1929
|
-
message = @"Please complete a second factor challenge to finish signing into this account.";
|
|
1930
|
-
break;
|
|
1931
|
-
case FIRAuthErrorCodeAccountExistsWithDifferentCredential:
|
|
1932
|
-
message = @"An account already exists with the same email address but different sign-in "
|
|
1933
|
-
@"credentials. Sign in using a provider associated with this email address.";
|
|
1934
|
-
break;
|
|
1935
|
-
case FIRAuthErrorCodeEmailAlreadyInUse:
|
|
1936
|
-
message = @"The email address is already in use by another account.";
|
|
1937
|
-
break;
|
|
1938
|
-
case FIRAuthErrorCodeCredentialAlreadyInUse:
|
|
1939
|
-
message = @"This credential is already associated with a different user account.";
|
|
1940
|
-
break;
|
|
1941
|
-
case FIRAuthErrorCodeUserDisabled:
|
|
1942
|
-
message = @"The user account has been disabled by an administrator.";
|
|
1943
|
-
break;
|
|
1944
|
-
case FIRAuthErrorCodeUserTokenExpired:
|
|
1945
|
-
message = @"The user's credential is no longer valid. The user must sign in again.";
|
|
1946
|
-
break;
|
|
1947
|
-
case FIRAuthErrorCodeUserNotFound:
|
|
1948
|
-
message = @"There is no user record corresponding to this identifier. The user may have been "
|
|
1949
|
-
@"deleted.";
|
|
1950
|
-
break;
|
|
1951
|
-
case FIRAuthErrorCodeInvalidUserToken:
|
|
1952
|
-
message = @"The user's credential is no longer valid. The user must sign in again.";
|
|
1953
|
-
break;
|
|
1954
|
-
case FIRAuthErrorCodeWeakPassword:
|
|
1955
|
-
message = @"The given password is invalid.";
|
|
1956
|
-
break;
|
|
1957
|
-
case FIRAuthErrorCodeOperationNotAllowed:
|
|
1958
|
-
message = @"This operation is not allowed. You must enable this service in the console.";
|
|
1959
|
-
break;
|
|
1960
|
-
case FIRAuthErrorCodeNetworkError:
|
|
1961
|
-
message = @"A network error has occurred, please try again.";
|
|
1962
|
-
break;
|
|
1963
|
-
case FIRAuthErrorCodeInternalError:
|
|
1964
|
-
message = @"An internal error has occurred, please try again.";
|
|
1965
|
-
break;
|
|
1966
|
-
case FIRAuthErrorCodeInvalidPhoneNumber:
|
|
1967
|
-
message = @"The format of the phone number provided is incorrect. "
|
|
1968
|
-
@"Please enter the phone number in a format that can be parsed into E.164 format. "
|
|
1969
|
-
@"E.164 phone numbers are written in the format [+][country code][subscriber "
|
|
1970
|
-
@"number including area code].";
|
|
1971
|
-
break;
|
|
1972
|
-
default:
|
|
1973
|
-
break;
|
|
1856
|
+
NSString *overrideMessage = [RNFBAuthErrorCodeMapper jsErrorMessageForAuthErrorCode:error.code];
|
|
1857
|
+
if (overrideMessage != nil) {
|
|
1858
|
+
message = overrideMessage;
|
|
1974
1859
|
}
|
|
1975
1860
|
|
|
1976
1861
|
NSDictionary *authCredentialDict = nil;
|
|
@@ -1986,7 +1871,7 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
1986
1871
|
resolverDict = [self multiFactorResolverToDict:resolver];
|
|
1987
1872
|
|
|
1988
1873
|
NSString *sessionKey = [NSString stringWithFormat:@"%@", @([resolver.session hash])];
|
|
1989
|
-
cachedResolver
|
|
1874
|
+
[cachedResolver putReplacing:sessionKey value:resolver];
|
|
1990
1875
|
}
|
|
1991
1876
|
#endif
|
|
1992
1877
|
|
|
@@ -2175,7 +2060,7 @@ static __strong NSMutableDictionary<NSString *, FIRTOTPSecret *> *cachedTotpSecr
|
|
|
2175
2060
|
NSString *authCredentialHash = [NSString stringWithFormat:@"%@", @([authCredential hash])];
|
|
2176
2061
|
|
|
2177
2062
|
// Temporarily store the non-serializable credential for later
|
|
2178
|
-
credentials
|
|
2063
|
+
[credentials putReplacing:authCredentialHash value:authCredential];
|
|
2179
2064
|
|
|
2180
2065
|
return @{
|
|
2181
2066
|
keyProviderId : authCredential.provider,
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
* Auth-state / id-token listener map. Unique `put` / `putOrDiscard`; callers `take` or `removeAll`
|
|
24
|
+
* then `remove` outside the HandleMap lock. Skip-if-registered is `get != nil`. Stored values are
|
|
25
|
+
* expected to respond to `remove`.
|
|
26
|
+
*/
|
|
27
|
+
@interface RNFBAuthListenerRegistry : NSObject
|
|
28
|
+
|
|
29
|
+
- (BOOL)put:(id)key value:(id)value error:(NSError *_Nullable *_Nullable)error;
|
|
30
|
+
/// Unique put. On collision, `remove`s the incoming value and leaves the existing mapping.
|
|
31
|
+
- (BOOL)putOrDiscard:(id)key value:(id)value;
|
|
32
|
+
- (nullable id)get:(id)key;
|
|
33
|
+
- (nullable id)take:(id)key;
|
|
34
|
+
- (void)takeAndRemove:(id)key;
|
|
35
|
+
- (void)removeAll;
|
|
36
|
+
|
|
37
|
+
@end
|
|
38
|
+
|
|
39
|
+
NS_ASSUME_NONNULL_END
|