From aae94e8e9ba7bc5fe52c280371b1810fe24b9a6a Mon Sep 17 00:00:00 2001 From: Prerak Mann Date: Sun, 28 Feb 2021 14:52:53 +0530 Subject: [PATCH] Copy swift code --- ios/Classes/Device.swift | 56 ++++++ ios/Classes/MPCManager.swift | 180 ++++++++++++++++++ ios/Classes/Message.swift | 80 ++++++++ .../SwiftNearbyConnectionsPlugin.swift | 172 ++++++++++++++++- 4 files changed, 484 insertions(+), 4 deletions(-) create mode 100644 ios/Classes/Device.swift create mode 100644 ios/Classes/MPCManager.swift create mode 100644 ios/Classes/Message.swift diff --git a/ios/Classes/Device.swift b/ios/Classes/Device.swift new file mode 100644 index 0000000..076b436 --- /dev/null +++ b/ios/Classes/Device.swift @@ -0,0 +1,56 @@ +import Foundation +import MultipeerConnectivity +import SwiftyJSON + +class Device: NSObject { + let peerID: MCPeerID + var session: MCSession? + var state = MCSessionState.notConnected + var lastMessageReceived: Message? + + static let messageReceivedNotification = Notification.Name("DeviceDidReceiveMessage") + + init(peerID: MCPeerID) { + self.peerID = peerID + super.init() + } + + func createSession() { + if self.session != nil { return } + self.session = MCSession(peer: MPCManager.instance.localPeerID, securityIdentity: nil, encryptionPreference: .required) + self.session?.delegate = self + } + + func disconnect() { + self.session?.disconnect() + self.session = nil + NotificationCenter.default.post(name: MPCManager.Notifications.deviceDidChangeState, object: self) + } + + func invite(with browser: MCNearbyServiceBrowser) { + if (self.state == MCSessionState.notConnected) { + self.createSession() + if let session = session { + browser.invitePeer(self.peerID, to: session, withContext: nil, timeout: 10) + } + } + } +} + +extension Device: MCSessionDelegate { + public func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) { + self.state = state + NotificationCenter.default.post(name: MPCManager.Notifications.deviceDidChangeState, object: nil) + } + + public func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { + NotificationCenter.default.post(name: Device.messageReceivedNotification, object: nil, userInfo: ["from": peerID, "data": data]) + } + + public func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) { } + + public func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) { } + + public func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) { } + +} diff --git a/ios/Classes/MPCManager.swift b/ios/Classes/MPCManager.swift new file mode 100644 index 0000000..1a4bc83 --- /dev/null +++ b/ios/Classes/MPCManager.swift @@ -0,0 +1,180 @@ +// +// 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 instance = MPCManager() + + var localPeerID: MCPeerID! + var enterbackgroundNotification: NSObjectProtocol! + private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid + var devices: [Device] = [] { + didSet { + deviceDidChange?() + } + } + + var deviceDidChange: (() -> Void)? + + // override init() { + // if let data = UserDefaults.standard.data(forKey: "peerID"), let id = NSKeyedUnarchiver.unarchiveObject(with: data) as? MCPeerID { + // self.localPeerID = id + // } else { + // let peerID = MCPeerID(displayName: UIDevice.current.name) + // let data = NSKeyedArchiver.archivedData(withRootObject: peerID) + // UserDefaults.standard.set(data, forKey: "peerID") + // self.localPeerID = peerID + // } + // super.init() + // } + + deinit{ + if let taskEnterBackground = enterbackgroundNotification { + NotificationCenter.default.removeObserver(taskEnterBackground) + } + } + + func setup(serviceType: String, deviceName: String) { + 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 + } + + self.advertiser = MCNearbyServiceAdvertiser(peer: localPeerID, discoveryInfo: nil, serviceType: serviceType) + self.advertiser.delegate = self + + 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() { + for device in self.devices { + device.disconnect() + } + self.browser.stopBrowsingForPeers() + } + + func invitePeer(deviceID: String) { + do { + let device = MPCManager.instance.findDevice(for: deviceID) + if(device?.state == MCSessionState.notConnected){ + device?.invite(with: self.browser) + } + } catch let error { + print(error.localizedDescription) + } + } + + func disconnectPeer(deviceID: String){ + let device = MPCManager.instance.findDevice(for: deviceID) + 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 deviceId: String) -> Device? { + for device in self.devices { + if device.peerID.displayName == deviceId { 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() + } + + +} diff --git a/ios/Classes/Message.swift b/ios/Classes/Message.swift new file mode 100644 index 0000000..030487a --- /dev/null +++ b/ios/Classes/Message.swift @@ -0,0 +1,80 @@ +// +// Message.swift +// multipeer_connections +// +// Created by NamIT on 9/3/20. +// + +import Foundation +import SwiftyJSON + +struct Message: Codable { + let body: String +} + +extension Device { + func send(text: String) throws { + try self.session?.send(text.data(using: .utf8) ?? Data(), toPeers: [self.peerID], with: .reliable) + } + + func send(json: JSON) throws { + try self.session?.send(json.rawData(), toPeers: [self.peerID], with: .reliable) + } + + func send(data: Data) throws { + try self.session?.send(data, toPeers: [self.peerID], with: .reliable) + } +} + +struct ReceivedResponse { + var deviceID: String? + var message: MessageReponse + + init(json: JSON) { + deviceID = json["deviceID"].string + message = MessageReponse(json: json["message"]) + } +} + +struct MessageReponse { + var items: [MessageResponseItem] = [] + var subTotal: NSNumber? + var rounding: NSNumber? + var total: NSNumber? + + init(json: JSON) { + items = json["items"].array?.compactMap({return MessageResponseItem(json: $0)}) ?? [] + subTotal = json["subTotal"].number + rounding = json["rounding"].number + total = json["total"].number + } +} + +struct MessageResponseItem { + var quantity: NSNumber? + var itemCollection: MessageItemCollection? + + init(json: JSON) { + quantity = json["quantity"].number + itemCollection = MessageItemCollection(json: json["itemCollection"]) + } +} + +struct MessageItemCollection { + var name: String? + var status: MessageItemCollectionStatus? + var imageUrl: String? + var price: NSNumber? + + init(json: JSON) { + name = json["name"].string + status = MessageItemCollectionStatus(rawValue: json["status"].stringValue) + imageUrl = json["imageUrl"].string + price = json["price"].number + } +} + +enum MessageItemCollectionStatus: String { + case active = "ACTIVE" +} + diff --git a/ios/Classes/SwiftNearbyConnectionsPlugin.swift b/ios/Classes/SwiftNearbyConnectionsPlugin.swift index ba8a96c..09f25cf 100644 --- a/ios/Classes/SwiftNearbyConnectionsPlugin.swift +++ b/ios/Classes/SwiftNearbyConnectionsPlugin.swift @@ -1,14 +1,178 @@ import Flutter import UIKit +import MultipeerConnectivity +import SwiftyJSON + +let SERVICE_TYPE = "nearby_connections" +let INVOKE_CHANGE_STATE_METHOD = "invoke_change_state_method" +let INVOKE_MESSAGE_RECEIVE_METHOD = "invoke_message_receive_method" + +enum MethodCall: String { + case initNearbyService = "init_nearby_service" + case startAdvertisingPeer = "start_advertising_peer" + case startBrowsingForPeers = "start_browsing_for_peers" + + case stopAdvertisingPeer = "stop_advertising_peer" + case stopBrowsingForPeers = "stop_browsing_for_peers" + + case invitePeer = "invite_peer" + case disconnectPeer = "disconnect_peer" + + case sendMessage = "send_message" +} public class SwiftNearbyConnectionsPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "nearby_connections", binaryMessenger: registrar.messenger()) - let instance = SwiftNearbyConnectionsPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) + let channel = FlutterMethodChannel(name: "nearby_connections", binaryMessenger: registrar.messenger()) + let instance = SwiftNearbyConnectionsPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + var currentReceivedDevice: Device? + + let channel: FlutterMethodChannel + + struct DeviceJson { + var deviceId:String + var deviceName:String + var state:Int + + func toStringAnyObject() -> [String: Any] { + return [ + "deviceId": deviceId, + "deviceName": deviceName, + "state": state + ] + } + } + + struct MessageJson { + var deviceId:String + var message:String + + func toStringAnyObject() -> [String: Any] { + return [ + "deviceId": deviceId, + "message": message + ] + } + } + + @objc func stateChanged(){ + let devices = MPCManager.instance.devices.compactMap({return DeviceJson(deviceId: $0.peerID.displayName, deviceName: $0.peerID.displayName, state: $0.state.rawValue)}) + channel.invokeMethod(INVOKE_CHANGE_STATE_METHOD, arguments: JSON(devices.compactMap({return $0.toStringAnyObject()})).rawString()) + } + + @objc func messageReceived(notification: Notification) { + do { + if let data = notification.userInfo?["data"] as? Data, let stringData = JSON(data).rawString() { + let dict = convertToDictionary(text: stringData) + self.channel.invokeMethod(INVOKE_MESSAGE_RECEIVE_METHOD, arguments: dict) + } + } catch let e { + print(e.localizedDescription) + } + } + + func convertToDictionary(text: String) -> [String: Any]? { + if let data = text.data(using: .utf8) { + do { + return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] + } catch { + print(error.localizedDescription) + } + } + return nil + } + + public init(channel:FlutterMethodChannel) { + self.channel = channel + super.init() + + NotificationCenter.default.addObserver(self, selector: #selector(stateChanged), name: MPCManager.Notifications.deviceDidChangeState, object: nil) + + NotificationCenter.default.addObserver(self, selector: #selector(messageReceived), name: Device.messageReceivedNotification, object: nil) + + MPCManager.instance.deviceDidChange = {[weak self] in + self?.stateChanged() + } } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - result("iOS " + UIDevice.current.systemVersion) + switch MethodCall(rawValue: call.method) { + case .initNearbyService: + guard let data = call.arguments as? Dictionary else { + result(false) + return + } + let serviceType:String = data["serviceType"] as? String ?? SERVICE_TYPE + var deviceName:String = data["deviceName"] as? String ?? "" + if (deviceName.isEmpty){ + deviceName = UIDevice.current.name + } + + MPCManager.instance.setup(serviceType: serviceType, deviceName: deviceName) + currentReceivedDevice = Device(peerID: MPCManager.instance.localPeerID) + result(true) + case .startAdvertisingPeer: + MPCManager.instance.startAdvertisingPeer() + result(true) + case .startBrowsingForPeers: + MPCManager.instance.startBrowsingForPeers() + result(true) + case .stopAdvertisingPeer: + MPCManager.instance.stopAdvertisingPeer() + result(true) + case .stopBrowsingForPeers: + MPCManager.instance.stopBrowsingForPeers() + result(true) + case .invitePeer: + guard let data = call.arguments as? Dictionary else { + result(false) + return + + } + guard let deviceId: String = data["deviceId"] as? String else { + result(false) + return + } + MPCManager.instance.invitePeer(deviceID: deviceId) + result(true) + + case .disconnectPeer: + guard let data = call.arguments as? Dictionary else { + result(false) + return + + } + let deviceId:String? = data["deviceId"] as? String ?? nil + if (deviceId != nil) { + MPCManager.instance.disconnectPeer(deviceID: deviceId!) + result(true) + } else { + result(false) + } + case .sendMessage: + guard let dict = call.arguments as? Dictionary else { + result(false) + return + } + do { + let jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) + if let device = MPCManager.instance.findDevice(for: dict["deviceId"] as! String) { + currentReceivedDevice = device + try device.send(data: jsonData) + result(true) + return + } + } catch let error as NSError { + print(error) + } + result(false) + default: + result(FlutterMethodNotImplemented) + return + } } + }