URLSessionWebSocketTask - boostcampwm2023/iOS05-Village GitHub Wiki

์ด์Šˆ ๋‚ด์šฉ

  • ์Šค์œ„ํ”„ํŠธ์—์„œ ์ œ๊ณตํ•˜๋Š” 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)
    }