mirror of
https://github.com/leonboe1/QuickDrop.git
synced 2026-11-11 02:51:11 +00:00
Refactoring
This commit is contained in:
1 parent
13716f41e2
commit
fb7dda8824
7 files changed
+244
-222
No files matched your search
@@ -0,0 +1,188 @@
|
||||
//
|
||||
// DataModel.swift
|
||||
// QuickDrop
|
||||
//
|
||||
// Created by Leon Böttger on 26.07.25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
|
||||
protocol InboundNearbyConnectionDelegate {
|
||||
func obtainUserConsent(for transfer: TransferMetadata, from device: RemoteDeviceInfo, connection: InboundNearbyConnection)
|
||||
func connectionWasTerminated(connection: InboundNearbyConnection, error: Error?)
|
||||
}
|
||||
|
||||
|
||||
protocol OutboundNearbyConnectionDelegate {
|
||||
func outboundConnectionWasEstablished(connection: OutboundNearbyConnection)
|
||||
func outboundConnection(connection: OutboundNearbyConnection, transferProgress: Double)
|
||||
func outboundConnectionTransferAccepted(connection: OutboundNearbyConnection)
|
||||
func outboundConnection(connection: OutboundNearbyConnection, failedWithError: Error)
|
||||
func outboundConnectionTransferFinished(connection: OutboundNearbyConnection)
|
||||
}
|
||||
|
||||
|
||||
public protocol MainAppDelegate {
|
||||
func obtainUserConsent(for transfer: TransferMetadata, from device: RemoteDeviceInfo)
|
||||
func incomingTransfer(id: String, didFinishWith error: Error?)
|
||||
func showFirewallAlert()
|
||||
func showCopiedToClipboardAlert()
|
||||
func showUnsupportedFileAlert(for: RemoteDeviceInfo?)
|
||||
}
|
||||
|
||||
|
||||
public protocol ShareExtensionDelegate: AnyObject {
|
||||
func addDevice(device: RemoteDeviceInfo)
|
||||
func removeDevice(id: String)
|
||||
func connectionWasEstablished(pinCode: String)
|
||||
func connectionFailed(with error: Error)
|
||||
func transferAccepted()
|
||||
func transferProgress(progress: Double)
|
||||
func transferFinished()
|
||||
}
|
||||
|
||||
|
||||
public struct RemoteDeviceInfo {
|
||||
public let name: String
|
||||
public let type: DeviceType
|
||||
public var id: String?
|
||||
|
||||
init(name: String, type: DeviceType, id: String? = nil) {
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.id = id
|
||||
}
|
||||
|
||||
init(info: EndpointInfo) {
|
||||
name = info.name
|
||||
type = info.deviceType
|
||||
}
|
||||
|
||||
|
||||
public enum DeviceType: Int32 {
|
||||
case unknown = 0
|
||||
case phone
|
||||
case tablet
|
||||
case computer
|
||||
|
||||
public static func fromRawValue(value: Int) -> DeviceType {
|
||||
switch value {
|
||||
case 0:
|
||||
return .unknown
|
||||
case 1:
|
||||
return .phone
|
||||
case 2:
|
||||
return .tablet
|
||||
case 3:
|
||||
return .computer
|
||||
default:
|
||||
return .unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct TransferMetadata {
|
||||
public let files: [FileMetadata]
|
||||
public let id: String
|
||||
public let pinCode: String?
|
||||
public let textDescription: String?
|
||||
public let type: TransferType
|
||||
|
||||
init(files: [FileMetadata], id: String, pinCode: String?, textDescription: String? = nil, transferType: TransferType = .file) {
|
||||
self.files = files
|
||||
self.id = id
|
||||
self.pinCode = pinCode
|
||||
self.textDescription = textDescription
|
||||
self.type = transferType
|
||||
}
|
||||
|
||||
public enum TransferType {
|
||||
case file
|
||||
case text
|
||||
case url
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct FileMetadata {
|
||||
public let name: String
|
||||
public let size: Int64
|
||||
public let mimeType: String
|
||||
}
|
||||
|
||||
|
||||
struct EndpointInfo {
|
||||
let name: String
|
||||
let deviceType: RemoteDeviceInfo.DeviceType
|
||||
|
||||
|
||||
init(name: String, deviceType: RemoteDeviceInfo.DeviceType) {
|
||||
self.name = name
|
||||
self.deviceType = deviceType
|
||||
}
|
||||
|
||||
|
||||
init?(data: Data) {
|
||||
guard data.count > 17 else { return nil }
|
||||
let deviceNameLength = Int(data[17])
|
||||
guard data.count >= deviceNameLength + 18 else { return nil }
|
||||
guard let deviceName = String(data: data[18 ..< (18 + deviceNameLength)], encoding: .utf8) else { return nil }
|
||||
let rawDeviceType = Int(data[0] & 7) >> 1
|
||||
name = deviceName
|
||||
deviceType = RemoteDeviceInfo.DeviceType.fromRawValue(value: rawDeviceType)
|
||||
}
|
||||
|
||||
|
||||
func serialize() -> Data {
|
||||
// 1 byte: Version(3 bits)|Visibility(1 bit)|Device Type(3 bits)|Reserved(1 bits)
|
||||
// Device types: unknown=0, phone=1, tablet=2, laptop=3
|
||||
var endpointInfo: [UInt8] = [UInt8(deviceType.rawValue << 1)]
|
||||
// 16 bytes: unknown random bytes
|
||||
for _ in 0 ... 15 {
|
||||
endpointInfo.append(UInt8.random(in: 0 ... 255))
|
||||
}
|
||||
// Device name in UTF-8 prefixed with 1-byte length
|
||||
var nameChars = [UInt8](name.utf8)
|
||||
if nameChars.count > 255 {
|
||||
nameChars = [UInt8](nameChars[0 ..< 255])
|
||||
}
|
||||
endpointInfo.append(UInt8(nameChars.count))
|
||||
for ch in nameChars {
|
||||
endpointInfo.append(UInt8(ch))
|
||||
}
|
||||
return Data(endpointInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum NearbyError: Error {
|
||||
case protocolError(_ message: String)
|
||||
case requiredFieldMissing(_ message: String)
|
||||
case ukey2
|
||||
case packetFilterError
|
||||
case inputOutput
|
||||
case canceled(reason: CancellationReason)
|
||||
|
||||
public enum CancellationReason {
|
||||
case userRejected, userCanceled, notEnoughSpace, unsupportedType, timedOut
|
||||
|
||||
public func localizedDescription() -> String {
|
||||
switch self {
|
||||
case .userRejected:
|
||||
"TransferDeclined".localized()
|
||||
case .userCanceled:
|
||||
"TransferCanceled".localized()
|
||||
case .notEnoughSpace:
|
||||
"NotEnoughSpace".localized()
|
||||
case .unsupportedType:
|
||||
"UnsupportedType".localized()
|
||||
case .timedOut:
|
||||
"TransferTimedOut".localized()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-12
@@ -73,6 +73,18 @@ extension String {
|
||||
}
|
||||
|
||||
|
||||
public enum UserDefaultsKeys: String, CaseIterable {
|
||||
case isEligibleForIap = "isEligibleForIap"
|
||||
case appLaunchedBefore = "ShowedWelcomeScreen"
|
||||
case plusVersion = "isPlusVersion"
|
||||
case transmissionCount = "reviewRequestCountKey"
|
||||
case automaticallyAcceptFiles = "automaticallyAcceptFiles"
|
||||
case saveFolderBookmark = "saveFolderBookmark"
|
||||
case openFinderAfterReceiving = "openFinderAfterReceiving"
|
||||
case endpointID = "endpointID"
|
||||
}
|
||||
|
||||
|
||||
public func isPlusVersion() -> Bool {
|
||||
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.plusVersion.rawValue)
|
||||
}
|
||||
@@ -86,15 +98,3 @@ public func isFileTransferRestricted() -> Bool {
|
||||
public func transmissionCount() -> Int {
|
||||
return UserDefaults.standard.integer(forKey: UserDefaultsKeys.transmissionCount.rawValue)
|
||||
}
|
||||
|
||||
|
||||
public enum UserDefaultsKeys: String, CaseIterable {
|
||||
case isEligibleForIap = "isEligibleForIap"
|
||||
case appLaunchedBefore = "ShowedWelcomeScreen"
|
||||
case plusVersion = "isPlusVersion"
|
||||
case transmissionCount = "reviewRequestCountKey"
|
||||
case automaticallyAcceptFiles = "automaticallyAcceptFiles"
|
||||
case saveFolderBookmark = "saveFolderBookmark"
|
||||
case openFinderAfterReceiving = "openFinderAfterReceiving"
|
||||
case endpointID = "endpointID"
|
||||
}
|
||||
@@ -499,9 +499,3 @@ class InboundNearbyConnection: NearbyConnection {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protocol InboundNearbyConnectionDelegate {
|
||||
func obtainUserConsent(for transfer: TransferMetadata, from device: RemoteDeviceInfo, connection: InboundNearbyConnection)
|
||||
func connectionWasTerminated(connection: InboundNearbyConnection, error: Error?)
|
||||
}
|
||||
@@ -593,14 +593,17 @@ class NearbyConnection {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct InternalFileInfo {
|
||||
let meta: FileMetadata
|
||||
let payloadID: Int64
|
||||
let destinationURL: URL
|
||||
var bytesTransferred: Int64 = 0
|
||||
var fileHandle: FileHandle?
|
||||
var progress: Progress?
|
||||
var created: Bool = false
|
||||
|
||||
|
||||
// -- MARK: - Internal Data Model
|
||||
|
||||
struct InternalFileInfo {
|
||||
let meta: FileMetadata
|
||||
let payloadID: Int64
|
||||
let destinationURL: URL
|
||||
var bytesTransferred: Int64 = 0
|
||||
var fileHandle: FileHandle?
|
||||
var progress: Progress?
|
||||
var created: Bool = false
|
||||
}
|
||||
}
|
||||
@@ -9,182 +9,6 @@ import Foundation
|
||||
import Network
|
||||
import System
|
||||
|
||||
public struct RemoteDeviceInfo {
|
||||
public let name: String
|
||||
public let type: DeviceType
|
||||
public var id: String?
|
||||
|
||||
init(name: String, type: DeviceType, id: String? = nil) {
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.id = id
|
||||
}
|
||||
|
||||
init(info: EndpointInfo) {
|
||||
name = info.name
|
||||
type = info.deviceType
|
||||
}
|
||||
|
||||
|
||||
public enum DeviceType: Int32 {
|
||||
case unknown = 0
|
||||
case phone
|
||||
case tablet
|
||||
case computer
|
||||
|
||||
public static func fromRawValue(value: Int) -> DeviceType {
|
||||
switch value {
|
||||
case 0:
|
||||
return .unknown
|
||||
case 1:
|
||||
return .phone
|
||||
case 2:
|
||||
return .tablet
|
||||
case 3:
|
||||
return .computer
|
||||
default:
|
||||
return .unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum NearbyError: Error {
|
||||
case protocolError(_ message: String)
|
||||
case requiredFieldMissing(_ message: String)
|
||||
case ukey2
|
||||
case packetFilterError
|
||||
case inputOutput
|
||||
case canceled(reason: CancellationReason)
|
||||
|
||||
public enum CancellationReason {
|
||||
case userRejected, userCanceled, notEnoughSpace, unsupportedType, timedOut
|
||||
|
||||
public func localizedDescription() -> String {
|
||||
switch self {
|
||||
case .userRejected:
|
||||
"TransferDeclined".localized()
|
||||
case .userCanceled:
|
||||
"TransferCanceled".localized()
|
||||
case .notEnoughSpace:
|
||||
"NotEnoughSpace".localized()
|
||||
case .unsupportedType:
|
||||
"UnsupportedType".localized()
|
||||
case .timedOut:
|
||||
"TransferTimedOut".localized()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct TransferMetadata {
|
||||
public let files: [FileMetadata]
|
||||
public let id: String
|
||||
public let pinCode: String?
|
||||
public let textDescription: String?
|
||||
public let type: TransferType
|
||||
|
||||
init(files: [FileMetadata], id: String, pinCode: String?, textDescription: String? = nil, transferType: TransferType = .file) {
|
||||
self.files = files
|
||||
self.id = id
|
||||
self.pinCode = pinCode
|
||||
self.textDescription = textDescription
|
||||
self.type = transferType
|
||||
}
|
||||
|
||||
public enum TransferType {
|
||||
case file
|
||||
case text
|
||||
case url
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct FileMetadata {
|
||||
public let name: String
|
||||
public let size: Int64
|
||||
public let mimeType: String
|
||||
}
|
||||
|
||||
|
||||
struct FoundServiceInfo {
|
||||
let service: NWBrowser.Result
|
||||
var device: RemoteDeviceInfo?
|
||||
}
|
||||
|
||||
|
||||
struct OutgoingTransferInfo {
|
||||
let service: NWBrowser.Result
|
||||
let device: RemoteDeviceInfo
|
||||
let connection: OutboundNearbyConnection
|
||||
let delegate: ShareExtensionDelegate
|
||||
}
|
||||
|
||||
|
||||
struct EndpointInfo {
|
||||
let name: String
|
||||
let deviceType: RemoteDeviceInfo.DeviceType
|
||||
|
||||
|
||||
init(name: String, deviceType: RemoteDeviceInfo.DeviceType) {
|
||||
self.name = name
|
||||
self.deviceType = deviceType
|
||||
}
|
||||
|
||||
|
||||
init?(data: Data) {
|
||||
guard data.count > 17 else { return nil }
|
||||
let deviceNameLength = Int(data[17])
|
||||
guard data.count >= deviceNameLength + 18 else { return nil }
|
||||
guard let deviceName = String(data: data[18 ..< (18 + deviceNameLength)], encoding: .utf8) else { return nil }
|
||||
let rawDeviceType = Int(data[0] & 7) >> 1
|
||||
name = deviceName
|
||||
deviceType = RemoteDeviceInfo.DeviceType.fromRawValue(value: rawDeviceType)
|
||||
}
|
||||
|
||||
|
||||
func serialize() -> Data {
|
||||
// 1 byte: Version(3 bits)|Visibility(1 bit)|Device Type(3 bits)|Reserved(1 bits)
|
||||
// Device types: unknown=0, phone=1, tablet=2, laptop=3
|
||||
var endpointInfo: [UInt8] = [UInt8(deviceType.rawValue << 1)]
|
||||
// 16 bytes: unknown random bytes
|
||||
for _ in 0 ... 15 {
|
||||
endpointInfo.append(UInt8.random(in: 0 ... 255))
|
||||
}
|
||||
// Device name in UTF-8 prefixed with 1-byte length
|
||||
var nameChars = [UInt8](name.utf8)
|
||||
if nameChars.count > 255 {
|
||||
nameChars = [UInt8](nameChars[0 ..< 255])
|
||||
}
|
||||
endpointInfo.append(UInt8(nameChars.count))
|
||||
for ch in nameChars {
|
||||
endpointInfo.append(UInt8(ch))
|
||||
}
|
||||
return Data(endpointInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public protocol MainAppDelegate {
|
||||
func obtainUserConsent(for transfer: TransferMetadata, from device: RemoteDeviceInfo)
|
||||
func incomingTransfer(id: String, didFinishWith error: Error?)
|
||||
func showFirewallAlert()
|
||||
func showCopiedToClipboardAlert()
|
||||
func showUnsupportedFileAlert(for: RemoteDeviceInfo?)
|
||||
}
|
||||
|
||||
public protocol ShareExtensionDelegate: AnyObject {
|
||||
func addDevice(device: RemoteDeviceInfo)
|
||||
func removeDevice(id: String)
|
||||
func connectionWasEstablished(pinCode: String)
|
||||
func connectionFailed(with error: Error)
|
||||
func transferAccepted()
|
||||
func transferProgress(progress: Double)
|
||||
func transferFinished()
|
||||
}
|
||||
|
||||
|
||||
public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearbyConnectionDelegate, OutboundNearbyConnectionDelegate {
|
||||
|
||||
@@ -482,4 +306,20 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
public func getActiveConnectionsCount() -> Int {
|
||||
return activeConnections.count
|
||||
}
|
||||
|
||||
|
||||
// -- MARK: - Internal Data Model
|
||||
|
||||
private struct FoundServiceInfo {
|
||||
let service: NWBrowser.Result
|
||||
var device: RemoteDeviceInfo?
|
||||
}
|
||||
|
||||
|
||||
private struct OutgoingTransferInfo {
|
||||
let service: NWBrowser.Result
|
||||
let device: RemoteDeviceInfo
|
||||
let connection: OutboundNearbyConnection
|
||||
let delegate: ShareExtensionDelegate
|
||||
}
|
||||
}
|
||||
@@ -470,22 +470,15 @@ class OutboundNearbyConnection: NearbyConnection {
|
||||
private static func sanitizeFileName(name: String) -> String {
|
||||
return name.replacingOccurrences(of: "[\\/\\\\?%\\*:\\|\"<>=]", with: "_", options: .regularExpression)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private struct OutgoingFileTransfer {
|
||||
let url: URL
|
||||
let payloadID: Int64
|
||||
let handle: FileHandle?
|
||||
let totalBytes: Int64
|
||||
var currentOffset: Int64
|
||||
}
|
||||
|
||||
|
||||
protocol OutboundNearbyConnectionDelegate {
|
||||
func outboundConnectionWasEstablished(connection: OutboundNearbyConnection)
|
||||
func outboundConnection(connection: OutboundNearbyConnection, transferProgress: Double)
|
||||
func outboundConnectionTransferAccepted(connection: OutboundNearbyConnection)
|
||||
func outboundConnection(connection: OutboundNearbyConnection, failedWithError: Error)
|
||||
func outboundConnectionTransferFinished(connection: OutboundNearbyConnection)
|
||||
|
||||
|
||||
// -- MARK: - Internal Data Model
|
||||
|
||||
private struct OutgoingFileTransfer {
|
||||
let url: URL
|
||||
let payloadID: Int64
|
||||
let handle: FileHandle?
|
||||
let totalBytes: Int64
|
||||
var currentOffset: Int64
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@
|
||||
EB5C36E42E34EC0E0052EFD7 /* License.txt in Resources */ = {isa = PBXBuildFile; fileRef = EB5C36E32E34EC0E0052EFD7 /* License.txt */; };
|
||||
EB5C36E52E34EC0E0052EFD7 /* License.txt in Resources */ = {isa = PBXBuildFile; fileRef = EB5C36E32E34EC0E0052EFD7 /* License.txt */; };
|
||||
EB5C36E72E3544FF0052EFD7 /* SaveFilesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB5C36E62E3544FB0052EFD7 /* SaveFilesManager.swift */; };
|
||||
EB5C36E92E3546120052EFD7 /* DataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB5C36E82E3546100052EFD7 /* DataModel.swift */; };
|
||||
EB6E743E2DD5C77700331466 /* ConnectionFailureTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB6E743D2DD5C76F00331466 /* ConnectionFailureTracker.swift */; };
|
||||
EB6F62D72D91F4AF00534EC2 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB6F62D62D91F4AC00534EC2 /* SettingsView.swift */; };
|
||||
EB7CD1452D6FB0C300CB0CB5 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB7CD1442D6FB0C100CB0CB5 /* Logger.swift */; };
|
||||
@@ -185,6 +186,7 @@
|
||||
EB5C36952E34EBBD0052EFD7 /* mul */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = mul; path = mul.lproj/ShareViewController.xcstrings; sourceTree = "<group>"; };
|
||||
EB5C36E32E34EC0E0052EFD7 /* License.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = License.txt; sourceTree = "<group>"; };
|
||||
EB5C36E62E3544FB0052EFD7 /* SaveFilesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveFilesManager.swift; sourceTree = "<group>"; };
|
||||
EB5C36E82E3546100052EFD7 /* DataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataModel.swift; sourceTree = "<group>"; };
|
||||
EB6E743D2DD5C76F00331466 /* ConnectionFailureTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionFailureTracker.swift; sourceTree = "<group>"; };
|
||||
EB6F62D62D91F4AC00534EC2 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
EB7CD1442D6FB0C100CB0CB5 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
||||
@@ -322,6 +324,7 @@
|
||||
69DA9A2729E18CB500A442DA /* Protobuf */,
|
||||
69DA9A1E29E0C0B300A442DA /* NearbyConnectionManager.swift */,
|
||||
69DA9A2229E17F0400A442DA /* InboundNearbyConnection.swift */,
|
||||
EB5C36E82E3546100052EFD7 /* DataModel.swift */,
|
||||
691F53BD2ABF03820089FD92 /* OutboundNearbyConnection.swift */,
|
||||
EB5C36E62E3544FB0052EFD7 /* SaveFilesManager.swift */,
|
||||
EB2F9BFA2DA04B67004010A2 /* Helpers.swift */,
|
||||
@@ -640,6 +643,7 @@
|
||||
69DCF4902AB70E9700CBE2CC /* InboundNearbyConnection.swift in Sources */,
|
||||
69DCF48B2AB70E8C00CBE2CC /* ukey.pb.swift in Sources */,
|
||||
EB2B41E42DC63ACC0070FC39 /* SleepManager.swift in Sources */,
|
||||
EB5C36E92E3546120052EFD7 /* DataModel.swift in Sources */,
|
||||
EB5C36E72E3544FF0052EFD7 /* SaveFilesManager.swift in Sources */,
|
||||
69DCF48A2AB70E8C00CBE2CC /* wire_format.pb.swift in Sources */,
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user