Skip to content

Commit ffc93a9

Browse files
committed
feat: add AuthStateChangeListenerRegistration type
1 parent 5a3e4c2 commit ffc93a9

File tree

4 files changed

+88
-21
lines changed

4 files changed

+88
-21
lines changed

Sources/Auth/AuthClient.swift

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,6 @@ import Foundation
55
import FoundationNetworking
66
#endif
77

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-
268
public actor AuthClient {
279
/// FetchHandler is a type alias for asynchronous network request handling.
2810
public typealias FetchHandler = @Sendable (
@@ -216,7 +198,7 @@ public actor AuthClient {
216198
@discardableResult
217199
public func onAuthStateChange(
218200
_ listener: @escaping AuthStateChangeListener
219-
) async -> AuthStateChangeListenerHandle {
201+
) async -> AuthStateChangeListenerRegistration {
220202
let handle = eventEmitter.attachListener(listener)
221203
await emitInitialSession(forHandle: handle)
222204
return handle
@@ -240,7 +222,7 @@ public actor AuthClient {
240222
}
241223

242224
continuation.onTermination = { _ in
243-
handle.cancel()
225+
handle.remove()
244226
}
245227
}
246228

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// AuthStateChangeListener.swift
3+
//
4+
//
5+
// Created by Guilherme Souza on 17/02/24.
6+
//
7+
8+
import ConcurrencyExtras
9+
import Foundation
10+
11+
/// A listener that can be removed by calling ``AuthStateChangeListenerRegistration/remove()``.
12+
///
13+
/// - Note: Listener is automatically removed on deinit.
14+
public protocol AuthStateChangeListenerRegistration: Sendable, AnyObject {
15+
/// Removes the listener. After the initial call, subsequent calls have no effect.
16+
func remove()
17+
}
18+
19+
final class AuthStateChangeListenerHandle: AuthStateChangeListenerRegistration {
20+
let _onRemove = LockIsolated((@Sendable () -> Void)?.none)
21+
22+
public func remove() {
23+
_onRemove.withValue {
24+
if $0 == nil {
25+
return
26+
}
27+
28+
$0?()
29+
$0 = nil
30+
}
31+
}
32+
33+
deinit {
34+
remove()
35+
}
36+
}
37+
38+
public typealias AuthStateChangeListener = @Sendable (
39+
_ event: AuthChangeEvent,
40+
_ session: Session?
41+
) -> Void

Sources/Auth/Internal/EventEmitter.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ final class DefaultEventEmitter: EventEmitter {
3535
let handle = AuthStateChangeListenerHandle()
3636
let key = ObjectIdentifier(handle)
3737

38-
handle.onCancel = { [weak self] in
38+
handle._onRemove.setValue { [weak self] in
3939
self?.listeners.withValue {
4040
$0[key] = nil
4141
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//
2+
// AuthStateChangeListenerHandleTests.swift
3+
//
4+
//
5+
// Created by Guilherme Souza on 17/02/24.
6+
//
7+
8+
@testable import Auth
9+
import ConcurrencyExtras
10+
import Foundation
11+
import XCTest
12+
13+
final class AuthStateChangeListenerHandleTests: XCTestCase {
14+
func testRemove() {
15+
let handle = AuthStateChangeListenerHandle()
16+
17+
let onRemoveCallCount = LockIsolated(0)
18+
handle._onRemove.setValue {
19+
onRemoveCallCount.withValue {
20+
$0 += 1
21+
}
22+
}
23+
24+
handle.remove()
25+
handle.remove()
26+
27+
XCTAssertEqual(onRemoveCallCount.value, 1)
28+
}
29+
30+
func testDeinit() {
31+
var handle: AuthStateChangeListenerHandle? = AuthStateChangeListenerHandle()
32+
33+
let onRemoveCallCount = LockIsolated(0)
34+
handle?._onRemove.setValue {
35+
onRemoveCallCount.withValue {
36+
$0 += 1
37+
}
38+
}
39+
40+
handle = nil
41+
42+
XCTAssertEqual(onRemoveCallCount.value, 1)
43+
}
44+
}

0 commit comments

Comments
 (0)