Add swift-format as an Xcode build phase

This commit is contained in:
Milan Stute
2021-03-08 22:34:45 +01:00
parent 036b99c2bd
commit 8127628ea5
23 changed files with 397 additions and 309 deletions
@@ -31,12 +31,12 @@ class AccessoryController: ObservableObject {
func updateWithDecryptedReports(devices: [FindMyDevice]) {
// Assign last locations
for device in FindMyController.shared.devices {
if let idx = self.accessories.firstIndex(where: {$0.id == Int(device.deviceId)}) {
if let idx = self.accessories.firstIndex(where: { $0.id == Int(device.deviceId) }) {
self.objectWillChange.send()
let accessory = self.accessories[idx]
let report = device.decryptedReports?
.sorted(by: {$0.timestamp ?? Date.distantPast > $1.timestamp ?? Date.distantPast })
.sorted(by: { $0.timestamp ?? Date.distantPast > $1.timestamp ?? Date.distantPast })
.first
accessory.lastLocation = report?.location
@@ -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 {
@@ -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
}
@@ -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
@@ -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 {
@@ -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]
}
@@ -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) })
}
}
@@ -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
@@ -7,8 +7,8 @@
//
import Foundation
import SwiftUI
import MapKit
import SwiftUI
struct AccessoryMapView: NSViewControllerRepresentable {
@ObservedObject var accessoryController: AccessoryController
@@ -5,9 +5,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
import AppKit
import Foundation
import SwiftUI
import AppKit
final class ActivityIndicator: NSViewRepresentable {
@@ -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())
}
@@ -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())
}
}
@@ -25,8 +25,9 @@ struct PopUpAlertView: View {
}
}
.background(RoundedRectangle(cornerRadius: 7.5)
.fill(Color.gray))
.background(
RoundedRectangle(cornerRadius: 7.5)
.fill(Color.gray))
}
}