diff --git a/DataSource/DataSource/Sources/Interface/NearbyNetworkInterface.swift b/DataSource/DataSource/Sources/Interface/NearbyNetworkInterface.swift index e3d0d01..5653900 100644 --- a/DataSource/DataSource/Sources/Interface/NearbyNetworkInterface.swift +++ b/DataSource/DataSource/Sources/Interface/NearbyNetworkInterface.swift @@ -8,7 +8,7 @@ import Foundation public protocol NearbyNetworkInterface { - var delegate: NearbyNetworkDelegate? { get set } + var connectionDelegate: NearbyNetworkConnectionDelegate? { get set } /// 주변 기기를 검색합니다. func startSearching() @@ -33,15 +33,15 @@ public protocol NearbyNetworkInterface { /// 연결된 기기들에게 데이터를 송신합니다. /// - Parameter data: 송신할 데이터 func send(data: Data) -} -public protocol NearbyNetworkDelegate: AnyObject { - /// 데이터를 수신했을 때 실행됩니다. + /// 연결된 기기들에게 파일을 송신합니다. /// - Parameters: - /// - data: 수신된 데이터 - /// - connection: 데이터를 송신한 기기 - func nearbyNetwork(_ sender: NearbyNetworkInterface, didReceive data: Data, from connection: NetworkConnection) + /// - fileURL: 파일의 URL + /// - info: 파일에 대한 정보 + func send(fileURL: URL, info: DataInformationDTO) async +} +public protocol NearbyNetworkConnectionDelegate: AnyObject { /// 주변 기기에게 연결 요청을 받았을 때 실행됩니다. /// - Parameters: /// - connectionHandler: 연결 요청 처리 Handler @@ -55,3 +55,19 @@ public protocol NearbyNetworkDelegate: AnyObject { /// 주변 기기와의 연결에 실패했을 때 실행됩니다. func nearbyNetworkCannotConnect(_ sender: NearbyNetworkInterface) } + +public protocol NearbyNetworkReceiptDelegate: AnyObject { + /// 데이터를 수신했을 때 실행됩니다. + /// - Parameters: + /// - data: 수신된 데이터 + func nearbyNetwork(_ sender: NearbyNetworkInterface, didReceive data: Data) + + /// 파일을 수신했을 때 실행됩니다. + /// - Parameters: + /// - URL: 수신한 파일의 URL + /// - info: 파일에 대한 정보 + func nearbyNetwork( + _ sender: NearbyNetworkInterface, + didReceiveURL URL: URL, + info: DataInformationDTO) +} diff --git a/DataSource/DataSource/Sources/Model/DataInformationDTO.swift b/DataSource/DataSource/Sources/Model/DataInformationDTO.swift index ed92559..5f56bfd 100644 --- a/DataSource/DataSource/Sources/Model/DataInformationDTO.swift +++ b/DataSource/DataSource/Sources/Model/DataInformationDTO.swift @@ -8,6 +8,6 @@ import Foundation public struct DataInformationDTO: Codable { - public let identifier: UUID + public let id: UUID public let type: AirplaINDataType } diff --git a/DataSource/DataSource/Sources/Repository/WhiteboardRepository.swift b/DataSource/DataSource/Sources/Repository/WhiteboardRepository.swift index 73aac01..dd73fcc 100644 --- a/DataSource/DataSource/Sources/Repository/WhiteboardRepository.swift +++ b/DataSource/DataSource/Sources/Repository/WhiteboardRepository.swift @@ -14,7 +14,7 @@ public final class WhiteboardRepository: WhiteboardRepositoryInterface { public init(nearbyNetworkInterface: NearbyNetworkInterface) { self.nearbyNetwork = nearbyNetworkInterface - self.nearbyNetwork.delegate = self + self.nearbyNetwork.connectionDelegate = self } public func startPublishing(with info: [Profile]) { @@ -33,14 +33,7 @@ public final class WhiteboardRepository: WhiteboardRepositoryInterface { } } -extension WhiteboardRepository: NearbyNetworkDelegate { - public func nearbyNetwork( - _ sender: any NearbyNetworkInterface, - didReceive data: Data, - from connection: NetworkConnection) { - // TODO: - - } - +extension WhiteboardRepository: NearbyNetworkConnectionDelegate { public func nearbyNetwork( _ sender: any NearbyNetworkInterface, didReceive connectionHandler: @escaping ( diff --git a/NearbyNetwork/NearbyNetwork.xcodeproj/project.pbxproj b/NearbyNetwork/NearbyNetwork.xcodeproj/project.pbxproj index 3076ae7..00c5227 100644 --- a/NearbyNetwork/NearbyNetwork.xcodeproj/project.pbxproj +++ b/NearbyNetwork/NearbyNetwork.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 00549AD62CEDDDB700DF8F6C /* MCSessionState+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00549AD52CEDDDB100DF8F6C /* MCSessionState+.swift */; }; + 00549AD82CEDDDCF00DF8F6C /* MCSession+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00549AD72CEDDDCF00DF8F6C /* MCSession+.swift */; }; 0080E86A2CE19EC40095B958 /* NearbyNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0080E8672CE19EC40095B958 /* NearbyNetworkService.swift */; }; 5B7C6EBF2CDB6C6E0024704A /* DataSource.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B7C6EBE2CDB6C6E0024704A /* DataSource.framework */; }; 5B7C6EC02CDB6C6E0024704A /* DataSource.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5B7C6EBE2CDB6C6E0024704A /* DataSource.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -27,6 +29,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 00549AD52CEDDDB100DF8F6C /* MCSessionState+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MCSessionState+.swift"; sourceTree = ""; }; + 00549AD72CEDDDCF00DF8F6C /* MCSession+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MCSession+.swift"; sourceTree = ""; }; 0080E8672CE19EC40095B958 /* NearbyNetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbyNetworkService.swift; sourceTree = ""; }; 5B7C6E652CDB6A560024704A /* NearbyNetwork.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NearbyNetwork.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 5B7C6EBE2CDB6C6E0024704A /* DataSource.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DataSource.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -44,9 +48,27 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 00549AD32CEDDDA300DF8F6C /* Common */ = { + isa = PBXGroup; + children = ( + 00549AD42CEDDDA800DF8F6C /* Extension */, + ); + path = Common; + sourceTree = ""; + }; + 00549AD42CEDDDA800DF8F6C /* Extension */ = { + isa = PBXGroup; + children = ( + 00549AD52CEDDDB100DF8F6C /* MCSessionState+.swift */, + 00549AD72CEDDDCF00DF8F6C /* MCSession+.swift */, + ); + path = Extension; + sourceTree = ""; + }; 0080E8682CE19EC40095B958 /* Sources */ = { isa = PBXGroup; children = ( + 00549AD32CEDDDA300DF8F6C /* Common */, 0080E8672CE19EC40095B958 /* NearbyNetworkService.swift */, ); path = Sources; @@ -190,6 +212,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 00549AD62CEDDDB700DF8F6C /* MCSessionState+.swift in Sources */, + 00549AD82CEDDDCF00DF8F6C /* MCSession+.swift in Sources */, 0080E86A2CE19EC40095B958 /* NearbyNetworkService.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/NearbyNetwork/NearbyNetwork/Sources/Common/Extension/MCSession+.swift b/NearbyNetwork/NearbyNetwork/Sources/Common/Extension/MCSession+.swift new file mode 100644 index 0000000..573ba24 --- /dev/null +++ b/NearbyNetwork/NearbyNetwork/Sources/Common/Extension/MCSession+.swift @@ -0,0 +1,32 @@ +// +// MCSession+.swift +// NearbyNetwork +// +// Created by 이동현 on 11/20/24. +// + +import MultipeerConnectivity + +// MARK: - Swift Concurrency로 wrapping +extension MCSession { + func sendResource( + at resourceURL: URL, + withName resourceName: String, + toPeer peer: MCPeerID + ) async throws { + typealias Continuation = CheckedContinuation + + try await withCheckedThrowingContinuation { (continuation: Continuation) in + self.sendResource( + at: resourceURL, + withName: resourceName, + toPeer: peer) { error in + if let error { + continuation.resume(throwing: error) + } else { + continuation.resume() + } + } + } + } +} diff --git a/NearbyNetwork/NearbyNetwork/Sources/Common/Extension/MCSessionState+.swift b/NearbyNetwork/NearbyNetwork/Sources/Common/Extension/MCSessionState+.swift new file mode 100644 index 0000000..097aa3b --- /dev/null +++ b/NearbyNetwork/NearbyNetwork/Sources/Common/Extension/MCSessionState+.swift @@ -0,0 +1,23 @@ +// +// MCSessionState+.swift +// NearbyNetwork +// +// Created by 이동현 on 11/20/24. +// + +import MultipeerConnectivity + +extension MCSessionState { + var description: String { + switch self { + case .notConnected: + return "연결 끊김" + case .connecting: + return "연결 중" + case .connected: + return "연결 됨" + @unknown default: + return "알 수 없음" + } + } +} diff --git a/NearbyNetwork/NearbyNetwork/Sources/NearbyNetworkService.swift b/NearbyNetwork/NearbyNetwork/Sources/NearbyNetworkService.swift index ea2d458..74612db 100644 --- a/NearbyNetwork/NearbyNetwork/Sources/NearbyNetworkService.swift +++ b/NearbyNetwork/NearbyNetwork/Sources/NearbyNetworkService.swift @@ -11,7 +11,8 @@ import MultipeerConnectivity import OSLog public final class NearbyNetworkService: NSObject { - public weak var delegate: NearbyNetworkDelegate? + public weak var connectionDelegate: NearbyNetworkConnectionDelegate? + public weak var receiptDelegate: NearbyNetworkReceiptDelegate? private let peerID: MCPeerID private let session: MCSession private var serviceAdvertiser: MCNearbyServiceAdvertiser @@ -20,7 +21,7 @@ public final class NearbyNetworkService: NSObject { private var foundPeers: [MCPeerID: NetworkConnection] = [:] { didSet { let foundPeers: [NetworkConnection] = foundPeers.values.map { $0 } - delegate?.nearbyNetwork(self, didFind: foundPeers) + connectionDelegate?.nearbyNetwork(self, didFind: foundPeers) } } private let logger = Logger() @@ -77,9 +78,8 @@ extension NearbyNetworkService: NearbyNetworkInterface { .first { $0.value == connection }? .key // TODO: Error 수정 - guard let peerID else { - throw NSError() - } + guard let peerID else { throw NSError() } + serviceBrowser.invitePeer( peerID, to: session, @@ -97,6 +97,30 @@ extension NearbyNetworkService: NearbyNetworkInterface { logger.log(level: .error, "데이터 전송 실패") } } + + public func send(fileURL: URL, info: DataSource.DataInformationDTO) async { + let infoJsonData = try? JSONEncoder().encode(info) + + guard + let infoJsonData, + let infoJsonString = String(data: infoJsonData, encoding: .utf8) + else { return } + + await withTaskGroup(of: Void.self) { taskGroup in + session.connectedPeers.forEach { peer in + taskGroup.addTask { + do { + try await self.session.sendResource( + at: fileURL, + withName: infoJsonString, + toPeer: peer) + } catch { + self.logger.log(level: .error, "\(peer)에게 file 데이터 전송 실패") + } + } + } + } + } } // MARK: - MCSessionDelegate @@ -131,7 +155,7 @@ extension NearbyNetworkService: MCSessionDelegate { logger.log(level: .error, "\(peerID.displayName)와 연결되어 있지 않음") return } - delegate?.nearbyNetwork(self, didReceive: data, from: connection) + receiptDelegate?.nearbyNetwork(self, didReceive: data) } public func session( @@ -159,7 +183,16 @@ extension NearbyNetworkService: MCSessionDelegate { at localURL: URL?, withError error: (any Error)? ) { - logger.log(level: .error, "\(peerID.displayName)로부터 데이터 수신을 완료함") + guard + let localURL, + let jsonData = resourceName.data(using: .utf8), + let dto = try? JSONDecoder().decode(DataInformationDTO.self, from: jsonData) + else{ return } + + receiptDelegate?.nearbyNetwork( + self, + didReceiveURL: localURL, + info: dto) } } @@ -171,7 +204,7 @@ extension NearbyNetworkService: MCNearbyServiceAdvertiserDelegate { withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void ) { - delegate?.nearbyNetwork(self, didReceive: { [weak self] isAccepted in + connectionDelegate?.nearbyNetwork(self, didReceive: { [weak self] isAccepted in invitationHandler(isAccepted, self?.session) }) } @@ -181,7 +214,7 @@ extension NearbyNetworkService: MCNearbyServiceAdvertiserDelegate { didNotStartAdvertisingPeer error: any Error ) { logger.log(level: .error, "Advertising 실패 \(error.localizedDescription)") - delegate?.nearbyNetworkCannotConnect(self) + connectionDelegate?.nearbyNetworkCannotConnect(self) } } @@ -203,19 +236,3 @@ extension NearbyNetworkService: MCNearbyServiceBrowserDelegate { foundPeers[peerID] = nil } } - -// MARK: - MCSessionState -extension MCSessionState { - var description: String { - switch self { - case .notConnected: - return "연결 끊김" - case .connecting: - return "연결 중" - case .connected: - return "연결 됨" - @unknown default: - return "알 수 없음" - } - } -} diff --git a/Persistence/Persistence/Souces/FilePersistence.swift b/Persistence/Persistence/Souces/FilePersistence.swift index bf597af..626561f 100644 --- a/Persistence/Persistence/Souces/FilePersistence.swift +++ b/Persistence/Persistence/Souces/FilePersistence.swift @@ -29,7 +29,7 @@ public struct FilePersistence: FilePersistenceInterface { return write( to: directoryURL, with: data, - fileName: dataInfo.identifier.uuidString) + fileName: dataInfo.id.uuidString) } public func load(path: URL) -> Data? {