pyios-firebase 0.2.0__py3-none-any.whl

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 (65) hide show
  1. pyios_firebase/__init__.py +26 -0
  2. pyios_firebase/analytics/__init__.py +434 -0
  3. pyios_firebase/analytics/names.py +251 -0
  4. pyios_firebase/app_check/__init__.py +483 -0
  5. pyios_firebase/auth/__init__.py +131 -0
  6. pyios_firebase/auth/_apple_name_cache.py +97 -0
  7. pyios_firebase/auth/_common.py +121 -0
  8. pyios_firebase/auth/_ui.py +21 -0
  9. pyios_firebase/auth/apple.py +326 -0
  10. pyios_firebase/auth/auth.py +592 -0
  11. pyios_firebase/auth/credentials.py +551 -0
  12. pyios_firebase/auth/errors.py +321 -0
  13. pyios_firebase/auth/facebook.py +240 -0
  14. pyios_firebase/auth/google.py +167 -0
  15. pyios_firebase/auth/multi_factor.py +394 -0
  16. pyios_firebase/auth/results.py +361 -0
  17. pyios_firebase/auth/user.py +457 -0
  18. pyios_firebase/core/__init__.py +32 -0
  19. pyios_firebase/crashlytics/__init__.py +463 -0
  20. pyios_firebase/database/__init__.py +62 -0
  21. pyios_firebase/database/database.py +144 -0
  22. pyios_firebase/database/errors.py +80 -0
  23. pyios_firebase/database/query.py +569 -0
  24. pyios_firebase/database/reference.py +428 -0
  25. pyios_firebase/database/snapshot.py +218 -0
  26. pyios_firebase/database/values.py +271 -0
  27. pyios_firebase/firestore/__init__.py +59 -0
  28. pyios_firebase/firestore/_limits.py +48 -0
  29. pyios_firebase/firestore/_query_state.py +228 -0
  30. pyios_firebase/firestore/_write.py +18 -0
  31. pyios_firebase/firestore/aggregate.py +137 -0
  32. pyios_firebase/firestore/collection_reference.py +100 -0
  33. pyios_firebase/firestore/document_change.py +63 -0
  34. pyios_firebase/firestore/document_reference.py +229 -0
  35. pyios_firebase/firestore/document_snapshot.py +102 -0
  36. pyios_firebase/firestore/enums.py +66 -0
  37. pyios_firebase/firestore/field_value.py +94 -0
  38. pyios_firebase/firestore/filter.py +319 -0
  39. pyios_firebase/firestore/firestore.py +184 -0
  40. pyios_firebase/firestore/listener_registration.py +55 -0
  41. pyios_firebase/firestore/query.py +679 -0
  42. pyios_firebase/firestore/query_snapshot.py +103 -0
  43. pyios_firebase/firestore/snapshot_metadata.py +36 -0
  44. pyios_firebase/firestore/transaction.py +130 -0
  45. pyios_firebase/firestore/write_batch.py +125 -0
  46. pyios_firebase/functions/__init__.py +329 -0
  47. pyios_firebase/messaging/__init__.py +37 -0
  48. pyios_firebase/messaging/messaging.py +435 -0
  49. pyios_firebase/messaging/notifications.py +292 -0
  50. pyios_firebase/performance/__init__.py +486 -0
  51. pyios_firebase/py.typed +0 -0
  52. pyios_firebase/remote_config/__init__.py +660 -0
  53. pyios_firebase/storage/__init__.py +41 -0
  54. pyios_firebase/storage/errors.py +103 -0
  55. pyios_firebase/storage/metadata.py +202 -0
  56. pyios_firebase/storage/reference.py +402 -0
  57. pyios_firebase/storage/storage.py +120 -0
  58. pyios_firebase/storage/task.py +198 -0
  59. pyios_firebase/types.py +93 -0
  60. pyios_firebase/util.py +413 -0
  61. pyios_firebase-0.2.0.dist-info/METADATA +1969 -0
  62. pyios_firebase-0.2.0.dist-info/RECORD +65 -0
  63. pyios_firebase-0.2.0.dist-info/WHEEL +5 -0
  64. pyios_firebase-0.2.0.dist-info/licenses/LICENSE +21 -0
  65. pyios_firebase-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,592 @@
