Skip to content

Commit a40aab7

Browse files
authored
Merge pull request #28 from boostcampwm-2024/feat/#24-refactor-connection-client
[Feat/#24] Connection Client Interface를 구현 및 데모 앱을 통해 세션연결 확인
2 parents 11bf15d + a25432b commit a40aab7

File tree

20 files changed

+253
-48
lines changed

20 files changed

+253
-48
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,4 @@ iOSInjectionProject/
132132
**/xcshareddata/WorkspaceSettings.xcsettings
133133

134134
# End of https://www.toptal.com/developers/gitignore/api/macos,xcode,swift
135-
PhotoGether/PhotoGether/Resource/Secrets.xcconfig
135+
*.xcconfig

PhotoGether/DomainLayer/PhotoGetherDomain/PhotoGetherDomain.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
60348F2C2CE47B8C002D1CEE /* ConnectionClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60348F2B2CE47B8C002D1CEE /* ConnectionClient.swift */; };
1011
7B1745462CE3599600E01D1A /* SignalingClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1745452CE3599600E01D1A /* SignalingClient.swift */; };
1112
7B1745482CE3599E00E01D1A /* WebRTCClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1745472CE3599E00E01D1A /* WebRTCClient.swift */; };
1213
7B1745512CE35CD500E01D1A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = 7B1745502CE35CD500E01D1A /* WebRTC */; };
@@ -71,6 +72,7 @@
7172

7273
/* Begin PBXFileReference section */
7374
05E553E92CDB9C4A00B70645 /* DomainUtility.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainUtility.framework; sourceTree = BUILT_PRODUCTS_DIR; };
75+
60348F2B2CE47B8C002D1CEE /* ConnectionClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionClient.swift; sourceTree = "<group>"; };
7476
7B1745452CE3599600E01D1A /* SignalingClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalingClient.swift; sourceTree = "<group>"; };
7577
7B1745472CE3599E00E01D1A /* WebRTCClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRTCClient.swift; sourceTree = "<group>"; };
7678
7B5951872CDB647A00B89C85 /* PhotoGetherDomain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PhotoGetherDomain.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -154,6 +156,7 @@
154156
7BB640902CE336E00090E35C /* WebSocketClient.swift */,
155157
7B1745452CE3599600E01D1A /* SignalingClient.swift */,
156158
7B1745472CE3599E00E01D1A /* WebRTCClient.swift */,
159+
60348F2B2CE47B8C002D1CEE /* ConnectionClient.swift */,
157160
7BB6408C2CE335CC0090E35C /* Protocol */,
158161
7BB6407F2CE3352C0090E35C /* Model */,
159162
);
@@ -377,6 +380,7 @@
377380
7BB6407D2CE3330C0090E35C /* WebRTCClientDelegate.swift in Sources */,
378381
7BB6407E2CE3330C0090E35C /* SignalingClientDelegate.swift in Sources */,
379382
7B1745462CE3599600E01D1A /* SignalingClient.swift in Sources */,
383+
60348F2C2CE47B8C002D1CEE /* ConnectionClient.swift in Sources */,
380384
7BB6408B2CE335590090E35C /* WebSocketClientDelegate.swift in Sources */,
381385
7B1745482CE3599E00E01D1A /* WebRTCClient.swift in Sources */,
382386
7BB6408D2CE336620090E35C /* SessionDescription.swift in Sources */,

PhotoGether/DomainLayer/PhotoGetherDomain/PhotoGetherDomain/ConnectionClientImpl.swift

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,57 @@ import Foundation
22
import WebRTC
33
import PhotoGetherDomainInterface
44

