@@ -14,6 +14,10 @@ import Helpers
1414 import WatchKit
1515#endif
1616
17+ #if canImport(ObjectiveC) && canImport(Combine)
18+ import Combine
19+ #endif
20+
1721typealias AuthClientID = Int
1822
1923struct AuthClientLoggerDecorator : SupabaseLogger {
@@ -27,21 +31,28 @@ struct AuthClientLoggerDecorator: SupabaseLogger {
2731 }
2832}
2933
30- public final class AuthClient : Sendable {
31- static let globalClientID = LockIsolated ( 0 )
32- let clientID : AuthClientID
34+ public actor AuthClient {
35+ static var globalClientID = 0
36+ nonisolated let clientID : AuthClientID
3337
34- private var api : APIClient { Dependencies [ clientID] . api }
35- var configuration : AuthClient . Configuration { Dependencies [ clientID] . configuration }
36- private var codeVerifierStorage : CodeVerifierStorage {
38+ nonisolated private var api : APIClient { Dependencies [ clientID] . api }
39+
40+ nonisolated var configuration : AuthClient . Configuration { Dependencies [ clientID] . configuration }
41+
42+ nonisolated private var codeVerifierStorage : CodeVerifierStorage {
3743 Dependencies [ clientID] . codeVerifierStorage
3844 }
39- private var date : @Sendable ( ) -> Date { Dependencies [ clientID] . date }
40- private var sessionManager : SessionManager { Dependencies [ clientID] . sessionManager }
41- private var eventEmitter : AuthStateChangeEventEmitter { Dependencies [ clientID] . eventEmitter }
42- private var logger : ( any SupabaseLogger ) ? { Dependencies [ clientID] . configuration. logger }
43- private var sessionStorage : SessionStorage { Dependencies [ clientID] . sessionStorage }
44- private var pkce : PKCE { Dependencies [ clientID] . pkce }
45+
46+ nonisolated private var date : @Sendable ( ) -> Date { Dependencies [ clientID] . date }
47+ nonisolated private var sessionManager : SessionManager { Dependencies [ clientID] . sessionManager }
48+ nonisolated private var eventEmitter : AuthStateChangeEventEmitter {
49+ Dependencies [ clientID] . eventEmitter
50+ }
51+ nonisolated private var logger : ( any SupabaseLogger ) ? {
52+ Dependencies [ clientID] . configuration. logger
53+ }
54+ nonisolated private var sessionStorage : SessionStorage { Dependencies [ clientID] . sessionStorage }
55+ nonisolated private var pkce : PKCE { Dependencies [ clientID] . pkce }
4556
4657 /// Returns the session, refreshing it if necessary.
4758 ///
@@ -55,26 +66,26 @@ public final class AuthClient: Sendable {
5566 /// Returns the current session, if any.
5667 ///
5768 /// The session returned by this property may be expired. Use ``session`` for a session that is guaranteed to be valid.
58- public var currentSession : Session ? {
69+ nonisolated public var currentSession : Session ? {
5970 sessionStorage. get ( )
6071 }
6172
6273 /// Returns the current user, if any.
6374 ///
6475 /// The user returned by this property may be outdated. Use ``user(jwt:)`` method to get an up-to-date user instance.
65- public var currentUser : User ? {
76+ nonisolated public var currentUser : User ? {
6677 currentSession? . user
6778 }
6879
6980 /// Namespace for accessing multi-factor authentication API.
70- public var mfa : AuthMFA {
81+ nonisolated public var mfa : AuthMFA {
7182 AuthMFA ( clientID: clientID)
7283 }
7384
7485 /// Namespace for the GoTrue admin methods.
7586 /// - Warning: This methods requires `service_role` key, be careful to never expose `service_role`
7687 /// key in the client.
77- public var admin : AuthAdmin {
88+ nonisolated public var admin : AuthAdmin {
7889 AuthAdmin ( clientID: clientID)
7990 }
8091
@@ -83,10 +94,8 @@ public final class AuthClient: Sendable {
8394 /// - Parameters:
8495 /// - configuration: The client configuration.
8596 public init ( configuration: Configuration ) {
86- clientID = AuthClient . globalClientID. withValue {
87- $0 += 1
88- return $0
89- }
97+ AuthClient . globalClientID += 1
98+ clientID = AuthClient . globalClientID
9099
91100 Dependencies [ clientID] = Dependencies (
92101 configuration: configuration,
@@ -103,63 +112,69 @@ public final class AuthClient: Sendable {
103112 Task { @MainActor in observeAppLifecycleChanges ( ) }
104113 }
105114
106- #if canImport(ObjectiveC)
115+ #if canImport(ObjectiveC) && canImport(Combine)
107116 @MainActor
108117 private func observeAppLifecycleChanges( ) {
118+ var didBecomeActiveNotification : NSNotification . Name ?
119+ var willResignActiveNotification : NSNotification . Name ?
120+
109121 #if canImport(UIKit)
110122 #if canImport(WatchKit)
111123 if #available( watchOS 7 . 0 , * ) {
112- NotificationCenter . default. addObserver (
113- self ,
114- selector: #selector( handleDidBecomeActive) ,
115- name: WKExtension . applicationDidBecomeActiveNotification,
116- object: nil
117- )
118- NotificationCenter . default. addObserver (
119- self ,
120- selector: #selector( handleWillResignActive) ,
121- name: WKExtension . applicationWillResignActiveNotification,
122- object: nil
123- )
124+ didBecomeActiveNotification = WKExtension . applicationDidBecomeActiveNotification
125+ willResignActiveNotification = WKExtension . applicationWillResignActiveNotification
124126 }
125127 #else
126- NotificationCenter . default. addObserver (
127- self ,
128- selector: #selector( handleDidBecomeActive) ,
129- name: UIApplication . didBecomeActiveNotification,
130- object: nil
131- )
132- NotificationCenter . default. addObserver (
133- self ,
134- selector: #selector( handleWillResignActive) ,
135- name: UIApplication . willResignActiveNotification,
136- object: nil
137- )
128+ didBecomeActiveNotification = UIApplication . didBecomeActiveNotification
129+ willResignActiveNotification = UIApplication . willResignActiveNotification
138130 #endif
139131 #elseif canImport(AppKit)
140- NotificationCenter . default. addObserver (
141- self ,
142- selector: #selector( handleDidBecomeActive) ,
143- name: NSApplication . didBecomeActiveNotification,
144- object: nil
145- )
146- NotificationCenter . default. addObserver (
147- self ,
148- selector: #selector( handleWillResignActive) ,
149- name: NSApplication . willResignActiveNotification,
150- object: nil
151- )
132+ didBecomeActiveNotification = NSApplication . didBecomeActiveNotification
133+ willResignActiveNotification = NSApplication . willResignActiveNotification
152134 #endif
135+
136+ if let didBecomeActiveNotification, let willResignActiveNotification {
137+ var cancellables = Set < AnyCancellable > ( )
138+
139+ NotificationCenter . default
140+ . publisher ( for: didBecomeActiveNotification)
141+ . sink (
142+ receiveCompletion: { _ in
143+ // hold ref to cancellable until it completes
144+ _ = cancellables
145+ } ,
146+ receiveValue: { [ weak self] _ in
147+ Task {
148+ await self ? . handleDidBecomeActive ( )
149+ }
150+ }
151+ )
152+ . store ( in: & cancellables)
153+
154+ NotificationCenter . default
155+ . publisher ( for: willResignActiveNotification)
156+ . sink (
157+ receiveCompletion: { _ in
158+ // hold ref to cancellable until it completes
159+ _ = cancellables
160+ } ,
161+ receiveValue: { [ weak self] _ in
162+ Task {
163+ await self ? . handleWillResignActive ( )
164+ }
165+ }
166+ )
167+ . store ( in: & cancellables)
168+ }
169+
153170 }
154171
155- @objc
156172 private func handleDidBecomeActive( ) {
157173 if configuration. autoRefreshToken {
158174 startAutoRefresh ( )
159175 }
160176 }
161177
162- @objc
163178 private func handleWillResignActive( ) {
164179 if configuration. autoRefreshToken {
165180 stopAutoRefresh ( )
@@ -170,6 +185,7 @@ public final class AuthClient: Sendable {
170185 // no-op
171186 }
172187 #endif
188+
173189 /// Listen for auth state changes.
174190 /// - Parameter listener: Block that executes when a new event is emitted.
175191 /// - Returns: A handle that can be used to manually unsubscribe.
@@ -189,7 +205,7 @@ public final class AuthClient: Sendable {
189205 /// Listen for auth state changes.
190206 ///
191207 /// An `.initialSession` is always emitted when this method is called.
192- public var authStateChanges :
208+ nonisolated public var authStateChanges :
193209 AsyncStream <
194210 (
195211 event: AuthChangeEvent ,
@@ -597,7 +613,7 @@ public final class AuthClient: Sendable {
597613 /// If that isn't the case, you should consider using
598614 /// ``signInWithOAuth(provider:redirectTo:scopes:queryParams:launchFlow:)`` or
599615 /// ``signInWithOAuth(provider:redirectTo:scopes:queryParams:configure:)``.
600- public func getOAuthSignInURL(
616+ nonisolated public func getOAuthSignInURL(
601617 provider: Provider ,
602618 scopes: String ? = nil ,
603619 redirectTo: URL ? = nil ,
@@ -672,7 +688,7 @@ public final class AuthClient: Sendable {
672688 scopes: scopes,
673689 queryParams: queryParams
674690 ) { @MainActor url in
675- try await withCheckedThrowingContinuation { continuation in
691+ try await withCheckedThrowingContinuation { [ configuration ] continuation in
676692 guard let callbackScheme = ( configuration. redirectToURL ?? redirectTo) ? . scheme else {
677693 preconditionFailure (
678694 " Please, provide a valid redirect URL, either thorugh `redirectTo` param, or globally thorugh `AuthClient.Configuration.redirectToURL`. "
@@ -767,7 +783,7 @@ public final class AuthClient: Sendable {
767783 /// supabase.auth.handle(url)
768784 /// }
769785 /// ```
770- public func handle( _ url: URL ) {
786+ nonisolated public func handle( _ url: URL ) {
771787 Task {
772788 do {
773789 try await session ( from: url)
@@ -1326,7 +1342,9 @@ public final class AuthClient: Sendable {
13261342 eventEmitter. emit ( . initialSession, session: session, token: token)
13271343 }
13281344
1329- private func prepareForPKCE( ) -> ( codeChallenge: String ? , codeChallengeMethod: String ? ) {
1345+ nonisolated private func prepareForPKCE( ) -> (
1346+ codeChallenge: String ? , codeChallengeMethod: String ?
1347+ ) {
13301348 guard configuration. flowType == . pkce else {
13311349 return ( nil , nil )
13321350 }
@@ -1350,7 +1368,7 @@ public final class AuthClient: Sendable {
13501368 || params [ " error_code " ] != nil && currentCodeVerifier != nil
13511369 }
13521370
1353- private func getURLForProvider(
1371+ nonisolated private func getURLForProvider(
13541372 url: URL ,
13551373 provider: Provider ,
13561374 scopes: String ? = nil ,
0 commit comments