import Foundation public final class ImClient: NSObject, URLSessionWebSocketDelegate, @unchecked Sendable { public weak var delegate: ImEventDelegate? private var webSocketTask: URLSessionWebSocketTask? private var session: URLSession? private let wsURL: URL private let token: String private let appId: String private var shouldReconnect = false public init(wsURL: URL, token: String, appId: String) { self.wsURL = wsURL self.token = token self.appId = appId super.init() } public func connect() { shouldReconnect = true var request = URLRequest(url: wsURL) request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") session = URLSession(configuration: .default, delegate: self, delegateQueue: nil) webSocketTask = session?.webSocketTask(with: request) webSocketTask?.resume() receiveMessage() } private func receiveMessage() { webSocketTask?.receive { [weak self] result in switch result { case .success(let message): if case .string(let text) = message { self?.handleMessage(text) } self?.receiveMessage() case .failure(let error): self?.delegate?.imClientDidError(error.localizedDescription) } } } private func handleMessage(_ text: String) { guard let data = text.data(using: .utf8), let msg = try? JSONDecoder().decode(ImMessage.self, from: data) else { return } if msg.chatType == .group { delegate?.imClientDidReceiveGroupMessage(msg) } else { delegate?.imClientDidReceiveMessage(msg) } } public func sendMessage(toId: String, chatType: ChatType, msgType: MsgType, content: String) { let payload: [String: Any] = [ "type": "chat.send", "data": ["appId": appId, "toId": toId, "chatType": chatType.rawValue, "msgType": msgType.rawValue, "content": content] ] guard let data = try? JSONSerialization.data(withJSONObject: payload), let text = String(data: data, encoding: .utf8) else { return } webSocketTask?.send(.string(text)) { _ in } } public func disconnect() { shouldReconnect = false webSocketTask?.cancel(with: .normalClosure, reason: nil) } public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) { delegate?.imClientDidConnect() } public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) { let reasonStr = reason.flatMap { String(data: $0, encoding: .utf8) } delegate?.imClientDidDisconnect(reason: reasonStr) } }