Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
cbd9712
feat/#95 :: Server에 Encodable Extension 추가
Kiyoung-Kim-57 Nov 25, 2024
a22ebf2
feat/#95 :: 서버에서 방 생성 요청 받았을 시 동작 구현
Kiyoung-Kim-57 Nov 25, 2024
ffa6079
feat/#95 :: ByteBuffer extension 추가
Kiyoung-Kim-57 Nov 25, 2024
88ce472
feat/#95 :: Server의 CreateRoomResponseDTO 수정
Kiyoung-Kim-57 Nov 25, 2024
412b30e
feat/#95 :: CreateRoomResponseDTO 생성
Kiyoung-Kim-57 Nov 25, 2024
0f2b391
feat/#95 :: Data에 toDTO extension 추가
Kiyoung-Kim-57 Nov 25, 2024
ab4d9de
feat/#95 :: RoomService가 서버에서 받은 Response 처리
Kiyoung-Kim-57 Nov 25, 2024
aa38db9
feat/#95 :: LocalVideoView 단일화
youn9k Nov 25, 2024
6d8ab76
feat/#95 :: captureVideo 메소드 수정
youn9k Nov 25, 2024
596dc67
chore/#95 :: CreateRoomUseCase 구현중
youn9k Nov 25, 2024
dc42cb6
chore/#95 :: RoomService Data->Domain으로 변경
youn9k Nov 25, 2024
42dd659
feat/#95 :: ConnectionRepository에 프로퍼티 추가
youn9k Nov 25, 2024
24c5a96
feat/#95 :: RoomService 인터페이스 수정
Kiyoung-Kim-57 Nov 25, 2024
cb67d24
fix/#95 :: Repository 수정에 따라 UseCase 수정
Kiyoung-Kim-57 Nov 25, 2024
4b7180d
fix/#95 :: swiftLint 오류 수정
Kiyoung-Kim-57 Nov 25, 2024
e6350b4
feat/#95 :: CreateRoomUseCase 수정
Kiyoung-Kim-57 Nov 25, 2024
af5db3c
chore/#95 :: 자잘한 오류 수정
Kiyoung-Kim-57 Nov 25, 2024
3112a95
style/#95 :: shareButton 이름 linkButton으로 변경
Kiyoung-Kim-57 Nov 25, 2024
7fb289a
feat/#95 :: CreateRoomUseCase의 execute에 discardableResult 적용
Kiyoung-Kim-57 Nov 25, 2024
c12e7b1
chore/#95 :: Scenedelegate에서 의존성 주입 관련 수정
Kiyoung-Kim-57 Nov 25, 2024
b4db373
feat/#95 :: WebSocketClient가 delegate를 여러 개 가지도록 수정
Kiyoung-Kim-57 Nov 25, 2024
b7b4ef2
feat/#95 :: WebSocketClientDelegate Deprecated
youn9k Nov 25, 2024
7ec8a4c
feat/#95 :: CreateRoomEntity 생성
Kiyoung-Kim-57 Nov 25, 2024
8db283b
feat/#95 :: RoomService의 createRoom 함수 반환 타입 변경
Kiyoung-Kim-57 Nov 25, 2024
a445ba6
chore/#95 :: 불필요한 공백 제거
Kiyoung-Kim-57 Nov 25, 2024
6c798a4
feat/#95 :: CreateRoomUseCase 구현
youn9k Nov 25, 2024
44ea1ce
feat/#95 :: UIActivityViewController를 통한 딥링크 uri 전달 기능 연결
youn9k Nov 25, 2024
7e93cf6
style/#95 :: CreateRoomEntity -> RoomOwnerEntity로 이름 변경
Kiyoung-Kim-57 Nov 25, 2024
c7da50e
feat/#95 :: UserInfoEntity 생성
Kiyoung-Kim-57 Nov 25, 2024
77671de
feat/#95 :: ConnectionClient에 UserInfo 추가
Kiyoung-Kim-57 Nov 25, 2024
dbeb2f7
feat/#95 :: ParsingRoomID -> ParsingIDs 로 변경
Kiyoung-Kim-57 Nov 25, 2024
b9fd74f
feat/#95 :: 딥링크로 들어온 게스트가 딥링크 URL 파싱
Kiyoung-Kim-57 Nov 25, 2024
40b2417
feat/#95 :: JoinRoomUseCase 인터페이스 구현
youn9k Nov 25, 2024
5fe7a32
feat/#95 :: 게스트 방 참가 로딩 화면 구현
youn9k Nov 25, 2024
4cf7a75
feat/#95 :: JoinRoomUseCaseImpl / Mock 구현
youn9k Nov 25, 2024
0854669
feat/#95 :: App SceneDelegate 호스트/게스트 화면 진입 분기 처리
youn9k Nov 25, 2024
b5b5578
chore/#95 :: 텍스트 정렬 수정
youn9k Nov 25, 2024
232d506
chore/#95 :: CreateRoomMessage -> CreateRoomResponseMessage 이름 변경
Kiyoung-Kim-57 Nov 25, 2024
080cecf
feat/#95 :: JoinRoom 로직에 필요한 구조체 구현
Kiyoung-Kim-57 Nov 25, 2024
5e2cdb8
feat/#95 :: WebSocketRequestable의 확장 메서드를 옮겼습니다.
Kiyoung-Kim-57 Nov 25, 2024
bd7957e
feat/#95 :: RoomService의 joinRoom 로직 구현
Kiyoung-Kim-57 Nov 25, 2024
3c2a746
feat/#95 :: fullScreen으로 띄우도록 변경
youn9k Nov 25, 2024
3b5c14e
feat/#95 :: Decoder를 매번 생성하지 않고 파라미터로 받을 수 있도록 변경
Kiyoung-Kim-57 Nov 26, 2024
d73da6a
feat/#95 :: 카메라 좌우 반전을 설정했습니다.
Kiyoung-Kim-57 Nov 26, 2024
97287de
style/#95 :: 각종 함수명, 변수명 Rename
Kiyoung-Kim-57 Nov 26, 2024
0ffc172
chore/#95 :: 커멘트 반영사항 적용
Kiyoung-Kim-57 Nov 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ public final class ConnectionClientImpl: ConnectionClient {
public var receivedDataPublisher = PassthroughSubject<Data, Never>()

public var remoteVideoView: UIView = CapturableVideoView()
public var localVideoView: UIView = CapturableVideoView()
public var userInfo: UserInfoEntity?

public var peerID: String = ""
public var roomID: String = ""

public init(signalingService: SignalingService, webRTCService: WebRTCService) {
public init(
signalingService: SignalingService,
webRTCService: WebRTCService,
userInfo: UserInfoEntity? = nil
) {
self.signalingService = signalingService
self.webRTCService = webRTCService
self.userInfo = userInfo

self.signalingService.delegate = self
self.webRTCService.delegate = self
Expand All @@ -27,30 +31,26 @@ public final class ConnectionClientImpl: ConnectionClient {

// VideoTrack과 나와 상대방의 화면을 볼 수 있는 뷰를 바인딩합니다.
self.bindRemoteVideo()
self.bindLocalVideo()
}

public func sendOffer() {
guard let userInfo else { return }

self.webRTCService.offer { sdp in
self.signalingService.send(sdp: sdp, peerID: self.peerID, roomID: self.roomID)
self.signalingService.send(
sdp: sdp,
peerID: userInfo.id,
roomID: userInfo.roomID ?? ""
)
}
}

public func sendData(data: Data) {
self.webRTCService.sendData(data)
}

public func captureVideos() -> [UIImage] {
let localCaptureImage = getCapturedVideos(isLocal: true)
let remoteCaptureImage = getCapturedVideos(isLocal: false)

return [localCaptureImage, remoteCaptureImage]
}

private func getCapturedVideos(isLocal: Bool) -> UIImage {
let targetVideo = isLocal ? self.localVideoView : self.remoteVideoView

guard let videoView = targetVideo as? CapturableVideoView else {
public func captureVideo() -> UIImage {
guard let videoView = self.remoteVideoView as? CapturableVideoView else {
return UIImage()
}

Expand All @@ -71,7 +71,7 @@ public final class ConnectionClientImpl: ConnectionClient {
self.webRTCService.renderRemoteVideo(to: remoteVideoView)
}

private func bindLocalVideo() {
public func bindLocalVideo(_ localVideoView: UIView) {
guard let localVideoView = localVideoView as? RTCMTLVideoView else { return }
self.webRTCService.startCaptureLocalVideo(renderer: localVideoView)
}
Expand Down Expand Up @@ -104,7 +104,13 @@ extension ConnectionClientImpl: SignalingServiceDelegate {
guard self.webRTCService.peerConnection.localDescription == nil else { return }

self.webRTCService.answer { sdp in
self.signalingService.send(sdp: sdp, peerID: self.peerID, roomID: self.roomID)
guard let userInfo = self.userInfo else { return }

self.signalingService.send(
sdp: sdp,
peerID: userInfo.id,
roomID: userInfo.roomID ?? ""
)
}
}
}
Expand All @@ -124,7 +130,13 @@ extension ConnectionClientImpl: WebRTCServiceDelegate {
_ service: WebRTCService,
didGenerateLocalCandidate candidate: RTCIceCandidate
) {
self.signalingService.send(candidate: candidate, peerID: self.peerID, roomID: self.roomID)
guard let userInfo else { return }

self.signalingService.send(
candidate: candidate,
peerID: userInfo.id,
roomID: userInfo.roomID ?? ""
)
}

public func webRTCService(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
import Foundation
import UIKit
import PhotoGetherDomainInterface

public final class ConnectionRepositoryImpl: ConnectionRepository {
public var clients: [ConnectionClient]

public init(clients: [ConnectionClient]) {
private let _localVideoView = CapturableVideoView()

public var localVideoView: UIView { _localVideoView }
public var capturedLocalVideo: UIImage? { _localVideoView.capturedImage }

public let roomService: RoomService

public init(clients: [ConnectionClient], roomService: RoomService) {
self.clients = clients
self.roomService = roomService
bindLocalVideo()
}

private func bindLocalVideo() {
self.clients.forEach { $0.bindLocalVideo(_localVideoView) }
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

public extension Data {
func toDTO(type: Decodable.Type) -> Decodable? {
return try? JSONDecoder().decode(type, from: self)
func toDTO<T: Decodable>(type: T.Type, decoder: JSONDecoder) -> T? {
return try? decoder.decode(type, from: self)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation

extension Encodable {
func toData(encoder: JSONEncoder) -> Data? {
return try? encoder.encode(self)
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Foundation
import PhotoGetherDomainInterface

public protocol RoomServiceDelegate: AnyObject {
func roomService(_ roomService: RoomService, didReceiveResponseCreateRoom response: String)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation
import PhotoGetherDomainInterface

public struct CreateRoomResponseMessage: Decodable {
let roomID: String
let hostID: String

public func toEntity() -> RoomOwnerEntity {
RoomOwnerEntity(roomID: self.roomID, hostID: self.hostID)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

public struct JoinRoomRequestMessage: Encodable {
public let roomID: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Foundation
import PhotoGetherDomainInterface

public struct JoinRoomResponseMessage: Decodable {
public let userID: String
public let clientsID: [String]

public init(userID: String, clientsID: [String]) {
self.userID = userID
self.clientsID = clientsID
}

public func toEntity() -> JoinRoomEntity {
JoinRoomEntity(userID: self.userID, clientsID: self.clientsID)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,32 +1,108 @@
import Foundation
import Combine
import PhotoGetherNetwork
import PhotoGetherDomainInterface

public final class RoomServiceImpl: RoomService {
public var createRoomResponsePublisher: AnyPublisher<RoomOwnerEntity, Error> {
_createRoomResponsePublisher.eraseToAnyPublisher()
}
public var joinRoomResponsePublisher: AnyPublisher<JoinRoomEntity, Error> {
_joinRoomResponsePublisher.eraseToAnyPublisher()
}
private let _createRoomResponsePublisher = PassthroughSubject<RoomOwnerEntity, Error>()
private let _joinRoomResponsePublisher = PassthroughSubject<JoinRoomEntity, Error>()
private var cancellables: Set<AnyCancellable> = []

private let decoder = JSONDecoder()
private let encoder = JSONEncoder()
private var webSocketClient: WebSocketClient

public init(webSocketClient: WebSocketClient) {
self.webSocketClient = webSocketClient
bindWebSocketClient()
}

public func createRoom() -> AnyPublisher<RoomOwnerEntity, Error> {
let createRoomRequest = RoomRequestDTO(messageType: .createRoom)

guard let data = createRoomRequest.toData(encoder: encoder) else {
debugPrint("방 생성 요청 데이터 인코딩 실패: \(createRoomRequest)")
return Fail(error: RoomServiceError.failedToEncoding).eraseToAnyPublisher()
}

webSocketClient.send(data: data)
return createRoomResponsePublisher
}

public func send(request: any WebSocketRequestable) {
guard let data = request.toData(encoder: encoder) else {
debugPrint("방 생성 요청 데이터 인코딩 실패: \(request)")
return
public func joinRoom(to roomID: String) -> AnyPublisher<JoinRoomEntity, Error> {
let joinRoomMessage = JoinRoomRequestMessage(roomID: roomID).toData(encoder: encoder)
let joinRoomRequest = RoomRequestDTO(messageType: .joinRoom, message: joinRoomMessage)

guard let data = joinRoomRequest.toData(encoder: encoder) else {
debugPrint("방 참가 요청 데이터 인코딩 실패: \(joinRoomRequest)")
return Fail(error: RoomServiceError.failedToEncoding).eraseToAnyPublisher()
}

webSocketClient.send(data: data)
return joinRoomResponsePublisher
}

private func bindWebSocketClient() {
self.webSocketClient.webSocketdidReceiveDataPublisher
.sink { [weak self] data in
guard let self else { return }

guard let response = data.toDTO(type: RoomResponseDTO.self, decoder: decoder) else { return }

switch response.messageType {
case .createRoom:
guard let message = response.message else { return }
guard let message = message.toDTO(
type: CreateRoomResponseMessage.self,
decoder: decoder
) else {
debugPrint("Decode Failed to CreateRoomMessage: \(message)")
return
}
let roomOwnerEntity = message.toEntity()
_createRoomResponsePublisher.send(roomOwnerEntity)

debugPrint("방 생성 성공: \(message.roomID) \n 유저 아이디: \(message.hostID)")
case .joinRoom:
guard let message = decodeMessage(
response.message,
type: JoinRoomResponseMessage.self
) else {
debugPrint("Decode Failed to JoinRoomEntity: \(String(describing: response.message))")
return
}
let joinRoomEntity = message.toEntity()
_joinRoomResponsePublisher.send(joinRoomEntity)

debugPrint("방 참가 성공\n 유저 아이디: \(message.userID) \n 방 유저들 아이디: \(message.clientsID)")
}
}.store(in: &cancellables)
}

private func decodeMessage<T: Decodable>(_ message: Data?, type: T.Type) -> T? {
guard let message = message else { return nil }
guard let dto = message.toDTO(type: type, decoder: decoder) else {
debugPrint("Decode Failed to: \(message)")
return nil
}

return dto
}
}

// MARK: WebSocketDelegate
extension RoomServiceImpl: WebSocketClientDelegate {
public func webSocketDidConnect(_ webSocket: WebSocketClient) { }
public enum RoomServiceError: LocalizedError {
case failedToEncoding

public func webSocketDidDisconnect(_ webSocket: WebSocketClient) { }

public func webSocket(_ webSocket: WebSocketClient, didReceiveData data: Data) {
// TODO: 생성된 방번호 고유 아이디가 담긴 정보 디코딩
public var errorDescription: String? {
switch self {
case .failedToEncoding:
return "Failed to encode room service request"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ final public class SignalingServiceImpl: SignalingService {

public init(webSocketClient: WebSocketClient) {
self.webSocketClient = webSocketClient
self.webSocketClient.delegate = self
self.webSocketClient.delegates.append(self)
}

public func connect() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,24 @@ public extension WebRTCServiceImpl {
fps: Int(fps.maxFrameRate)
)

flipVideoRenderer(renderer)

self.localVideoTrack?.add(renderer)
}

/// remoteVideoTrack에서 수신된 모든 프레임을 렌더링할 렌더러를 등록합니다.
func renderRemoteVideo(to renderer: RTCVideoRenderer) {
flipVideoRenderer(renderer)

self.remoteVideoTrack?.add(renderer)
}

/// 들어오는 화면에 좌우 반전을 적용합니다
private func flipVideoRenderer(_ renderer: RTCVideoRenderer) {
guard let renderedView = renderer as? UIView else { return }
renderedView.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
}

private func configureAudioSession() {
self.rtcAudioSession.lockForConfiguration()
do {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import Foundation
import Combine

public protocol WebSocketClient {
var delegate: WebSocketClientDelegate? { get set }
var delegates: [WebSocketClientDelegate] { get set }
var webSocketDidConnectPublisher: AnyPublisher<Void, Never> { get }
var webSocketDidDisconnectPublisher: AnyPublisher<Void, Never> { get }
var webSocketdidReceiveDataPublisher: AnyPublisher<Data, Never> { get }

func connect()
func send(data: Data)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation

@available(*, deprecated, message: "Publisher로 교체될 예정입니다.")
public protocol WebSocketClientDelegate: AnyObject {
func webSocketDidConnect(_ webSocket: WebSocketClient)
func webSocketDidDisconnect(_ webSocket: WebSocketClient)
Expand Down
Loading
Loading