Skip to content

Commit

Permalink
[GWL-85] 서버 구조에 맞게 웹 소켓 연동 (#200)
Browse files Browse the repository at this point in the history
* test: 서버 환경과 비슷하게 소켓 테스트 수정

- 테스트를 실환경으로 수정하면서 오류가 발생하는 것을 고칠 예정입니다.

* feat: test 코드에 맞게 MockWebSocketSession 기능 변경

* add: Socket 모델 debug 로그 추가

* fix: 헬스 데이터로 snapshot을 적용하는 코드가 누락된 점 수정

* style: 거리 데이터와 타이머 초 UI 수정

- 거리 데이터는 소수점 세 자리까지 보여줍니다.
- 타이머는 00분 00초로 시작합니다.

* delete: 불필요한 주석 제거
  • Loading branch information
WhiteHyun authored Dec 3, 2023
1 parent c380762 commit 9799be4
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 14 deletions.
31 changes: 26 additions & 5 deletions iOS/Projects/Core/Network/Sources/MockWebSocketSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,30 @@ import Foundation

// MARK: - MockWebSocketTask

public final class MockWebSocketTask: WebSocketTaskProtocol {
public final class MockWebSocketTask<DataModel: Codable>: WebSocketTaskProtocol {
private var sentMessage: URLSessionWebSocketTask.Message?
private var receiveContinuation: CheckedContinuation<URLSessionWebSocketTask.Message, Never>?
private let jsonEncoder: JSONEncoder = .init()
private let jsonDecoder: JSONDecoder = .init()

public init() {}

public func send(_ message: URLSessionWebSocketTask.Message) async throws {
sentMessage = message
receiveContinuation?.resume(returning: message)
switch message {
case let .data(data):
let socketFrame = try jsonDecoder.decode(WebSocketFrame<DataModel>.self, from: data)
let jsonData = try jsonEncoder.encode(socketFrame.data)
guard let jsonString = String(data: jsonData, encoding: .utf8) else {
throw MockWebSocketError.stringConversionFailed
}

let stringMessage = URLSessionWebSocketTask.Message.string(jsonString)
sentMessage = stringMessage
receiveContinuation?.resume(returning: stringMessage)
default:
sentMessage = message
receiveContinuation?.resume(returning: message)
}
}

public func receive() async throws -> URLSessionWebSocketTask.Message {
Expand All @@ -37,12 +52,18 @@ public final class MockWebSocketTask: WebSocketTaskProtocol {

// MARK: - MockWebSocketSession

public struct MockWebSocketSession: URLSessionWebSocketProtocol {
var webSocketTask: MockWebSocketTask = .init()
public struct MockWebSocketSession<DataModel: Codable>: URLSessionWebSocketProtocol {
var webSocketTask: MockWebSocketTask<DataModel> = .init()

public init() {}

public func webSocketTask(with _: URLRequest) -> WebSocketTaskProtocol {
return webSocketTask
}
}

// MARK: - MockWebSocketError

private enum MockWebSocketError: Error {
case stringConversionFailed
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import XCTest

final class SessionWebSocketProtocolTests: XCTestCase {
private var mockSession: MockWebSocketSession?
private var mockSession: MockWebSocketSession<TestModel>?
private var socketProvider: TNSocketProvider<MockEndPoint>?

struct TestModel: Codable, Equatable {
Expand All @@ -23,7 +23,7 @@ final class SessionWebSocketProtocolTests: XCTestCase {

override func setUp() {
super.setUp()
let mockSession = MockWebSocketSession()
let mockSession = MockWebSocketSession<TestModel>()
self.mockSession = mockSession
socketProvider = TNSocketProvider(session: mockSession, endPoint: MockEndPoint())
}
Expand All @@ -43,14 +43,21 @@ final class SessionWebSocketProtocolTests: XCTestCase {
try await self.socketProvider?.send(model: testModel)
}

// assert

// Receive 메서드를 비동기로 호출하여 결과 검증
let receivedMessage = try await socketProvider?.receive()
guard case let .data(data) = receivedMessage else {
XCTFail("Received message is not of type .data")
guard case let .string(string) = receivedMessage else {
XCTFail("Received message is not of type .string")
return
}

guard let jsonData = string.data(using: .utf8) else {
XCTFail("data cannot parse to data")
return
}

let receivedModel = try JSONDecoder().decode(WebSocketFrame<TestModel>.self, from: data)
XCTAssertEqual(receivedModel.data, testModel, "Received model does not match sent model")
let receivedModel = try JSONDecoder().decode(TestModel.self, from: jsonData)
XCTAssertEqual(receivedModel, testModel, "Received model does not match sent model")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,19 @@ struct WorkoutHealthRealTimeModel: Codable {
/// 현재 심박수
let heartRate: Double?
}

// MARK: - WorkoutRealTimeModel + CustomStringConvertible

extension WorkoutRealTimeModel: CustomStringConvertible {
var description: String {
return "id: \(id)\nroomID: \(roomID)\nnickname: \(nickname)\nhealth: \(health)"
}
}

// MARK: - WorkoutHealthRealTimeModel + CustomStringConvertible

extension WorkoutHealthRealTimeModel: CustomStringConvertible {
var description: String {
return "distance: \(distance ?? 0)\ncalories: \(calories ?? 0)\nheartRate: \(heartRate ?? 0)"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ final class WorkoutSessionCoordinator: WorkoutSessionCoordinating {
let healthRepository = HealthRepository()

// TODO: 같이하기, 혼자하기 모드에 따라 session 주입을 다르게 해야합니다.
let socketRepository = WorkoutSocketRepository(session: MockWebSocketSession(), dependency: dependency)
let socketRepository = WorkoutSocketRepository(session: MockWebSocketSession<WorkoutRealTimeModel>(), dependency: dependency)

let sessionUseCase = WorkoutSessionUseCase(
healthRepository: healthRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ final class SessionParticipantCell: UICollectionViewCell {
}

func configure(with model: WorkoutHealthRealTimeModel?) {
distanceLabel.text = "\(model?.distance ?? 0)"
// 소수점 세 자리까지만 표현
distanceLabel.text = "\(((model?.distance ?? 0) * 1000).rounded(.toNearestOrAwayFromZero) / 1000)m"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,15 @@ public final class WorkoutSessionViewController: UIViewController {
case let .fetchMyHealthForm(myHealthForm):
self?.healthData = myHealthForm
case let .fetchParticipantsIncludedMySelf(model):
Log.make().debug("\(model)")
self?.realTimeModelByID[model.id] = model.health
var snapshot = self?.participantsDataSource?.snapshot()
snapshot?.reconfigureItems([model.id])
if let snapshot {
self?.participantsDataSource?.apply(snapshot)
} else {
Log.make().error("snapshot이 생성되지 못했습니다. 헬스 데이터로 UI를 그리지 못합니다.")
}
}
}
.store(in: &subscriptions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ final class WorkoutSessionContainerViewController: UIViewController {
private let recordTimerLabel: UILabel = {
let label = UILabel()
label.font = .preferredFont(forTextStyle: .largeTitle)
label.text = "0분 0초"
label.text = "00분 00초"
return label
}()

Expand Down

0 comments on commit 9799be4

Please sign in to comment.