diff --git a/yobble/Services/SocketService.swift b/yobble/Services/SocketService.swift index 5260554..cfb9f05 100644 --- a/yobble/Services/SocketService.swift +++ b/yobble/Services/SocketService.swift @@ -39,6 +39,7 @@ final class SocketService { private var socket: SocketIOClient? private var heartbeatTimer: DispatchSourceTimer? private var heartbeatAckInFlight = false + private var lastHeartbeatSentAt: Date? private var consecutiveHeartbeatMisses = 0 #endif @@ -193,6 +194,10 @@ final class SocketService { self?.handleHeartbeatSuccess() } + socket.on("message") { [weak self] data, _ in + self?.handleMessageEvent(data) + } + self.manager = manager self.socket = socket } @@ -224,6 +229,7 @@ final class SocketService { heartbeatTimer = nil heartbeatAckInFlight = false consecutiveHeartbeatMisses = 0 + lastHeartbeatSentAt = nil } private func performHeartbeatCheck() { @@ -246,43 +252,61 @@ final class SocketService { } private func sendHeartbeat(on socket: SocketIOClient) { - guard !heartbeatAckInFlight else { return } - heartbeatAckInFlight = true - - socket.emitWithAck(heartbeatEventName, ["timestamp": Date().timeIntervalSince1970]) - .timingOut(after: heartbeatTimeout) { [weak self] data in - guard let self else { return } - self.heartbeatAckInFlight = false - - if self.isSuccessfulHeartbeatResponse(data) { - self.handleHeartbeatSuccess() - } else { - self.handleMissedHeartbeat(for: socket) - } + if heartbeatAckInFlight { + if let lastSentAt = lastHeartbeatSentAt, + Date().timeIntervalSince(lastSentAt) >= heartbeatTimeout { + heartbeatAckInFlight = false + handleMissedHeartbeat(for: socket) + } else { + return } + } + + heartbeatAckInFlight = true + lastHeartbeatSentAt = Date() + + socket.emit(heartbeatEventName, ["data": "ping"]) } - private func isSuccessfulHeartbeatResponse(_ data: [Any]) -> Bool { - guard !data.isEmpty else { return true } + private func handleMessageEvent(_ data: [Any]) { + guard let payload = data.first else { return } - if let stringValue = data.first as? String, stringValue.uppercased() == "NO ACK" { - return false + let messageText: String? + + if let dictionary = payload as? [String: Any] { + if let nestedData = dictionary["data"] as? [String: Any], + let nestedMessage = nestedData["message"] as? String { + messageText = nestedMessage + } else { + messageText = dictionary["message"] as? String + } + } else if let stringValue = payload as? String { + messageText = stringValue + } else { + messageText = nil } - if let ackStatus = data.first as? SocketAckStatus, ackStatus == .noAck { - return false + guard let message = messageText?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() else { + return } - return true + if message == "pong" { + handleHeartbeatSuccess() + } } private func handleHeartbeatSuccess() { consecutiveHeartbeatMisses = 0 + heartbeatAckInFlight = false + lastHeartbeatSentAt = nil updateConnectionState(.connected) } private func handleMissedHeartbeat(for socket: SocketIOClient) { consecutiveHeartbeatMisses += 1 + heartbeatAckInFlight = false + lastHeartbeatSentAt = nil + updateConnectionState(.connecting) guard consecutiveHeartbeatMisses > maxHeartbeatMissCount else { return } diff --git a/yobble/config.swift b/yobble/config.swift index 572c155..7e1338d 100644 --- a/yobble/config.swift +++ b/yobble/config.swift @@ -6,7 +6,7 @@ struct AppConfig { static let PROTOCOL = "https" static let API_SERVER = "\(PROTOCOL)://api.yobble.org" static let SOCKET_PATH = "/socket.io/" - static let SOCKET_HEARTBEAT_EVENT = "ping" + static let SOCKET_HEARTBEAT_EVENT = "client_message" static let USER_AGENT = "yobble ios" static let APP_BUILD = "appstore" // appstore / freestore