project/개발 업무

iOS RxDelegateProxy 만들어보기 #2 (StarScream + RxProxy)

lgvv 2022. 1. 12. 16:15

iOS RxDelegateProxy 만들어보기 #2

iOS 인턴을 수행하고 있는데, RxStarscream이 관리가 안되어서 Starscream 기반으로 RxStarscream을 사내에서 직접 만들어서 사용하기로 함.

문제 RxStarscream의 RxSwift 버전이 4점대에 멈춰있고, 현재는 6점대로 수정한 PR도 받아주지 않는 상황이라서 오픈소스를 사용할 때는 이런 점들도 잘 고려해야겠다 싶음!

 

 

연관포스팅

 

문제상황

소켓 통신을 위해 Starscream을 사용하고 있는데 RxSwift로 사용할 수 있게 해야함.

RxStarscream이 존재하나 관리가 되지 않아 RxSwift 4점대 (현재 6점대)

Starscrem + RxProxy를 활용해 프로젝트 내부에서 사용할 수 있도록 구현해보고자 함.

 

문제상황

소켓 통신을 위해 Starscream을 사용하고 있는데 RxSwift로 사용할 수 있게 해야함.

RxStarscream이 존재하나 관리가 되지 않아 RxSwift 4점대 (현재 6점대)

 


코드 구현체

프록시로 묶어서 처리할 수 있음

import UIKit
import RxSwift
import RxCocoa
import Starscream

public enum WebSocketEvent {
    case connected
    case disconnected(Error?)
    case message(String)
    case data(Data)
    case pong
}

public final class RxWebSocketDelegateProxy<Client: WebSocketClient>: DelegateProxy<Client, NSObjectProtocol>,
                                                                      DelegateProxyType,
                                                                      WebSocketDelegate,
                                                                      WebSocketPongDelegate
{

    private weak var forwardDelegate: WebSocketDelegate?
    private weak var forwardPongDelegate: WebSocketPongDelegate?

    fileprivate let subject = PublishSubject<WebSocketEvent>()

    required public init(websocket: Client) {
        super.init(parentObject: websocket, delegateProxy: RxWebSocketDelegateProxy.self)
    }

    public static func currentDelegate(for object: Client) -> NSObjectProtocol? {
        return object.delegate as? NSObjectProtocol
    }

    public static func setCurrentDelegate(_ delegate: NSObjectProtocol?, to object: Client) {
        object.delegate = delegate as? WebSocketDelegate
        object.pongDelegate = delegate as? WebSocketPongDelegate
    }

    public static func registerKnownImplementations() {
        self.register { RxWebSocketDelegateProxy(websocket: $0) }
    }

    public func websocketDidConnect(socket: WebSocketClient) {
        subject.onNext(WebSocketEvent.connected)
        forwardDelegate?.websocketDidConnect(socket: socket)
    }

    public func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
        subject.onNext(WebSocketEvent.disconnected(error))
        forwardDelegate?.websocketDidDisconnect(socket: socket, error: error)
    }

    public func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
        subject.onNext(WebSocketEvent.message(text))
        forwardDelegate?.websocketDidReceiveMessage(socket: socket, text: text)
    }

    public func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
        subject.onNext(WebSocketEvent.data(data))
        forwardDelegate?.websocketDidReceiveData(socket: socket, data: data)
    }

    public func websocketDidReceivePong(socket: WebSocketClient, data: Data?) {
        subject.onNext(WebSocketEvent.pong)
        forwardPongDelegate?.websocketDidReceivePong(socket: socket, data: data)
    }

    deinit {
        subject.onCompleted()
    }
}

extension Reactive where Base: WebSocketClient {

    public var response: Observable<WebSocketEvent> {
        return RxWebSocketDelegateProxy.proxy(for: base).subject
    }

    public var text: Observable<String> {
        return self.response
            .filter {
                switch $0 {
                case .message:
                    return true
                default:
                    return false
                }
            }
            .map {
                switch $0 {
                case .message(let message):
                    return message
                default:
                    return String()
                }
        }
    }

    public var connected: Observable<Bool> {
        return response
            .filter {
                switch $0 {
                case .connected, .disconnected:
                    return true
                default:
                    return false
                }
            }
            .map {
                switch $0 {
                case .connected:
                    return true
                default:
                    return false
                }
        }
    }

    public func write(data: Data) -> Observable<Void> {
        return Observable.create { sub in
            self.base.write(data: data) {
                sub.onNext(())
                sub.onCompleted()
            }

            return Disposables.create()
        }
    }

    public func write(ping: Data) -> Observable<Void> {
        return Observable.create { sub in
            self.base.write(ping: ping) {
                sub.onNext(())
                sub.onCompleted()
            }

            return Disposables.create()
        }
    }

    public func write(string: String) -> Observable<Void> {
        return Observable.create { sub in
            self.base.write(string: string) {
                sub.onNext(())
                sub.onCompleted()
            }

            return Disposables.create()
        }
    }
}