feat(ios): files sending
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// NearbyCommand.swift
|
||||
// nearby_service
|
||||
//
|
||||
// Created by Kseniia Nikitina on 4/2/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
class NearbyStartCommand {
|
||||
|
||||
init( id: String, filesCount: Int) {
|
||||
self.id = id
|
||||
self.filesCount = filesCount
|
||||
}
|
||||
|
||||
static func fromUserInfo(userInfo: NearbyUserInfo)-> NearbyStartCommand? {
|
||||
if let id = userInfo.dictionary["id"] as? String ,
|
||||
let filesCount = userInfo.dictionary["filesCount"] as? Int
|
||||
{
|
||||
return NearbyStartCommand(
|
||||
id: id, filesCount: filesCount
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toDictionary() -> [String: Any] {
|
||||
return ["id": id, "filesCount": filesCount]
|
||||
}
|
||||
|
||||
let id: String
|
||||
let filesCount: Int
|
||||
}
|
||||
@@ -71,7 +71,7 @@ extension NearbyDevice {
|
||||
return deviceDict
|
||||
}
|
||||
|
||||
func toJsonString() -> String? {
|
||||
func toDartFormat() -> String? {
|
||||
var result: String?
|
||||
do {
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: toDictionary())
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// NearbyMessage.swift
|
||||
// nearby_service
|
||||
//
|
||||
// Created by Kseniia Nikitina on 4/2/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MultipeerConnectivity
|
||||
|
||||
|
||||
class NearbyMessage {
|
||||
init(content: NearbyMessageContent, senderName: String, senderPeerID: MCPeerID) {
|
||||
self.content = content
|
||||
self.senderName = senderName
|
||||
self.senderPeerID = senderPeerID
|
||||
}
|
||||
|
||||
let content: NearbyMessageContent
|
||||
let senderPeerID: MCPeerID
|
||||
let senderName: String
|
||||
|
||||
static func fromUserInfo(userInfo: NearbyUserInfo)-> NearbyMessage? {
|
||||
if let jsonContent = userInfo.dictionary["content"] as? [String : Any],
|
||||
let content = NearbyMessageContent.typedFromJson(json: jsonContent),
|
||||
let name = userInfo.dictionary["name"] as? String {
|
||||
return NearbyMessage(
|
||||
content: content,
|
||||
senderName: name,
|
||||
senderPeerID: userInfo.peerID
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toDictionary() -> [String: Any] {
|
||||
return [
|
||||
"name": senderName,
|
||||
"content": content.toJson()
|
||||
]
|
||||
}
|
||||
|
||||
func toDartFormat() -> String? {
|
||||
do {
|
||||
let object = [
|
||||
"sender": ["id": senderPeerID.displayName, "displayName": senderName],
|
||||
"content": content.toJson()
|
||||
]
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: object)
|
||||
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
||||
return jsonString
|
||||
}
|
||||
} catch let error {
|
||||
Logger.error(message: error.localizedDescription)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
//
|
||||
// NearbyMessage.swift
|
||||
// nearby_service
|
||||
//
|
||||
// Created by Kseniia Nikitina on 4/2/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MultipeerConnectivity
|
||||
|
||||
class NearbyMessageContent {
|
||||
|
||||
init(type: MessageContentType) {
|
||||
self.type = type
|
||||
}
|
||||
|
||||
let type: MessageContentType
|
||||
|
||||
static func typedFromJson(json: [String: Any]) -> NearbyMessageContent? {
|
||||
if let typeString: String = json["type"] as? String {
|
||||
let type = MessageContentType.fromString(value: typeString)
|
||||
if (type == MessageContentType.text) {
|
||||
return NearbyMessageTextContent.fromJson(json: json)
|
||||
} else if (type == MessageContentType.filesRequest) {
|
||||
return NearbyMessageFilesRequest.fromJson(json: json)
|
||||
} else if (type == MessageContentType.filesResponse) {
|
||||
return NearbyMessageFilesResponse.fromJson(json: json)
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
func toJson() -> [String : Any] {
|
||||
return ["type": type.name]
|
||||
}
|
||||
}
|
||||
|
||||
class NearbyMessageTextContent : NearbyMessageContent {
|
||||
init(value: String) {
|
||||
self.value = value
|
||||
super.init(type: MessageContentType.text)
|
||||
}
|
||||
|
||||
static func fromJson(json:[String: Any]) -> NearbyMessageTextContent? {
|
||||
if let message: String = json["value"] as? String {
|
||||
return NearbyMessageTextContent(value: message)
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
override func toJson() -> [String : Any] {
|
||||
return ["value": value].merging( super.toJson()) { (current, _) in current}
|
||||
}
|
||||
|
||||
let value: String
|
||||
}
|
||||
|
||||
class NearbyMessageFilesContent : NearbyMessageContent {
|
||||
|
||||
init(files: Array<String>, id: String, type: MessageContentType) {
|
||||
self.files = files
|
||||
self.id = id
|
||||
super.init(type: type)
|
||||
}
|
||||
|
||||
static func fromJsonRaw(type: MessageContentType, json: [String: Any]) -> NearbyMessageFilesContent? {
|
||||
if let filesObjects: Array = json["files"] as? Array<Dictionary<String, AnyObject>> {
|
||||
let files : [String]? = filesObjects.map({ $0["path"] as? String }).compactMap({$0})
|
||||
if let id: String = json["id"] as? String{
|
||||
if let requireFiles = files {
|
||||
return NearbyMessageFilesContent(files: requireFiles, id: id, type: type)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
override func toJson() -> [String : Any] {
|
||||
return [
|
||||
"files": files.map{["path": $0]},
|
||||
"id": id,
|
||||
].merging( super.toJson()) { (current, _) in current}
|
||||
}
|
||||
|
||||
let files: Array<String>
|
||||
let id: String
|
||||
}
|
||||
|
||||
class NearbyMessageFilesRequest : NearbyMessageFilesContent {
|
||||
init(files: Array<String>, id: String) {
|
||||
super.init(files: files, id: id, type: MessageContentType.filesRequest)
|
||||
}
|
||||
static func fromJson( json: [String: Any]) -> NearbyMessageFilesRequest? {
|
||||
let message = NearbyMessageFilesContent.fromJsonRaw(type: MessageContentType.filesRequest, json: json)
|
||||
if let requireMessage = message {
|
||||
return NearbyMessageFilesRequest(files: requireMessage.files, id: requireMessage.id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
class NearbyMessageFilesResponse : NearbyMessageFilesContent {
|
||||
init(files: Array<String>, id: String, response: Bool) {
|
||||
self.response = response
|
||||
super.init(files: files, id: id, type: MessageContentType.filesResponse)
|
||||
}
|
||||
static func fromJson( json: [String: Any]) -> NearbyMessageFilesResponse? {
|
||||
let message = NearbyMessageFilesContent.fromJsonRaw(type: MessageContentType.filesResponse, json: json)
|
||||
if let requireMessage = message,
|
||||
let response = json["response"] as? Bool {
|
||||
return NearbyMessageFilesResponse(files: requireMessage.files, id: requireMessage.id, response: response)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
override func toJson() -> [String : Any] {
|
||||
return [
|
||||
"response": response
|
||||
].merging( super.toJson()) { (current, _) in current}
|
||||
}
|
||||
|
||||
let response: Bool
|
||||
}
|
||||
|
||||
|
||||
enum MessageContentType {
|
||||
case text
|
||||
case filesRequest
|
||||
case filesResponse
|
||||
|
||||
static func fromString(value: String) -> MessageContentType {
|
||||
if (value == text.name) {
|
||||
return text
|
||||
} else if (value == filesRequest.name) {
|
||||
return filesRequest
|
||||
} else if (value == filesResponse.name) {
|
||||
return filesResponse
|
||||
} else {
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
var name : String {
|
||||
switch self {
|
||||
case .text: return "text"
|
||||
case .filesRequest: return "filesRequest"
|
||||
case .filesResponse: return "filesResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,23 +27,39 @@ class NearbySession: NSObject {
|
||||
extension NearbySession: MCSessionDelegate {
|
||||
func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) {
|
||||
self.state = state
|
||||
|
||||
}
|
||||
|
||||
func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: ON_MESSAGE_RECEIVED,
|
||||
object: nil,
|
||||
userInfo: ["from": peerID, "data": data]
|
||||
userInfo: NearbyUserInfo.message(peerID: peerID, data: data)?.toDictionary()
|
||||
)
|
||||
}
|
||||
|
||||
func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {
|
||||
}
|
||||
func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {}
|
||||
|
||||
func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) {
|
||||
|
||||
}
|
||||
|
||||
func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) {
|
||||
guard let localURL = localURL else { return }
|
||||
|
||||
let destinationURL = localURL.deletingLastPathComponent().appendingPathComponent("\(resourceName)")
|
||||
|
||||
|
||||
do {
|
||||
try FileManager.default.moveItem(at: localURL, to: destinationURL)
|
||||
} catch {
|
||||
Logger.error(message: "Error moving file: \(error)")
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: ON_RESOURCE_RECEIVED,
|
||||
object: nil,
|
||||
userInfo: NearbyUserInfo.resource(peerID: peerID, url: destinationURL)?.toDictionary()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// NearbyUserInfo.swift
|
||||
// nearby_service
|
||||
//
|
||||
// Created by Kseniia Nikitina on 4/2/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MultipeerConnectivity
|
||||
|
||||
class NearbyUserInfo {
|
||||
init(peerID: MCPeerID, dictionary: [String: Any]) {
|
||||
self.peerID = peerID
|
||||
self.dictionary = dictionary
|
||||
}
|
||||
|
||||
static func resource(peerID: MCPeerID, url: URL?) -> NearbyUserInfo? {
|
||||
var dictionary: [String: Any] = [:]
|
||||
|
||||
if let requireUrl = url {
|
||||
dictionary["url"] = requireUrl
|
||||
}
|
||||
return NearbyUserInfo(
|
||||
peerID: peerID,
|
||||
dictionary: dictionary
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
static func message(peerID: MCPeerID, data: Data) -> NearbyUserInfo? {
|
||||
do {
|
||||
if let dict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
||||
return NearbyUserInfo(
|
||||
peerID: peerID,
|
||||
dictionary: dict
|
||||
)
|
||||
}
|
||||
} catch let error {
|
||||
Logger.error(message: error.localizedDescription)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
static func fromDictionary(userInfo: [AnyHashable : Any]?) -> NearbyUserInfo? {
|
||||
if let dictionary = userInfo?["dictionary"] as? [String: Any],
|
||||
let peerID = userInfo?["peerID"] as? MCPeerID {
|
||||
return NearbyUserInfo(peerID: peerID, dictionary:dictionary)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toDictionary() -> [AnyHashable : Any]? {
|
||||
return ["peerID": peerID, "dictionary": dictionary]
|
||||
}
|
||||
|
||||
let peerID: MCPeerID
|
||||
let dictionary: [String: Any]
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
import Foundation
|
||||
import MultipeerConnectivity
|
||||
|
||||
let SERVICE_TYPE = "mp-connection"
|
||||
let PEER_ID = "PEER-ID"
|
||||
let DEVICE_NAME = "DEVICE-NAME"
|
||||
let ON_MESSAGE_RECEIVED = Notification.Name("NearbySessionOnMessageReceived")
|
||||
|
||||
class MyDeviceDataGenerator {
|
||||
static func generate(name: String?) -> NearbyDevice {
|
||||
return NearbyDevice(
|
||||
peerID: ArchivedData.getPeerID(),
|
||||
name: ArchivedData.getRequireName(from: name),
|
||||
deviceType: UIDevice.current.model,
|
||||
os: UIDevice.current.systemName,
|
||||
osVersion: UIDevice.current.systemVersion
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class ArchivedData {
|
||||
static func getPeerID() -> MCPeerID {
|
||||
if let archivedPeerID = getArchivedPeerID() {
|
||||
return archivedPeerID
|
||||
} else {
|
||||
let peerID = MCPeerID(
|
||||
displayName: UIDevice.current.name.replacingOccurrences(of: " ", with: "_")
|
||||
+ "_"
|
||||
+ String(Int.random(in: 1000..<50000))
|
||||
)
|
||||
savePeerID(for: peerID)
|
||||
return peerID
|
||||
}
|
||||
}
|
||||
|
||||
static func getRequireName(from name: String?) -> String {
|
||||
let archivedName = ArchivedData.getArchivedName()
|
||||
if let newName = name {
|
||||
if (archivedName != newName) {
|
||||
ArchivedData.saveName(for: newName)
|
||||
}
|
||||
return newName
|
||||
}
|
||||
return archivedName ?? UIDevice.current.name
|
||||
}
|
||||
|
||||
static func getArchivedPeerID() -> MCPeerID? {
|
||||
guard let savedData = UserDefaults.standard.data(forKey: PEER_ID),
|
||||
let unarchivedPeerID = try? NSKeyedUnarchiver.unarchivedObject(ofClass: MCPeerID.self, from: savedData)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
return unarchivedPeerID
|
||||
}
|
||||
|
||||
static func savePeerID(for peerID: MCPeerID) {
|
||||
|
||||
do {
|
||||
UserDefaults.standard.set(
|
||||
try NSKeyedArchiver.archivedData(withRootObject: peerID, requiringSecureCoding: false),
|
||||
forKey: PEER_ID
|
||||
)
|
||||
} catch let e {
|
||||
Logger.error(message: e.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
static func getArchivedName() -> String? {
|
||||
guard let savedData = UserDefaults.standard.data(forKey: DEVICE_NAME),
|
||||
let unarchivedName = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSData.self, from: savedData)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
return String(data: unarchivedName as Data, encoding: .utf8)
|
||||
}
|
||||
|
||||
static func saveName(for name: String) {
|
||||
if let data = name.data(using: .utf8) {
|
||||
do {
|
||||
UserDefaults.standard.set(
|
||||
try NSKeyedArchiver.archivedData(withRootObject: data, requiringSecureCoding: false),
|
||||
forKey: DEVICE_NAME
|
||||
)
|
||||
} catch let e {
|
||||
Logger.error(message: e.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,11 +26,11 @@ class NearbyManager: NSObject {
|
||||
}
|
||||
|
||||
func getSavedDeviceName(result: @escaping FlutterResult) {
|
||||
result(ArchivedData.getArchivedName())
|
||||
result(Archiver.getName())
|
||||
}
|
||||
|
||||
func getCurrentDevice(result: @escaping FlutterResult) {
|
||||
result(device.toJsonString())
|
||||
result(device.toDartFormat())
|
||||
}
|
||||
|
||||
func openServicesSettings(result: @escaping FlutterResult) {
|
||||
@@ -65,7 +65,7 @@ class NearbyManager: NSObject {
|
||||
}
|
||||
|
||||
func getPeers(result: @escaping FlutterResult) {
|
||||
result(NearbyDevicesStore.instance.getDevicesToJsonString())
|
||||
result(NearbyDevicesStore.instance.toDartFormat())
|
||||
}
|
||||
|
||||
func invite(for deviceId: String, result: @escaping FlutterResult) {
|
||||
@@ -101,17 +101,14 @@ class NearbyManager: NSObject {
|
||||
result(true)
|
||||
}
|
||||
|
||||
func send(for message: String, with receiverId: String, result: @escaping FlutterResult) {
|
||||
func send(for content: NearbyMessageContent, with receiverId: String, result: @escaping FlutterResult) {
|
||||
let device = NearbyDevicesStore.instance.find(for: receiverId)
|
||||
|
||||
do {
|
||||
if let requireDevice = device {
|
||||
let data = [
|
||||
"name": self.device.name,
|
||||
"message": message
|
||||
]
|
||||
let message = NearbyMessage(content: content, senderName: self.device.name, senderPeerID: self.device.peerID)
|
||||
try requireDevice.session?.session?.send(
|
||||
try JSONSerialization.data(withJSONObject: data),
|
||||
try JSONSerialization.data(withJSONObject: message.toDictionary()),
|
||||
toPeers: [requireDevice.peerID],
|
||||
with: MCSessionSendDataMode.reliable
|
||||
)
|
||||
@@ -123,7 +120,34 @@ class NearbyManager: NSObject {
|
||||
result(true)
|
||||
}
|
||||
|
||||
|
||||
func sendFiles(id: String, paths: [String], with receiverId: String) {
|
||||
do {
|
||||
let device = NearbyDevicesStore.instance.find(for: receiverId)
|
||||
if let requireDevice = device {
|
||||
try requireDevice.session?.session?.send(
|
||||
try JSONSerialization.data(withJSONObject: NearbyStartCommand( id: id, filesCount: paths.count).toDictionary()),
|
||||
toPeers: [requireDevice.peerID],
|
||||
with: MCSessionSendDataMode.reliable
|
||||
)
|
||||
|
||||
for path in paths {
|
||||
let url = URL(fileURLWithPath: path)
|
||||
if FileManager.default.fileExists(atPath: url.path) {
|
||||
requireDevice.session?.session?.sendResource(
|
||||
at: url,
|
||||
withName: url.lastPathComponent,
|
||||
toPeer: requireDevice.peerID
|
||||
)
|
||||
} else {
|
||||
Logger.error(message: "File does not exist: " + path)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} catch let error {
|
||||
Logger.error(message: error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NearbyManager: MCNearbyServiceAdvertiserDelegate {
|
||||
@@ -139,6 +163,7 @@ extension NearbyManager: MCNearbyServiceAdvertiserDelegate {
|
||||
self.invitationHandlers[peerID.displayName] = invitationHandler
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension NearbyManager: MCNearbyServiceBrowserDelegate {
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
//
|
||||
// NearbyMessageConverter.swift
|
||||
// nearby_service
|
||||
//
|
||||
// Created by Kseniia Nikitina on 29/1/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MultipeerConnectivity
|
||||
|
||||
class NearbyMessageConverter {
|
||||
static func convert(userInfo: [AnyHashable : Any]?) -> String? {
|
||||
if let data = getMessageData(userInfo: userInfo),
|
||||
let peerID = getMessagePeerID(userInfo: userInfo) {
|
||||
return createMessage(data: data, peerID: peerID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
static private func getMessageData(userInfo: [AnyHashable : Any]?) -> [String: String]? {
|
||||
do {
|
||||
if let data = userInfo?["data"] as? Data,
|
||||
let dictionary = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] {
|
||||
return dictionary
|
||||
}
|
||||
} catch let error {
|
||||
Logger.error(message: error.localizedDescription)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
static private func getMessagePeerID(userInfo: [AnyHashable:Any]?) -> MCPeerID? {
|
||||
return userInfo?["from"] as? MCPeerID
|
||||
|
||||
}
|
||||
|
||||
static private func createMessage(data: [String: String], peerID: MCPeerID)-> String? {
|
||||
do {
|
||||
if let message = data["message"],
|
||||
let name = data["name"] {
|
||||
|
||||
var result: String?
|
||||
let jsonObject = [
|
||||
"content": ["value": message, "type": "text"],
|
||||
"sender": ["id": peerID.displayName, "displayName": name],
|
||||
] as [String : Any]
|
||||
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject)
|
||||
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
||||
result = jsonString
|
||||
}
|
||||
return result
|
||||
}
|
||||
} catch let error {
|
||||
Logger.error(message: error.localizedDescription)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// NearbyServicePluginOnReceived.swift
|
||||
// nearby_service
|
||||
//
|
||||
// Created by Kseniia Nikitina on 4/2/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Flutter
|
||||
|
||||
extension NearbyServicePlugin {
|
||||
@objc func onMessageReceived(notification: Notification) {
|
||||
DispatchQueue.main.async {
|
||||
if let userInfo = NearbyUserInfo.fromDictionary(userInfo: notification.userInfo) {
|
||||
if let message = NearbyMessage.fromUserInfo(userInfo: userInfo) {
|
||||
if message.content is NearbyMessageFilesResponse {
|
||||
let response = message.content as! NearbyMessageFilesResponse
|
||||
if (response.response) {
|
||||
self.manager.sendFiles(
|
||||
id: response.id,
|
||||
paths: response.files,
|
||||
with: message.senderPeerID.displayName
|
||||
)
|
||||
}
|
||||
}
|
||||
self.channel.invokeMethod(DART_COMMAND_MESSAGE_RECEIVED, arguments: message.toDartFormat())
|
||||
|
||||
} else if let command = NearbyStartCommand.fromUserInfo(userInfo: userInfo) {
|
||||
NearbyFilesStore.instance.startReceiving(command: command)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func onResourceReceived(notification: Notification) {
|
||||
DispatchQueue.main.async {
|
||||
if let userInfo = NearbyUserInfo.fromDictionary(userInfo: notification.userInfo) {
|
||||
|
||||
if let url = userInfo.dictionary["url"] as? URL {
|
||||
NearbyFilesStore.instance.add(url: url)
|
||||
if (NearbyFilesStore.instance.checkIsFull()) {
|
||||
self.channel.invokeMethod(DART_COMMAND_RESOURCES_RECEIVED, arguments: NearbyFilesStore.instance.toDartFormat())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,16 @@ import Flutter
|
||||
import MultipeerConnectivity
|
||||
import UIKit
|
||||
|
||||
public class NearbyServicePlugin: NSObject, FlutterPlugin {
|
||||
public class NearbyServicePlugin: NSObject, FlutterPlugin{
|
||||
|
||||
|
||||
let manager: NearbyManager;
|
||||
let channel: FlutterMethodChannel
|
||||
|
||||
init(manager: NearbyManager, channel: FlutterMethodChannel) {
|
||||
self.manager = manager
|
||||
self.channel = channel
|
||||
|
||||
}
|
||||
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
@@ -34,15 +36,20 @@ public class NearbyServicePlugin: NSObject, FlutterPlugin {
|
||||
let manager = NearbyManager()
|
||||
let instance = NearbyServicePlugin(manager: manager, channel: channel)
|
||||
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
instance,
|
||||
selector: #selector(onMessageReceived),
|
||||
name: ON_MESSAGE_RECEIVED,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
instance,
|
||||
selector: #selector(onResourceReceived),
|
||||
name: ON_RESOURCE_RECEIVED,
|
||||
object: nil
|
||||
)
|
||||
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
}
|
||||
|
||||
public func detachFromEngine(for registrar: FlutterPluginRegistrar) {
|
||||
@@ -52,18 +59,14 @@ public class NearbyServicePlugin: NSObject, FlutterPlugin {
|
||||
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
|
||||
|
||||
switch call.method {
|
||||
case "getPlatformVersion":
|
||||
result("iOS " + UIDevice.current.systemVersion)
|
||||
case "getPlatformModel":
|
||||
result(UIDevice.current.name)
|
||||
case "initialize":
|
||||
if let deviceName: String = getArgument(for: "deviceName", call: call) {
|
||||
manager.initialize(for: deviceName, result: result)
|
||||
} else {
|
||||
manager.initialize(for: nil, result: result)
|
||||
}
|
||||
manager.initialize(for: getArgument(for: "deviceName", call: call), result: result)
|
||||
case "getSavedDeviceName":
|
||||
manager.getSavedDeviceName(result: result)
|
||||
case "getCurrentDevice":
|
||||
@@ -102,12 +105,12 @@ public class NearbyServicePlugin: NSObject, FlutterPlugin {
|
||||
}
|
||||
|
||||
case "send":
|
||||
if let content: Dictionary<String, AnyObject> = getArgument(for: "content", call: call),
|
||||
let message: String = content["value"] as? String {
|
||||
if let contentJson: Dictionary<String, AnyObject> = getArgument(for: "content", call: call),
|
||||
let content: NearbyMessageContent = NearbyMessageContent.typedFromJson(json: contentJson) {
|
||||
if let receiver : Dictionary<String, AnyObject> = getArgument(for: "receiver", call: call),
|
||||
let receiverId : String = receiver["id"] as? String
|
||||
{
|
||||
manager.send(for: message, with: receiverId, result: result)
|
||||
manager.send(for: content, with: receiverId, result: result)
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
@@ -119,14 +122,7 @@ public class NearbyServicePlugin: NSObject, FlutterPlugin {
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
@objc func onMessageReceived(notification: Notification) {
|
||||
DispatchQueue.main.async {
|
||||
let result = NearbyMessageConverter.convert(userInfo: notification.userInfo)
|
||||
self.channel.invokeMethod("invoke_nearby_service_message_received", arguments: result)
|
||||
}
|
||||
}
|
||||
|
||||
private func getArgument<T>(for name: String, call: FlutterMethodCall) -> T? {
|
||||
func getArgument<T>(for name: String, call: FlutterMethodCall) -> T? {
|
||||
guard let data = call.arguments as? Dictionary<String, AnyObject> else {
|
||||
return nil
|
||||
}
|
||||
@@ -136,3 +132,4 @@ public class NearbyServicePlugin: NSObject, FlutterPlugin {
|
||||
return argument
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,22 +22,7 @@ class NearbyDevicesStore : NSObject {
|
||||
return device.peerID.displayName == deviceId
|
||||
}
|
||||
}
|
||||
|
||||
func getDevicesToJsonString() -> String {
|
||||
let devicesObject = devices.map { device in
|
||||
return device.toDictionary()
|
||||
}
|
||||
do {
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: devicesObject)
|
||||
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
||||
return jsonString
|
||||
}
|
||||
} catch {
|
||||
return "[]"
|
||||
}
|
||||
return "[]"
|
||||
}
|
||||
|
||||
|
||||
|
||||
func add(for peerID: MCPeerID, discoveryInfo: [String: String]? = nil) -> NearbyDevice? {
|
||||
devices = devices.filter{$0.peerID.displayName != peerID.displayName}
|
||||
@@ -55,4 +40,20 @@ class NearbyDevicesStore : NSObject {
|
||||
func clear() {
|
||||
self.devices = []
|
||||
}
|
||||
|
||||
func toDartFormat() -> String {
|
||||
let devicesObject = devices.map { device in
|
||||
return device.toDictionary()
|
||||
}
|
||||
do {
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: devicesObject)
|
||||
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
||||
return jsonString
|
||||
}
|
||||
} catch {
|
||||
return "[]"
|
||||
}
|
||||
return "[]"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// NearbyFilesStore.swift
|
||||
// nearby_service
|
||||
//
|
||||
// Created by Kseniia Nikitina on 3/2/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class NearbyFilesStore {
|
||||
static let instance = NearbyFilesStore()
|
||||
|
||||
private var paths : [String] = []
|
||||
private var id: String? = nil
|
||||
private var maxCount: Int = 0
|
||||
private var count: Int = 0
|
||||
|
||||
func startReceiving(command: NearbyStartCommand) {
|
||||
self.paths.removeAll()
|
||||
self.id = command.id
|
||||
self.maxCount = command.filesCount
|
||||
self.count = 0
|
||||
}
|
||||
|
||||
func add(url: URL) {
|
||||
paths.append(url.path)
|
||||
self.count = self.count + 1
|
||||
}
|
||||
|
||||
func checkIsFull() -> Bool {
|
||||
return maxCount <= count
|
||||
}
|
||||
|
||||
func toDartFormat() -> String? {
|
||||
let pathsObject = paths.map { ["path": $0]}
|
||||
do {
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: pathsObject)
|
||||
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
||||
return jsonString
|
||||
}
|
||||
} catch {
|
||||
return "[]"
|
||||
}
|
||||
return "[]"
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -1,4 +1,5 @@
|
||||
import Flutter
|
||||
import MultipeerConnectivity
|
||||
|
||||
class ConnectedDeviceStreamHandler: NSObject, FlutterStreamHandler {
|
||||
private var eventSink: FlutterEventSink?
|
||||
@@ -21,8 +22,11 @@ class ConnectedDeviceStreamHandler: NSObject, FlutterStreamHandler {
|
||||
self.timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
var result: String?
|
||||
if let device = NearbyDevicesStore.instance.find(for: deviceId) {
|
||||
result = device.toJsonString()
|
||||
if let device = NearbyDevicesStore.instance.find(for: deviceId),
|
||||
let session = device.session {
|
||||
if (session.state == MCSessionState.connected) {
|
||||
result = device.toDartFormat()
|
||||
}
|
||||
}
|
||||
self.eventSink?(result)
|
||||
}
|
||||
+1
-1
@@ -20,7 +20,7 @@ class NearbyPeersStreamHandler: NSObject, FlutterStreamHandler {
|
||||
self.timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
|
||||
let devicesList = NearbyDevicesStore.instance.getDevicesToJsonString()
|
||||
let devicesList = NearbyDevicesStore.instance.toDartFormat()
|
||||
self.eventSink?(devicesList)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// ArchivedData.swift
|
||||
// nearby_service
|
||||
//
|
||||
// Created by Kseniia Nikitina on 4/2/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MultipeerConnectivity
|
||||
|
||||
|
||||
class Archiver {
|
||||
static func getPeerID() -> MCPeerID? {
|
||||
guard let savedData = UserDefaults.standard.data(forKey: PEER_ID),
|
||||
let unarchivedPeerID = try? NSKeyedUnarchiver.unarchivedObject(ofClass: MCPeerID.self, from: savedData)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
return unarchivedPeerID
|
||||
}
|
||||
|
||||
static func savePeerID(for peerID: MCPeerID) {
|
||||
|
||||
do {
|
||||
UserDefaults.standard.set(
|
||||
try NSKeyedArchiver.archivedData(withRootObject: peerID, requiringSecureCoding: false),
|
||||
forKey: PEER_ID
|
||||
)
|
||||
} catch let e {
|
||||
Logger.error(message: e.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static func getName() -> String? {
|
||||
guard let savedData = UserDefaults.standard.data(forKey: DEVICE_NAME),
|
||||
let unarchivedName = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSData.self, from: savedData)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
return String(data: unarchivedName as Data, encoding: .utf8)
|
||||
}
|
||||
|
||||
static func saveName(for name: String) {
|
||||
if let data = name.data(using: .utf8) {
|
||||
do {
|
||||
UserDefaults.standard.set(
|
||||
try NSKeyedArchiver.archivedData(withRootObject: data, requiringSecureCoding: false),
|
||||
forKey: DEVICE_NAME
|
||||
)
|
||||
} catch let e {
|
||||
Logger.error(message: e.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// Constants.swift
|
||||
// nearby_service
|
||||
//
|
||||
// Created by Kseniia Nikitina on 4/2/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
let SERVICE_TYPE = "mp-connection"
|
||||
let PEER_ID = "PEER-ID"
|
||||
let DEVICE_NAME = "DEVICE-NAME"
|
||||
let ON_MESSAGE_RECEIVED = Notification.Name("NearbySessionOnMessageReceived")
|
||||
let ON_RESOURCE_RECEIVED = Notification.Name("NearbySessionOnResourceReceived")
|
||||
|
||||
let DART_COMMAND_MESSAGE_RECEIVED = "invoke_nearby_service_message_received"
|
||||
let DART_COMMAND_RESOURCES_RECEIVED = "invoke_nearby_service_resources_received"
|
||||
@@ -0,0 +1,39 @@
|
||||
import Foundation
|
||||
import MultipeerConnectivity
|
||||
|
||||
|
||||
class MyDeviceDataGenerator {
|
||||
static func generate(name: String?) -> NearbyDevice {
|
||||
return NearbyDevice(
|
||||
peerID: getPeerID(),
|
||||
name: getNameArchived(or: name),
|
||||
deviceType: UIDevice.current.model,
|
||||
os: UIDevice.current.systemName,
|
||||
osVersion: UIDevice.current.systemVersion
|
||||
)
|
||||
}
|
||||
static private func getPeerID() -> MCPeerID {
|
||||
if let archivedPeerID = Archiver.getPeerID() {
|
||||
return archivedPeerID
|
||||
} else {
|
||||
let peerID = MCPeerID(
|
||||
displayName: UIDevice.current.name.replacingOccurrences(of: " ", with: "_")
|
||||
+ "_"
|
||||
+ String(Int.random(in: 1000..<50000))
|
||||
)
|
||||
Archiver.savePeerID(for: peerID)
|
||||
return peerID
|
||||
}
|
||||
}
|
||||
static private func getNameArchived(or name: String?) -> String {
|
||||
let archivedName = Archiver.getName()
|
||||
if let newName = name {
|
||||
if (archivedName != newName) {
|
||||
Archiver.saveName(for: newName)
|
||||
}
|
||||
return newName
|
||||
}
|
||||
return archivedName ?? UIDevice.current.name
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user