diff --git a/NearbyShare/Connection/InboundNearbyConnection.swift b/NearbyShare/Connection/InboundNearbyConnection.swift index db7e103..4f5daeb 100644 --- a/NearbyShare/Connection/InboundNearbyConnection.swift +++ b/NearbyShare/Connection/InboundNearbyConnection.swift @@ -527,7 +527,7 @@ class InboundNearbyConnection: NearbyConnection { func checkIfCanProceed(metadata: TransferMetadata) { - let acceptAutomatically = isAuthenticated || Settings.shared.automaticallyAcceptFiles + let acceptAutomatically = isAuthenticated || Settings.sharedInstance.automaticallyAcceptFiles DispatchQueue.main.async { if acceptAutomatically { diff --git a/NearbyShare/Connection/NearbyConnectionManager.swift b/NearbyShare/Connection/NearbyConnectionManager.swift index f85aadc..940cd24 100644 --- a/NearbyShare/Connection/NearbyConnectionManager.swift +++ b/NearbyShare/Connection/NearbyConnectionManager.swift @@ -62,7 +62,7 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb // MARK: Public Properties - public var endpointID: [UInt8] = generateEndpointID(forceRegeneration: false) + public var endpointID: [UInt8] = getEndpointID(forceRegeneration: false) public var connectionUpdateCallback: (Bool) -> Void = { _ in } { didSet { informAboutStatus() }} public var changedDeviceNameCallback: () -> Void = { } @@ -264,11 +264,11 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb } - private static func generateEndpointID(forceRegeneration: Bool) -> [UInt8] { + private static func getEndpointID(forceRegeneration: Bool) -> [UInt8] { // Try to retrieve from UserDefaults if !forceRegeneration, - let savedString = Settings.shared.endpointID, + let savedString = Settings.sharedInstance.endpointID, let savedData = savedString.data(using: .utf8) { log("[NearbyConnectionManager] Using cached endpoint ID: \(savedString)") return [UInt8](savedData) @@ -283,7 +283,7 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb // Save to UserDefaults as String let idString = String(bytes: id, encoding: .utf8) ?? "" - Settings.shared.endpointID = idString + Settings.sharedInstance.endpointID = idString log("[NearbyConnectionManager] Storing new endpoint ID: \(idString) ======================================") return id @@ -292,7 +292,7 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb private func initMDNS(forceIDRegeneration: Bool) { - self.endpointID = Self.generateEndpointID(forceRegeneration: forceIDRegeneration) + self.endpointID = Self.getEndpointID(forceRegeneration: forceIDRegeneration) let nameBytes: [UInt8] = [ 0x23, // PCP @@ -573,19 +573,18 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb let domain = Domain.instance(curve: .EC256r1) let (pubKey, privKey) = domain.makeKeyPair() + qrCodePrivateKey = privKey + var keyData = Data() keyData.append(contentsOf: [0, 0, 2]) - let keyBytes = Data(pubKey.w.x.asSignedBytes()) - - // Sometimes, for some keys, there will be a leading zero byte. Strip that, Android really hates it (it breaks the endpoint info) - keyData.append(keyBytes.suffixOfAtMost(numBytes: 32)) + keyData.append(Data(pubKey.w.x.asSignedBytes()).suffixOfAtMost(numBytes: 32)) let ikm = SymmetricKey(data: keyData) + qrCodeAdvertisingToken = HKDF.deriveKey(ikm: ikm, salt: Data(), info: "advertisingContext".data(using: .utf8)!, outputLength: 16).data() qrCodeNameEncryptionKey = HKDF.deriveKey(ikm: ikm, salt: Data(), info: "encryptionKey".data(using: .utf8)!, outputLength: 16) - do { let qrKey = keyData.urlSafeBase64EncodedString() let qrCodeImage = try QRCode.build @@ -622,6 +621,7 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb let conn = OutboundNearbyConnection(connection: nwconn, id: deviceID, urlsToSend: localUrls, textToSend: textToSend) conn.delegate = self conn.qrCodePrivateKey = qrCodePrivateKey + let transfer = OutgoingTransferInfo(service: info.service, device: info.device!, connection: conn, delegate: delegate) outgoingTransfers[deviceID] = transfer conn.start() @@ -739,7 +739,7 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb return securityScopeUrl } - if let bookmarkData = Settings.shared.saveFolderBookmark { + if let bookmarkData = Settings.sharedInstance.saveFolderBookmark { var isStale = false do { diff --git a/NearbyShare/Util/Helpers.swift b/NearbyShare/Util/Helpers.swift index f84701a..2dcc8b1 100644 --- a/NearbyShare/Util/Helpers.swift +++ b/NearbyShare/Util/Helpers.swift @@ -111,10 +111,10 @@ extension String { public func fullVersion() -> Bool { - return Settings.shared.gotPlus + return Settings.sharedInstance.gotPlus } public func isFileTransferRestricted() -> Bool { - (!fullVersion() && Settings.shared.incomingTransmissionCount > 1) + (!fullVersion() && Settings.sharedInstance.incomingTransmissionCount > 1) } diff --git a/QuickDrop/AppDelegate.swift b/QuickDrop/AppDelegate.swift index feca71d..94a2cfd 100644 --- a/QuickDrop/AppDelegate.swift +++ b/QuickDrop/AppDelegate.swift @@ -77,13 +77,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele iapManager = IAPManager.sharedInstance // app did not lauch before - if !Settings.shared.appLaunchedBefore { + if !Settings.sharedInstance.appLaunchedBefore { log("[AppDelegate] Opening Welcome Screen") // open welcome screen openIntroductionWindow() // user installed the app after the IAP was implemented, set the user as eligible for IAP - Settings.shared.isEligibleForIap = true + Settings.sharedInstance.isEligibleForIap = true } else { // app launched before @@ -91,9 +91,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele log("[AppDelegate] Downloaded from GitHub.") #else // user installed the app before the IAP was implemented, grant the plus version - if !Settings.shared.isEligibleForIap { + if !Settings.sharedInstance.isEligibleForIap { log("[AppDelegate] Granting QuickDrop+ for old user") - Settings.shared.gotPlus = true + Settings.sharedInstance.gotPlus = true UserDefaults.standard.set(true, forKey: Settings.UserDefaultsKeys.plusVersionLegacy.rawValue) } else { if !fullVersion() { @@ -165,7 +165,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele @objc func openApplicableScreen() { - if Settings.shared.appLaunchedBefore { + if Settings.sharedInstance.appLaunchedBefore { openMainWindow() } else { @@ -200,7 +200,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele NSWorkspace.shared.activateFileViewerSelecting([url]) } case "removeData": - Settings.shared.deleteAllUserDefaults() + Settings.sharedInstance.deleteAllUserDefaults() default: break } @@ -289,7 +289,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele } let introductionView = IntroductionView(startReceiving: startReceiving) { - Settings.shared.appLaunchedBefore = true + Settings.sharedInstance.appLaunchedBefore = true self.introductionWindow?.close() self.introductionWindow = nil self.openMainWindow() diff --git a/QuickDrop/Settings.swift b/QuickDrop/Settings.swift index eb0ed87..132faa2 100644 --- a/QuickDrop/Settings.swift +++ b/QuickDrop/Settings.swift @@ -5,13 +5,14 @@ // Created by Leon Böttger on 09.09.25. // +import LUI import SwiftUI -class Settings: ObservableObject { +final class Settings: SharedInstance { private init() {} - static var shared = Settings() + static var sharedInstance = Settings() // MARK: - Published UserDefaults Backed Properties @@ -65,14 +66,14 @@ class Settings: ObservableObject { } defaults.synchronize() - Settings.shared = Settings() + Settings.sharedInstance = Settings() } // MARK: - UserDefaults Keys - public enum UserDefaultsKeys: String, CaseIterable { + public enum UserDefaultsKeys: String, CaseIterable, RawRepresentable { case isEligibleForIap = "isEligibleForIap" case appLaunchedBefore = "ShowedWelcomeScreen" case plusVersion = "plusVersion" diff --git a/QuickDrop/Util/ErrorAlertHandler.swift b/QuickDrop/Util/ErrorAlertHandler.swift index 7124c34..991102f 100644 --- a/QuickDrop/Util/ErrorAlertHandler.swift +++ b/QuickDrop/Util/ErrorAlertHandler.swift @@ -83,7 +83,7 @@ class ErrorAlertHandler { let title = String(format: "TransferError".localized(), arguments: [deviceName]) log("Showing alert with title: \"\(title)\" and description: \"\(description)\"") - log("Unsuccessful transmission. Already successful transmissions: \(Settings.shared.incomingTransmissionCount)") + log("Unsuccessful transmission. Already successful transmissions: \(Settings.sharedInstance.incomingTransmissionCount)") #if os(macOS) diff --git a/QuickDrop/View/QuickDropToastView.swift b/QuickDrop/View/QuickDropToastView.swift index 5da3274..dd5e797 100644 --- a/QuickDrop/View/QuickDropToastView.swift +++ b/QuickDrop/View/QuickDropToastView.swift @@ -12,7 +12,7 @@ import SwiftUI let toastViewSize = CGSize(width: 400, height: 120) struct QuickDropToastView: View { - @ObservedObject var settings = Settings.shared + @ObservedObject var settings = Settings.sharedInstance @ObservedObject var receiveModel: ReceiveModel @State var autoHider = DispatchWorkItem(block: {}) diff --git a/QuickDrop/View/SettingsView.swift b/QuickDrop/View/SettingsView.swift index 7712743..fd6b5f6 100644 --- a/QuickDrop/View/SettingsView.swift +++ b/QuickDrop/View/SettingsView.swift @@ -11,7 +11,7 @@ import LUI struct SettingsView: View { - @ObservedObject var settings = Settings.shared + @ObservedObject var settings = Settings.sharedInstance @ObservedObject var connectionManager = NearbyConnectionManager.shared @State private var isChangeDeviceNameAlertPresented = false @@ -116,8 +116,8 @@ struct SettingsView: View { let bookmarkData = try url.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil) - Settings.shared.saveFolderBookmark = bookmarkData - Settings.shared.openFinderAfterReceiving = true + Settings.sharedInstance.saveFolderBookmark = bookmarkData + Settings.sharedInstance.openFinderAfterReceiving = true } catch { log("Failed to save security-scoped bookmark: \(error)") diff --git a/QuickDrop/View/TutorialView.swift b/QuickDrop/View/TutorialView.swift index 057b92e..adf2544 100644 --- a/QuickDrop/View/TutorialView.swift +++ b/QuickDrop/View/TutorialView.swift @@ -15,7 +15,7 @@ struct TutorialView: View { let openPlus: () -> Void @State private var licenseWindow: NSWindow? - @ObservedObject var settings = Settings.shared + @ObservedObject var settings = Settings.sharedInstance var body: some View { @@ -55,7 +55,7 @@ struct TutorialView: View { #if DEBUG Button { - Settings.shared.deleteAllUserDefaults() + Settings.sharedInstance.deleteAllUserDefaults() } label: { Text("ResetAllSettings") .underline() diff --git a/QuickDrop/ViewModel/ReceiveModel.swift b/QuickDrop/ViewModel/ReceiveModel.swift index 6096a03..1d012c8 100644 --- a/QuickDrop/ViewModel/ReceiveModel.swift +++ b/QuickDrop/ViewModel/ReceiveModel.swift @@ -247,7 +247,7 @@ class ReceiveModel: ObservableObject, InboundAppDelegate { #endif - let currentCount = Settings.shared.incomingTransmissionCount + let currentCount = Settings.sharedInstance.incomingTransmissionCount #if os(macOS) @@ -296,13 +296,13 @@ class ReceiveModel: ObservableObject, InboundAppDelegate { } // Show received files if wanted - if Settings.shared.openFinderAfterReceiving { + if Settings.sharedInstance.openFinderAfterReceiving { self.toastActions?.openFilesAction() } } #endif - Settings.shared.incomingTransmissionCount = currentCount + 1 + Settings.sharedInstance.incomingTransmissionCount = currentCount + 1 log("[ReceiveModel] Successful transmission. Current count: \(currentCount)") } } diff --git a/QuickDropiOS/Configuration.swift b/QuickDropiOS/Configuration.swift index fec8497..e088961 100644 --- a/QuickDropiOS/Configuration.swift +++ b/QuickDropiOS/Configuration.swift @@ -16,7 +16,7 @@ let configuration = LUIConfiguration( setPlusVersion: { result in #if !GITHUB DispatchQueue.main.async { - Settings.shared.gotPlus = result + Settings.sharedInstance.gotPlus = result } #endif }, diff --git a/QuickDropiOS/InfoPlist.xcstrings b/QuickDropiOS/InfoPlist.xcstrings index 4b8f742..eb3245b 100644 --- a/QuickDropiOS/InfoPlist.xcstrings +++ b/QuickDropiOS/InfoPlist.xcstrings @@ -32,7 +32,7 @@ "en" : { "stringUnit" : { "state" : "new", - "value" : "" + "value" : "Copyright © 2025-2026 Leon Böttger" } } } diff --git a/QuickDropiOS/View/ContentView.swift b/QuickDropiOS/View/ContentView.swift index d4d66a2..0f64f42 100644 --- a/QuickDropiOS/View/ContentView.swift +++ b/QuickDropiOS/View/ContentView.swift @@ -12,7 +12,7 @@ import StoreKit struct ContentView: View { @ObservedObject var luiSettings = LUISettings.sharedInstance - @ObservedObject var settings = Settings.shared + @ObservedObject var settings = Settings.sharedInstance var body: some View { AppRootView(isPlus: $settings.gotPlus, phoneView: { @@ -56,7 +56,7 @@ struct DeviceListView: View { @Environment(\.scenePhase) var scenePhase @Environment(\.sheetActive) var sheetActive @ObservedObject var nearbyConnectionManager = NearbyConnectionManager.shared - @ObservedObject var settings = Settings.shared + @ObservedObject var settings = Settings.sharedInstance var body: some View { @@ -202,7 +202,7 @@ struct DeviceListView: View { #if !EXTENSION NearbyConnectionManager.shared.becomeVisible() - if Settings.shared.incomingTransmissionCount > 0 { + if Settings.sharedInstance.incomingTransmissionCount > 0 { runAfter(seconds: 0.3) { requestReviewOnce() }