5-
public final class ConnectionClientImpl {
6-
private let signalClient: SignalingClientImpl
5+
public final class ConnectionClientImpl: ConnectionClient {
6+
private let signalingClient: SignalingClientImpl
77
private let webRTCClient: WebRTCClientImpl
88

9-
// TODO: 영상 정보
9+
public var remoteVideoView: UIView = RTCMTLVideoView()
10+
public var localVideoView: UIView = RTCMTLVideoView()
1011
// TODO: 음성 정보
1112

12-
public init(signalClient: SignalingClientImpl, webRTCClient: WebRTCClientImpl) {
13-
self.signalClient = signalClient
13+
public init(signalingClient: SignalingClientImpl, webRTCClient: WebRTCClientImpl) {
14+
self.signalingClient = signalingClient
1415
self.webRTCClient = webRTCClient
1516

16-
self.signalClient.delegate = self
17+
self.signalingClient.delegate = self
1718
self.webRTCClient.delegate = self
1819

1920
// 서버 자동 연결
2021
self.connect()
22+
23+
// VideoTrack과 나와 상대방의 화면을 볼 수 있는 뷰를 바인딩합니다.
24+
self.bindRemoteVideo()
25+
self.bindLocalVideo()
2126
}
2227

23-
public func connect() {
24-
self.signalClient.connect()
28+
public func sendOffer() {
29+
self.webRTCClient.offer { sdp in
30+
self.signalingClient.send(sdp: sdp)
31+
}
2532
}
2633

2734
public func sendData(data: Data) {
2835
self.webRTCClient.sendData(data)
2936
}
3037

38+
private func connect() {
39+
self.signalingClient.connect()
40+
}
41+
42+
/// remoteVideoTrack과 상대방의 화면을 볼 수 있는 뷰를 바인딩합니다.
43+
private func bindRemoteVideo() {
44+
guard let remoteVideoView = remoteVideoView as? RTCMTLVideoView else { return }
45+
self.webRTCClient.renderRemoteVideo(to: remoteVideoView)
46+
}
47+
48+
private func bindLocalVideo() {
49+
guard let localVideoView = localVideoView as? RTCMTLVideoView else { return }
50+
self.webRTCClient.startCaptureLocalVideo(renderer: localVideoView)
51+
}
3152
}
3253

33-
extension ConnectionClientImpl: SignalingClientDelegate {
54+
// MARK: SignalingClientDelegate
55+
extension ConnectionClientImpl {
3456
public func signalClientDidConnect(
3557
_ signalingClient: SignalingClient
3658
) {
@@ -56,7 +78,7 @@ extension ConnectionClientImpl: SignalingClientDelegate {
5678
guard self.webRTCClient.peerConnection.localDescription == nil else { return }
5779

5880
self.webRTCClient.answer { sdp in
59-
self.signalClient.send(sdp: sdp)
81+
self.signalingClient.send(sdp: sdp)
6082
}
6183
}
6284
}
@@ -69,13 +91,14 @@ extension ConnectionClientImpl: SignalingClientDelegate {
6991
}
7092
}
7193

72-
extension ConnectionClientImpl: WebRTCClientDelegate {
94+
// MARK: WebRTCClientDelegate
95+
extension ConnectionClientImpl {
7396
/// SDP 가 생성되면 LocalCandidate 가 생성되기 시작 (가능한 경로만큼 생성됨)
7497
public func webRTCClient(
7598
_ client: WebRTCClient,
7699
didGenerateLocalCandidate candidate: RTCIceCandidate
77100
) {
78-
self.signalClient.send(candidate: candidate)
101+
self.signalingClient.send(candidate: candidate)
79102
}
80103

81104
public func webRTCClient(

PhotoGether/DomainLayer/PhotoGetherDomain/PhotoGetherDomain/SignalingClientImpl.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ final public class SignalingClientImpl: SignalingClient {
1010

1111
public init(webSocketClient: WebSocketClient) {
1212
self.webSocketClient = webSocketClient
13+
self.webSocketClient.delegate = self
1314
}
1415

1516
public func connect() {
16-
self.webSocketClient.delegate = self
1717
self.webSocketClient.connect()
1818
}
1919

@@ -38,7 +38,8 @@ final public class SignalingClientImpl: SignalingClient {
3838
}
3939
}
4040

41-
extension SignalingClientImpl: WebSocketClientDelegate {
41+
// MARK: WebSocketClientDelegate
42+
extension SignalingClientImpl {
4243
public func webSocketDidConnect(_ webSocket: WebSocketClient) {
4344
self.delegate?.signalClientDidConnect(self)
4445
}
@@ -66,6 +67,9 @@ extension SignalingClientImpl: WebSocketClientDelegate {
6667
self.delegate?.signalClient(self, didReceiveCandidate: iceCandidate.rtcIceCandidate)
6768
case .sdp(let sessionDescription):
6869
self.delegate?.signalClient(self, didReceiveRemoteSdp: sessionDescription.rtcSessionDescription)
70+
@unknown default:
71+
debugPrint("Unknown Message Type: \(message)")
72+
return
6973
}
7074
}
7175
}

PhotoGether/DomainLayer/PhotoGetherDomain/PhotoGetherDomain/WebRTCClientImpl.swift

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public final class WebRTCClientImpl: NSObject, WebRTCClient {
2626
private var localDataChannel: RTCDataChannel?
2727
private var remoteDataChannel: RTCDataChannel?
2828

29-
required init(iceServers: [String]) {
29+
public required init(iceServers: [String]) {
3030
let config = RTCConfiguration()
3131
config.iceServers = [RTCIceServer(urlStrings: iceServers)]
3232
config.sdpSemantics = .unifiedPlan
@@ -59,7 +59,7 @@ public final class WebRTCClientImpl: NSObject, WebRTCClient {
5959
}
6060

6161
// MARK: SDP
62-
extension WebRTCClientImpl {
62+
public extension WebRTCClientImpl {
6363
func offer(completion: @escaping (_ sdp: RTCSessionDescription) -> Void) {
6464
let constraints = RTCMediaConstraints(
6565
mandatoryConstraints: self.mediaConstraints,
@@ -108,7 +108,7 @@ extension WebRTCClientImpl {
108108
}
109109

110110
// MARK: Video/Audio/Data
111-
extension WebRTCClientImpl {
111+
public extension WebRTCClientImpl {
112112
func startCaptureLocalVideo(renderer: RTCVideoRenderer) {
113113
guard let capturer = self.videoCapturer as? RTCCameraVideoCapturer else { return }
114114
guard let frontCamera = RTCCameraVideoCapturer.captureDevices().first(where: {
@@ -137,6 +137,7 @@ extension WebRTCClientImpl {
137137
self.localVideoTrack?.add(renderer)
138138
}
139139

140+
/// remoteVideoTrack에서 수신된 모든 프레임을 렌더링할 렌더러를 등록합니다.
140141
func renderRemoteVideo(to renderer: RTCVideoRenderer) {
141142
self.remoteVideoTrack?.add(renderer)
142143
}
@@ -224,8 +225,29 @@ extension WebRTCClientImpl {
224225
}
225226
}
226227

228+
// MARK: Audio control
229+
public extension WebRTCClientImpl {
230+
func muteAudio() {
231+
self.setAudioEnabled(false)
232+
}
233+
234+
func unmuteAudio() {
235+
self.setAudioEnabled(true)
236+
}
237+
238+
private func setAudioEnabled(_ isEnabled: Bool) {
239+
setTrackEnabled(RTCAudioTrack.self, isEnabled: isEnabled)
240+
}
241+
242+
private func setTrackEnabled<T: RTCMediaStreamTrack>(_ type: T.Type, isEnabled: Bool) {
243+
peerConnection.transceivers
244+
.compactMap { return $0.sender.track as? T }
245+
.forEach { $0.isEnabled = isEnabled }
246+
}
247+
}
248+
227249
// MARK: PeerConnectionDelegate
228-
extension WebRTCClientImpl: RTCPeerConnectionDelegate {
250+
extension WebRTCClientImpl {
229251
public func peerConnection(
230252
_ peerConnection: RTCPeerConnection,
231253
didChange stateChanged: RTCSignalingState
@@ -291,29 +313,8 @@ extension WebRTCClientImpl: RTCPeerConnectionDelegate {
291313
}
292314
}
293315

294-
// MARK: Audio control
295-
extension WebRTCClientImpl {
296-
func muteAudio() {
297-
self.setAudioEnabled(false)
298-
}
299-
300-
func unmuteAudio() {
301-
self.setAudioEnabled(true)
302-
}
303-
304-
private func setAudioEnabled(_ isEnabled: Bool) {
305-
setTrackEnabled(RTCAudioTrack.self, isEnabled: isEnabled)
306-
}
307-
308-
private func setTrackEnabled<T: RTCMediaStreamTrack>(_ type: T.Type, isEnabled: Bool) {
309-
peerConnection.transceivers
310-
.compactMap { return $0.sender.track as? T }
311-
.forEach { $0.isEnabled = isEnabled }
312-
}
313-
}
314-
315316
// MARK: DataChannelDelegate
316-
extension WebRTCClientImpl: RTCDataChannelDelegate {
317+
extension WebRTCClientImpl {
317318
public func dataChannelDidChangeState(
318319
_ dataChannel: RTCDataChannel
319320
) {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Foundation
2+
import WebRTC
3+
4+
public protocol ConnectionClient: SignalingClientDelegate, WebRTCClientDelegate {
5+
var remoteVideoView: UIView { get }
6+
var localVideoView: UIView { get }
7+
8+
func sendOffer()
9+
func sendData(data: Data)
10+
}

PhotoGether/DomainLayer/PhotoGetherDomain/PhotoGetherDomainInterface/SignalingClient.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Foundation
22
import WebRTC
33

4-
public protocol SignalingClient {
4+
public protocol SignalingClient: WebSocketClientDelegate {
55
var delegate: SignalingClientDelegate? { get }
66

77
func connect()
Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,22 @@
11
import Foundation
2+
import WebRTC
23

3-
public protocol WebRTCClient {
4+
public protocol WebRTCClient: RTCPeerConnectionDelegate, RTCDataChannelDelegate {
5+
// MARK: SDP
6+
func offer(completion: @escaping (_ sdp: RTCSessionDescription) -> Void)
7+
func answer(completion: @escaping (_ sdp: RTCSessionDescription) -> Void)
8+
func set(remoteSdp: RTCSessionDescription, completion: @escaping (Error?) -> Void)
9+
func set(localSdp: RTCSessionDescription, completion: @escaping (Error?) -> Void)
10+
func set(remoteCandidate: RTCIceCandidate, completion: @escaping (Error?) -> Void)
11+
12+
// MARK: Video
13+
func startCaptureLocalVideo(renderer: RTCVideoRenderer)
14+
func renderRemoteVideo(to renderer: RTCVideoRenderer)
15+
16+
// MARK: Data
17+
func sendData(_ data: Data)
18+
19+
// MARK: Audio
20+
func muteAudio()
21+
func unmuteAudio()
422
}

PhotoGether/PresentationLayer/EditPhotoRoomFeature/EditPhotoRoomFeatureDemo/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"images" : [
33
{
4+
"filename" : "EditRoomIcon.png",
45
"idiom" : "universal",
56
"platform" : "ios",
67
"size" : "1024x1024"
Loading

0 commit comments

Comments
 (0)