ACActionCable
ACActionCable 是 Ruby on Rails 6 的 Action Cable WebSocket 服务器的 Swift 5 客户端。它是 Action-Cable-Swift 的一个分支。它旨在具有良好的测试、无需依赖且易于使用。
安装
CocoaPods
将以下行添加到您的 Podfile
:
pod 'ACActionCable', '~> 1.0.0'
用法
实现 ACWebSocketProtocol
您可以使用 ACActionCable 与您喜欢的任何 WebSocket 库。只需创建一个实现 ACWebSocketProtocol
的类。如果您使用 Starscream,只需将 ACStarscreamWebSocket
复制到您的项目中。
创建一个用于保存 ACClient 的单例类
// MyClient.swift
import ACActionCable
class MyClient {
static let shared = MyClient()
private let client: ACClient
private init() {
let socket = ACStarscreamWebSocket(stringURL: "https://myrailsapp.com/cable") // Your concrete implementation of ACWebSocketProtocol (see above)
client = ACClient(socket: socket, connectionMonitorTimeout: 6) // Leave connectionMonitorTimeout nil to disable connection monitoring
}
}
连接和断开连接
您可以根据服务器的需求设置自定义头信息
// MyClient.swift
func connect() {
client.headers = [
"Auth": "Token",
"Origin": "https://myrailsapp.com",
]
client.connect()
}
func disconnect() {
client.disconnect()
}
当您的应用处于活动状态或放弃活动状态时,您可能希望连接或断开连接。
// SceneDelegate.swift
func sceneDidBecomeActive(_ scene: UIScene) {
MyClient.shared.connect()
}
func sceneWillResignActive(_ scene: UIScene) {
MyClient.shared.disconnect()
}
您可能还希望在用户登录或登出时连接或断开连接。
订阅和取消订阅
// MyClient.swift
func subscribe(to channelIdentifier: ACChannelIdentifier, with messageHandler: @escaping ACMessageHandler) -> ACSubscription {
client.subscribe(to: channelIdentifier, with: messageHandler)!
}
func unsubscribe(from subscription: ACSubscription) {
client.unsubscribe(from: subscription)
}
// ChatChannel.swift
import ACActionCable
class ChatChannel {
private var subscription: ACSubscription?
func subscribe(to roomId: Int) {
guard subscription == nil else { return }
let channelIdentifier = ACChannelIdentifier(channelName: "ChatChannel", identifier: ["room_id": roomId])!
subscription = MyClient.shared.subscribe(to: channelIdentifier, with: handleMessage(_:))
}
func unsubscribe() {
guard let subscription = subscription else { return }
MyClient.shared.unsubscribe(from: subscription)
self.subscription = nil
}
private func handleMessage(_ message: ACMessage) {
switch message.type {
case .confirmSubscription:
print("ChatChannel subscribed")
case .rejectSubscription:
print("Server rejected ChatChannel subscription")
default:
// TODO: Use MyObject (see below)
break
}
}
}
Decodable
消息
注册您的 ACActionCable 可自动解码您的模型。例如,如果您的服务器广播以下消息
{
"identifier":"{\"channel\":\"ChatChannel\",\"room_id\":42}",
"message": {
"my_object":{
"sender_id": 311,
"text": "Hello, room 42!"
}
}
}
那么 ACActionCable 可以自动将其解码成以下对象
// MyObject.swift
struct MyObject: Codable { // Must implement Decodable or Codable
let senderId: Int
let text: String
}
您需要做的只是注册该对象。
// MyClient.swift
private init() {
// ...
ACMessageBodyObject.register(MyObject.self)
}
// ChatChannel.swift
private func handleMessage(_ message: ACMessage) {
switch message.type {
case .confirmSubscription:
print("Subscribed")
case .rejectSubscription:
print("Server rejected ChatChannel subscription")
default:
switch message.body {
case .dictionary(let dictionary):
switch dictionary.object {
case let myObject as MyObject:
print("Received message from sender \(myObject.senderId): \(myObject.text)")
// Received message from sender 311: "Hello, room 42!"
default:
print("Warning: ChatChannel ignored unrecognized message")
}
default:
break
}
}
}
发送消息
ACActionCable 自动编码您的 Encodable
对象
// MyObject.swift
struct MyObject: Codable { // Must implement Encodable or Codable
let senderId: Int
let text: String
}
// ChatChannel.swift
func speak(_ text: String) {
subscription?.send(actionName: "speak", object: MyObject(senderId: 99, text: text))
}
调用 channel.speak("my message")
将会导致以下内容发送
{
"command":"message",
"data":"{\"action\":\"speak\",\"my_object\":{\"sender_id\":99,\"text\":\"my message\"}}",
"identifier":"{\"channel\":\"ChatChannel\",\"room_id\":42}"
}
(可选) 修改编码/解码日期格式
默认情况下,使用 .secondsSince1970
编码或解码 Date
对象。如果您需要更改到另一种格式
ACCommand.encoder.dateEncodingStrategy = .iso8601 // or any other JSONEncoder.DateEncodingStrategy
ACMessage.decoder.dateDecodingStrategy = .iso8601 // or any other JSONDecoder.DateDecodingStrategy
(可选) 添加 ACClientTap
如果您需要监听 ACClient
的内部状态,请使用 ACClientTap
.
// MyClient.swift
private init() {
// ...
let tap = ACClientTap(
onConnected: { (headers) in
print("Client connected with headers: \(headers.debugDescription)")
}, onDisconnected: { (reason) in
print("Client disconnected with reason: \(reason.debugDescription)")
}, onText: { (text) in
print("Client received text: \(text)")
}, onMessage: { (message) in
print("Client received message: \(message)")
})
client.add(tap)
}
贡献
请自己修复问题,然后 创建一个拉取请求。请为您的功能或修复添加新测试,并确保所有测试都通过!