Add macOS support (#23)
* Add macOS support * Update README * Update to v0.2.0 * Document breaking changes * Delete widget tests
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// NearbyCommand.swift
|
||||
// nearby_service
|
||||
//
|
||||
// Created by Kseniia Nikitina on 4/2/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MultipeerConnectivity
|
||||
|
||||
|
||||
class NearbyStartCommand {
|
||||
|
||||
init(id: String, senderName: String, filesCount: Int) {
|
||||
self.id = id
|
||||
self.senderName = senderName
|
||||
self.filesCount = filesCount
|
||||
}
|
||||
|
||||
static func fromUserInfo(userInfo: NearbyUserInfo)-> NearbyStartCommand? {
|
||||
if let name = userInfo.dictionary["name"] as? String,
|
||||
let filesCount = userInfo.dictionary["filesCount"] as? Int,
|
||||
let id = userInfo.dictionary["id"] as? String
|
||||
{
|
||||
return NearbyStartCommand(id: id, senderName: name, filesCount: filesCount)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toDictionary() -> [String: Any] {
|
||||
return ["name": senderName, "filesCount": filesCount, "id": id]
|
||||
}
|
||||
|
||||
let id: String
|
||||
let senderName: String
|
||||
let filesCount: Int
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
|
||||
import Foundation
|
||||
import MultipeerConnectivity
|
||||
|
||||
class NearbyDevice : NSObject {
|
||||
var name: String
|
||||
var peerID: MCPeerID
|
||||
var deviceType: String?
|
||||
var os: String?
|
||||
var osVersion: String?
|
||||
var session: NearbySession?
|
||||
|
||||
|
||||
init(
|
||||
peerID: MCPeerID,
|
||||
name:String,
|
||||
deviceType:String? = nil,
|
||||
os: String? = nil,
|
||||
osVersion: String? = nil
|
||||
) {
|
||||
self.name=name
|
||||
self.peerID = peerID
|
||||
self.deviceType=deviceType
|
||||
self.os=os
|
||||
self.osVersion=osVersion
|
||||
}
|
||||
|
||||
func createSession(for peerID: MCPeerID) -> NearbySession {
|
||||
self.session = NearbySession.create(peerID: peerID)
|
||||
return session!
|
||||
}
|
||||
|
||||
func deleteSession() {
|
||||
self.session = nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension NearbyDevice {
|
||||
static func fromDictionary(for dictionary: [String: String]?, with peerID: MCPeerID) -> NearbyDevice {
|
||||
let device = NearbyDevice(
|
||||
peerID: peerID,
|
||||
name:(dictionary?["displayName"] as String?) ?? peerID.displayName,
|
||||
deviceType: dictionary?["deviceType"] as String?,
|
||||
os: dictionary?["os"] as String?,
|
||||
osVersion: dictionary?["osVersion"] as String?
|
||||
)
|
||||
return device
|
||||
|
||||
}
|
||||
|
||||
func toDictionary() -> [String: String] {
|
||||
var deviceDict: [String: String] = [
|
||||
"displayName": self.name,
|
||||
"id": self.peerID.displayName,
|
||||
]
|
||||
if let os = self.os {
|
||||
deviceDict["os"] = os
|
||||
}
|
||||
if let deviceType = self.deviceType {
|
||||
deviceDict["deviceType"] = deviceType
|
||||
}
|
||||
if let osVersion = self.osVersion {
|
||||
deviceDict["osVersion"] = osVersion
|
||||
}
|
||||
if let session = self.session {
|
||||
deviceDict["state"] = String(session.state.rawValue)
|
||||
} else {
|
||||
deviceDict["state"] = String(0)
|
||||
}
|
||||
return deviceDict
|
||||
}
|
||||
|
||||
func toDartFormat() -> String? {
|
||||
var result: String?
|
||||
do {
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: toDictionary())
|
||||
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
||||
result = jsonString
|
||||
}
|
||||
} catch let error {
|
||||
Logger.error(message: error.localizedDescription)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -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,163 @@
|
||||
//
|
||||
// NearbyMessage.swift
|
||||
// nearby_service
|
||||
//
|
||||
// Created by Kseniia Nikitina on 4/2/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MultipeerConnectivity
|
||||
|
||||
class NearbyMessageContent {
|
||||
|
||||
init(id: String, type: MessageContentType) {
|
||||
self.type = type
|
||||
self.id = id
|
||||
}
|
||||
|
||||
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.textRequest) {
|
||||
return NearbyMessageTextRequest.fromJson(json: json)
|
||||
} else if (type == MessageContentType.textResponse) {
|
||||
return NearbyMessageTextResponse.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;
|
||||
}
|
||||
|
||||
static func fromJsonRaw(type: MessageContentType, json: [String: Any]) -> NearbyMessageContent? {
|
||||
if let id: String = json["id"] as? String {
|
||||
return NearbyMessageContent(id: id, type: type)
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
func toJson() -> [String : Any] {
|
||||
return ["type": type.name, "id": id]
|
||||
}
|
||||
|
||||
let id: String
|
||||
}
|
||||
|
||||
class NearbyMessageTextRequest : NearbyMessageContent {
|
||||
init(id: String, value: String) {
|
||||
self.value = value
|
||||
super.init(id: id, type: MessageContentType.textRequest)
|
||||
}
|
||||
|
||||
static func fromJson(json:[String: Any]) -> NearbyMessageTextRequest? {
|
||||
if let value: String = json["value"] as? String,
|
||||
let content = NearbyMessageContent.fromJsonRaw(type: MessageContentType.textRequest, json:json) {
|
||||
return NearbyMessageTextRequest(id: content.id, value: value)
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
override func toJson() -> [String : Any] {
|
||||
return ["value": value].merging( super.toJson()) { (current, _) in current}
|
||||
}
|
||||
|
||||
let value: String
|
||||
}
|
||||
|
||||
class NearbyMessageTextResponse :NearbyMessageContent {
|
||||
|
||||
init(id: String) {
|
||||
super.init(id: id, type: MessageContentType.textResponse)
|
||||
}
|
||||
|
||||
static func fromJson( json: [String: Any]) -> NearbyMessageTextResponse? {
|
||||
let content = NearbyMessageContent.fromJsonRaw(type: MessageContentType.textResponse, json: json)
|
||||
if let requireContent = content {
|
||||
return NearbyMessageTextResponse(id: requireContent.id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
class NearbyMessageFilesRequest : NearbyMessageContent {
|
||||
init(files: Array<String>, id: String) {
|
||||
self.files = files
|
||||
super.init(id: id, type: MessageContentType.filesRequest)
|
||||
}
|
||||
static func fromJson(json: [String: Any]) -> NearbyMessageFilesRequest? {
|
||||
let content = NearbyMessageContent.fromJsonRaw(type: MessageContentType.filesRequest, json: json)
|
||||
if let filesObjects: Array = json["files"] as? Array<Dictionary<String, AnyObject>> {
|
||||
let files : [String]? = filesObjects.map({ $0["path"] as? String }).compactMap({$0})
|
||||
if let requireContent = content {
|
||||
if let requireFiles = files {
|
||||
return NearbyMessageFilesRequest(files: requireFiles, id: requireContent.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
let files: Array<String>
|
||||
|
||||
override func toJson() -> [String : Any] {
|
||||
return [
|
||||
"files": files.map{["path": $0]},
|
||||
].merging( super.toJson()) { (current, _) in current}
|
||||
}
|
||||
}
|
||||
|
||||
class NearbyMessageFilesResponse : NearbyMessageContent {
|
||||
init(id: String, response: Bool) {
|
||||
self.response = response
|
||||
super.init(id: id, type: MessageContentType.filesResponse)
|
||||
}
|
||||
static func fromJson( json: [String: Any]) -> NearbyMessageFilesResponse? {
|
||||
let message = NearbyMessageContent.fromJsonRaw(type: MessageContentType.filesResponse, json: json)
|
||||
if let requireMessage = message,
|
||||
let response = json["isAccepted"] as? Bool {
|
||||
return NearbyMessageFilesResponse(id: requireMessage.id, response: response)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
override func toJson() -> [String : Any] {
|
||||
return [
|
||||
"isAccepted": response
|
||||
].merging( super.toJson()) { (current, _) in current}
|
||||
}
|
||||
|
||||
let response: Bool
|
||||
}
|
||||
|
||||
|
||||
enum MessageContentType {
|
||||
case textRequest
|
||||
case textResponse
|
||||
case filesRequest
|
||||
case filesResponse
|
||||
|
||||
static func fromString(value: String) -> MessageContentType {
|
||||
if (value == textRequest.name) {
|
||||
return textRequest
|
||||
} else if (value == textResponse.name) {
|
||||
return textResponse
|
||||
} else if (value == filesRequest.name) {
|
||||
return filesRequest
|
||||
} else if (value == filesResponse.name) {
|
||||
return filesResponse
|
||||
} else {
|
||||
return textRequest
|
||||
}
|
||||
}
|
||||
|
||||
var name : String {
|
||||
switch self {
|
||||
case .textRequest: return "textRequest"
|
||||
case .textResponse: return "textResponse"
|
||||
case .filesRequest: return "filesRequest"
|
||||
case .filesResponse: return "filesResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// NearbySession.swift
|
||||
// nearby_service
|
||||
//
|
||||
// Created by Kseniia Nikitina on 16/1/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MultipeerConnectivity
|
||||
|
||||
class NearbySession: NSObject {
|
||||
var session: MCSession!
|
||||
var state: MCSessionState = MCSessionState.notConnected
|
||||
|
||||
private init(peerID: MCPeerID) {
|
||||
self.session = MCSession(peer: peerID)
|
||||
}
|
||||
|
||||
static func create(peerID: MCPeerID) -> NearbySession {
|
||||
let instance = NearbySession(peerID: peerID)
|
||||
instance.session.delegate = instance
|
||||
return instance
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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: NearbyUserInfo.message(peerID: peerID, data: data)?.toDictionary()
|
||||
)
|
||||
}
|
||||
|
||||
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 }
|
||||
|
||||
var destinationURL = localURL.deletingLastPathComponent().appendingPathComponent("\(resourceName)")
|
||||
|
||||
if FileManager.default.fileExists(atPath: destinationURL.path) {
|
||||
destinationURL = localURL.deletingLastPathComponent().appendingPathComponent("New_\(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]
|
||||
}
|
||||
Reference in New Issue
Block a user