2
2
import ConcurrencyExtras
3
3
import Foundation
4
4
@_exported import Functions
5
+ import HTTPTypes
5
6
import Helpers
6
7
import IssueReporting
7
8
@_exported import PostgREST
8
9
@_exported import Realtime
9
10
@_exported import Storage
10
- import HTTPTypes
11
11
12
12
#if canImport(FoundationNetworking)
13
13
import FoundationNetworking
@@ -33,10 +33,11 @@ public final class SupabaseClient: Sendable {
33
33
/// Supabase Auth allows you to create and manage user sessions for access to data that is secured by access policies.
34
34
public var auth : AuthClient {
35
35
if options. auth. accessToken != nil {
36
- reportIssue ( """
37
- Supabase Client is configured with the auth.accessToken option,
38
- accessing supabase.auth is not possible.
39
- """ )
36
+ reportIssue (
37
+ """
38
+ Supabase Client is configured with the auth.accessToken option,
39
+ accessing supabase.auth is not possible.
40
+ """ )
40
41
}
41
42
return _auth
42
43
}
@@ -80,7 +81,14 @@ public final class SupabaseClient: Sendable {
80
81
let _realtime : UncheckedSendable < RealtimeClient >
81
82
82
83
/// Realtime client for Supabase
83
- public let realtimeV2 : RealtimeClientV2
84
+ public var realtimeV2 : RealtimeClientV2 {
85
+ mutableState. withValue {
86
+ if $0. realtime == nil {
87
+ $0. realtime = _initRealtimeClient ( )
88
+ }
89
+ return $0. realtime!
90
+ }
91
+ }
84
92
85
93
/// Supabase Functions allows you to deploy and invoke edge functions.
86
94
public var functions : FunctionsClient {
@@ -112,6 +120,7 @@ public final class SupabaseClient: Sendable {
112
120
var storage : SupabaseStorageClient ?
113
121
var rest : PostgrestClient ?
114
122
var functions : FunctionsClient ?
123
+ var realtime : RealtimeClientV2 ?
115
124
116
125
var changedAccessToken : String ?
117
126
}
@@ -189,18 +198,6 @@ public final class SupabaseClient: Sendable {
189
198
)
190
199
)
191
200
192
- var realtimeOptions = options. realtime
193
- realtimeOptions. headers. merge ( with: _headers)
194
-
195
- if realtimeOptions. logger == nil {
196
- realtimeOptions. logger = options. global. logger
197
- }
198
-
199
- realtimeV2 = RealtimeClientV2 (
200
- url: supabaseURL. appendingPathComponent ( " /realtime/v1 " ) ,
201
- options: realtimeOptions
202
- )
203
-
204
201
if options. auth. accessToken == nil {
205
202
listenForAuthEvents ( )
206
203
}
@@ -351,11 +348,7 @@ public final class SupabaseClient: Sendable {
351
348
}
352
349
353
350
private func adapt( request: URLRequest ) async -> URLRequest {
354
- let token : String ? = if let accessToken = options. auth. accessToken {
355
- try ? await accessToken ( )
356
- } else {
357
- try ? await auth. session. accessToken
358
- }
351
+ let token = try ? await _getAccessToken ( )
359
352
360
353
var request = request
361
354
if let token {
@@ -364,6 +357,14 @@ public final class SupabaseClient: Sendable {
364
357
return request
365
358
}
366
359
360
+ private func _getAccessToken( ) async throws -> String {
361
+ if let accessToken = options. auth. accessToken {
362
+ try await accessToken ( )
363
+ } else {
364
+ try await auth. session. accessToken
365
+ }
366
+ }
367
+
367
368
private func listenForAuthEvents( ) {
368
369
let task = Task {
369
370
for await (event, session) in auth. authStateChanges {
@@ -377,7 +378,9 @@ public final class SupabaseClient: Sendable {
377
378
378
379
private func handleTokenChanged( event: AuthChangeEvent , session: Session ? ) async {
379
380
let accessToken : String ? = mutableState. withValue {
380
- if [ . initialSession, . signedIn, . tokenRefreshed] . contains ( event) , $0. changedAccessToken != session? . accessToken {
381
+ if [ . initialSession, . signedIn, . tokenRefreshed] . contains ( event) ,
382
+ $0. changedAccessToken != session? . accessToken
383
+ {
381
384
$0. changedAccessToken = session? . accessToken
382
385
return session? . accessToken ?? supabaseKey
383
386
}
@@ -393,4 +396,33 @@ public final class SupabaseClient: Sendable {
393
396
realtime. setAuth ( accessToken)
394
397
await realtimeV2. setAuth ( accessToken)
395
398
}
399
+
400
+ private func _initRealtimeClient( ) -> RealtimeClientV2 {
401
+ var realtimeOptions = options. realtime
402
+ realtimeOptions. headers. merge ( with: _headers)
403
+
404
+ if realtimeOptions. logger == nil {
405
+ realtimeOptions. logger = options. global. logger
406
+ }
407
+
408
+ if realtimeOptions. accessToken == nil {
409
+ realtimeOptions. accessToken = { [ weak self] in
410
+ try await self ? . _getAccessToken ( ) ?? " "
411
+ }
412
+ } else {
413
+ reportIssue (
414
+ """
415
+ You assigned a custom `accessToken` closure to the RealtimeClientV2. This might not work as you expect
416
+ as SupabaseClient uses Auth for pulling an access token to send on the realtime channels.
417
+
418
+ Please make sure you know what you're doing.
419
+ """
420
+ )
421
+ }
422
+
423
+ return RealtimeClientV2 (
424
+ url: supabaseURL. appendingPathComponent ( " /realtime/v1 " ) ,
425
+ options: realtimeOptions
426
+ )
427
+ }
396
428
}
0 commit comments