From 8127628ea5ac490b14065914f84af2acb79deaee Mon Sep 17 00:00:00 2001 From: Milan Stute Date: Mon, 8 Mar 2021 14:42:59 +0100 Subject: [PATCH] Add swift-format as an Xcode build phase --- OpenHaystack/.swift-format | 7 + .../OpenHaystack.xcodeproj/project.pbxproj | 19 ++ .../OpenHaystack/AnisetteDataManager.swift | 94 +++++----- .../OpenHaystack/FindMy/DecryptReports.swift | 18 +- .../FindMy/FindMyController.swift | 28 +-- .../FindMy/FindMyKeyDecoder.swift | 31 ++-- OpenHaystack/OpenHaystack/FindMy/Models.swift | 20 +-- .../HaystackApp/AccessoryController.swift | 4 +- .../HaystackApp/KeychainController.swift | 15 +- .../Mail Plugin/MailPluginManager.swift | 19 +- .../HaystackApp/MicrobitController.swift | 9 +- .../HaystackApp/Model/Accessory.swift | 42 +++-- .../HaystackApp/Model/PreviewData.swift | 10 +- .../Views/AccessoryListEntry.swift | 64 ++++--- .../Views/AccessoryMapAnnotation.swift | 68 +++---- .../HaystackApp/Views/AccessoryMapView.swift | 2 +- .../HaystackApp/Views/ActivityIndicator.swift | 2 +- .../HaystackApp/Views/IconSelectionView.swift | 58 +++--- .../Views/OpenHaystackMainView.swift | 168 ++++++++++-------- .../HaystackApp/Views/PopUpAlertView.swift | 5 +- .../OpenHaystack/MapViewController.swift | 10 +- OpenHaystack/OpenHaystack/SavePanel.swift | 4 +- .../OpenHaystackTests/OpenHaystackTests.swift | 9 +- 23 files changed, 397 insertions(+), 309 deletions(-) create mode 100644 OpenHaystack/.swift-format diff --git a/OpenHaystack/.swift-format b/OpenHaystack/.swift-format new file mode 100644 index 0000000..7126af6 --- /dev/null +++ b/OpenHaystack/.swift-format @@ -0,0 +1,7 @@ +{ + "version": 1, + "lineLength": 180, + "indentation": { + "spaces": 4 + } +} \ No newline at end of file diff --git a/OpenHaystack/OpenHaystack.xcodeproj/project.pbxproj b/OpenHaystack/OpenHaystack.xcodeproj/project.pbxproj index 4ca4eba..2101302 100644 --- a/OpenHaystack/OpenHaystack.xcodeproj/project.pbxproj +++ b/OpenHaystack/OpenHaystack.xcodeproj/project.pbxproj @@ -343,6 +343,7 @@ 781EB3F625DAD7EA00FEAA19 /* Frameworks */, 781EB3FC25DAD7EA00FEAA19 /* Resources */, 78EC227325DBC9240042B775 /* SwiftLint */, + F125DE4525F65E0700135D32 /* Run swift-format */, 78286DC225E5669100F65511 /* Embed PlugIns */, F14B2C7E25EFBB11002DC056 /* Set Version Number from Git */, ); @@ -493,6 +494,24 @@ shellPath = /bin/sh; shellScript = "if which swiftlint >/dev/null; then\n swiftlint autocorrect && swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; }; + F125DE4525F65E0700135D32 /* Run swift-format */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Run swift-format"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swift-format >/dev/null; then\n swift-format format -r -i \"$SRCROOT\" && swift-format lint -r \"$SRCROOT\"\nelse\n echo \"warning: swift-format not installed, download from https://github.com/apple/swift-format\"\nfi\n"; + }; F14B2C7E25EFBB11002DC056 /* Set Version Number from Git */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/OpenHaystack/OpenHaystack/AnisetteDataManager.swift b/OpenHaystack/OpenHaystack/AnisetteDataManager.swift index db54f02..a1c016a 100644 --- a/OpenHaystack/OpenHaystack/AnisetteDataManager.swift +++ b/OpenHaystack/OpenHaystack/AnisetteDataManager.swift @@ -8,21 +8,22 @@ import Foundation import OSLog -/// Uses the AltStore Mail plugin to access recent anisette data +/// Uses the AltStore Mail plugin to access recent anisette data. public class AnisetteDataManager: NSObject { @objc static let shared = AnisetteDataManager() private var anisetteDataCompletionHandlers: [String: (Result) -> Void] = [:] private var anisetteDataTimers: [String: Timer] = [:] private override init() { - super.init() + super.init() - dlopen("/System/Library/PrivateFrameworks/AuthKit.framework/AuthKit", RTLD_NOW) + dlopen("/System/Library/PrivateFrameworks/AuthKit.framework/AuthKit", RTLD_NOW) - DistributedNotificationCenter.default() - .addObserver(self, selector: #selector(AnisetteDataManager.handleAppleDataResponse(_:)), - name: Notification.Name("de.tu-darmstadt.seemoo.OpenHaystack.AnisetteDataResponse"), object: nil) - } + DistributedNotificationCenter.default() + .addObserver( + self, selector: #selector(AnisetteDataManager.handleAppleDataResponse(_:)), + name: Notification.Name("de.tu-darmstadt.seemoo.OpenHaystack.AnisetteDataResponse"), object: nil) + } func requestAnisetteData(_ completion: @escaping (Result) -> Void) { if let accountData = self.requestAnisetteDataAuthKit() { @@ -31,19 +32,20 @@ public class AnisetteDataManager: NSObject { return } - let requestUUID = UUID().uuidString - self.anisetteDataCompletionHandlers[requestUUID] = completion + let requestUUID = UUID().uuidString + self.anisetteDataCompletionHandlers[requestUUID] = completion - let timer = Timer(timeInterval: 1.0, repeats: false) { (_) in - self.finishRequest(forUUID: requestUUID, result: .failure(AnisetteDataError.pluginNotFound)) - } - self.anisetteDataTimers[requestUUID] = timer + let timer = Timer(timeInterval: 1.0, repeats: false) { (_) in + self.finishRequest(forUUID: requestUUID, result: .failure(AnisetteDataError.pluginNotFound)) + } + self.anisetteDataTimers[requestUUID] = timer - RunLoop.main.add(timer, forMode: .default) + RunLoop.main.add(timer, forMode: .default) - DistributedNotificationCenter.default() - .postNotificationName(Notification.Name("de.tu-darmstadt.seemoo.OpenHaystack.FetchAnisetteData"), - object: nil, userInfo: ["requestUUID": requestUUID], options: .deliverImmediately) + DistributedNotificationCenter.default() + .postNotificationName( + Notification.Name("de.tu-darmstadt.seemoo.OpenHaystack.FetchAnisetteData"), + object: nil, userInfo: ["requestUUID": requestUUID], options: .deliverImmediately) } func requestAnisetteDataAuthKit() -> AppleAccountData? { @@ -52,27 +54,28 @@ public class AnisetteDataManager: NSObject { let dateFormatter = ISO8601DateFormatter() guard let machineID = anisetteData["X-Apple-I-MD-M"] as? String, - let otp = anisetteData["X-Apple-I-MD"] as? String, - let localUserId = anisetteData["X-Apple-I-MD-LU"] as? String, - let dateString = anisetteData["X-Apple-I-Client-Time"] as? String, - let date = dateFormatter.date(from: dateString), - let deviceClass = NSClassFromString("AKDevice") + let otp = anisetteData["X-Apple-I-MD"] as? String, + let localUserId = anisetteData["X-Apple-I-MD-LU"] as? String, + let dateString = anisetteData["X-Apple-I-Client-Time"] as? String, + let date = dateFormatter.date(from: dateString), + let deviceClass = NSClassFromString("AKDevice") else { return nil } let device: AKDevice = deviceClass.current() let routingInfo = (anisetteData["X-Apple-I-MD-RINFO"] as? NSNumber)?.uint64Value ?? 0 - let accountData = AppleAccountData(machineID: machineID, - oneTimePassword: otp, - localUserID: localUserId, - routingInfo: routingInfo, - deviceUniqueIdentifier: device.uniqueDeviceIdentifier(), - deviceSerialNumber: device.serialNumber(), - deviceDescription: device.serverFriendlyDescription(), - date: date, - locale: Locale.current, - timeZone: TimeZone.current) + let accountData = AppleAccountData( + machineID: machineID, + oneTimePassword: otp, + localUserID: localUserId, + routingInfo: routingInfo, + deviceUniqueIdentifier: device.uniqueDeviceIdentifier(), + deviceSerialNumber: device.serialNumber(), + deviceDescription: device.serverFriendlyDescription(), + date: date, + locale: Locale.current, + timeZone: TimeZone.current) if let spToken = ReportsFetcher().fetchSearchpartyToken() { accountData.searchPartyToken = spToken @@ -88,25 +91,25 @@ public class AnisetteDataManager: NSObject { completion(nil) case .success(let data): // Return only the headers - completion([ - "X-Apple-I-MD-M": data.machineID, - "X-Apple-I-MD": data.oneTimePassword, - "X-Apple-I-TimeZone": String(data.timeZone.abbreviation() ?? "UTC"), - "X-Apple-I-Client-Time": ISO8601DateFormatter().string(from: data.date), - "X-Apple-I-MD-RINFO": String(data.routingInfo) + completion( + [ + "X-Apple-I-MD-M": data.machineID, + "X-Apple-I-MD": data.oneTimePassword, + "X-Apple-I-TimeZone": String(data.timeZone.abbreviation() ?? "UTC"), + "X-Apple-I-Client-Time": ISO8601DateFormatter().string(from: data.date), + "X-Apple-I-MD-RINFO": String(data.routingInfo), ] as [AnyHashable: Any]) } } } } -private extension AnisetteDataManager { +extension AnisetteDataManager { - @objc func handleAppleDataResponse(_ notification: Notification) { + @objc fileprivate func handleAppleDataResponse(_ notification: Notification) { guard let userInfo = notification.userInfo, let requestUUID = userInfo["requestUUID"] as? String else { return } - if - let archivedAnisetteData = userInfo["anisetteData"] as? Data, + if let archivedAnisetteData = userInfo["anisetteData"] as? Data, let appleAccountData = try? NSKeyedUnarchiver.unarchivedObject(ofClass: AppleAccountData.self, from: archivedAnisetteData) { if let range = appleAccountData.deviceDescription.lowercased().range(of: "(com.apple.mail") { @@ -122,11 +125,10 @@ private extension AnisetteDataManager { } } - @objc func handleAnisetteDataResponse(_ notification: Notification) { + @objc fileprivate func handleAnisetteDataResponse(_ notification: Notification) { guard let userInfo = notification.userInfo, let requestUUID = userInfo["requestUUID"] as? String else { return } - if - let archivedAnisetteData = userInfo["anisetteData"] as? Data, + if let archivedAnisetteData = userInfo["anisetteData"] as? Data, let anisetteData = try? NSKeyedUnarchiver.unarchivedObject(ofClass: ALTAnisetteData.self, from: archivedAnisetteData) { if let range = anisetteData.deviceDescription.lowercased().range(of: "(com.apple.mail") { @@ -143,7 +145,7 @@ private extension AnisetteDataManager { } } - func finishRequest(forUUID requestUUID: String, result: Result) { + fileprivate func finishRequest(forUUID requestUUID: String, result: Result) { let completionHandler = self.anisetteDataCompletionHandlers[requestUUID] self.anisetteDataCompletionHandlers[requestUUID] = nil diff --git a/OpenHaystack/OpenHaystack/FindMy/DecryptReports.swift b/OpenHaystack/OpenHaystack/FindMy/DecryptReports.swift index d9d0248..3c811c1 100755 --- a/OpenHaystack/OpenHaystack/FindMy/DecryptReports.swift +++ b/OpenHaystack/OpenHaystack/FindMy/DecryptReports.swift @@ -5,12 +5,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import Foundation import CryptoKit +import Foundation struct DecryptReports { - /// Decrypt a find my report with the according key + /// Decrypt a find my report with the according key. + /// /// - Parameters: /// - report: An encrypted FindMy Report /// - key: A FindMyKey @@ -40,7 +41,8 @@ struct DecryptReports { return locationReport } - /// Decrypt the payload + /// Decrypt the payload. + /// /// - Parameters: /// - payload: Encrypted payload part /// - symmetricKey: Symmetric key @@ -63,18 +65,18 @@ struct DecryptReports { static func decode(content: Data, report: FindMyReport) -> FindMyLocationReport { var longitude: Int32 = 0 - _ = withUnsafeMutableBytes(of: &longitude, {content.subdata(in: 4..<8).copyBytes(to: $0)}) + _ = withUnsafeMutableBytes(of: &longitude, { content.subdata(in: 4..<8).copyBytes(to: $0) }) longitude = Int32(bigEndian: longitude) var latitude: Int32 = 0 - _ = withUnsafeMutableBytes(of: &latitude, {content.subdata(in: 0..<4).copyBytes(to: $0)}) + _ = withUnsafeMutableBytes(of: &latitude, { content.subdata(in: 0..<4).copyBytes(to: $0) }) latitude = Int32(bigEndian: latitude) var accuracy: UInt8 = 0 - _ = withUnsafeMutableBytes(of: &accuracy, {content.subdata(in: 8..<9).copyBytes(to: $0)}) + _ = withUnsafeMutableBytes(of: &accuracy, { content.subdata(in: 8..<9).copyBytes(to: $0) }) - let latitudeDec = Double(latitude)/10000000.0 - let longitudeDec = Double(longitude)/10000000.0 + let latitudeDec = Double(latitude) / 10000000.0 + let longitudeDec = Double(longitude) / 10000000.0 return FindMyLocationReport(lat: latitudeDec, lng: longitudeDec, acc: accuracy, dP: report.datePublished, t: report.timestamp, c: report.confidence) } diff --git a/OpenHaystack/OpenHaystack/FindMy/FindMyController.swift b/OpenHaystack/OpenHaystack/FindMy/FindMyController.swift index 7559609..7fd03bb 100755 --- a/OpenHaystack/OpenHaystack/FindMy/FindMyController.swift +++ b/OpenHaystack/OpenHaystack/FindMy/FindMyController.swift @@ -5,9 +5,9 @@ // // SPDX-License-Identifier: AGPL-3.0-only +import Combine import Foundation import SwiftUI -import Combine class FindMyController: ObservableObject { static let shared = FindMyController() @@ -26,7 +26,7 @@ class FindMyController: ObservableObject { } } - func importReports(reports: [FindMyReport], and keys: Data, completion:@escaping () -> Void) throws { + func importReports(reports: [FindMyReport], and keys: Data, completion: @escaping () -> Void) throws { let devices = try PropertyListDecoder().decode([FindMyDevice].self, from: keys) self.devices = devices @@ -76,7 +76,7 @@ class FindMyController: ObservableObject { self.devices = devices - // Decrypt reports again with additional information + // Decrypt reports again with additional information self.decryptReports { } @@ -97,10 +97,10 @@ class FindMyController: ObservableObject { // Only use the newest keys for testing let keys = devices[deviceIndex].keys - let keyHashes = keys.map({$0.hashedKey.base64EncodedString()}) + let keyHashes = keys.map({ $0.hashedKey.base64EncodedString() }) // 21 days - let duration: Double = (24 * 60 * 60) * 21 + let duration: Double = (24 * 60 * 60) * 21 let startDate = Date() - duration fetcher.query(forHashes: keyHashes, start: startDate, duration: duration, searchPartyToken: searchPartyToken) { jd in @@ -136,10 +136,10 @@ class FindMyController: ObservableObject { } #if EXPORT - if let encoded = try? JSONEncoder().encode(reports) { - let outputDirectory = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first! - try? encoded.write(to: outputDirectory.appendingPathComponent("reports.json")) - } + if let encoded = try? JSONEncoder().encode(reports) { + let outputDirectory = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first! + try? encoded.write(to: outputDirectory.appendingPathComponent("reports.json")) + } #endif DispatchQueue.main.async { @@ -164,14 +164,14 @@ class FindMyController: ObservableObject { let device = devices[deviceIdx] // Map the keys in a dictionary for faster access - guard let reports = device.reports else {continue} - let keyMap = device.keys.reduce(into: [String: FindMyKey](), {$0[$1.hashedKey.base64EncodedString()] = $1}) + guard let reports = device.reports else { continue } + let keyMap = device.keys.reduce(into: [String: FindMyKey](), { $0[$1.hashedKey.base64EncodedString()] = $1 }) let accessQueue = DispatchQueue(label: "threadSafeAccess", qos: .userInitiated, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil) var decryptedReports = [FindMyLocationReport](repeating: FindMyLocationReport(lat: 0, lng: 0, acc: 0, dP: Date(), t: Date(), c: 0), count: reports.count) DispatchQueue.concurrentPerform(iterations: reports.count) { (reportIdx) in let report = reports[reportIdx] - guard let key = keyMap[report.id] else {return} + guard let key = keyMap[report.id] else { return } do { // Decrypt the report let locationReport = try DecryptReports.decrypt(report: report, with: key) @@ -208,8 +208,8 @@ struct FindMyControllerKey: EnvironmentKey { extension EnvironmentValues { var findMyController: FindMyController { - get {self[FindMyControllerKey.self]} - set {self[FindMyControllerKey.self] = newValue} + get { self[FindMyControllerKey.self] } + set { self[FindMyControllerKey.self] = newValue } } } diff --git a/OpenHaystack/OpenHaystack/FindMy/FindMyKeyDecoder.swift b/OpenHaystack/OpenHaystack/FindMy/FindMyKeyDecoder.swift index 61b9ed0..4d7beeb 100644 --- a/OpenHaystack/OpenHaystack/FindMy/FindMyKeyDecoder.swift +++ b/OpenHaystack/OpenHaystack/FindMy/FindMyKeyDecoder.swift @@ -5,16 +5,18 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import Foundation import CryptoKit +import Foundation /// Decode key files found in newer macOS versions. class FindMyKeyDecoder { - /// Key files can be in different format. The old <= 10.15.3 have been using normal plists. Newer once use a binary format which needs different parsing + /// Key files can be in different format. + /// + /// The old <= 10.15.3 have been using normal plists. Newer once use a binary format which needs different parsing. enum KeyFileFormat { /// Catalina > 10.15.4 key file format | Big Sur 11.0 Beta 1 uses a similar key file format that can be parsed identically. /// macOS 10.15.7 uses a new key file format that has not been reversed yet. - /// (The key files are protected by sandboxing and only usable from a SIP disabled) + /// (The key files are protected by sandboxing and only usable from a SIP disabled) case catalina_10_15_4 } @@ -59,7 +61,7 @@ class FindMyKeyDecoder { while i + 117 < keyFile.count { // We could not identify what those keys were - _ = keyFile.subdata(in: i.. $1.timestamp ?? Date.distantPast }) + .sorted(by: { $0.timestamp ?? Date.distantPast > $1.timestamp ?? Date.distantPast }) .first accessory.lastLocation = report?.location diff --git a/OpenHaystack/OpenHaystack/HaystackApp/KeychainController.swift b/OpenHaystack/OpenHaystack/HaystackApp/KeychainController.swift index 55a01ff..b87beb9 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/KeychainController.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/KeychainController.swift @@ -6,18 +6,18 @@ // SPDX-License-Identifier: AGPL-3.0-only import Foundation -import Security import OSLog +import Security struct KeychainController { - static func loadAccessoriesFromKeychain(test: Bool=false) -> [Accessory] { + static func loadAccessoriesFromKeychain(test: Bool = false) -> [Accessory] { var query: [CFString: Any] = [ kSecClass: kSecClassGenericPassword, kSecAttrLabel: "FindMyAccessories", kSecAttrService: "SEEMOO-FINDMY", kSecMatchLimit: kSecMatchLimitOne, - kSecReturnData: true + kSecReturnData: true, ] if test { @@ -27,7 +27,8 @@ struct KeychainController { var result: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &result) guard status == errSecSuccess, - let resultData = result as? Data else { + let resultData = result as? Data + else { return [] } @@ -42,13 +43,13 @@ struct KeychainController { return [] } - static func storeInKeychain(accessories: [Accessory], test: Bool=false) throws { + static func storeInKeychain(accessories: [Accessory], test: Bool = false) throws { // Store or update var attributes: [CFString: Any] = [ kSecClass: kSecClassGenericPassword, kSecAttrLabel: "FindMyAccessories", kSecAttrService: "SEEMOO-FINDMY", - kSecValueData: try PropertyListEncoder().encode(accessories) + kSecValueData: try PropertyListEncoder().encode(accessories), ] if test { @@ -62,7 +63,7 @@ struct KeychainController { var query: [CFString: Any] = [ kSecClass: kSecClassGenericPassword, kSecAttrLabel: "FindMyAccessories", - kSecAttrService: "SEEMOO-FINDMY" + kSecAttrService: "SEEMOO-FINDMY", ] if test { diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Mail Plugin/MailPluginManager.swift b/OpenHaystack/OpenHaystack/HaystackApp/Mail Plugin/MailPluginManager.swift index 9179141..d410df4 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Mail Plugin/MailPluginManager.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Mail Plugin/MailPluginManager.swift @@ -5,13 +5,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only +import AppKit import Foundation import OSLog -import AppKit let mailBundleName = "OpenHaystackMail" -/// Manages plugin installation +/// Manages plugin installation. struct MailPluginManager { let pluginsFolderURL = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("Library/Mail/Bundles") @@ -22,7 +22,7 @@ struct MailPluginManager { return FileManager.default.fileExists(atPath: pluginURL.path) } - /// Shows a NSSavePanel to install the mail plugin at the required place + /// Shows a NSSavePanel to install the mail plugin at the required place. func askForPermission() -> Bool { let panel = NSSavePanel() @@ -73,11 +73,12 @@ struct MailPluginManager { } - /// Copy a folder recursively + /// Copy a folder recursively. + /// /// - Parameters: /// - from: Folder source /// - to: Folder destination - /// - Throws: An error if copying or acessing files fails + /// - Throws: An error if copying or acessing files fails func copyFolder(from: URL, to: URL) throws { // Create the folder try? FileManager.default.createDirectory(at: to, withIntermediateDirectories: false, attributes: nil) @@ -102,11 +103,13 @@ struct MailPluginManager { try FileManager.default.removeItem(at: pluginURL) } - /// Copy plugin to downloads folder + /// Copy plugin to downloads folder. + /// /// - Throws: An error if the copy fails, because of missing permissions - func pluginDownload() throws { + func pluginDownload() throws { guard let localPluginURL = Bundle.main.url(forResource: mailBundleName, withExtension: "mailbundle"), - let downloadsFolder = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first else { + let downloadsFolder = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first + else { throw PluginError.downloadFailed } diff --git a/OpenHaystack/OpenHaystack/HaystackApp/MicrobitController.swift b/OpenHaystack/OpenHaystack/HaystackApp/MicrobitController.swift index 7ae9007..e8dd1e2 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/MicrobitController.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/MicrobitController.swift @@ -9,19 +9,21 @@ import Foundation struct MicrobitController { - /// Find all microbits connected to this mac + /// Find all microbits connected to this Mac. + /// /// - Throws: If a volume is inaccessible /// - Returns: an array of urls static func findMicrobits() throws -> [URL] { let fm = FileManager.default let volumes = try fm.contentsOfDirectory(atPath: "/Volumes") - let microbits: [URL] = volumes.filter({$0.lowercased().contains("microbit")}).map({URL(fileURLWithPath: "/Volumes").appendingPathComponent($0)}) + let microbits: [URL] = volumes.filter({ $0.lowercased().contains("microbit") }).map({ URL(fileURLWithPath: "/Volumes").appendingPathComponent($0) }) return microbits } - /// Deploy the firmware to a USB connected microbit at the given URL + /// Deploy the firmware to a USB connected microbit at the given URL. + /// /// - Parameters: /// - microbitURL: URL to the microbit /// - firmwareFile: Firmware file as binary data @@ -32,6 +34,7 @@ struct MicrobitController { } /// Patch the given firmware. + /// /// This will replace the pattern data (the place for the key) with the actual key /// - Parameters: /// - firmware: The firmware data that should be patched diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Model/Accessory.swift b/OpenHaystack/OpenHaystack/HaystackApp/Model/Accessory.swift index 1da85cb..98811f0 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Model/Accessory.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Model/Accessory.swift @@ -5,11 +5,11 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import Foundation +import CoreLocation import CryptoKit +import Foundation import Security import SwiftUI -import CoreLocation class Accessory: ObservableObject, Codable, Identifiable, Equatable { let name: String @@ -39,9 +39,10 @@ class Accessory: ObservableObject, Codable, Identifiable, Equatable { self.privateKey = try container.decode(Data.self, forKey: .privateKey) self.icon = (try? container.decode(String.self, forKey: .icon)) ?? "briefcase.fill" - if var colorComponents = try? container.decode([CGFloat].self, forKey: .colorComponents), + if var colorComponents = try? container.decode([CGFloat].self, forKey: .colorComponents), let spaceName = try? container.decode(String.self, forKey: .colorSpaceName), - let cgColor = CGColor(colorSpace: CGColorSpace(name: spaceName as CFString)!, components: &colorComponents) { + let cgColor = CGColor(colorSpace: CGColorSpace(name: spaceName as CFString)!, components: &colorComponents) + { self.color = Color(cgColor) } else { self.color = Color.white @@ -57,7 +58,8 @@ class Accessory: ObservableObject, Codable, Identifiable, Equatable { try container.encode(self.icon, forKey: .icon) if let colorComponents = self.color.cgColor?.components, - let colorSpace = self.color.cgColor?.colorSpace?.name { + let colorSpace = self.color.cgColor?.colorSpace?.name + { try container.encode(colorComponents, forKey: .colorComponents) try container.encode(colorSpace as String, forKey: .colorSpaceName) } @@ -79,7 +81,7 @@ class Accessory: ObservableObject, Codable, Identifiable, Equatable { // Drop the first byte to just have the 28 bytes version publicKey = publicKey.dropFirst() assert(publicKey.count == 28) - guard publicKey.count == 28 else {throw KeyError.keyDerivationFailed} + guard publicKey.count == 28 else { throw KeyError.keyDerivationFailed } return publicKey } @@ -103,20 +105,22 @@ class Accessory: ObservableObject, Codable, Identifiable, Equatable { func toFindMyDevice() throws -> FindMyDevice { - let findMyKey = FindMyKey(advertisedKey: try self.getAdvertisementKey(), - hashedKey: try self.hashedPublicKey(), - privateKey: self.privateKey, - startTime: nil, - duration: nil, - pu: nil, - yCoordinate: nil, - fullKey: nil) + let findMyKey = FindMyKey( + advertisedKey: try self.getAdvertisementKey(), + hashedKey: try self.hashedPublicKey(), + privateKey: self.privateKey, + startTime: nil, + duration: nil, + pu: nil, + yCoordinate: nil, + fullKey: nil) - return FindMyDevice(deviceId: String(self.id), - keys: [findMyKey], - catalinaBigSurKeyFiles: nil, - reports: nil, - decryptedReports: nil) + return FindMyDevice( + deviceId: String(self.id), + keys: [findMyKey], + catalinaBigSurKeyFiles: nil, + reports: nil, + decryptedReports: nil) } enum CodingKeys: String, CodingKey { diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Model/PreviewData.swift b/OpenHaystack/OpenHaystack/HaystackApp/Model/PreviewData.swift index 5d31196..99fcfeb 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Model/PreviewData.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Model/PreviewData.swift @@ -20,19 +20,19 @@ struct PreviewData { let longitude: Double = 13.413306 let backpack = try! Accessory(name: "Backpack", color: Color.green, iconName: "briefcase.fill") - backpack.lastLocation = CLLocation(latitude: latitude + (Double(arc4random() % 1000))/100000, longitude: longitude + (Double(arc4random() % 1000))/100000) + backpack.lastLocation = CLLocation(latitude: latitude + (Double(arc4random() % 1000)) / 100000, longitude: longitude + (Double(arc4random() % 1000)) / 100000) let bag = try! Accessory(name: "Bag", color: Color.blue, iconName: "latch.2.case.fill") - bag.lastLocation = CLLocation(latitude: latitude + (Double(arc4random() % 1000))/100000, longitude: longitude + (Double(arc4random() % 1000))/100000) + bag.lastLocation = CLLocation(latitude: latitude + (Double(arc4random() % 1000)) / 100000, longitude: longitude + (Double(arc4random() % 1000)) / 100000) let car = try! Accessory(name: "Car", color: Color.red, iconName: "car.fill") - car.lastLocation = CLLocation(latitude: latitude + (Double(arc4random() % 1000))/100000, longitude: longitude + (Double(arc4random() % 1000))/100000) + car.lastLocation = CLLocation(latitude: latitude + (Double(arc4random() % 1000)) / 100000, longitude: longitude + (Double(arc4random() % 1000)) / 100000) let keys = try! Accessory(name: "Keys", color: Color.orange, iconName: "key.fill") - keys.lastLocation = CLLocation(latitude: latitude + (Double(arc4random() % 1000))/100000, longitude: longitude + (Double(arc4random() % 1000))/100000) + keys.lastLocation = CLLocation(latitude: latitude + (Double(arc4random() % 1000)) / 100000, longitude: longitude + (Double(arc4random() % 1000)) / 100000) let items = try! Accessory(name: "Items", color: Color.gray, iconName: "mappin") - items.lastLocation = CLLocation(latitude: latitude + (Double(arc4random() % 1000))/100000, longitude: longitude + (Double(arc4random() % 1000))/100000) + items.lastLocation = CLLocation(latitude: latitude + (Double(arc4random() % 1000)) / 100000, longitude: longitude + (Double(arc4random() % 1000)) / 100000) return [backpack, bag, car, keys, items] } diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Views/AccessoryListEntry.swift b/OpenHaystack/OpenHaystack/HaystackApp/Views/AccessoryListEntry.swift index 4bf8d7b..a1b724d 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Views/AccessoryListEntry.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Views/AccessoryListEntry.swift @@ -5,8 +5,8 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import SwiftUI import OSLog +import SwiftUI struct AccessoryListEntry: View { var accessory: Accessory @@ -18,39 +18,47 @@ struct AccessoryListEntry: View { var body: some View { VStack { HStack { - Button(action: { - self.zoomOn(self.accessory) - }, label: { - HStack { - Text(accessory.name) - Spacer() + Button( + action: { + self.zoomOn(self.accessory) + }, + label: { + HStack { + Text(accessory.name) + Spacer() + } + .contentShape(Rectangle()) } - .contentShape(Rectangle()) - }) + ) .buttonStyle(PlainButtonStyle()) HStack(alignment: .center) { - Button(action: {self.zoomOn(self.accessory)}, label: { - Circle() - .strokeBorder(accessory.color, lineWidth: 2.0) - .background( - ZStack { - Circle().fill(Color("PinColor")) - Image(systemName: accessory.icon) - .padding(3) - } + Button( + action: { self.zoomOn(self.accessory) }, + label: { + Circle() + .strokeBorder(accessory.color, lineWidth: 2.0) + .background( + ZStack { + Circle().fill(Color("PinColor")) + Image(systemName: accessory.icon) + .padding(3) + } ) - .frame(width: 30, height: 30) - }) + .frame(width: 30, height: 30) + } + ) .buttonStyle(PlainButtonStyle()) - Button(action: { - self.deployAccessoryToMicrobit(accessory) - }, label: { - Text("Deploy") - }) + Button( + action: { + self.deployAccessoryToMicrobit(accessory) + }, + label: { + Text("Deploy") + }) } .padding(.trailing) @@ -60,10 +68,10 @@ struct AccessoryListEntry: View { } .contentShape(Rectangle()) .contextMenu { - Button("Delete", action: {self.delete(accessory)}) + Button("Delete", action: { self.delete(accessory) }) Divider() - Button("Copy advertisment key (Base64)", action: {self.copyPublicKey(of: accessory)}) - Button("Copy key id (Base64)", action: {self.copyPublicKeyHash(of: accessory)}) + Button("Copy advertisment key (Base64)", action: { self.copyPublicKey(of: accessory) }) + Button("Copy key id (Base64)", action: { self.copyPublicKeyHash(of: accessory) }) } } diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Views/AccessoryMapAnnotation.swift b/OpenHaystack/OpenHaystack/HaystackApp/Views/AccessoryMapAnnotation.swift index 4af25b7..7b3cbce 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Views/AccessoryMapAnnotation.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Views/AccessoryMapAnnotation.swift @@ -41,7 +41,7 @@ class AccessoryAnnotationView: MKAnnotationView { } func updateView() { - guard let accessory = (self.annotation as? AccessoryAnnotation)?.accessory else {return} + guard let accessory = (self.annotation as? AccessoryAnnotation)?.accessory else { return } self.pinView?.removeFromSuperview() self.pinView = NSHostingView(rootView: AccessoryPinView(accessory: accessory)) @@ -71,39 +71,39 @@ class AccessoryAnnotationView: MKAnnotationView { self.canShowCallout = true } -// override func draw(_ dirtyRect: NSRect) { -// guard let accessoryAnnotation = self.annotation as? AccessoryAnnotation else { -// super.draw(dirtyRect) -// return -// } -// -// let path = NSBezierPath(ovalIn: dirtyRect) -// path.lineWidth = 2.0 -// -// guard let cgColor = accessoryAnnotation.accessory.color.cgColor, -// let strokeColor = NSColor(cgColor: cgColor)?.withAlphaComponent(0.8) else {return} -// -// NSColor(named: NSColor.Name("PinColor"))?.setFill() -// -// path.fill() -// -// strokeColor.setStroke() -// path.stroke() -// -// let accessory = accessoryAnnotation.accessory -// -// guard let image = NSImage(systemSymbolName: accessory.icon, accessibilityDescription: accessory.name) else {return} -// -// let ratio = image.size.width / image.size.height -// let imageWidth: CGFloat = 20 -// let imageHeight = imageWidth / ratio -// let imageRect = NSRect( -// x: dirtyRect.width/2 - imageWidth/2, -// y: dirtyRect.height/2 - imageHeight/2, -// width: imageWidth, height: imageHeight) -// -// image.draw(in: imageRect) -// } + // override func draw(_ dirtyRect: NSRect) { + // guard let accessoryAnnotation = self.annotation as? AccessoryAnnotation else { + // super.draw(dirtyRect) + // return + // } + // + // let path = NSBezierPath(ovalIn: dirtyRect) + // path.lineWidth = 2.0 + // + // guard let cgColor = accessoryAnnotation.accessory.color.cgColor, + // let strokeColor = NSColor(cgColor: cgColor)?.withAlphaComponent(0.8) else {return} + // + // NSColor(named: NSColor.Name("PinColor"))?.setFill() + // + // path.fill() + // + // strokeColor.setStroke() + // path.stroke() + // + // let accessory = accessoryAnnotation.accessory + // + // guard let image = NSImage(systemSymbolName: accessory.icon, accessibilityDescription: accessory.name) else {return} + // + // let ratio = image.size.width / image.size.height + // let imageWidth: CGFloat = 20 + // let imageHeight = imageWidth / ratio + // let imageRect = NSRect( + // x: dirtyRect.width/2 - imageWidth/2, + // y: dirtyRect.height/2 - imageHeight/2, + // width: imageWidth, height: imageHeight) + // + // image.draw(in: imageRect) + // } struct AccessoryPinView: View { var accessory: Accessory diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Views/AccessoryMapView.swift b/OpenHaystack/OpenHaystack/HaystackApp/Views/AccessoryMapView.swift index 4297b50..51a88ab 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Views/AccessoryMapView.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Views/AccessoryMapView.swift @@ -7,8 +7,8 @@ // import Foundation -import SwiftUI import MapKit +import SwiftUI struct AccessoryMapView: NSViewControllerRepresentable { @ObservedObject var accessoryController: AccessoryController diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Views/ActivityIndicator.swift b/OpenHaystack/OpenHaystack/HaystackApp/Views/ActivityIndicator.swift index 1fd3d71..0fd299e 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Views/ActivityIndicator.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Views/ActivityIndicator.swift @@ -5,9 +5,9 @@ // // SPDX-License-Identifier: AGPL-3.0-only +import AppKit import Foundation import SwiftUI -import AppKit final class ActivityIndicator: NSViewRepresentable { diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Views/IconSelectionView.swift b/OpenHaystack/OpenHaystack/HaystackApp/Views/IconSelectionView.swift index 13c27f2..e2965fd 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Views/IconSelectionView.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Views/IconSelectionView.swift @@ -16,24 +16,29 @@ struct IconSelectionView: View { var body: some View { ZStack { - Button(action: { - withAnimation { - self.showImagePicker.toggle() + Button( + action: { + withAnimation { + self.showImagePicker.toggle() + } + }, + label: { + Circle() + .strokeBorder(Color.gray, lineWidth: 0.5) + .background( + Image(systemName: self.selectedImageName) + ) + .frame(width: 30, height: 30) } - }, label: { - Circle() - .strokeBorder(Color.gray, lineWidth: 0.5) - .background( - Image(systemName: self.selectedImageName) - ) - .frame(width: 30, height: 30) - }) + ) .buttonStyle(PlainButtonStyle()) - .popover(isPresented: self.$showImagePicker, content: { - ImageSelectionList(selectedImageName: self.$selectedImageName) { - self.showImagePicker = false - } - }) + .popover( + isPresented: self.$showImagePicker, + content: { + ImageSelectionList(selectedImageName: self.$selectedImageName) { + self.showImagePicker = false + } + }) } } } @@ -59,16 +64,19 @@ struct ImageSelectionList: View { var body: some View { List(self.selectableIcons, id: \.self) { iconName in - Button(action: { - self.selectedImageName = iconName - self.dismiss() - }, label: { - HStack { - Spacer() - Image(systemName: iconName) - Spacer() + Button( + action: { + self.selectedImageName = iconName + self.dismiss() + }, + label: { + HStack { + Spacer() + Image(systemName: iconName) + Spacer() + } } - }) + ) .buttonStyle(PlainButtonStyle()) .contentShape(Rectangle()) } diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift b/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift index c203635..47a82d2 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift @@ -5,9 +5,9 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import SwiftUI -import OSLog import MapKit +import OSLog +import SwiftUI struct OpenHaystackMainView: View { @@ -63,19 +63,25 @@ struct OpenHaystackMainView: View { } } - .alert(item: self.$alertType, content: { alertType in - return self.alert(for: alertType) - }) + .alert( + item: self.$alertType, + content: { alertType in + return self.alert(for: alertType) + } + ) .onChange(of: self.searchPartyToken) { (searchPartyToken) in - guard !searchPartyToken.isEmpty, self.accessories.isEmpty == false else {return} + guard !searchPartyToken.isEmpty, self.accessories.isEmpty == false else { return } self.downloadLocationReports() } - .onChange(of: self.popUpAlertType, perform: { popUpAlert in - guard popUpAlert != nil else {return} - DispatchQueue.main.asyncAfter(deadline: .now() + 2) { - self.popUpAlertType = nil + .onChange( + of: self.popUpAlertType, + perform: { popUpAlert in + guard popUpAlert != nil else { return } + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + self.popUpAlertType = nil + } } - }) + ) .onAppear { self.onAppear() } @@ -105,9 +111,12 @@ struct OpenHaystackMainView: View { IconSelectionView(selectedImageName: self.$selectedIcon) } - Button(action: self.addAccessory, label: { - Text("Generate key and deploy") - }) + Button( + action: self.addAccessory, + label: { + Text("Generate key and deploy") + } + ) .disabled(self.keyName.isEmpty) .padding(.bottom) @@ -130,20 +139,21 @@ struct OpenHaystackMainView: View { } } - /// Accessory List view + /// Accessory List view. var accessoryList: some View { List(self.accessories) { accessory in - AccessoryListEntry(accessory: accessory, - alertType: self.$alertType, - delete: self.delete(accessory:), - deployAccessoryToMicrobit: self.deployAccessoryToMicrobit(accessory:), - zoomOn: {self.focusedAccessory = $0}) + AccessoryListEntry( + accessory: accessory, + alertType: self.$alertType, + delete: self.delete(accessory:), + deployAccessoryToMicrobit: self.deployAccessoryToMicrobit(accessory:), + zoomOn: { self.focusedAccessory = $0 }) } .background(Color.clear) .cornerRadius(15.0) } - /// Overlay for the map that is gray and shows an activity indicator when loading + /// Overlay for the map that is gray and shows an activity indicator when loading. var mapOverlay: some View { ZStack { if self.isLoading { @@ -177,10 +187,13 @@ struct OpenHaystackMainView: View { .pickerStyle(SegmentedPickerStyle()) .frame(width: 150, alignment: .center) - Button(action: self.downloadLocationReports, label: { - Image(systemName: "arrow.clockwise") - Text("Reload") - }) + Button( + action: self.downloadLocationReports, + label: { + Image(systemName: "arrow.clockwise") + Text("Reload") + } + ) .opacity(1.0) .disabled(self.accessories.isEmpty) } @@ -189,7 +202,7 @@ struct OpenHaystackMainView: View { } } - /// Add an accessory with the provided details + /// Add an accessory with the provided details. func addAccessory() { let keyName = self.keyName self.keyName = "" @@ -223,7 +236,8 @@ struct OpenHaystackMainView: View { } guard !self.searchPartyToken.isEmpty, - let tokenData = self.searchPartyToken.data(using: .utf8) else { + let tokenData = self.searchPartyToken.data(using: .utf8) + else { self.alertType = .searchPartyToken return } @@ -244,7 +258,7 @@ struct OpenHaystackMainView: View { FindMyController.shared.devices = findMyDevices FindMyController.shared.fetchReports(with: tokenData) { error in - let reports = FindMyController.shared.devices.compactMap({$0.reports}).flatMap({$0}) + let reports = FindMyController.shared.devices.compactMap({ $0.reports }).flatMap({ $0 }) if reports.isEmpty { withAnimation { self.popUpAlertType = .noReportsFound @@ -257,7 +271,7 @@ struct OpenHaystackMainView: View { self.isLoading = false } - guard error != nil else {return} + guard error != nil else { return } os_log("Error: %@", String(describing: error)) } @@ -265,11 +279,11 @@ struct OpenHaystackMainView: View { } - /// Delete an accessory from the list of accessories + /// Delete an accessory from the list of accessories. func delete(accessory: Accessory) { do { var accessories = self.accessories - guard let idx = accessories.firstIndex(of: accessory) else {return} + guard let idx = accessories.firstIndex(of: accessory) else { return } accessories.remove(at: idx) @@ -284,12 +298,13 @@ struct OpenHaystackMainView: View { } - /// Deploy the public key of the accessory to a BBC microbit + /// Deploy the public key of the accessory to a BBC microbit. func deployAccessoryToMicrobit(accessory: Accessory) { do { let microbits = try MicrobitController.findMicrobits() guard let microBitURL = microbits.first, - let firmwareURL = Bundle.main.url(forResource: "firmware", withExtension: "bin") else { + let firmwareURL = Bundle.main.url(forResource: "firmware", withExtension: "bin") + else { self.alertType = .deployFailed return } @@ -314,7 +329,8 @@ struct OpenHaystackMainView: View { /// Checks if the search party token can be fetched without the Mail Plugin. If true the plugin is not needed for this environment. (e.g. when SIP is disabled) let reportsFetcher = ReportsFetcher() if let token = reportsFetcher.fetchSearchpartyToken(), - let tokenString = String(data: token, encoding: .ascii) { + let tokenString = String(data: token, encoding: .ascii) + { self.searchPartyToken = tokenString return } @@ -330,7 +346,7 @@ struct OpenHaystackMainView: View { } } - /// Ask to install and activate the mail plugin + /// Ask to install and activate the mail plugin. func installMailPlugin() { let pluginManager = MailPluginManager() guard pluginManager.isMailPluginInstalled == false else { @@ -338,7 +354,7 @@ struct OpenHaystackMainView: View { return } do { - try pluginManager.installMailPlugin() + try pluginManager.installMailPlugin() } catch { DispatchQueue.main.async { self.alertType = .pluginInstallFailed @@ -371,8 +387,8 @@ struct OpenHaystackMainView: View { } } completion?(false) + } } - } } } @@ -386,7 +402,8 @@ struct OpenHaystackMainView: View { // MARK: - Alerts - /// Create an alert for the given alert type + /// Create an alert for the given alert type. + /// /// - Parameter alertType: current alert type /// - Returns: A SwiftUI Alert func alert(for alertType: AlertType) -> Alert { @@ -394,51 +411,60 @@ struct OpenHaystackMainView: View { case .keyError: return Alert(title: Text("Could not create accessory"), message: Text(String(describing: self.errorDescription)), dismissButton: Alert.Button.cancel()) case .searchPartyToken: - return Alert(title: Text("Add the search party token"), - message: Text( - """ - Please paste the search party token below after copying itfrom the macOS Keychain. - The item that contains the key can be found by searching for: - com.apple.account.DeviceLocator.search-party-token - """ - ), - dismissButton: Alert.Button.okay()) + return Alert( + title: Text("Add the search party token"), + message: Text( + """ + Please paste the search party token below after copying itfrom the macOS Keychain. + The item that contains the key can be found by searching for: + com.apple.account.DeviceLocator.search-party-token + """ + ), + dismissButton: Alert.Button.okay()) case .deployFailed: - return Alert(title: Text("Could not deploy"), - message: Text("Deploying to microbit failed. Please reconnect the device over USB"), - dismissButton: Alert.Button.okay()) + return Alert( + title: Text("Could not deploy"), + message: Text("Deploying to microbit failed. Please reconnect the device over USB"), + dismissButton: Alert.Button.okay()) case .deployedSuccessfully: - return Alert(title: Text("Deploy successfull"), - message: Text("This device will now be tracked by all iPhones and you can use this app to find its last reported location"), - dismissButton: Alert.Button.okay()) + return Alert( + title: Text("Deploy successfull"), + message: Text("This device will now be tracked by all iPhones and you can use this app to find its last reported location"), + dismissButton: Alert.Button.okay()) case .deletionFailed: return Alert(title: Text("Could not delete accessory"), dismissButton: Alert.Button.okay()) case .noReportsFound: - return Alert(title: Text("No reports found"), - message: Text("Your accessory might have not been found yet or it is not powered. Make sure it has enough power to be found by nearby iPhones"), - dismissButton: Alert.Button.okay()) + return Alert( + title: Text("No reports found"), + message: Text("Your accessory might have not been found yet or it is not powered. Make sure it has enough power to be found by nearby iPhones"), + dismissButton: Alert.Button.okay()) case .activatePlugin: let message = - """ - To access your Apple ID for downloading location reports we need to use a plugin in Apple Mail. - Please make sure Apple Mail is running. - Open Mail -> Preferences -> General -> Manage Plug-Ins... -> Select Haystack + """ + To access your Apple ID for downloading location reports we need to use a plugin in Apple Mail. + Please make sure Apple Mail is running. + Open Mail -> Preferences -> General -> Manage Plug-Ins... -> Select Haystack - We do not access any of your e-mail data. This is just necessary, because Apple blocks access to certain iCloud tokens otherwise. - """ + We do not access any of your e-mail data. This is just necessary, because Apple blocks access to certain iCloud tokens otherwise. + """ - return Alert(title: Text("Install & Activate Mail Plugin"), message: Text(message), - primaryButton: .default(Text("Okay"), action: {self.installMailPlugin()}), - secondaryButton: .cancel()) + return Alert( + title: Text("Install & Activate Mail Plugin"), message: Text(message), + primaryButton: .default(Text("Okay"), action: { self.installMailPlugin() }), + secondaryButton: .cancel()) case .pluginInstallFailed: - return Alert(title: Text("Mail Plugin installation failed"), - message: Text("To access the location reports of your devices an Apple Mail plugin is necessary" + - "\nThe installtion of this plugin has failed.\n\n Please download it manually unzip it and move it to /Library/Mail/Bundles"), - primaryButton: .default(Text("Download plug-in"), action: { - self.downloadPlugin() - }), secondaryButton: .cancel()) + return Alert( + title: Text("Mail Plugin installation failed"), + message: Text( + "To access the location reports of your devices an Apple Mail plugin is necessary" + + "\nThe installtion of this plugin has failed.\n\n Please download it manually unzip it and move it to /Library/Mail/Bundles"), + primaryButton: .default( + Text("Download plug-in"), + action: { + self.downloadPlugin() + }), secondaryButton: .cancel()) } } diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Views/PopUpAlertView.swift b/OpenHaystack/OpenHaystack/HaystackApp/Views/PopUpAlertView.swift index 33067c3..6edab30 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Views/PopUpAlertView.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Views/PopUpAlertView.swift @@ -25,8 +25,9 @@ struct PopUpAlertView: View { } } - .background(RoundedRectangle(cornerRadius: 7.5) - .fill(Color.gray)) + .background( + RoundedRectangle(cornerRadius: 7.5) + .fill(Color.gray)) } } diff --git a/OpenHaystack/OpenHaystack/MapViewController.swift b/OpenHaystack/OpenHaystack/MapViewController.swift index 0e470f8..dfb764d 100755 --- a/OpenHaystack/OpenHaystack/MapViewController.swift +++ b/OpenHaystack/OpenHaystack/MapViewController.swift @@ -25,7 +25,7 @@ final class MapViewController: NSViewController, MKMapViewDelegate { } // Zoom to first location - if let location = devices.first?.decryptedReports?.first { + if let location = devices.first?.decryptedReports?.first { let coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude) let span = MKCoordinateSpan(latitudeDelta: 5.0, longitudeDelta: 5.0) let region = MKCoordinateRegion(center: coordinate, span: span) @@ -36,7 +36,7 @@ final class MapViewController: NSViewController, MKMapViewDelegate { // Add pins for device in devices { - guard let reports = device.decryptedReports else {continue} + guard let reports = device.decryptedReports else { continue } for report in reports { let pin = MKPointAnnotation() pin.title = device.deviceId @@ -49,7 +49,7 @@ final class MapViewController: NSViewController, MKMapViewDelegate { func zoom(on accessory: Accessory?) { self.focusedAccessory = accessory - guard let location = accessory?.lastLocation else {return} + guard let location = accessory?.lastLocation else { return } let span = MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005) let region = MKCoordinateRegion(center: location.coordinate, span: span) DispatchQueue.main.async { @@ -63,7 +63,7 @@ final class MapViewController: NSViewController, MKMapViewDelegate { } // Zoom to first location - if focusedAccessory == nil, let location = accessories.first(where: {$0.lastLocation != nil})?.lastLocation { + if focusedAccessory == nil, let location = accessories.first(where: { $0.lastLocation != nil })?.lastLocation { let span = MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005) let region = MKCoordinateRegion(center: location.coordinate, span: span) DispatchQueue.main.async { @@ -73,7 +73,7 @@ final class MapViewController: NSViewController, MKMapViewDelegate { // Add pins for accessory in accessories { - guard accessory.lastLocation != nil else {continue} + guard accessory.lastLocation != nil else { continue } let annotation = AccessoryAnnotation(accessory: accessory) self.mapView.addAnnotation(annotation) diff --git a/OpenHaystack/OpenHaystack/SavePanel.swift b/OpenHaystack/OpenHaystack/SavePanel.swift index 7523143..d487802 100644 --- a/OpenHaystack/OpenHaystack/SavePanel.swift +++ b/OpenHaystack/OpenHaystack/SavePanel.swift @@ -5,8 +5,8 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import Foundation import AppKit +import Foundation class SavePanel: NSObject, NSOpenSavePanelDelegate { @@ -40,7 +40,7 @@ class SavePanel: NSObject, NSOpenSavePanelDelegate { } func panel(_ sender: Any, userEnteredFilename filename: String, confirmed okFlag: Bool) -> String? { - guard okFlag else {return nil} + guard okFlag else { return nil } return filename } diff --git a/OpenHaystack/OpenHaystackTests/OpenHaystackTests.swift b/OpenHaystack/OpenHaystackTests/OpenHaystackTests.swift index babf1b4..b77c317 100644 --- a/OpenHaystack/OpenHaystackTests/OpenHaystackTests.swift +++ b/OpenHaystack/OpenHaystackTests/OpenHaystackTests.swift @@ -5,8 +5,9 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import XCTest import CryptoKit +import XCTest + @testable import OpenHaystack class OpenHaystackTests: XCTestCase { @@ -72,7 +73,7 @@ class OpenHaystackTests: XCTestCase { XCTAssertNotEqual(publicKey, accessory.privateKey) } - func testStoreAccessories() throws { + func testStoreAccessories() throws { let accessory = try Accessory(name: "Test accessory") try KeychainController.storeInKeychain(accessories: [accessory], test: true) let fetchedAccessories = KeychainController.loadAccessoriesFromKeychain(test: true) @@ -107,7 +108,7 @@ class OpenHaystackTests: XCTestCase { _ = try MicrobitController.patchFirmware(firmware, pattern: pattern, with: key) XCTFail("Should thrown an erorr before") } catch PatchingError.patternNotFound { - // This should be thrown + // This should be thrown } catch { XCTFail("Unexpected error") } @@ -183,7 +184,7 @@ class OpenHaystackTests: XCTestCase { XCTAssertNotNil(sharedKey) // Now we follow the standard key derivation used in OF - let derivedKey = DecryptReports.kdf(fromSharedSecret: sharedKey, andEphemeralKey: ephPublicKey ) + let derivedKey = DecryptReports.kdf(fromSharedSecret: sharedKey, andEphemeralKey: ephPublicKey) // Let's encrypt some test string let message = "This is a message that should be encrypted" let messageData = message.data(using: .ascii)!