22import ConcurrencyExtras
33import Foundation
44@_exported import Functions
5+ import HTTPTypes
56import Helpers
67import IssueReporting
78@_exported import PostgREST
89@_exported import Realtime
910@_exported import Storage
10- import HTTPTypes
1111
1212#if canImport(FoundationNetworking)
1313 import FoundationNetworking
@@ -33,10 +33,11 @@ public final class SupabaseClient: Sendable {
3333 /// Supabase Auth allows you to create and manage user sessions for access to data that is secured by access policies.
3434 public var auth : AuthClient {
3535 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+ """ )
4041 }
4142 return _auth
4243 }
@@ -80,7 +81,14 @@ public final class SupabaseClient: Sendable {
8081 let _realtime : UncheckedSendable < RealtimeClient >
8182
8283 /// 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+ }
8492
8593 /// Supabase Functions allows you to deploy and invoke edge functions.
8694 public var functions : FunctionsClient {
@@ -112,6 +120,7 @@ public final class SupabaseClient: Sendable {
112120 var storage : SupabaseStorageClient ?
113121 var rest : PostgrestClient ?
114122 var functions : FunctionsClient ?
123+ var realtime : RealtimeClientV2 ?
115124
116125 var changedAccessToken : String ?
117126 }
@@ -189,18 +198,6 @@ public final class SupabaseClient: Sendable {
189198 )
190199 )
191200
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-
204201 if options. auth. accessToken == nil {
205202 listenForAuthEvents ( )
206203 }
@@ -351,11 +348,7 @@ public final class SupabaseClient: Sendable {
351348 }
352349
353350 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 ( )
359352
360353 var request = request
361354 if let token {
@@ -364,6 +357,14 @@ public final class SupabaseClient: Sendable {
364357 return request
365358 }
366359
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+
367368 private func listenForAuthEvents( ) {
368369 let task = Task {
369370 for await (event, session) in auth. authStateChanges {
@@ -377,7 +378,9 @@ public final class SupabaseClient: Sendable {
377378
378379 private func handleTokenChanged( event: AuthChangeEvent , session: Session ? ) async {
379380 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+ {
381384 $0. changedAccessToken = session? . accessToken
382385 return session? . accessToken ?? supabaseKey
383386 }
@@ -393,4 +396,33 @@ public final class SupabaseClient: Sendable {
393396 realtime. setAuth ( accessToken)
394397 await realtimeV2. setAuth ( accessToken)
395398 }
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+ }
396428}
0 commit comments