How to handle websocket "requests" and "responses" in Swift?

1.6k Views Asked by At

Imagine we have chat service with websocket. We are sending message as JSON with property request_id. Server catches our message, saves it to the DB and returns message's id by sending another data back to the client with passed request_id. Client has to find out what exactly "request" is this "response" related to and save data to the local DB.

For example:

  1. Client sends message:
{
  "request_id": "1st",
  "content": "first message"
}
  1. Server receives message, saves it and returns new message's id
{
  "request_id": "1st",
  "message_id": 123242139857814
}

Starscream lib doesn't provide such mechanic, unlike Alammofire (that uses closures). I am looking for a solution that will be compatible with CoreData and RxSwift. How can I handle server's "response" without loosing "request" context?

1

There are 1 best solutions below

0
On

There are lots of ways. As an example, you can use the next code

class Mapping<T: Hashable, U: Hashable> {

    typealias ForwardKey = T
    typealias ForwardValue = U

    typealias ReverseKey = ForwardValue
    typealias ReverseValue = ForwardKey

    typealias KeyPair = (ForwardKey, ReverseKey?)
    typealias ValuePair = (ForwardValue, ReverseValue)

    private var forward: [AnyHashable: AnyHashable]
    private var reverse: [AnyHashable: AnyHashable]
    private let emptyHashable = AnyHashable("")

    init() {
        forward = [:]
        reverse = [:]
    }

    func insert(pair: KeyPair) {
        if let base = pair.1 {
            forward[pair.0] = base
            reverse[base] = pair.0
        } else {
            forward[pair.0] = emptyHashable
        }
    }

    func update(pair: KeyPair) {
        insert(pair: pair)
    }

    subscript(forward key: ForwardKey) -> ForwardValue? {
        return forward.first(where: { $0.key.hashValue == key.hashValue })?.value as? ForwardValue
    }

    subscript(reverse key: ReverseKey) -> ReverseValue? {
        if let result = reverse.first(where: { $0.key.hashValue == key.hashValue })?.value as? ReverseValue {
            return result
        }

        return forward.first(where: { $0.key.hashValue == key.hashValue })?.key as? ReverseValue
    }

}

struct Request: Hashable {
    let requestID: String
    let content: String

    func hash(into hasher: inout Hasher) {
        hasher.combine(requestID)
    }
}

struct Response: Hashable {
    let requestID: String
    let messageID: String

    func hash(into hasher: inout Hasher) {
        hasher.combine(requestID)
    }
}

class WebSocket {

    private var callback: ((Request, Response) -> ())?
    private let mapping = Mapping<Request, Response>()

    func request(_ request: Request, callback: @escaping (Request, Response) -> ()) {
        mapping.insert(pair: (request, nil))
        self.callback = callback

        // Send webSocket message
    }

    // WebSocketDelegate
    // Since you are using Starscream
    func websocketDidReceiveMessage(/*socket: WebSocketClient,*/ text: String) {
        // Parse text to Response
        let response = Response(requestID: "1", messageID: "2")

        if let req = mapping[reverse: response] {
            mapping.update(pair: (req, response))
            callback?(req, response)
        }
    }

}

let webSocket = WebSocket()

webSocket.request(Request(requestID: "1", content: "A")) { request, response in
    print(request) // Request(requestID: "1", content: "A")
    print(response) // Response(requestID: "1", messageID: "2")
}

webSocket.websocketDidReceiveMessage(text: "")