@@ -6,6 +6,10 @@ private struct MessageSubscriptionWrapper {
66 var serial : String
77}
88
9+ #if DEBUG
10+ extension ARTMessage : @retroactive @unchecked Sendable { }
11+ #endif
12+
913// TODO: Don't have a strong understanding of why @MainActor is needed here. Revisit as part of https://github.com/ably-labs/ably-chat-swift/issues/83
1014@MainActor
1115internal final class DefaultMessages : Messages , EmitsDiscontinuities {
@@ -54,52 +58,62 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities {
5458 // (CHA-M5k) Incoming realtime events that are malformed (unknown field should be ignored) shall not be emitted to subscribers.
5559 let eventListener = channel. subscribe ( RealtimeMessageName . chatMessage. rawValue) { message in
5660 Task {
57- // TODO: Revisit errors thrown as part of https://github.com/ably-labs/ably-chat-swift/issues/32
58- guard let ablyCocoaData = message. data,
59- let data = JSONValue ( ablyCocoaData: ablyCocoaData) . objectValue,
60- let text = data [ " text " ] ? . stringValue
61- else {
62- throw ARTErrorInfo . create ( withCode: 50000 , status: 500 , message: " Received incoming message without data or text " )
63- }
61+ do {
62+ // TODO: Revisit errors thrown as part of https://github.com/ably-labs/ably-chat-swift/issues/32
63+ guard let ablyCocoaData = message. data,
64+ let data = JSONValue ( ablyCocoaData: ablyCocoaData) . objectValue,
65+ let text = data [ " text " ] ? . stringValue
66+ else {
67+ throw ARTErrorInfo . create ( withCode: 50000 , status: 500 , message: " Received incoming message without data or text " )
68+ }
6469
65- guard let ablyCocoaExtras = message. extras else {
66- throw ARTErrorInfo . create ( withCode: 50000 , status: 500 , message: " Received incoming message without extras " )
67- }
70+ guard let ablyCocoaExtras = message. extras else {
71+ throw ARTErrorInfo . create ( withCode: 50000 , status: 500 , message: " Received incoming message without extras " )
72+ }
6873
69- let extras = JSONValue . objectFromAblyCocoaExtras ( ablyCocoaExtras)
74+ let extras = JSONValue . objectFromAblyCocoaExtras ( ablyCocoaExtras)
7075
71- guard let serial = message. serial else {
72- throw ARTErrorInfo . create ( withCode: 50000 , status: 500 , message: " Received incoming message without serial " )
73- }
76+ guard let serial = message. serial else {
77+ throw ARTErrorInfo . create ( withCode: 50000 , status: 500 , message: " Received incoming message without serial " )
78+ }
7479
75- guard let clientID = message. clientId else {
76- throw ARTErrorInfo . create ( withCode: 50000 , status: 500 , message: " Received incoming message without clientId " )
77- }
80+ guard let clientID = message. clientId else {
81+ throw ARTErrorInfo . create ( withCode: 50000 , status: 500 , message: " Received incoming message without clientId " )
82+ }
7883
79- let metadata = try data. optionalObjectValueForKey ( " metadata " )
84+ let metadata = try data. optionalObjectValueForKey ( " metadata " )
8085
81- let headers : Headers ? = if let headersJSONObject = try extras. optionalObjectValueForKey ( " headers " ) {
82- try headersJSONObject. mapValues { try HeadersValue ( jsonValue: $0) }
83- } else {
84- nil
85- }
86+ let headers : Headers ? = if let headersJSONObject = try extras. optionalObjectValueForKey ( " headers " ) {
87+ try headersJSONObject. mapValues { try HeadersValue ( jsonValue: $0) }
88+ } else {
89+ nil
90+ }
8691
87- guard let action = MessageAction . fromRealtimeAction ( message. action) else {
88- return
89- }
92+ guard let action = MessageAction . fromRealtimeAction ( message. action) else {
93+ return
94+ }
95+
96+ let message = Message (
97+ serial: serial,
98+ action: action,
99+ clientID: clientID,
100+ roomID: self . roomID,
101+ text: text,
102+ createdAt: message. timestamp,
103+ metadata: metadata ?? . init( ) ,
104+ headers: headers ?? . init( )
105+ )
90106
91- let message = Message (
92- serial: serial,
93- action: action,
94- clientID: clientID,
95- roomID: self . roomID,
96- text: text,
97- createdAt: message. timestamp,
98- metadata: metadata ?? . init( ) ,
99- headers: headers ?? . init( )
100- )
101-
102- messageSubscription. emit ( message)
107+ messageSubscription. emit ( message)
108+ } catch {
109+ self . logger. log ( message: " Malformed message received: \( error) " , level: . debug)
110+ #if DEBUG
111+ for subscription in self . malformedMessageSubscriptions {
112+ subscription. emit ( message)
113+ }
114+ #endif
115+ throw error
116+ }
103117 }
104118 }
105119
@@ -118,6 +132,18 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities {
118132 return messageSubscription
119133 }
120134
135+ #if DEBUG
136+ /// Subscription of malformed message events for testing purposes.
137+ private var malformedMessageSubscriptions : [ Subscription < ARTMessage > ] = [ ]
138+
139+ /// Returns a subscription which emits malformed message events for testing purposes.
140+ internal func testsOnly_subscribeToMalformedMessageEvents( ) -> Subscription < ARTMessage > {
141+ let subscription = Subscription < ARTMessage > ( bufferingPolicy: . unbounded)
142+ malformedMessageSubscriptions. append ( subscription)
143+ return subscription
144+ }
145+ #endif
146+
121147 // (CHA-M6a) A method must be exposed that accepts the standard Ably REST API query parameters. It shall call the “REST API”#rest-fetching-messages and return a PaginatedResult containing messages, which can then be paginated through.
122148 internal func get( options: QueryOptions ) async throws -> any PaginatedResult < Message > {
123149 try await chatAPI. getMessages ( roomId: roomID, params: options)
@@ -163,7 +189,7 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities {
163189 }
164190 }
165191
166- // (CHA-M4d ) If a channel UPDATE event is received and resumed=false, then it must be assumed that messages have been missed. The subscription point of any subscribers must be reset to the attachSerial.
192+ // (CHA-M5d ) If a channel UPDATE event is received and resumed=false, then it must be assumed that messages have been missed. The subscription point of any subscribers must be reset to the attachSerial.
167193 channel. on ( . update) { [ weak self] stateChange in
168194 Task {
169195 do {
0 commit comments