apple/iOS, UIKit, Documentation

iOS Starscream 총정리

lgvv 2022. 1. 12. 15:09

iOS Starscream 총 정리

 

Starscream을 통해서 소켓통신을 하는 방법에 대해서 알아보고자 함.

Starscream이란 iOS에서 소켓 통신을 도와주는 오픈소스

웹소켓 기반으로 사이드프로젝트에서 사용해보았고, 인턴에서도 또 사용하는데 자주 사용하는 것 같아서 공부

 

연관포스팅

 

오픈소스

https://github.com/daltoniam/Starscream

 

 

샘플 코드

소켓을 Interface를 통해 구현해서 내부 구현체를 가리는 형태로 개발

  • 의존성 주입형태로 사용하도록 개선
  • 인터페이스를 받아서 사용하는 영역에 따라 약간 다른 구현체를 가지도록 `Default`라는 키워드 사용 (객체지향적으로 개발!)
    • Default (기본 웹소켓 사용 객체)
    • Search (검색 웹소켓 사용 객체)
    • Chat (채팅 웹소켓)
    • 위 형태로 Interface를 따르도록하여 내부 구현의 차이점을 가져갈 수 있음.
import UIKit
import RxSwift
import RxCocoa
import Starscream

enum WebSocketError {
    case invalidURL
}

public protocol WebSocketInterface {
    /// 웹소켓 이벤트를 받는 리스너
    var listener: ((WebSocketEventType) -> Void)? { get set }
    
    /// 소켓 객체
    var socket: WebSocket? { get }
    
    /// 소켓 연결
    func connect() throws
    /// 소켓 연결 끊기
    func disconnect()
    /// 소켓에 데이터 요청
    func request(from string: String)
}

public enum WebSocketEventType {
    case websocketDidConnect(socket: WebSocketClient)
    case websocketDidDisconnect(socket: WebSocketClient, error: Error?)
    case websocketDidReceiveMessage(socket: WebSocketClient, text: String)
    case websocketDidReceiveData(socket: WebSocketClient, data: Data)
}

public final class DefaultWebSocket: WebSocketInterface {
    
    var listener: ((WebSocketEventType) -> Void)?
    var socket: WebSocket?
    
    public init() { }
    
    /// 웹소켓 연결 여부, 성공시 컴플리션 반환
    func connect() throws {
        let urlString = "{SERVER URL STRING}"
        
        guard let url = URL(string: urlString) else {
            throw WebSocketError.invalidURL
        }
        var request = URLRequest(url: url)
        // HTTPUpgare error가 나타날 경우
        request.addValue("{YOUR SERVER}", forHTTPHeaderField: "Origin")
        request.timeoutInterval = 10
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        
        socket = WebSocket(request: request)
        socket.delegate = self
        socket.connect()
    }
    
    /// 웹소켓 연결 종려
    func disconnect() {
        if let socket {
            webSocket.disconnect()
        } else {
            print("scoket is not connected")
        }
    }
    
    /// 웹소켓에 요청
    func request(from string: String) {
        if let socket {
            socket.write(string: string)
        } else {
            print("socket is not connected")
        }
    }
}

// MARK: - DefaultWebSocket 구현체
extension DefaultWebSocket: WebSocketDelegate {
    func websocketDidConnect(socket: WebSocketClient) {
        print("websocket did connect")
        
        listener?(.websocketDidConnect(scoket: socket))
    }
    
    func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
        if let error {
            print(error)
        } else {
            print("websocket did disconnect")
        }
        
        listener?(.websocketDidDisconnect(scoket: socket, error: error))
    }
    
    /// 성공시 메시지가 여기로 내려옴
    func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
        print("websocket did receive message: \(text)")
        
        listener?(.websocketDidReceiveMessage(scoket: socket, text: text))
    }
    
    func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
        print("websocket did receive data: \(data)")
        
        listener?(.websocketDidReceiveData(scoket: socket, data: data))
    }
}

 

 

Usage

final class ViewController: UIViewController {
    
    private var socket: WebSocketInterface
    
    init(socket: WebSocketInterface = DefaultWebSocket()) {
        self.socket = socket
        
        do {
            try socket.connect()
            bind()
        } catch let error {
            print(error)
        }
    }
    
    private func bind() {
        socket.listener = { [weak self] event in
            switch event {
            case let .websocketDidConnect(socket):
                print("websocket did connect")
            case let .websocketDidDisconnect(socket, error):
                print("websocket did disconnect")
            case let .websocketDidReceiveMessage(socket, text):
                print("websocket did receive message: \(text)")
            case let .websocketDidReceiveData(socket, data):
                print("websocket did receive data: \(data)")
            }
            
        }
    }
    
}