import MultipeerConnectivity import Foundation struct NoteInvitation: Identifiable { struct NoteContent: Codable { let title: String let noteSnapshot: String } var id: MCPeerID { invitatorID } let noteName: String let invitatorID: MCPeerID let note: NoteContent private var invitationHandler: ((Bool) -> Void)? init( noteName: String, invitatorID: MCPeerID, note: NoteContent, invitationHandler: ((Bool) -> Void)? = nil ) { self.noteName = noteName self.invitatorID = invitatorID self.note = note self.invitationHandler = invitationHandler } mutating func accept() { invitationHandler?(true) invitationHandler = nil } mutating func decline() { invitationHandler?(false) invitationHandler = nil } } @Observable final class NoteEditingSessionClient: NSObject { private let session: MCSession private let advertiser: MCNearbyServiceAdvertiser private let ownPeer: MCPeerID var invitations: [NoteInvitation] = [] { didSet { print(invitations) } } init(peer: MCPeerID) { ownPeer = peer session = MCSession( peer: peer, securityIdentity: nil, encryptionPreference: .required ) advertiser = MCNearbyServiceAdvertiser( peer: peer, discoveryInfo: [:], serviceType: "peered" ) super.init() advertiser.delegate = self } func startBrowsingForNotes() { advertiser.startAdvertisingPeer() } func stopBrowsingForNotes() { advertiser.stopAdvertisingPeer() } func send(note: String, to peer: MCPeerID) { try! session.send( note.data(using: .utf8)!, toPeers: [peer], with: .reliable ) } } extension NoteEditingSessionClient: MCNearbyServiceAdvertiserDelegate { func advertiser( _ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void ) { guard let context, let noteContent = try? JSONDecoder().decode(NoteInvitation.NoteContent.self, from: context) else { fatalError() } invitations.append( .init( noteName: noteContent.title, invitatorID: peerID, note: noteContent, invitationHandler: { [weak self, invitationHandler] accepted in guard let self else { return } invitationHandler(accepted, self.session) DispatchQueue.main.async { guard let idx = self.invitations.firstIndex(where: { $0.id == peerID }) else { return } self.invitations.remove(at: idx) } } ) ) } }