add ping pong

This commit is contained in:
cheykrym 2025-10-21 04:43:14 +03:00
parent f886386374
commit 41adedee40
2 changed files with 45 additions and 21 deletions

View File

@ -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
}

View File

@ -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