Skip to content

URLSessionWebSocketTask

동재 edited this page Dec 14, 2023 · 1 revision

이슈 내용

  • 스위프트에서 제공하는 URLSessionWebsocketTask에 대해서 학습

선택 사항

  • 기존에 많이 사용되었던 외부라이브러리인 [socket.io](http://socket.io) starstream 보다
  • 애플에서 제공하는 소켓통신에 대해서 공부해보고싶었습니다.

선택

  • URLSessionWebsocketTask

학습내용

final class WebSocket: NSObject {
		static let shared = WebSocket()
    var url: URL?
	  // 연결이 끊겼을 때, 소켓연결도 끊음.
    private var webSocketTask: URLSessionWebSocketTask? {
        didSet { oldValue?.cancel(with: .goingAway, reason: nil)}
    }
    private var timer: Timer?
    private override init() {}
	

		func openWebSocket() throws {
				// URLSessionConfiguration을 통해 헤더를 넣음.
        let configuration = URLSessionConfiguration.default
        guard let accessToken = JWTManager.shared.get()?.accessToken else { return }
        configuration.httpAdditionalHeaders = ["Authorization": "Bearer \(accessToken)"]
        
        guard let url = url else { throw WebSocketError.invalidURL }

        let urlSession = URLSession(configuration: configuration)
        let webSocketTask = urlSession.webSocketTask(with: url)
				// 소켓통신 시작.
        webSocketTask.resume()
        
        self.webSocketTask = webSocketTask

        self.startPing()  // 핑을 통해 소켓 끊김 방지
        
        self.receiveEvent() // receive 이벤트 발생 감지
    }

		func closeWebSocket() {
        self.webSocketTask = nil
        self.timer?.invalidate()
    }

		private func send(data: Data) {
        self.webSocketTask?.send(.data(data)) { error in
            if let error = error {
                print("오류 발생: \(error)")
            } else {
                print("메시지 전송 완료")
            }
        }
    }

		func receiveEvent() {
        guard let webSocketTask = self.webSocketTask else {
            return
        }
        
        webSocketTask.receive { result in
            switch result {
            case .success(let message):
                if case .string(let text) = message {
                    if let jsonData = text.data(using: .utf8) {
                        do {
                            let decoder = JSONDecoder()
                            let message = try decoder.decode(ReceiveMessageDTO.self, from: jsonData)
                            MessageManager.shared.messageSubject.send(message)
                        } catch {
                            dump(error)
                        }
                    }
                }
                self.receiveEvent()
            case .failure(let error):
                print("Error receiving message: \(error)")
            }
        }
    }
    
    private func startPing() {
        self.timer?.invalidate()
        self.timer = Timer.scheduledTimer(
            withTimeInterval: 10,
            repeats: true,
            block: { [weak self] _ in self?.ping() }
        )
    }
    
    private func ping() {
        self.webSocketTask?.sendPing(pongReceiveHandler: { [weak self] error in
            guard error != nil else { return }
            self?.startPing()
        })
    }
}

메세지를 json으로 변경해서 send() 호출

		func sendJoinRoom(roomID: Int) {
        let joinRoomEvent = """
        {
          "event": "join-room",
          "data": {
            "room_id": \(roomID)
          }
        }
        """
        guard let jsonData = joinRoomEvent.data(using: .utf8) else { return }
        send(data: jsonData)
    }
    
    func sendMessage(roomID: Int, sender: String, message: String, count: Int) {
        let sendMessageEvent = """
        {
          "event": "send-message",
          "data": {
            "room_id": \(roomID),
            "message": "\(message)",
            "sender": "\(sender)",
            "count": \(count)
          }
        }
        """
        guard let jsonData = sendMessageEvent.data(using: .utf8) else { return }
        send(data: jsonData)
    }
Clone this wiki locally