Compare commits
No commits in common. "e9d870bd12dc2256ae6584c50cdd341b1d0e6f5b" and "f284696e215f3f134da1cfbc9e97f62da6c80f13" have entirely different histories.
e9d870bd12
...
f284696e21
@ -1,14 +1,8 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import SwiftData
|
import SwiftData
|
||||||
|
|
||||||
private enum MainScreenSelection: Int {
|
|
||||||
case unlock = 0
|
|
||||||
case settings = 1
|
|
||||||
case history = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct SesameWatchApp: App {
|
struct Sesame_Watch_Watch_AppApp: App {
|
||||||
|
|
||||||
@State
|
@State
|
||||||
var modelContainer: ModelContainer
|
var modelContainer: ModelContainer
|
||||||
@ -19,32 +13,36 @@ struct SesameWatchApp: App {
|
|||||||
let keyManagement = KeyManagement()
|
let keyManagement = KeyManagement()
|
||||||
|
|
||||||
@State
|
@State
|
||||||
private var selectedScreen: MainScreenSelection = .unlock
|
var selected: Int = 0
|
||||||
|
|
||||||
@State
|
@State
|
||||||
var didLaunchFromComplication = false
|
var didLaunchFromComplication = false
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
let modelContainer = SesameWatchApp.loadModelContainer()
|
do {
|
||||||
self.modelContainer = modelContainer
|
let modelContainer = try ModelContainer(for: HistoryItem.self)
|
||||||
self.coordinator = .init(modelContext: modelContainer.mainContext)
|
self.modelContainer = modelContainer
|
||||||
|
self.coordinator = .init(modelContext: modelContainer.mainContext)
|
||||||
|
} catch {
|
||||||
|
fatalError("Failed to create model container: \(error)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
TabView(selection: $selectedScreen) {
|
TabView(selection: $selected) {
|
||||||
ContentView(coordinator: coordinator, didLaunchFromComplication: $didLaunchFromComplication)
|
ContentView(coordinator: coordinator, didLaunchFromComplication: $didLaunchFromComplication)
|
||||||
.tag(MainScreenSelection.unlock)
|
.tag(1)
|
||||||
SettingsView()
|
SettingsView()
|
||||||
.environmentObject(keyManagement)
|
.environmentObject(keyManagement)
|
||||||
.tag(MainScreenSelection.settings)
|
.tag(2)
|
||||||
HistoryView()
|
HistoryView()
|
||||||
.tag(MainScreenSelection.history)
|
.tag(3)
|
||||||
}
|
}
|
||||||
.tabViewStyle(PageTabViewStyle())
|
.tabViewStyle(PageTabViewStyle())
|
||||||
.onOpenURL { url in
|
.onOpenURL { url in
|
||||||
|
selected = 0
|
||||||
didLaunchFromComplication = true
|
didLaunchFromComplication = true
|
||||||
selectedScreen = .unlock
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.modelContainer(modelContainer)
|
.modelContainer(modelContainer)
|
||||||
|
@ -67,8 +67,6 @@
|
|||||||
88E197D429EDCE7600BF1D19 /* UInt32+Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2C5C1DC281B3AC400769EF6 /* UInt32+Coding.swift */; };
|
88E197D429EDCE7600BF1D19 /* UInt32+Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2C5C1DC281B3AC400769EF6 /* UInt32+Coding.swift */; };
|
||||||
88E197D729EDCFE800BF1D19 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E197D629EDCFE800BF1D19 /* Date+Extensions.swift */; };
|
88E197D729EDCFE800BF1D19 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E197D629EDCFE800BF1D19 /* Date+Extensions.swift */; };
|
||||||
88E197D829EDD13B00BF1D19 /* SymmetricKey+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45CA27A464C000D6E650 /* SymmetricKey+Extensions.swift */; };
|
88E197D829EDD13B00BF1D19 /* SymmetricKey+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45CA27A464C000D6E650 /* SymmetricKey+Extensions.swift */; };
|
||||||
88E35EF52B3B0A9800485A66 /* App+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E35EF42B3B0A9800485A66 /* App+Extensions.swift */; };
|
|
||||||
88E35EF62B3B0A9800485A66 /* App+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E35EF42B3B0A9800485A66 /* App+Extensions.swift */; };
|
|
||||||
E240654B2A8153C6009C1AD8 /* SettingsListTextItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E240654A2A8153C6009C1AD8 /* SettingsListTextItem.swift */; };
|
E240654B2A8153C6009C1AD8 /* SettingsListTextItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E240654A2A8153C6009C1AD8 /* SettingsListTextItem.swift */; };
|
||||||
E240654F2A8159B7009C1AD8 /* SettingsTextInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E240654E2A8159B7009C1AD8 /* SettingsTextInputView.swift */; };
|
E240654F2A8159B7009C1AD8 /* SettingsTextInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E240654E2A8159B7009C1AD8 /* SettingsTextInputView.swift */; };
|
||||||
E24065512A819066009C1AD8 /* SettingsTextItemLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24065502A819066009C1AD8 /* SettingsTextItemLink.swift */; };
|
E24065512A819066009C1AD8 /* SettingsTextItemLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24065502A819066009C1AD8 /* SettingsTextItemLink.swift */; };
|
||||||
@ -162,7 +160,6 @@
|
|||||||
88E197B329EDC9BC00BF1D19 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
88E197B329EDC9BC00BF1D19 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||||
88E197B529EDC9BD00BF1D19 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
88E197B529EDC9BD00BF1D19 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
88E197D629EDCFE800BF1D19 /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = "<group>"; };
|
88E197D629EDCFE800BF1D19 /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
88E35EF42B3B0A9800485A66 /* App+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+Extensions.swift"; sourceTree = "<group>"; };
|
|
||||||
E240654A2A8153C6009C1AD8 /* SettingsListTextItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsListTextItem.swift; sourceTree = "<group>"; };
|
E240654A2A8153C6009C1AD8 /* SettingsListTextItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsListTextItem.swift; sourceTree = "<group>"; };
|
||||||
E240654E2A8159B7009C1AD8 /* SettingsTextInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTextInputView.swift; sourceTree = "<group>"; };
|
E240654E2A8159B7009C1AD8 /* SettingsTextInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTextInputView.swift; sourceTree = "<group>"; };
|
||||||
E24065502A819066009C1AD8 /* SettingsTextItemLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTextItemLink.swift; sourceTree = "<group>"; };
|
E24065502A819066009C1AD8 /* SettingsTextItemLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTextItemLink.swift; sourceTree = "<group>"; };
|
||||||
@ -286,7 +283,6 @@
|
|||||||
884A45CA27A464C000D6E650 /* SymmetricKey+Extensions.swift */,
|
884A45CA27A464C000D6E650 /* SymmetricKey+Extensions.swift */,
|
||||||
88AEE3802B22327F0034EDA9 /* UInt32+Random.swift */,
|
88AEE3802B22327F0034EDA9 /* UInt32+Random.swift */,
|
||||||
8860D76D2B246FC400849FAC /* Text+Extensions.swift */,
|
8860D76D2B246FC400849FAC /* Text+Extensions.swift */,
|
||||||
88E35EF42B3B0A9800485A66 /* App+Extensions.swift */,
|
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -558,7 +554,6 @@
|
|||||||
E24EE77227FDCCC00011CFD2 /* Data+Hex.swift in Sources */,
|
E24EE77227FDCCC00011CFD2 /* Data+Hex.swift in Sources */,
|
||||||
8860D7482B23294600849FAC /* SignedMessage+Crypto.swift in Sources */,
|
8860D7482B23294600849FAC /* SignedMessage+Crypto.swift in Sources */,
|
||||||
8860D74C2B232A7700849FAC /* SesameHeader.swift in Sources */,
|
8860D74C2B232A7700849FAC /* SesameHeader.swift in Sources */,
|
||||||
88E35EF52B3B0A9800485A66 /* App+Extensions.swift in Sources */,
|
|
||||||
8860D7622B23803E00849FAC /* ServerChallenge.swift in Sources */,
|
8860D7622B23803E00849FAC /* ServerChallenge.swift in Sources */,
|
||||||
8860D7432B22858600849FAC /* Date+Timestamp.swift in Sources */,
|
8860D7432B22858600849FAC /* Date+Timestamp.swift in Sources */,
|
||||||
88AEE3862B22376D0034EDA9 /* Message+Crypto.swift in Sources */,
|
88AEE3862B22376D0034EDA9 /* Message+Crypto.swift in Sources */,
|
||||||
@ -600,7 +595,6 @@
|
|||||||
888362362A80F4420032BBB2 /* HistoryView.swift in Sources */,
|
888362362A80F4420032BBB2 /* HistoryView.swift in Sources */,
|
||||||
8860D75B2B237FB600849FAC /* SignedMessage+Crypto.swift in Sources */,
|
8860D75B2B237FB600849FAC /* SignedMessage+Crypto.swift in Sources */,
|
||||||
E240654F2A8159B7009C1AD8 /* SettingsTextInputView.swift in Sources */,
|
E240654F2A8159B7009C1AD8 /* SettingsTextInputView.swift in Sources */,
|
||||||
88E35EF62B3B0A9800485A66 /* App+Extensions.swift in Sources */,
|
|
||||||
88E197D329EDCE6E00BF1D19 /* MessageResult.swift in Sources */,
|
88E197D329EDCE6E00BF1D19 /* MessageResult.swift in Sources */,
|
||||||
88E197D129EDCE5F00BF1D19 /* Data+Hex.swift in Sources */,
|
88E197D129EDCE5F00BF1D19 /* Data+Hex.swift in Sources */,
|
||||||
8860D76C2B246F5E00849FAC /* UInt32+Random.swift in Sources */,
|
8860D76C2B246F5E00849FAC /* UInt32+Random.swift in Sources */,
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
import SwiftUI
|
|
||||||
import SwiftData
|
|
||||||
|
|
||||||
extension App {
|
|
||||||
|
|
||||||
static func loadModelContainer() -> ModelContainer {
|
|
||||||
do {
|
|
||||||
return try ModelContainer(for: HistoryItem.self)
|
|
||||||
} catch {
|
|
||||||
print("[WARNING] Removing default SwiftData storage")
|
|
||||||
removeDefaultModelContainer()
|
|
||||||
}
|
|
||||||
// Try again to load an empty container
|
|
||||||
do {
|
|
||||||
return try ModelContainer(for: HistoryItem.self)
|
|
||||||
} catch {
|
|
||||||
fatalError("Failed to create empty model container: \(error)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func removeDefaultModelContainer() {
|
|
||||||
guard let appSupportDir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).last else {
|
|
||||||
fatalError("Failed to get application support directory")
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
try FileManager.default.contentsOfDirectory(at: appSupportDir, includingPropertiesForKeys: nil)
|
|
||||||
.filter { $0.lastPathComponent.hasPrefix("default") }
|
|
||||||
.forEach {
|
|
||||||
try FileManager.default.removeItem(at: $0)
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
print(error)
|
|
||||||
fatalError("Failed to remove default SwiftData database files")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -41,23 +41,6 @@ final class RequestCoordinator: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkConnection(using route: TransmissionType? = nil) {
|
|
||||||
guard !isPerformingRequest else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
isPerformingRequest = true
|
|
||||||
Task {
|
|
||||||
let route = route ?? connectionType.transmissionTypes.first!
|
|
||||||
let (finalResult, _) = await performChallenge(route: route)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.state = finalResult.result
|
|
||||||
self.isPerformingRequest = false
|
|
||||||
}
|
|
||||||
print("Finished connection test: \(finalResult)")
|
|
||||||
scheduleReturnToReadyState()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func startUnlock() {
|
func startUnlock() {
|
||||||
guard !isPerformingRequest else {
|
guard !isPerformingRequest else {
|
||||||
return
|
return
|
||||||
|
@ -73,8 +73,7 @@ struct ContentView: View {
|
|||||||
.animation(.easeInOut, value: coordinator.state.color)
|
.animation(.easeInOut, value: coordinator.state.color)
|
||||||
.sheet(isPresented: $showSettingsSheet) {
|
.sheet(isPresented: $showSettingsSheet) {
|
||||||
SettingsView(
|
SettingsView(
|
||||||
keyManager: coordinator.keyManager,
|
keyManager: coordinator.keyManager,
|
||||||
coordinator: coordinator,
|
|
||||||
serverAddress: $coordinator.serverPath,
|
serverAddress: $coordinator.serverPath,
|
||||||
localAddress: $coordinator.localAddress)
|
localAddress: $coordinator.localAddress)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ final class HistoryItem {
|
|||||||
|
|
||||||
let startDate: Date
|
let startDate: Date
|
||||||
|
|
||||||
let message: HistoryMessage
|
let message: Message
|
||||||
|
|
||||||
let route: TransmissionType
|
let route: TransmissionType
|
||||||
|
|
||||||
@ -14,11 +14,7 @@ final class HistoryItem {
|
|||||||
|
|
||||||
init(message: Message, startDate: Date, route: TransmissionType, finishDate: Date) {
|
init(message: Message, startDate: Date, route: TransmissionType, finishDate: Date) {
|
||||||
self.startDate = startDate
|
self.startDate = startDate
|
||||||
self.message = .init(
|
self.message = message
|
||||||
messageType: message.messageType,
|
|
||||||
clientChallenge: Int(message.clientChallenge),
|
|
||||||
serverChallenge: Int(message.serverChallenge),
|
|
||||||
result: message.result)
|
|
||||||
self.finishDate = finishDate
|
self.finishDate = finishDate
|
||||||
self.route = route
|
self.route = route
|
||||||
}
|
}
|
||||||
@ -32,59 +28,6 @@ final class HistoryItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HistoryMessage {
|
|
||||||
|
|
||||||
/// The type of message being sent.
|
|
||||||
let messageType: MessageType
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The random nonce created by the remote
|
|
||||||
*
|
|
||||||
* This nonce is a random number created by the remote, different for each unlock request.
|
|
||||||
* It is set for all message types.
|
|
||||||
*/
|
|
||||||
let clientChallenge: Int
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A random number to sign by the remote
|
|
||||||
*
|
|
||||||
* This nonce is set by the server after receiving an initial message.
|
|
||||||
* It is set for the message types `challenge`, `request`, and `response`.
|
|
||||||
*/
|
|
||||||
let serverChallenge: Int
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The response status for the previous message.
|
|
||||||
*
|
|
||||||
* It is set only for messages from the server, e.g. the `challenge` and `response` message types.
|
|
||||||
* Must be set to `MessageAccepted` for other messages.
|
|
||||||
*/
|
|
||||||
let result: MessageResult
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HistoryMessage: Encodable {
|
|
||||||
|
|
||||||
func encode(to encoder: Encoder) throws {
|
|
||||||
var container = encoder.unkeyedContainer()
|
|
||||||
try container.encode(messageType)
|
|
||||||
try container.encode(clientChallenge)
|
|
||||||
try container.encode(serverChallenge)
|
|
||||||
try container.encode(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HistoryMessage: Decodable {
|
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
|
||||||
var container = try decoder.unkeyedContainer()
|
|
||||||
self.messageType = try container.decode(MessageType.self)
|
|
||||||
self.clientChallenge = try container.decode(Int.self)
|
|
||||||
self.serverChallenge = try container.decode(Int.self)
|
|
||||||
self.result = try container.decode(MessageResult.self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extension HistoryItem: Identifiable {
|
extension HistoryItem: Identifiable {
|
||||||
|
|
||||||
var id: Double {
|
var id: Double {
|
||||||
|
@ -8,7 +8,11 @@ struct SesameApp: App {
|
|||||||
var modelContainer: ModelContainer
|
var modelContainer: ModelContainer
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.modelContainer = SesameApp.loadModelContainer()
|
do {
|
||||||
|
self.modelContainer = try ModelContainer(for: HistoryItem.self)
|
||||||
|
} catch {
|
||||||
|
fatalError("Failed to create model container: \(error)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import SwiftData
|
|
||||||
import SFSafeSymbols
|
|
||||||
|
|
||||||
struct SettingsView: View {
|
struct SettingsView: View {
|
||||||
|
|
||||||
let keyManager: KeyManagement
|
let keyManager: KeyManagement
|
||||||
|
|
||||||
@ObservedObject
|
|
||||||
var coordinator: RequestCoordinator
|
|
||||||
|
|
||||||
@Binding
|
@Binding
|
||||||
var serverAddress: String
|
var serverAddress: String
|
||||||
|
|
||||||
@ -25,19 +20,6 @@ struct SettingsView: View {
|
|||||||
TextField("Server address", text: $serverAddress)
|
TextField("Server address", text: $serverAddress)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
.padding(.leading, 8)
|
.padding(.leading, 8)
|
||||||
HStack {
|
|
||||||
Button("Test") {
|
|
||||||
coordinator.checkConnection(using: .throughServer)
|
|
||||||
}.padding(8)
|
|
||||||
if coordinator.state == .deviceAvailable {
|
|
||||||
Image(systemSymbol: .checkmarkCircle)
|
|
||||||
.foregroundColor(.green)
|
|
||||||
} else if coordinator.state != .notChecked {
|
|
||||||
Text(coordinator.state.description)
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.padding(.vertical, 8)
|
}.padding(.vertical, 8)
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text("Local address")
|
Text("Local address")
|
||||||
@ -62,20 +44,11 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
struct SettingsView_Previews: PreviewProvider {
|
||||||
do {
|
static var previews: some View {
|
||||||
let config = ModelConfiguration(isStoredInMemoryOnly: true)
|
SettingsView(
|
||||||
let container = try ModelContainer(for: HistoryItem.self, configurations: config)
|
|
||||||
|
|
||||||
let item = HistoryItem.mock
|
|
||||||
container.mainContext.insert(item)
|
|
||||||
try container.mainContext.save()
|
|
||||||
return SettingsView(
|
|
||||||
keyManager: KeyManagement(),
|
keyManager: KeyManagement(),
|
||||||
coordinator: .init(modelContext: container.mainContext),
|
|
||||||
serverAddress: .constant("https://example.com"),
|
serverAddress: .constant("https://example.com"),
|
||||||
localAddress: .constant("192.168.178.42"))
|
localAddress: .constant("192.168.178.42"))
|
||||||
} catch {
|
|
||||||
fatalError("Failed to create model container.")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user