Reputation: 11
Interestingly, when I use a JavaScript client, I can receive the messages without issues (code provided below). This confirms that the backend is sending messages correctly. I’d appreciate any insights into what might be causing this issue.
iOS WebSocket Manager (Starscream):
import Foundation
import Starscream
import SwiftUI
class WebSocketManager: WebSocketDelegate {
static let shared = WebSocketManager()
@AppStorage("userId") private var userId: String = ""
var socket: WebSocket!
var isConnected: Bool = false
var subscribedTopics: Set<String> = []
var onMessageReceived: ((ChatMessage) -> Void)?
private init() {
connectWebSocket()
}
func connectWebSocket() {
var request = URLRequest(url: URL(string: "ws://192.168.0.80:9001/ws")!)
request.timeoutInterval = 5
if let jwtToken = TokenManager.shared.getAccessToken() {
request.setValue("Bearer \(jwtToken)", forHTTPHeaderField: "Authorization")
}
socket = WebSocket(request: request)
socket.delegate = self
socket.connect()
let friends = CoreDataFriendManager.shared.loadAllFriends()
friends.forEach { friend in
if let topicName = friend.topicName {
subscribeToMessages(with: topicName)
}
}
listenToWebSocketMessagesGlobally()
}
func sendMessage(_ message: SendMessageRequest) {
if !isConnected { return }
if let token = TokenManager.shared.getAccessToken() {
let stompMessage = """
SEND
destination:/app/send-message
content-type:application/json
Authorization: Bearer \(token)
{
"recipientId": "\(message.recipientId.uuidString)",
"encryptedMessage": "\(message.encryptedMessage)"
}
\0
"""
socket.write(string: stompMessage)
}
}
func subscribeToMessages(with topicName: String) {
if subscribedTopics.contains(topicName) { return }
let subscribeMessage = """
SUBSCRIBE
id:sub-\(topicName)
destination:/topic/messages/\(topicName)
\0
"""
socket.write(string: subscribeMessage)
subscribedTopics.insert(topicName)
}
// WebSocket-Event-Handling
func didReceive(event: WebSocketEvent, client: WebSocketClient) {
switch event {
case .connected(let headers):
print("WebSocket verbunden mit Headern: \(headers)")
isConnected = true
for topicName in subscribedTopics {
subscribeToMessages(with: topicName)
}
case .disconnected(let reason, let code):
print("WebSocket getrennt: \(reason) mit Code \(code). Versuche erneut zu verbinden.")
isConnected = false
reconnectWebSocket()
case .text(let string):
print("Empfangene WebSocket Nachricht: \(string)")
if string.contains("MESSAGE") {
let components = string.split(separator: "\n").map(String.init)
if let jsonData = components.last?.data(using: .utf8) {
do {
let serverMessage = try JSONDecoder().decode(ServerMessage.self, from: jsonData)
let chatMessage = convertServerMessageToChatMessage(serverMessage)
onMessageReceived?(chatMessage)
print("Nachricht erfolgreich dekodiert und verarbeitet: \(chatMessage)")
} catch {
print("Fehler beim Dekodieren der Nachricht: \(error)")
}
}
} else {
print("Keine gültige STOMP-Nachricht empfangen oder Nachricht nicht im erwarteten Format.")
}
case .binary(let data):
print("Binary-Daten empfangen: \(data)")
case .pong(let data):
print("Pong empfangen: \(data?.count ?? 0) bytes")
case .ping(let data):
print("Ping empfangen: \(data?.count ?? 0) bytes")
case .error(let error):
print("WebSocket-Fehler: \(error?.localizedDescription ?? "Unbekannter Fehler")")
isConnected = false
case .viabilityChanged(let viable):
print("Verbindungsfähigkeit geändert: \(viable)")
case .reconnectSuggested(let suggested):
print("Reconnect vorgeschlagen: \(suggested)")
if suggested {
reconnectWebSocket()
}
case .cancelled:
print("WebSocket abgebrochen.")
isConnected = false
default:
break
}
}
private func convertServerMessageToChatMessage(_ serverMessage: ServerMessage) -> ChatMessage {
return ChatMessage(id: serverMessage.messageId, text: serverMessage.encryptedMessage, isSentByCurrentUser: false, timestamp: serverMessage.timestamp, senderName: "TestUser")
}
}
JavaScript WebSocket Client (Working Correctly):
Here’s the JavaScript code that successfully receives messages:
const Stomp = require('stompjs');
const WebSocket = require('ws'); // ws-Paket verwenden
const socket = new WebSocket('ws://192.168.0.80:9001/ws');
const stompClient = Stomp.over(socket);
socket.on('open', () => console.log("WebSocket-Verbindung geöffnet"));
socket.on('close', () => console.log("WebSocket-Verbindung geschlossen"));
socket.on('error', (error) => console.error("WebSocket-Fehler:", error));
const headers = {
Authorization: 'Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIwNjQ3OGQxZC1hZTMxLTQ0MjktYWY4ZS02ZGMzMDVlMTYwOTciLCJpYXQiOjE3Mjk5NjUxNzMsImV4cCI6MTcyOTk2ODc3M30.xPdkLLv4LArvn00jAChKjsSE5RPt5zipgYGE0CNxe5BYsAvEomhIQohodNPTJat2pMVTgn2NkauZ8K3iiOt7DA'
};
stompClient.connect(headers, function (frame) {
console.log('STOMP-Verbindung hergestellt:', frame);
stompClient.subscribe('/topic/messages/1ed08549-ade6-4baa-b741-5634b75379f4', function (message) {
console.log("Nachricht empfangen:", message.body);
console.log("Header der Nachricht:", message.headers);
console.log("Inhalt der Nachricht:", message.body);
try {
const parsedMessage = JSON.parse(message.body);
console.log("JSON-Dekodierte Nachricht:", parsedMessage);
} catch (error) {
console.error("Fehler beim Parsen der Nachricht:", error);
}
});
const messageBody = JSON.stringify({
recipientId: "dd703e8e-8df1-4d27-9676-672d8c95d326",
encryptedMessage: "Hallo von User A!"
});
console.log("Sende Nachricht:", messageBody);
stompClient.send("/app/send-message", headers, messageBody);
},
function (error) {
console.error("Verbindungsfehler mit STOMP:", error);
});
stompClient.debug = function (str) {
console.log("STOMP-DEBUG:", str);
};
Received Message in JavaScript Client:
Here’s what the message looks like in the JS client, and it’s received successfully:
STOMP-DEBUG: <<< MESSAGE
destination:/topic/messages/1ed08549-ade6-4baa-b741-5634b75379f4
content-type:application/json
subscription:sub-0
message-id:1bbe8fc0-2a8e-3acf-5029-fd03ce44360f-25
content-length:832
{"messageId":"ee393914-db5a-4d29-b1ab-399c2a0dd726","encryptedMessage":"ddpdUSkCLaQupWQRR290LTeoX5NREazoLMcYM2JXmFQbiHpmv72+fMeI1FtDtg2DnRfhkpgzRi4048KklRYyUqpIvQUbH/U4Ow4MA06sVO6Xb4pKbQeyzme9Hhw8SHjFawQ9uzMvfR7Mt31aqL0T4ksJ0sefnS2EM84715l97WlYuOkilbnGkLkIM/OfUbggKRMvl1EDAnmxI3Ytkz7/donI2tzJol9TbQE0sLvZEQUUlZW8fF4PF+QyyNC1Zk21Yd5LbmwJg4uptmyZIi1NXGfzDKi/d1+WaatKxicPENgs1LV7XtM8pKfRpwvrkTeXG40y7x864Awh4g4qBbemOS9/2TO4xLRPFyx2DUx+wNW3KTgk9qu64eC+I10TsstJZa1WzOEDOR1ta+mnImHZcYJRdDOxvgAPWD7LPTurAScigepm8knh4e73YEPkEffPUbS1ORVotrAxO9ve31YsTC2D2OtAexPotDoJiEM7JdFHGrfRGF4Gje6zL8W1nMBN07rkIve7BN8lm2YTYkkSe0Vmq2Gso9flkimgT+g/TnHawyi/J5tsPexKgrFCkXgTANE/ZByFQRcfn/cTH9oJcV/WbYJl2ATi0TF7onqsacQvHVhCT1rPdScPU1XLdXY0einVA6VCGGZWUJBy0LydXc0sXlpn2XH/DZu6aSDdlRc=","timestamp":"2024-10-26T19:24:43.481808Z","status":"SENT","deleted":false}
Nachricht empfangen: {"messageId":"ee393914-db5a-4d29-b1ab-399c2a0dd726","encryptedMessage":"ddpdUSkCLaQupWQRR290LTeoX5NREazoLMcYM2JXmFQbiHpmv72+fMeI1FtDtg2DnRfhkpgzRi4048KklRYyUqpIvQUbH/U4Ow4MA06sVO6Xb4pKbQeyzme9Hhw8SHjFawQ9uzMvfR7Mt31aqL0T4ksJ0sefnS2EM84715l97WlYuOkilbnGkLkIM/OfUbggKRMvl1EDAnmxI3Ytkz7/donI2tzJol9TbQE0sLvZEQUUlZW8fF4PF+QyyNC1Zk21Yd5LbmwJg4uptmyZIi1NXGfzDKi/d1+WaatKxicPENgs1LV7XtM8pKfRpwvrkTeXG40y7x864Awh4g4qBbemOS9/2TO4xLRPFyx2DUx+wNW3KTgk9qu64eC+I10TsstJZa1WzOEDOR1ta+mnImHZcYJRdDOxvgAPWD7LPTurAScigepm8knh4e73YEPkEffPUbS1ORVotrAxO9ve31YsTC2D2OtAexPotDoJiEM7JdFHGrfRGF4Gje6zL8W1nMBN07rkIve7BN8lm2YTYkkSe0Vmq2Gso9flkimgT+g/TnHawyi/J5tsPexKgrFCkXgTANE/ZByFQRcfn/cTH9oJcV/WbYJl2ATi0TF7onqsacQvHVhCT1rPdScPU1XLdXY0einVA6VCGGZWUJBy0LydXc0sXlpn2XH/DZu6aSDdlRc=","timestamp":"2024-10-26T19:24:43.481808Z","status":"SENT","deleted":false}
Header der Nachricht: {
'content-length': '832',
'message-id': '1bbe8fc0-2a8e-3acf-5029-fd03ce44360f-25',
subscription: 'sub-0',
'content-type': 'application/json',
destination: '/topic/messages/1ed08549-ade6-4baa-b741-5634b75379f4'
}
Inhalt der Nachricht: {"messageId":"ee393914-db5a-4d29-b1ab-399c2a0dd726","encryptedMessage":"ddpdUSkCLaQupWQRR290LTeoX5NREazoLMcYM2JXmFQbiHpmv72+fMeI1FtDtg2DnRfhkpgzRi4048KklRYyUqpIvQUbH/U4Ow4MA06sVO6Xb4pKbQeyzme9Hhw8SHjFawQ9uzMvfR7Mt31aqL0T4ksJ0sefnS2EM84715l97WlYuOkilbnGkLkIM/OfUbggKRMvl1EDAnmxI3Ytkz7/donI2tzJol9TbQE0sLvZEQUUlZW8fF4PF+QyyNC1Zk21Yd5LbmwJg4uptmyZIi1NXGfzDKi/d1+WaatKxicPENgs1LV7XtM8pKfRpwvrkTeXG40y7x864Awh4g4qBbemOS9/2TO4xLRPFyx2DUx+wNW3KTgk9qu64eC+I10TsstJZa1WzOEDOR1ta+mnImHZcYJRdDOxvgAPWD7LPTurAScigepm8knh4e73YEPkEffPUbS1ORVotrAxO9ve31YsTC2D2OtAexPotDoJiEM7JdFHGrfRGF4Gje6zL8W1nMBN07rkIve7BN8lm2YTYkkSe0Vmq2Gso9flkimgT+g/TnHawyi/J5tsPexKgrFCkXgTANE/ZByFQRcfn/cTH9oJcV/WbYJl2ATi0TF7onqsacQvHVhCT1rPdScPU1XLdXY0einVA6VCGGZWUJBy0LydXc0sXlpn2XH/DZu6aSDdlRc=","timestamp":"2024-10-26T19:24:43.481808Z","status":"SENT","deleted":false}
JSON-Dekodierte Nachricht: {
messageId: 'ee393914-db5a-4d29-b1ab-399c2a0dd726',
encryptedMessage: 'ddpdUSkCLaQupWQRR290LTeoX5NREazoLMcYM2JXmFQbiHpmv72+fMeI1FtDtg2DnRfhkpgzRi4048KklRYyUqpIvQUbH/U4Ow4MA06sVO6Xb4pKbQeyzme9Hhw8SHjFawQ9uzMvfR7Mt31aqL0T4ksJ0sefnS2EM84715l97WlYuOkilbnGkLkIM/OfUbggKRMvl1EDAnmxI3Ytkz7/donI2tzJol9TbQE0sLvZEQUUlZW8fF4PF+QyyNC1Zk21Yd5LbmwJg4uptmyZIi1NXGfzDKi/d1+WaatKxicPENgs1LV7XtM8pKfRpwvrkTeXG40y7x864Awh4g4qBbemOS9/2TO4xLRPFyx2DUx+wNW3KTgk9qu64eC+I10TsstJZa1WzOEDOR1ta+mnImHZcYJRdDOxvgAPWD7LPTurAScigepm8knh4e73YEPkEffPUbS1ORVotrAxO9ve31YsTC2D2OtAexPotDoJiEM7JdFHGrfRGF4Gje6zL8W1nMBN07rkIve7BN8lm2YTYkkSe0Vmq2Gso9flkimgT+g/TnHawyi/J5tsPexKgrFCkXgTANE/ZByFQRcfn/cTH9oJcV/WbYJl2ATi0TF7onqsacQvHVhCT1rPdScPU1XLdXY0einVA6VCGGZWUJBy0LydXc0sXlpn2XH/DZu6aSDdlRc=',
timestamp: '2024-10-26T19:24:43.481808Z',
status: 'SENT',
deleted: false
}
Backend WebSocket Configuration (Java, Spring Boot):
Here’s the Java backend setup, which handles WebSocket connections and the /ws endpoint. The configuration includes the message broker setup and the controller that processes incoming messages.
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOrigins("*");
}
}
@Controller
public class WebSocketController {
@Autowired
private SimpMessagingTemplate messagingTemplate;
@MessageMapping("/send-message")
public void sendMessage(SendMessageRequest request, SimpMessageHeaderAccessor headerAccessor) {
String topic = "/topic/messages/" + friendService.getTopicName(request.getSenderId(), request.getRecipientId());
messagingTemplate.convertAndSend(topic, request);
}
}
Why is my Swift client not receiving messages, even though:
1. It successfully connects to the backend (and includes the token in the header).
2. Messages are successfully sent from the Swift client.
3. The JavaScript client receives messages without any issues.
Are there any known issues or configuration requirements in Starscream or STOMP for iOS that might lead to this behavior? Any help would be greatly appreciated!
Upvotes: 0
Views: 40