ACActionCable 2.2.0

ACActionCable 2.2.0

维护者:Julian Tigler.



  • 由:
  • Julian Tigler 和 Fabian Jäger

ACActionCable

ACActionCableRuby on Rails 6 的 Action Cable WebSocket 服务器的 Swift 5 客户端。它是 Action-Cable-Swift 的一个分支。它旨在具有良好的测试、无需依赖且易于使用。

安装

CocoaPods

如果您尚未使用 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)
}

贡献

请自己修复问题,然后 创建一个拉取请求。请为您的功能或修复添加新测试,并确保所有测试都通过!