@@ -5,10 +5,29 @@ import Foundation
5
5
import FoundationNetworking
6
6
#endif
7
7
8
+ public final class AuthStateChangeListenerHandle {
9
+ var onCancel : ( @Sendable ( ) -> Void ) ?
10
+
11
+ public func cancel( ) {
12
+ onCancel ? ( )
13
+ onCancel = nil
14
+ }
15
+
16
+ deinit {
17
+ cancel ( )
18
+ }
19
+ }
20
+
21
+ public typealias AuthStateChangeListener = @Sendable (
22
+ _ event: AuthChangeEvent ,
23
+ _ session: Session ?
24
+ ) -> Void
25
+
8
26
public actor AuthClient {
9
27
/// FetchHandler is a type alias for asynchronous network request handling.
10
- public typealias FetchHandler =
11
- @Sendable ( _ request: URLRequest ) async throws -> ( Data , URLResponse )
28
+ public typealias FetchHandler = @Sendable (
29
+ _ request: URLRequest
30
+ ) async throws -> ( Data , URLResponse )
12
31
13
32
/// Configuration struct represents the client configuration.
14
33
public struct Configuration : Sendable {
@@ -150,7 +169,7 @@ public actor AuthClient {
150
169
sessionManager: . live,
151
170
codeVerifierStorage: . live,
152
171
api: api,
153
- eventEmitter: . live ,
172
+ eventEmitter: EventEmitter ( ) ,
154
173
sessionStorage: . live,
155
174
logger: configuration. logger
156
175
)
@@ -187,19 +206,38 @@ public actor AuthClient {
187
206
)
188
207
}
189
208
209
+ /// Listen for auth state changes.
210
+ ///
211
+ /// An `.initialSession` is always emitted when this method is called.
212
+ @discardableResult
213
+ public func onAuthStateChange(
214
+ _ listener: @escaping AuthStateChangeListener
215
+ ) -> AuthStateChangeListenerHandle {
216
+ let handle = eventEmitter. attachListener ( listener)
217
+ Task {
218
+ await emitInitialSession ( forHandle: handle)
219
+ }
220
+ return handle
221
+ }
222
+
190
223
/// Listen for auth state changes.
191
224
///
192
225
/// An `.initialSession` is always emitted when this method is called.
193
226
public var authStateChanges : AsyncStream < (
194
227
event: AuthChangeEvent ,
195
228
session: Session ?
196
229
) > {
197
- let ( id, stream) = eventEmitter. attachListener ( )
198
- logger? . debug ( " auth state change listener with id ' \( id. uuidString) ' attached. " )
230
+ let ( stream, continuation) = AsyncStream< (
231
+ event: AuthChangeEvent,
232
+ session: Session?
233
+ ) > . makeStream( )
234
+
235
+ let handle = onAuthStateChange { event, session in
236
+ continuation. yield ( ( event, session) )
237
+ }
199
238
200
- Task { [ id] in
201
- await emitInitialSession ( forStreamWithID: id)
202
- logger? . debug ( " initial session for listener with id ' \( id. uuidString) ' emitted. " )
239
+ continuation. onTermination = { _ in
240
+ handle. cancel ( )
203
241
}
204
242
205
243
return stream
@@ -884,9 +922,9 @@ public actor AuthClient {
884
922
return session
885
923
}
886
924
887
- private func emitInitialSession( forStreamWithID id : UUID ) async {
925
+ private func emitInitialSession( forHandle handle : AuthStateChangeListenerHandle ) async {
888
926
let session = try ? await session
889
- eventEmitter. emit ( . initialSession, session, id )
927
+ eventEmitter. emit ( . initialSession, session: session , handle : handle )
890
928
}
891
929
892
930
private func prepareForPKCE( ) -> ( codeChallenge: String ? , codeChallengeMethod: String ? ) {
0 commit comments