Skip to content

Commit 831a1f7

Browse files
authored
refactor: simplify Auth dependency system (#278)
* refactor: simplify Auth dependency system * Remove unused code from tests * wip
1 parent 363aa00 commit 831a1f7

17 files changed

+389
-414
lines changed

Sources/Auth/AuthAdmin.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ import Foundation
99
@_spi(Internal) import _Helpers
1010

1111
public actor AuthAdmin {
12-
private var configuration: AuthClient.Configuration {
13-
Dependencies.current.value!.configuration
14-
}
12+
@Dependency(\.configuration)
13+
private var configuration: AuthClient.Configuration
1514

16-
private var api: APIClient {
17-
Dependencies.current.value!.api
18-
}
15+
@Dependency(\.api)
16+
private var api: APIClient
1917

2018
/// Delete a user. Requires `service_role` key.
2119
/// - Parameter id: The id of the user you want to delete.

Sources/Auth/AuthClient.swift

Lines changed: 49 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -56,33 +56,26 @@ public actor AuthClient {
5656
}
5757
}
5858

59-
private var configuration: Configuration {
60-
Dependencies.current.value!.configuration
61-
}
59+
@Dependency(\.configuration)
60+
private var configuration: Configuration
6261

63-
private var api: APIClient {
64-
Dependencies.current.value!.api
65-
}
62+
@Dependency(\.api)
63+
private var api: APIClient
6664

67-
private var sessionManager: any SessionManager {
68-
Dependencies.current.value!.sessionManager
69-
}
65+
@Dependency(\.eventEmitter)
66+
private var eventEmitter: EventEmitter
7067

71-
private var codeVerifierStorage: CodeVerifierStorage {
72-
Dependencies.current.value!.codeVerifierStorage
73-
}
68+
@Dependency(\.sessionManager)
69+
private var sessionManager: SessionManager
7470

75-
private var eventEmitter: any EventEmitter {
76-
Dependencies.current.value!.eventEmitter
77-
}
71+
@Dependency(\.codeVerifierStorage)
72+
private var codeVerifierStorage: CodeVerifierStorage
7873

79-
private var currentDate: @Sendable () -> Date {
80-
Dependencies.current.value!.currentDate
81-
}
74+
@Dependency(\.currentDate)
75+
private var currentDate: @Sendable () -> Date
8276

83-
private var logger: (any SupabaseLogger)? {
84-
Dependencies.current.value!.logger
85-
}
77+
@Dependency(\.logger)
78+
private var logger: (any SupabaseLogger)?
8679

8780
/// Returns the session, refreshing it if necessary.
8881
///
@@ -141,17 +134,20 @@ public actor AuthClient {
141134
/// - Parameters:
142135
/// - configuration: The client configuration.
143136
public init(configuration: Configuration) {
144-
let api = APIClient.live(http: HTTPClient(
145-
logger: configuration.logger,
146-
fetchHandler: configuration.fetch
147-
))
137+
let api = APIClient.live(
138+
configuration: configuration,
139+
http: HTTPClient(
140+
logger: configuration.logger,
141+
fetchHandler: configuration.fetch
142+
)
143+
)
148144

149145
self.init(
150146
configuration: configuration,
151-
sessionManager: DefaultSessionManager.shared,
147+
sessionManager: .live,
152148
codeVerifierStorage: .live,
153149
api: api,
154-
eventEmitter: DefaultEventEmitter.shared,
150+
eventEmitter: .live,
155151
sessionStorage: .live,
156152
logger: configuration.logger
157153
)
@@ -160,31 +156,29 @@ public actor AuthClient {
160156
/// This internal initializer is here only for easy injecting mock instances when testing.
161157
init(
162158
configuration: Configuration,
163-
sessionManager: any SessionManager,
159+
sessionManager: SessionManager,
164160
codeVerifierStorage: CodeVerifierStorage,
165161
api: APIClient,
166-
eventEmitter: any EventEmitter,
162+
eventEmitter: EventEmitter,
167163
sessionStorage: SessionStorage,
168164
logger: (any SupabaseLogger)?
169165
) {
170166
mfa = AuthMFA()
171167
admin = AuthAdmin()
172168

173-
Dependencies.current.setValue(
174-
Dependencies(
175-
configuration: configuration,
176-
sessionManager: sessionManager,
177-
api: api,
178-
eventEmitter: eventEmitter,
179-
sessionStorage: sessionStorage,
180-
sessionRefresher: SessionRefresher(
181-
refreshSession: { [weak self] in
182-
try await self?.refreshSession(refreshToken: $0) ?? .empty
183-
}
184-
),
185-
codeVerifierStorage: codeVerifierStorage,
186-
logger: logger
187-
)
169+
Current = Dependencies(
170+
configuration: configuration,
171+
sessionManager: sessionManager,
172+
api: api,
173+
eventEmitter: eventEmitter,
174+
sessionStorage: sessionStorage,
175+
sessionRefresher: SessionRefresher(
176+
refreshSession: { [weak self] in
177+
try await self?.refreshSession(refreshToken: $0) ?? .empty
178+
}
179+
),
180+
codeVerifierStorage: codeVerifierStorage,
181+
logger: logger
188182
)
189183
}
190184

@@ -310,7 +304,11 @@ public actor AuthClient {
310304

311305
/// Log in an existing user with an email and password.
312306
@discardableResult
313-
public func signIn(email: String, password: String, captchaToken: String? = nil) async throws -> Session {
307+
public func signIn(
308+
email: String,
309+
password: String,
310+
captchaToken: String? = nil
311+
) async throws -> Session {
314312
try await _signIn(
315313
request: .init(
316314
path: "/token",
@@ -329,7 +327,11 @@ public actor AuthClient {
329327

330328
/// Log in an existing user with a phone and password.
331329
@discardableResult
332-
public func signIn(phone: String, password: String, captchaToken: String? = nil) async throws -> Session {
330+
public func signIn(
331+
phone: String,
332+
password: String,
333+
captchaToken: String? = nil
334+
) async throws -> Session {
333335
try await _signIn(
334336
request: .init(
335337
path: "/token",
@@ -931,7 +933,7 @@ public actor AuthClient {
931933

932934
private func emitInitialSession(forToken token: ObservationToken) async {
933935
let session = try? await session
934-
eventEmitter.emit(.initialSession, session: session, token: token)
936+
eventEmitter.emit(.initialSession, session, token)
935937
}
936938

937939
private func prepareForPKCE() -> (codeChallenge: String?, codeChallengeMethod: String?) {

Sources/Auth/AuthMFA.swift

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,17 @@ import Foundation
33

44
/// Contains the full multi-factor authentication API.
55
public actor AuthMFA {
6-
private var api: APIClient {
7-
Dependencies.current.value!.api
8-
}
6+
@Dependency(\.api)
7+
private var api: APIClient
98

10-
private var sessionManager: any SessionManager {
11-
Dependencies.current.value!.sessionManager
12-
}
9+
@Dependency(\.configuration)
10+
private var configuration: AuthClient.Configuration
1311

14-
private var configuration: AuthClient.Configuration {
15-
Dependencies.current.value!.configuration
16-
}
12+
@Dependency(\.sessionManager)
13+
private var sessionManager: SessionManager
1714

18-
private var eventEmitter: any EventEmitter {
19-
Dependencies.current.value!.eventEmitter
20-
}
15+
@Dependency(\.eventEmitter)
16+
private var eventEmitter: EventEmitter
2117

2218
/// Starts the enrollment process for a new Multi-Factor Authentication (MFA) factor. This method
2319
/// creates a new `unverified` factor.

Sources/Auth/Internal/APIClient.swift

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@ struct APIClient: Sendable {
66
}
77

88
extension APIClient {
9-
static func live(http: HTTPClient) -> Self {
10-
var configuration: AuthClient.Configuration {
11-
Dependencies.current.value!.configuration
12-
}
13-
14-
return APIClient(
9+
static func live(
10+
configuration: AuthClient.Configuration,
11+
http: HTTPClient
12+
) -> Self {
13+
APIClient(
1514
execute: { request in
1615
var request = request
1716
request.headers.merge(configuration.headers) { r, _ in r }
@@ -45,7 +44,9 @@ extension APIClient {
4544
extension APIClient {
4645
@discardableResult
4746
func authorizedExecute(_ request: Request) async throws -> Response {
48-
let session = try await Dependencies.current.value!.sessionManager.session()
47+
@Dependency(\.sessionManager) var sessionManager
48+
49+
let session = try await sessionManager.session()
4950

5051
var request = request
5152
request.headers["Authorization"] = "Bearer \(session.accessToken)"

Sources/Auth/Internal/CodeVerifierStorage.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@ struct CodeVerifierStorage: Sendable {
88
}
99

1010
extension CodeVerifierStorage {
11-
static var live: Self = {
12-
var localStorage: any AuthLocalStorage {
13-
Dependencies.current.value!.configuration.localStorage
14-
}
11+
static let live: Self = {
12+
@Dependency(\.configuration.localStorage) var localStorage
1513

1614
let key = "supabase.code-verifier"
1715

Sources/Auth/Internal/Dependencies.swift

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,42 @@ import ConcurrencyExtras
33
import Foundation
44

55
struct Dependencies: Sendable {
6-
static let current = LockIsolated(Dependencies?.none)
7-
86
var configuration: AuthClient.Configuration
9-
var sessionManager: any SessionManager
7+
var sessionManager: SessionManager
108
var api: APIClient
11-
var eventEmitter: any EventEmitter
9+
var eventEmitter: EventEmitter
1210
var sessionStorage: SessionStorage
1311
var sessionRefresher: SessionRefresher
1412
var codeVerifierStorage: CodeVerifierStorage
1513
var currentDate: @Sendable () -> Date = { Date() }
1614
var logger: (any SupabaseLogger)?
1715
}
16+
17+
private let _Current = LockIsolated<Dependencies?>(nil)
18+
var Current: Dependencies {
19+
get {
20+
guard let instance = _Current.value else {
21+
fatalError("Current should be set before usage.")
22+
}
23+
24+
return instance
25+
}
26+
set {
27+
_Current.withValue { Current in
28+
Current = newValue
29+
}
30+
}
31+
}
32+
33+
@propertyWrapper
34+
struct Dependency<Value> {
35+
var wrappedValue: Value {
36+
Current[keyPath: keyPath]
37+
}
38+
39+
let keyPath: KeyPath<Dependencies, Value>
40+
41+
init(_ keyPath: KeyPath<Dependencies, Value>) {
42+
self.keyPath = keyPath
43+
}
44+
}

Sources/Auth/Internal/EventEmitter.swift

Lines changed: 33 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,60 +2,53 @@ import ConcurrencyExtras
22
import Foundation
33
@_spi(Internal) import _Helpers
44

5-
protocol EventEmitter: Sendable {
6-
func attachListener(
5+
struct EventEmitter: Sendable {
6+
var attachListener: @Sendable (
77
_ listener: @escaping AuthStateChangeListener
88
) -> ObservationToken
99

10-
func emit(
10+
var emit: @Sendable (
1111
_ event: AuthChangeEvent,
12-
session: Session?,
13-
token: ObservationToken?
14-
)
12+
_ session: Session?,
13+
_ token: ObservationToken?
14+
) -> Void
1515
}
1616

1717
extension EventEmitter {
1818
func emit(
1919
_ event: AuthChangeEvent,
2020
session: Session?
2121
) {
22-
emit(event, session: session, token: nil)
22+
emit(event, session, nil)
2323
}
2424
}
2525

26-
final class DefaultEventEmitter: EventEmitter {
27-
static let shared = DefaultEventEmitter()
28-
29-
private init() {}
30-
31-
let emitter = _Helpers.EventEmitter<(AuthChangeEvent, Session?)?>(
32-
initialEvent: nil,
33-
emitsLastEventWhenAttaching: false
34-
)
35-
36-
func attachListener(
37-
_ listener: @escaping AuthStateChangeListener
38-
) -> ObservationToken {
39-
emitter.attach { event in
40-
guard let event else { return }
41-
listener(event.0, event.1)
42-
}
43-
}
44-
45-
func emit(
46-
_ event: AuthChangeEvent,
47-
session: Session?,
48-
token: ObservationToken? = nil
49-
) {
50-
NotificationCenter.default.post(
51-
name: AuthClient.didChangeAuthStateNotification,
52-
object: nil,
53-
userInfo: [
54-
AuthClient.authChangeEventInfoKey: event,
55-
AuthClient.authChangeSessionInfoKey: session as Any,
56-
]
26+
extension EventEmitter {
27+
static let live: EventEmitter = {
28+
let emitter = _Helpers.EventEmitter<(AuthChangeEvent, Session?)?>(
29+
initialEvent: nil,
30+
emitsLastEventWhenAttaching: false
5731
)
5832

59-
emitter.emit((event, session), to: token)
60-
}
33+
return EventEmitter(
34+
attachListener: { listener in
35+
emitter.attach { event in
36+
guard let event else { return }
37+
listener(event.0, event.1)
38+
}
39+
},
40+
emit: { event, session, token in
41+
NotificationCenter.default.post(
42+
name: AuthClient.didChangeAuthStateNotification,
43+
object: nil,
44+
userInfo: [
45+
AuthClient.authChangeEventInfoKey: event,
46+
AuthClient.authChangeSessionInfoKey: session as Any,
47+
]
48+
)
49+
50+
emitter.emit((event, session), to: token)
51+
}
52+
)
53+
}()
6154
}

0 commit comments

Comments
 (0)