feat: 初始化 iOS 远程控制端项目

This commit is contained in:
2026-07-29 10:34:02 +08:00
parent 11173eb04c
commit 1e37aa64cc
20 changed files with 2610 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
import Foundation
protocol SignalingClientDelegate: AnyObject {
func signalingDidConnect()
func signalingDidDisconnect()
func signaling(didFail error: String)
func signaling(didReceive message: SignalMessage)
}
/// WebSocket URLSessionWebSocketTask
/// REGISTERdeviceType = CONTROLLER
/// 线
final class SignalingClient: NSObject {
weak var delegate: SignalingClientDelegate?
private let serverUrl: String
private let deviceId: String
private var session: URLSession?
private var task: URLSessionWebSocketTask?
private var manuallyClosed = false
init(serverUrl: String, deviceId: String) {
self.serverUrl = serverUrl
self.deviceId = deviceId
super.init()
}
func connect() {
guard let url = URL(string: serverUrl) else {
notifyError("无效的服务器地址: \(serverUrl)")
return
}
manuallyClosed = false
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 15
session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
task = session?.webSocketTask(with: url)
task?.resume()
receiveLoop()
}
func disconnect() {
manuallyClosed = true
task?.cancel(with: .normalClosure, reason: "Disconnecting".data(using: .utf8))
task = nil
session?.invalidateAndCancel()
session = nil
}
func send(_ message: SignalMessage) {
guard let task else { return }
guard let data = try? JSONEncoder().encode(message),
let json = String(data: data, encoding: .utf8) else { return }
task.send(.string(json)) { error in
if let error {
NSLog("[Signaling] send error: \(error.localizedDescription)")
}
}
}
var isConnected: Bool { task != nil }
// MARK: - Private
private func registerDevice() {
var msg = SignalMessage()
msg.type = "REGISTER"
msg.fromDeviceId = deviceId
msg.deviceType = "CONTROLLER"
send(msg)
}
private func receiveLoop() {
task?.receive { [weak self] result in
guard let self else { return }
switch result {
case .success(let wsMessage):
if case .string(let text) = wsMessage,
let data = text.data(using: .utf8),
let message = try? JSONDecoder().decode(SignalMessage.self, from: data) {
DispatchQueue.main.async {
self.delegate?.signaling(didReceive: message)
}
}
self.receiveLoop()
case .failure(let error):
if !self.manuallyClosed {
self.notifyError(error.localizedDescription)
}
}
}
}
private func notifyError(_ text: String) {
DispatchQueue.main.async {
self.delegate?.signaling(didFail: text)
}
}
}
// MARK: - URLSessionWebSocketDelegate
extension SignalingClient: URLSessionWebSocketDelegate {
func urlSession(_ session: URLSession,
webSocketTask: URLSessionWebSocketTask,
didOpenWithProtocol protocol: String?) {
registerDevice()
DispatchQueue.main.async {
self.delegate?.signalingDidConnect()
}
}
func urlSession(_ session: URLSession,
webSocketTask: URLSessionWebSocketTask,
didCloseWith closeCode: URLSessionWebSocketTask.CloseCode,
reason: Data?) {
guard !manuallyClosed else { return }
DispatchQueue.main.async {
self.delegate?.signalingDidDisconnect()
}
}
}