@@ -38,6 +38,7 @@ public final class RealtimeChannelV2: Sendable {
38
38
let config : RealtimeChannelConfig
39
39
let logger : ( any SupabaseLogger ) ?
40
40
let socket : RealtimeClientV2
41
+ let maxRetryAttempt = 5
41
42
42
43
@MainActor var joinRef : String ? { mutableState. joinRef }
43
44
@@ -84,6 +85,40 @@ public final class RealtimeChannelV2: Sendable {
84
85
/// Subscribes to the channel
85
86
@MainActor
86
87
public func subscribe( ) async {
88
+ logger? . debug ( " Starting subscription to channel ' \( topic) ' (attempt 1/ \( maxRetryAttempt) ) " )
89
+
90
+ var attempts = 0
91
+
92
+ while attempts < maxRetryAttempt {
93
+ attempts += 1
94
+
95
+ do {
96
+ logger? . debug ( " Attempting to subscribe to channel ' \( topic) ' (attempt \( attempts) / \( maxRetryAttempt) ) " )
97
+ try await withTimeout ( interval: socket. options. timeoutInterval) { [ self ] in
98
+ await _subscribe ( )
99
+ }
100
+ logger? . debug ( " Successfully subscribed to channel ' \( topic) ' " )
101
+ return
102
+ } catch {
103
+ if error is TimeoutError {
104
+ logger? . debug ( " Subscribe timed out for channel ' \( topic) ' (attempt \( attempts) / \( maxRetryAttempt) ) " )
105
+ if attempts < maxRetryAttempt {
106
+ logger? . debug ( " Retrying subscription to channel ' \( topic) '... " )
107
+ } else {
108
+ logger? . error ( " Failed to subscribe to channel ' \( topic) ' after \( maxRetryAttempt) attempts due to timeout " )
109
+ }
110
+ } else {
111
+ logger? . error ( " Subscribe failed for channel ' \( topic) ' (attempt \( attempts) / \( maxRetryAttempt) ): \( error) " )
112
+ break
113
+ }
114
+ }
115
+ }
116
+
117
+ logger? . error ( " Subscription to channel ' \( topic) ' failed after \( attempts) attempts " )
118
+ }
119
+
120
+ @MainActor
121
+ private func _subscribe( ) async {
87
122
if socket. status != . connected {
88
123
if socket. options. connectOnSubscribe != true {
89
124
reportIssue (
@@ -95,7 +130,6 @@ public final class RealtimeChannelV2: Sendable {
95
130
}
96
131
97
132
status = . subscribing
98
- logger? . debug ( " Subscribing to channel \( topic) " )
99
133
100
134
let joinConfig = RealtimeJoinConfig (
101
135
broadcast: config. broadcast,
@@ -104,35 +138,23 @@ public final class RealtimeChannelV2: Sendable {
104
138
isPrivate: config. isPrivate
105
139
)
106
140
141
+ let accessToken = await socket. _getAccessToken ( )
107
142
let payload = RealtimeJoinPayload (
108
143
config: joinConfig,
109
- accessToken: await socket . _getAccessToken ( ) ,
144
+ accessToken: accessToken ,
110
145
version: socket. options. headers [ . xClientInfo]
111
146
)
112
147
113
148
let joinRef = socket. makeRef ( )
114
149
mutableState. joinRef = joinRef
115
150
116
- logger? . debug ( " Subscribing to channel with body: \( joinConfig) " )
117
-
118
151
await push (
119
152
ChannelEvent . join,
120
153
ref: joinRef,
121
154
payload: try ! JSONObject ( payload)
122
155
)
123
156
124
- do {
125
- try await withTimeout ( interval: socket. options. timeoutInterval) { [ self ] in
126
- _ = await statusChange. first { @Sendable in $0 == . subscribed }
127
- }
128
- } catch {
129
- if error is TimeoutError {
130
- logger? . debug ( " Subscribe timed out. " )
131
- await subscribe ( )
132
- } else {
133
- logger? . error ( " Subscribe failed: \( error) " )
134
- }
135
- }
157
+ _ = await statusChange. first { @Sendable in $0 == . subscribed }
136
158
}
137
159
138
160
public func unsubscribe( ) async {
@@ -149,7 +171,6 @@ public final class RealtimeChannelV2: Sendable {
149
171
" manually updating auth token per channel is not recommended, please use `setAuth` in RealtimeClient instead. "
150
172
)
151
173
public func updateAuth( jwt: String ? ) async {
152
- logger? . debug ( " Updating auth token for channel \( topic) " )
153
174
await push (
154
175
ChannelEvent . accessToken,
155
176
payload: [ " access_token " : jwt. map { . string( $0) } ?? . null]
@@ -264,7 +285,6 @@ public final class RealtimeChannelV2: Sendable {
264
285
func onMessage( _ message: RealtimeMessageV2 ) async {
265
286
do {
266
287
guard let eventType = message. _eventType else {
267
- logger? . debug ( " Received message without event type: \( message) " )
268
288
return
269
289
}
270
290
@@ -275,12 +295,9 @@ public final class RealtimeChannelV2: Sendable {
275
295
276
296
case . system:
277
297
if message. status == . ok {
278
- logger? . debug ( " Subscribed to channel \( message. topic) " )
279
298
status = . subscribed
280
299
} else {
281
- logger? . debug (
282
- " Failed to subscribe to channel \( message. topic) : \( message. payload) "
283
- )
300
+ logger? . error ( " Failed to subscribe to channel ' \( topic) ' via system event: \( message. payload) " )
284
301
}
285
302
286
303
callbackManager. triggerSystem ( message: message)
@@ -306,13 +323,12 @@ public final class RealtimeChannelV2: Sendable {
306
323
307
324
if self . status != . subscribed {
308
325
self . status = . subscribed
309
- logger? . debug ( " Subscribed to channel \( message. topic) " )
310
326
}
311
327
}
312
328
313
329
case . postgresChanges:
314
330
guard let data = message. payload [ " data " ] else {
315
- logger? . debug ( " Expected \" data \" key in message payload. " )
331
+ logger? . error ( " Expected \" data \" key in message payload. " )
316
332
return
317
333
}
318
334
@@ -370,12 +386,11 @@ public final class RealtimeChannelV2: Sendable {
370
386
371
387
case . close:
372
388
socket. _remove ( self )
373
- logger? . debug ( " Unsubscribed from channel \( message. topic) " )
374
389
status = . unsubscribed
375
390
376
391
case . error:
377
- logger? . debug (
378
- " Received an error in channel \( message . topic) . That could be as a result of an invalid access token"
392
+ logger? . error (
393
+ " Received an error in channel ' \( topic) '. This could be due to an invalid access token: \( message . payload ) "
379
394
)
380
395
381
396
case . presenceDiff:
@@ -388,7 +403,7 @@ public final class RealtimeChannelV2: Sendable {
388
403
callbackManager. triggerPresenceDiffs ( joins: joins, leaves: [ : ] , rawMessage: message)
389
404
}
390
405
} catch {
391
- logger? . debug ( " Failed: \( error) " )
406
+ logger? . error ( " Failed to process message for channel ' \( topic ) ' : \( error) " )
392
407
}
393
408
}
394
409
@@ -543,9 +558,11 @@ public final class RealtimeChannelV2: Sendable {
543
558
@MainActor
544
559
@discardableResult
545
560
func push( _ event: String , ref: String ? = nil , payload: JSONObject = [ : ] ) async -> PushStatus {
561
+ let messageRef = ref ?? socket. makeRef ( )
562
+
546
563
let message = RealtimeMessageV2 (
547
564
joinRef: joinRef,
548
- ref: ref ?? socket . makeRef ( ) ,
565
+ ref: messageRef ,
549
566
topic: self . topic,
550
567
event: event,
551
568
payload: payload
@@ -556,12 +573,18 @@ public final class RealtimeChannelV2: Sendable {
556
573
mutableState. pushes [ ref] = push
557
574
}
558
575
559
- return await push. send ( )
576
+ let status = await push. send ( )
577
+ return status
560
578
}
561
579
562
580
@MainActor
563
581
private func didReceiveReply( ref: String , status: String ) {
564
582
let push = mutableState. pushes. removeValue ( forKey: ref)
565
- push? . didReceive ( status: PushStatus ( rawValue: status) ?? . ok)
583
+ if let push = push {
584
+ let pushStatus = PushStatus ( rawValue: status) ?? . ok
585
+ push. didReceive ( status: pushStatus)
586
+ } else {
587
+ logger? . error ( " No push found for reply on channel ' \( topic) ': ref=' \( ref) ' " )
588
+ }
566
589
}
567
590
}
0 commit comments