// // MPCManager.swift // multipeer_connections // // Created by NamIT on 9/3/20. // import Foundation import MultipeerConnectivity class MPCManager: NSObject { var advertiser: MCNearbyServiceAdvertiser! var browser: MCNearbyServiceBrowser! struct Notifications { static let deviceDidChangeState = Notification.Name("deviceDidChangeState") static let adOnConnectionInitiated = Notification.Name("adOnConnectionInitiated") static let adOnConnectionResult = Notification.Name("adOnConnectionResult") static let adOnDisconnected = Notification.Name("adOnDisconnected") static let disOnConnectionInitiated = Notification.Name("disOnConnectionInitiated") static let disOnConnectionResult = Notification.Name("disOnConnectionResult") static let disOnDisconnected = Notification.Name("disOnDisconnected") static let disOnEndpointFound = Notification.Name("disOnEndpointFound") static let disOnEndpointLost = Notification.Name("disOnEndpointLost") } static let instance = MPCManager() var localPeerID: MCPeerID! var enterbackgroundNotification: NSObjectProtocol! private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid var devices: [Device] = [] var deviceDidChange: (() -> Void)? deinit{ if let taskEnterBackground = enterbackgroundNotification { NotificationCenter.default.removeObserver(taskEnterBackground) } } func setup(serviceType: String, deviceName: String, isAdvert: Bool) { if let data = UserDefaults.standard.data(forKey: deviceName), let id = NSKeyedUnarchiver.unarchiveObject(with: data) as? MCPeerID { self.localPeerID = id } else { let peerID = MCPeerID(displayName: deviceName) let data = NSKeyedArchiver.archivedData(withRootObject: peerID) UserDefaults.standard.set(data, forKey: deviceName) self.localPeerID = peerID } if(isAdvert){ self.advertiser = MCNearbyServiceAdvertiser(peer: localPeerID, discoveryInfo: nil, serviceType: serviceType) self.advertiser.delegate = self } else{ self.browser = MCNearbyServiceBrowser(peer: localPeerID, serviceType: serviceType) self.browser.delegate = self } enterbackgroundNotification = NotificationCenter.default.addObserver( forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil, using: { [weak self](notification) in self?.enteredBackground() } ) } func startAdvertisingPeer() { self.advertiser.startAdvertisingPeer() } func startBrowsingForPeers() { self.browser.startBrowsingForPeers() } func stopAdvertisingPeer() { self.advertiser.stopAdvertisingPeer() } func stopBrowsingForPeers() { self.browser.stopBrowsingForPeers() } func invitePeer(endpointId: String) { do { let device = MPCManager.instance.findDevice(for: endpointId) if(device?.state == MCSessionState.notConnected){ device?.invite(with: self.browser) } } } func disconnectPeer(deviceID: String){ let device = MPCManager.instance.findDevice(for: deviceID) device?.disconnect() } func disconnectAllPeers(){ for device in self.devices { device.disconnect() } } func addNewDevice(for id: MCPeerID) -> Device { // devices = devices.filter{$0.peerID.displayName != id.displayName} let device = Device(peerID: id) self.devices.append(device) return device } func findDevice(for endpointId: String) -> Device? { for device in self.devices { if device.endpointId == endpointId { return device } } return nil } func findDevice(for id: MCPeerID) -> Device? { if let device = devices.first(where: {$0.peerID == id}) { return device } return nil } @objc func enteredBackground() { for device in self.devices { device.disconnect() } DispatchQueue.global().async {[weak self] in guard let `self` = self else {return} // Request the task assertion and save the ID. self.backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: "Finish Network Tasks") { // End the task if time expires. UIApplication.shared.endBackgroundTask(self.backgroundTaskID) self.backgroundTaskID = .invalid } // Send the data synchronously. self.devices = [] // End the task assertion. UIApplication.shared.endBackgroundTask(self.backgroundTaskID) self.backgroundTaskID = .invalid } } } extension MPCManager: MCNearbyServiceAdvertiserDelegate { func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) { let device = self.addNewDevice(for: peerID) device.createSession() invitationHandler(true, device.session) // Handle our incoming peer } } extension MPCManager: MCNearbyServiceBrowserDelegate { func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) { // found peer, create a device with this peerID addNewDevice(for: peerID) } func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) { // lost peer, disconnect and remove the device with this peerID let device = self.findDevice(for: peerID) devices = devices.filter{$0.peerID.displayName != peerID.displayName} device?.disconnect() } }