1
+ """``Auth``: Firebase Authentication for the app. Sign-in, sign-out, listeners,
2
+ password reset and email links."""
3
+
4
+ from __future__ import annotations
5
+
6
+ __all__ = ("Auth", "AuthStateListener")
7
+
8
+ from collections.abc import Callable
9
+ from ctypes import byref
10
+ from typing import Any, ClassVar, Final
11
+
12
+ from rubicon.objc import ns_from_py, objc_id
13
+
14
+ from ..types import ObjCObject
15
+ from ..util import listener_block, objc_class, objc_instance
16
+ from ._common import (
17
+ error_block,
18
+ flag,
19
+ missing_callback_scheme,
20
+ result_block,
21
+ string_block,
22
+ text,
23
+ )
24
+ from .credentials import AuthCredential, OAuthProvider
25
+ from .errors import AuthError, auth_error_from_nserror, simple_auth_error
26
+ from .results import ActionCodeInfo, ActionCodeSettings, AuthDataResult
27
+ from .user import User
28
+
29
+ ErrorCallback = Callable[[AuthError | None], None]
30
+ DataResultCallback = Callable[[AuthDataResult | None, AuthError | None], None]
31
+ UserListener = Callable[[User | None], None]
32
+
33
+ APNS_TOKEN_TYPE_UNKNOWN: Final = 0
34
+ """``set_apns_token`` type: let Firebase find out (sandbox or production)."""
35
+ APNS_TOKEN_TYPE_SANDBOX: Final = 1
36
+ """``set_apns_token`` type: a development (sandbox) APNs token."""
37
+ APNS_TOKEN_TYPE_PRODUCTION: Final = 2
38
+ """``set_apns_token`` type: a production APNs token."""
39
+
40
+
41
+ class AuthStateListener:
42
+ """Given back by ``add_state_did_change_listener`` and
43
+ ``add_id_token_did_change_listener``. Call ``remove()`` to stop.
44
+
45
+ Args:
46
+ handle: the native listener handle.
47
+ remove: removes the handle from Firebase.
48
+ release: releases the listener's callback block.
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ handle: ObjCObject,
54
+ remove: Callable[[ObjCObject], None],
55
+ release: Callable[[], None],
56
+ ) -> None:
57
+ self._handle: ObjCObject | None = handle
58
+ self._remove = remove
59
+ self._release = release
60
+
61
+ def remove(self) -> None:
62
+ """Stop the updates. Safe to call more than once."""
63
+ if self._handle is None:
64
+ return
65
+ self._remove(self._handle)
66
+ self._handle = None
67
+ self._release()
68
+
69
+
70
+ def _throwing(call: Callable[[Any], Any]) -> AuthError | None:
71
+ """Run a Swift ``throws`` method (an ``NSError **`` argument): its error, or ``None``."""
72
+ error = objc_id()
73
+ ok = call(byref(error))
74
+ if ok or not error.value:
75
+ return None
76
+ return auth_error_from_nserror(objc_instance(error))
77
+
78
+
79
+ class Auth:
80
+ """Firebase Authentication. Get it with ``Auth.auth()``.
81
+
82
+ ::
83
+
84
+ auth = Auth.auth()
85
+ auth.sign_in_with_email_password("ada@example.com", "s3cret!", on_signed_in)
86
+
87
+ Every callback gets ``(result, error)`` (or just ``(error)``), where
88
+ ``error`` is an ``AuthError`` or ``None``.
89
+
90
+ Args:
91
+ native: the native ``FIRAuth``. Leave it out for the default app's.
92
+ """
93
+
94
+ _instance: ClassVar[Auth | None] = None
95
+ # native FIRAuth addresses that use the emulator (the SDK does not expose it)
96
+ _emulated: ClassVar[set[int]] = set()
97
+
98
+ def __init__(self, native: ObjCObject | None = None) -> None:
99
+ self._native: ObjCObject = (
100
+ native if native is not None else objc_class("FIRAuth").auth()
101
+ )
102
+
103
+ @classmethod
104
+ def auth(cls) -> Auth:
105
+ """The shared ``Auth`` for the default Firebase app."""
106
+ if cls._instance is None:
107
+ cls._instance = cls(objc_class("FIRAuth").auth())
108
+ return cls._instance
109
+
110
+ @property
111
+ def native(self) -> ObjCObject:
112
+ """The ``FIRAuth``, for SDK calls this wrapper does not cover."""
113
+ return self._native
114
+
115
+ # ---- the current user ---------------------------------------------------------
116
+
117
+ @property
118
+ def current_user(self) -> User | None:
119
+ """The signed-in user, or ``None``.
120
+
121
+ Right after app start this can be ``None`` for a moment while Firebase
122
+ restores the saved user; use ``add_state_did_change_listener`` to know
123
+ for sure.
124
+ """
125
+ user = self._native.currentUser
126
+ return None if user is None else User(user)
127
+
128
+ def update_current_user(
129
+ self, user: User, callback: ErrorCallback | None = None
130
+ ) -> None:
131
+ """Make ``user`` the signed-in user (for example one from another
132
+ ``Auth`` instance or a shared keychain).
133
+
134
+ Args:
135
+ user: the user to sign in as.
136
+ callback: gets ``None`` or the error.
137
+ """
138
+ self._native.updateCurrentUser_completion_(user.user, error_block(callback))
139
+
140
+ def sign_out(self) -> AuthError | None:
141
+ """Sign the current user out of Firebase (Google, Apple and other
142
+ providers keep their own sessions).
143
+
144
+ Returns:
145
+ ``None``, or the error (for example a keychain error).
146
+ """
147
+ return _throwing(self._native.signOut_)
148
+
149
+ # ---- sign-in ----------------------------------------------------------------
150
+
151
+ def sign_in_with_email_password(
152
+ self, email: str, password: str, callback: DataResultCallback | None = None
153
+ ) -> None:
154
+ """Sign in with an email address and password.
155
+
156
+ Args:
157
+ email: the email address.
158
+ password: the password.
159
+ callback: gets ``(AuthDataResult, None)``, or ``(None, error)``.
160
+ With email enumeration protection (the default), a wrong email
161
+ or password both give ``INVALID_CREDENTIAL``.
162
+ """
163
+ self._native.signInWithEmail_password_completion_(
164
+ email, password, result_block(callback, AuthDataResult)
165
+ )
166
+
167
+ def create_user_with_email_password(
168
+ self, email: str, password: str, callback: DataResultCallback | None = None
169
+ ) -> None:
170
+ """Create an account with an email address and password, and sign in.
171
+
172
+ Args:
173
+ email: the email address.
174
+ password: the password (at least 6 characters, or your project's
175
+ password policy).
176
+ callback: gets ``(AuthDataResult, None)``, or ``(None, error)``
177
+ (``EMAIL_ALREADY_IN_USE``, ``WEAK_PASSWORD``, ``INVALID_EMAIL``...).
178
+ """
179
+ self._native.createUserWithEmail_password_completion_(
180
+ email, password, result_block(callback, AuthDataResult)
181
+ )
182
+
183
+ def sign_in_anonymously(self, callback: DataResultCallback | None = None) -> None:
184
+ """Sign in as a new anonymous user (or the current anonymous one).
185
+
186
+ Link a credential later (``User.link_with_credential``) to keep the
187
+ account when the user signs up.
188
+
189
+ Args:
190
+ callback: gets ``(AuthDataResult, None)``, or ``(None, error)``.
191
+ """
192
+ self._native.signInAnonymouslyWithCompletion_(
193
+ result_block(callback, AuthDataResult)
194
+ )
195
+
196
+ def sign_in_with_custom_token(
197
+ self, token: str, callback: DataResultCallback | None = None
198
+ ) -> None:
199
+ """Sign in with a custom token made by your backend (Admin SDK
200
+ ``create_custom_token``).
201
+
202
+ Args:
203
+ token: the custom token.
204
+ callback: gets ``(AuthDataResult, None)``, or ``(None, error)``
205
+ (``INVALID_CUSTOM_TOKEN``, ``CUSTOM_TOKEN_MISMATCH``).
206
+ """
207
+ self._native.signInWithCustomToken_completion_(
208
+ token, result_block(callback, AuthDataResult)
209
+ )
210
+
211
+ def sign_in_with_credential(
212
+ self, credential: AuthCredential, callback: DataResultCallback | None = None
213
+ ) -> None:
214
+ """Sign in with a credential from any provider.
215
+
216
+ Args:
217
+ credential: the credential, from one of the provider classes.
218
+ callback: gets ``(AuthDataResult, None)``, or ``(None, error)``.
219
+ ``ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL`` means the email is
220
+ already used with another provider: sign in with that one, then
221
+ link this credential.
222
+ """
223
+ self._native.signInWithCredential_completion_(
224
+ credential.native, result_block(callback, AuthDataResult)
225
+ )
226
+
227
+ def sign_in_with_provider(
228
+ self, provider: OAuthProvider, callback: DataResultCallback | None = None
229
+ ) -> None:
230
+ """Sign in through an OAuth provider's web flow (Microsoft, Yahoo,
231
+ GitHub, OIDC, SAML), in an in-app browser.
232
+
233
+ Args:
234
+ provider: the provider, for example ``OAuthProvider("yahoo.com")``.
235
+ callback: gets ``(AuthDataResult, None)``, or ``(None, error)``.
236
+ ``error.cancelled`` is True when the user closed the browser.
237
+ If the callback URL scheme is missing from ``Info.plist``, the
238
+ error says which one to add (the SDK itself would stop the app).
239
+ """
240
+ problem = missing_callback_scheme(self._native)
241
+ if problem is not None:
242
+ if callback is not None:
243
+ callback(None, simple_auth_error(problem))
244
+ return
245
+ self._native.signInWithProvider_UIDelegate_completion_(
246
+ provider.native, None, result_block(callback, AuthDataResult)
247
+ )
248
+
249
+ # ---- email links ------------------------------------------------------------------
250
+
251
+ def send_sign_in_link(
252
+ self,
253
+ email: str,
254
+ action_code_settings: ActionCodeSettings,
255
+ callback: ErrorCallback | None = None,
256
+ ) -> None:
257
+ """Email a passwordless sign-in link.
258
+
259
+ Save ``email`` on the device: you need it again in
260
+ ``sign_in_with_email_link``.
261
+
262
+ Args:
263
+ email: where to send the link.
264
+ action_code_settings: where the link leads; ``handle_code_in_app``
265
+ must be True.
266
+ callback: gets ``None`` once the email is sent, or the error.
267
+ """
268
+ self._native.sendSignInLinkToEmail_actionCodeSettings_completion_(
269
+ email, action_code_settings.to_native(), error_block(callback)
270
+ )
271
+
272
+ def is_sign_in_with_email_link(self, link: str) -> bool:
273
+ """Whether ``link`` (a URL the app was opened with) is a sign-in link.
274
+
275
+ Args:
276
+ link: the full URL.
277
+
278
+ Returns:
279
+ True if it can be passed to ``sign_in_with_email_link``.
280
+ """
281
+ return bool(self._native.isSignInWithEmailLink_(link))
282
+
283
+ def sign_in_with_email_link(
284
+ self, email: str, link: str, callback: DataResultCallback | None = None
285
+ ) -> None:
286
+ """Sign in with an email link from ``send_sign_in_link``.
287
+
288
+ Args:
289
+ email: the address the link was sent to.
290
+ link: the full link the user opened.
291
+ callback: gets ``(AuthDataResult, None)``, or ``(None, error)``
292
+ (``INVALID_ACTION_CODE`` / ``EXPIRED_ACTION_CODE``).
293
+ """
294
+ self._native.signInWithEmail_link_completion_(
295
+ email, link, result_block(callback, AuthDataResult)
296
+ )
297
+
298
+ # ---- password reset and action codes --------------------------------------
299
+
300
+ def send_password_reset(
301
+ self,
302
+ email: str,
303
+ callback: ErrorCallback | None = None,
304
+ action_code_settings: ActionCodeSettings | None = None,
305
+ ) -> None:
306
+ """Email a password reset link.
307
+
308
+ Args:
309
+ email: the account's email address.
310
+ callback: gets ``None`` once the email is sent, or the error. With
311
+ email enumeration protection on, an unknown address is not an
312
+ error.
313
+ action_code_settings: where the link leads, or ``None`` for the
314
+ Firebase default page.
315
+ """
316
+ settings = (
317
+ None if action_code_settings is None else action_code_settings.to_native()
318
+ )
319
+ self._native.sendPasswordResetWithEmail_actionCodeSettings_completion_(
320
+ email, settings, error_block(callback)
321
+ )
322
+
323
+ def verify_password_reset_code(
324
+ self, code: str, callback: Callable[[str | None, AuthError | None], None]
325
+ ) -> None:
326
+ """Check a password reset code (the ``oobCode`` of the link).
327
+
328
+ Args:
329
+ code: the code.
330
+ callback: gets ``(email, None)`` with the account's address, or
331
+ ``(None, error)``.
332
+ """
333
+ self._native.verifyPasswordResetCode_completion_(code, string_block(callback))
334
+
335
+ def confirm_password_reset(
336
+ self, code: str, new_password: str, callback: ErrorCallback | None = None
337
+ ) -> None:
338
+ """Set a new password with a password reset code.
339
+
340
+ Args:
341
+ code: the ``oobCode`` of the reset link.
342
+ new_password: the new password.
343
+ callback: gets ``None`` or the error.
344
+ """
345
+ self._native.confirmPasswordResetWithCode_newPassword_completion_(
346
+ code, new_password, error_block(callback)
347
+ )
348
+
349
+ def check_action_code(
350
+ self,
351
+ code: str,
352
+ callback: Callable[[ActionCodeInfo | None, AuthError | None], None],
353
+ ) -> None:
354
+ """Find out what an email action code is for, without using it.
355
+
356
+ Args:
357
+ code: the ``oobCode`` of the link.
358
+ callback: gets ``(ActionCodeInfo, None)``, or ``(None, error)``.
359
+ """
360
+ self._native.checkActionCode_completion_(
361
+ code, result_block(callback, ActionCodeInfo.from_native)
362
+ )
363
+
364
+ def apply_action_code(
365
+ self, code: str, callback: ErrorCallback | None = None
366
+ ) -> None:
367
+ """Use an email action code: verify an email address, or recover / change one.
368
+
369
+ Args:
370
+ code: the ``oobCode`` of the link.
371
+ callback: gets ``None`` or the error.
372
+ """
373
+ self._native.applyActionCode_completion_(code, error_block(callback))
374
+
375
+ def fetch_sign_in_methods(
376
+ self, email: str, callback: Callable[[list[str] | None, AuthError | None], None]
377
+ ) -> None:
378
+ """The sign-in methods used with ``email``.
379
+
380
+ Deprecated by Firebase: with email enumeration protection on (the
381
+ default), the list is always empty.
382
+
383
+ Args:
384
+ email: the email address.
385
+ callback: gets ``(methods, None)``, for example ``["password"]``, or
386
+ ``(None, error)``.
387
+ """
388
+ self._native.fetchSignInMethodsForEmail_completion_(
389
+ email, result_block(callback, lambda methods: [str(m) for m in methods])
390
+ )
391
+
392
+ def revoke_token(
393
+ self, authorization_code: str, callback: ErrorCallback | None = None
394
+ ) -> None:
395
+ """Revoke a Sign in with Apple token, before deleting an Apple user
396
+ (App Store rule).
397
+
398
+ Args:
399
+ authorization_code: the ``authorizationCode`` Apple gave on a recent
400
+ sign-in.
401
+ callback: gets ``None`` or the error.
402
+ """
403
+ self._native.revokeTokenWithAuthorizationCode_completion_(
404
+ authorization_code, error_block(callback)
405
+ )
406
+
407
+ # ---- listeners ----------------------------------------------------------------
408
+
409
+ def _listen(
410
+ self, add: str, remove: str, listener: UserListener
411
+ ) -> AuthStateListener:
412
+ def on_change(_auth_ptr: Any, user_ptr: Any) -> None:
413
+ listener(User(objc_instance(user_ptr)) if user_ptr else None)
414
+
415
+ block, release = listener_block(on_change, objc_id, objc_id)
416
+ handle = getattr(self._native, add)(block)
417
+ return AuthStateListener(handle, getattr(self._native, remove), release)
418
+
419
+ def add_state_did_change_listener(
420
+ self, listener: UserListener
421
+ ) -> AuthStateListener:
422
+ """Listen to sign-in and sign-out.
423
+
424
+ ``listener`` runs now with the current user (or ``None``), then each
425
+ time a user signs in or out.
426
+
427
+ Args:
428
+ listener: gets the ``User``, or ``None`` when signed out.
429
+
430
+ Returns:
431
+ An ``AuthStateListener``. Call ``.remove()`` to stop.
432
+ """
433
+ return self._listen(
434
+ "addAuthStateDidChangeListener_",
435
+ "removeAuthStateDidChangeListener_",
436
+ listener,
437
+ )
438
+
439
+ def add_id_token_did_change_listener(
440
+ self, listener: UserListener
441
+ ) -> AuthStateListener:
442
+ """Listen to sign-in, sign-out and ID token refreshes (about once an
443
+ hour), for example to keep your backend's copy of the token fresh.
444
+
445
+ Args:
446
+ listener: gets the ``User``, or ``None`` when signed out.
447
+
448
+ Returns:
449
+ An ``AuthStateListener``. Call ``.remove()`` to stop.
450
+ """
451
+ return self._listen(
452
+ "addIDTokenDidChangeListener_", "removeIDTokenDidChangeListener_", listener
453
+ )
454
+
455
+ # ---- settings -----------------------------------------------------------------
456
+
457
+ def use_emulator(self, host: str = "127.0.0.1", port: int = 9099) -> None:
458
+ """Send this app's Auth calls to the local Auth emulator.
459
+
460
+ Call it right after ``core.configure()``, before any other Auth call.
461
+ Use it only in development and test builds. It also turns app
462
+ verification off, so phone sign-in works without APNs or reCAPTCHA.
463
+
464
+ Args:
465
+ host: the emulator's host. ``"127.0.0.1"`` works in the iOS
466
+ Simulator; on a device, use the Mac's LAN address.
467
+ port: the emulator's port (``firebase emulators:start`` uses 9099).
468
+ """
469
+ self._native.useEmulatorWithHost_port_(host, int(port))
470
+ Auth._emulated.add(int(self._native.ptr.value or 0))
471
+
472
+ @property
473
+ def is_using_emulator(self) -> bool:
474
+ """True after ``use_emulator``."""
475
+ return int(self._native.ptr.value or 0) in Auth._emulated
476
+
477
+ @property
478
+ def language_code(self) -> str | None:
479
+ """The language of emails, SMS and web flows Firebase sends, for
480
+ example ``"fr"``; ``None`` for the project default."""
481
+ return text(self._native.languageCode)
482
+
483
+ @language_code.setter
484
+ def language_code(self, code: str | None) -> None:
485
+ self._native.languageCode = code
486
+
487
+ def use_app_language(self) -> None:
488
+ """Set ``language_code`` to the app's current language."""
489
+ self._native.useAppLanguage()
490
+
491
+ @property
492
+ def tenant_id(self) -> str | None:
493
+ """The tenant users sign in to (multi-tenancy with Identity Platform),
494
+ or ``None``."""
495
+ return text(self._native.tenantID)
496
+
497
+ @tenant_id.setter
498
+ def tenant_id(self, tenant_id: str | None) -> None:
499
+ self._native.tenantID = tenant_id
500
+
501
+ @property
502
+ def app_verification_disabled_for_testing(self) -> bool:
503
+ """Skip app verification for phone sign-in with test numbers (set in
504
+ the Firebase console). For tests only."""
505
+ settings = self._native.settings
506
+ return settings is not None and flag(
507
+ settings, "isAppVerificationDisabledForTesting"
508
+ )
509
+
510
+ @app_verification_disabled_for_testing.setter
511
+ def app_verification_disabled_for_testing(self, disabled: bool) -> None:
512
+ settings = self._native.settings.copy()
513
+ settings.appVerificationDisabledForTesting = bool(disabled)
514
+ self._native.settings = settings
515
+
516
+ def use_user_access_group(self, access_group: str | None) -> AuthError | None:
517
+ """Share the signed-in user with other apps (and extensions) through a
518
+ keychain access group.
519
+
520
+ Args:
521
+ access_group: the keychain group (with your team ID prefix), or
522
+ ``None`` to stop sharing.
523
+
524
+ Returns:
525
+ ``None``, or the error (for example a keychain error).
526
+ """
527
+ return _throwing(
528
+ lambda error: self._native.useUserAccessGroup_error_(access_group, error)
529
+ )
530
+
531
+ def get_stored_user(
532
+ self, access_group: str | None
533
+ ) -> tuple[User | None, AuthError | None]:
534
+ """The user saved in a keychain access group, without switching to it
535
+ (for example the user signed in to your other app).
536
+
537
+ Sign in as them with ``update_current_user``.
538
+
539
+ Args:
540
+ access_group: the keychain access group (with your team ID
541
+ prefix), or ``None`` for the app's own keychain.
542
+
543
+ Returns:
544
+ ``(user, None)``, ``(None, None)`` if no user is saved there, or
545
+ ``(None, error)`` (for example without the Keychain Sharing
546
+ capability).
547
+ """
548
+ error = objc_id()
549
+ user = self._native.getStoredUserForAccessGroup_error_(
550
+ access_group, byref(error)
551
+ )
552
+ if error.value:
553
+ return None, auth_error_from_nserror(objc_instance(error))
554
+ return (None if user is None else User(user)), None
555
+
556
+ # ---- app delegate hooks (phone and OAuth sign-in) ---------------------------
557
+
558
+ def can_handle_url(self, url: str) -> bool:
559
+ """Give a URL the app was opened with to Firebase Auth (reCAPTCHA and
560
+ OAuth redirects).
561
+
562
+ Args:
563
+ url: the URL.
564
+
565
+ Returns:
566
+ True if Firebase Auth handled it.
567
+ """
568
+ return bool(self._native.canHandleURL_(objc_class("NSURL").URLWithString_(url)))
569
+
570
+ def can_handle_notification(self, user_info: dict[str, Any]) -> bool:
571
+ """Give a remote notification to Firebase Auth (silent push for phone
572
+ sign-in).
573
+
574
+ Args:
575
+ user_info: the notification's ``userInfo``.
576
+
577
+ Returns:
578
+ True if it was for Firebase Auth.
579
+ """
580
+ return bool(self._native.canHandleNotification_(ns_from_py(user_info)))
581
+
582
+ def set_apns_token(
583
+ self, token: bytes, token_type: int = APNS_TOKEN_TYPE_UNKNOWN
584
+ ) -> None:
585
+ """Give Firebase Auth the app's APNs device token (phone sign-in).
586
+
587
+ Args:
588
+ token: the device token from
589
+ ``didRegisterForRemoteNotificationsWithDeviceToken``.
590
+ token_type: ``APNS_TOKEN_TYPE_UNKNOWN`` / ``SANDBOX`` / ``PRODUCTION``.
591
+ """
592
+ self._native.setAPNSToken_type_(ns_from_py(bytes(token)), int(token_type))