@@ -14,6 +14,10 @@ import Helpers
14
14
import WatchKit
15
15
#endif
16
16
17
+ #if canImport(ObjectiveC) && canImport(Combine)
18
+ import Combine
19
+ #endif
20
+
17
21
typealias AuthClientID = Int
18
22
19
23
struct AuthClientLoggerDecorator : SupabaseLogger {
@@ -27,21 +31,28 @@ struct AuthClientLoggerDecorator: SupabaseLogger {
27
31
}
28
32
}
29
33
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
33
37
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 {
37
43
Dependencies [ clientID] . codeVerifierStorage
38
44
}
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 }
45
56
46
57
/// Returns the session, refreshing it if necessary.
47
58
///
@@ -55,26 +66,26 @@ public final class AuthClient: Sendable {
55
66
/// Returns the current session, if any.
56
67
///
57
68
/// 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 ? {
59
70
sessionStorage. get ( )
60
71
}
61
72
62
73
/// Returns the current user, if any.
63
74
///
64
75
/// 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 ? {
66
77
currentSession? . user
67
78
}
68
79
69
80
/// Namespace for accessing multi-factor authentication API.
70
- public var mfa : AuthMFA {
81
+ nonisolated public var mfa : AuthMFA {
71
82
AuthMFA ( clientID: clientID)
72
83
}
73
84
74
85
/// Namespace for the GoTrue admin methods.
75
86
/// - Warning: This methods requires `service_role` key, be careful to never expose `service_role`
76
87
/// key in the client.
77
- public var admin : AuthAdmin {
88
+ nonisolated public var admin : AuthAdmin {
78
89
AuthAdmin ( clientID: clientID)
79
90
}
80
91
@@ -83,10 +94,8 @@ public final class AuthClient: Sendable {
83
94
/// - Parameters:
84
95
/// - configuration: The client configuration.
85
96
public init ( configuration: Configuration ) {
86
- clientID = AuthClient . globalClientID. withValue {
87
- $0 += 1
88
- return $0
89
- }
97
+ AuthClient . globalClientID += 1
98
+ clientID = AuthClient . globalClientID
90
99
91
100
Dependencies [ clientID] = Dependencies (
92
101
configuration: configuration,
@@ -103,63 +112,69 @@ public final class AuthClient: Sendable {
103
112
Task { @MainActor in observeAppLifecycleChanges ( ) }
104
113
}
105
114
106
- #if canImport(ObjectiveC)
115
+ #if canImport(ObjectiveC) && canImport(Combine)
107
116
@MainActor
108
117
private func observeAppLifecycleChanges( ) {
118
+ var didBecomeActiveNotification : NSNotification . Name ?
119
+ var willResignActiveNotification : NSNotification . Name ?
120
+
109
121
#if canImport(UIKit)
110
122
#if canImport(WatchKit)
111
123
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
124
126
}
125
127
#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
138
130
#endif
139
131
#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
152
134
#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
+
153
170
}
154
171
155
- @objc
156
172
private func handleDidBecomeActive( ) {
157
173
if configuration. autoRefreshToken {
158
174
startAutoRefresh ( )
159
175
}
160
176
}
161
177
162
- @objc
163
178
private func handleWillResignActive( ) {
164
179
if configuration. autoRefreshToken {
165
180
stopAutoRefresh ( )
@@ -170,6 +185,7 @@ public final class AuthClient: Sendable {
170
185
// no-op
171
186
}
172
187
#endif
188
+
173
189
/// Listen for auth state changes.
174
190
/// - Parameter listener: Block that executes when a new event is emitted.
175
191
/// - Returns: A handle that can be used to manually unsubscribe.
@@ -189,7 +205,7 @@ public final class AuthClient: Sendable {
189
205
/// Listen for auth state changes.
190
206
///
191
207
/// An `.initialSession` is always emitted when this method is called.
192
- public var authStateChanges :
208
+ nonisolated public var authStateChanges :
193
209
AsyncStream <
194
210
(
195
211
event: AuthChangeEvent ,
@@ -597,7 +613,7 @@ public final class AuthClient: Sendable {
597
613
/// If that isn't the case, you should consider using
598
614
/// ``signInWithOAuth(provider:redirectTo:scopes:queryParams:launchFlow:)`` or
599
615
/// ``signInWithOAuth(provider:redirectTo:scopes:queryParams:configure:)``.
600
- public func getOAuthSignInURL(
616
+ nonisolated public func getOAuthSignInURL(
601
617
provider: Provider ,
602
618
scopes: String ? = nil ,
603
619
redirectTo: URL ? = nil ,
@@ -672,7 +688,7 @@ public final class AuthClient: Sendable {
672
688
scopes: scopes,
673
689
queryParams: queryParams
674
690
) { @MainActor url in
675
- try await withCheckedThrowingContinuation { continuation in
691
+ try await withCheckedThrowingContinuation { [ configuration ] continuation in
676
692
guard let callbackScheme = ( configuration. redirectToURL ?? redirectTo) ? . scheme else {
677
693
preconditionFailure (
678
694
" 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 {
767
783
/// supabase.auth.handle(url)
768
784
/// }
769
785
/// ```
770
- public func handle( _ url: URL ) {
786
+ nonisolated public func handle( _ url: URL ) {
771
787
Task {
772
788
do {
773
789
try await session ( from: url)
@@ -1326,7 +1342,9 @@ public final class AuthClient: Sendable {
1326
1342
eventEmitter. emit ( . initialSession, session: session, token: token)
1327
1343
}
1328
1344
1329
- private func prepareForPKCE( ) -> ( codeChallenge: String ? , codeChallengeMethod: String ? ) {
1345
+ nonisolated private func prepareForPKCE( ) -> (
1346
+ codeChallenge: String ? , codeChallengeMethod: String ?
1347
+ ) {
1330
1348
guard configuration. flowType == . pkce else {
1331
1349
return ( nil , nil )
1332
1350
}
@@ -1350,7 +1368,7 @@ public final class AuthClient: Sendable {
1350
1368
|| params [ " error_code " ] != nil && currentCodeVerifier != nil
1351
1369
}
1352
1370
1353
- private func getURLForProvider(
1371
+ nonisolated private func getURLForProvider(
1354
1372
url: URL ,
1355
1373
provider: Provider ,
1356
1374
scopes: String ? = nil ,
0 commit comments