Synchronize selected TOTP entries to Apple Watch (#55)
This commit is contained in:
@@ -603,6 +603,8 @@ fileprivate struct FfiConverterData: FfiConverterRustBuffer {
|
||||
|
||||
public protocol MobileAuthenticationProtocol: AnyObject, Sendable {
|
||||
|
||||
func acknowledgeWatchSnapshot(receipt: Data) throws -> MobileWatchSnapshotStatus
|
||||
|
||||
func addEntryEditorField(editor: UInt64, kind: MobileEntryEditorFieldKind, name: String?, value: String) throws -> MobileEntryEditorPage
|
||||
|
||||
func beginCreateEntry(directory: String, name: String, passphrase: String?) throws -> MobileEntryEditorSession
|
||||
@@ -623,6 +625,8 @@ public protocol MobileAuthenticationProtocol: AnyObject, Sendable {
|
||||
|
||||
func entryPage(path: String, unixSeconds: UInt64) throws -> MobileEntryPresentation
|
||||
|
||||
func failWatchSnapshot(revision: UInt64, detail: String) throws -> MobileWatchSnapshotStatus
|
||||
|
||||
func generateEntryEditorPassword(editor: UInt64, length: UInt32?, noSymbols: Bool) throws -> MobileEntryEditorPage
|
||||
|
||||
func gitIdentity() throws -> MobileGitIdentity
|
||||
@@ -637,6 +641,8 @@ public protocol MobileAuthenticationProtocol: AnyObject, Sendable {
|
||||
|
||||
func prepareEntryMutation(path: String, action: MobileMutationAction) throws -> MobileMutationPlan
|
||||
|
||||
func prepareWatchSnapshot(platformPairingIdentity: String) throws -> MobileWatchSnapshotTransfer
|
||||
|
||||
func removeApplicationToken() throws
|
||||
|
||||
func removeEntryEditorField(editor: UInt64, field: UInt64) throws -> MobileEntryEditorPage
|
||||
@@ -661,6 +667,8 @@ public protocol MobileAuthenticationProtocol: AnyObject, Sendable {
|
||||
|
||||
func setTotpWatchShared(path: String, shared: Bool, unixSeconds: UInt64) throws -> MobileTotpDetail
|
||||
|
||||
func setWatchSnapshotUnavailable(unpaired: Bool, detail: String) throws -> MobileWatchSnapshotStatus
|
||||
|
||||
func state() throws -> MobileAuthenticationState
|
||||
|
||||
func totpDetail(path: String, unixSeconds: UInt64) throws -> MobileTotpDetail
|
||||
@@ -675,6 +683,8 @@ public protocol MobileAuthenticationProtocol: AnyObject, Sendable {
|
||||
|
||||
func updateEntryEditor(editor: UInt64, fields: [MobileEntryEditorInput]) throws -> MobileEntryEditorPage
|
||||
|
||||
func watchSnapshotStatus() throws -> MobileWatchSnapshotStatus
|
||||
|
||||
}
|
||||
open class MobileAuthentication: MobileAuthenticationProtocol, @unchecked Sendable {
|
||||
fileprivate let handle: UInt64
|
||||
@@ -729,6 +739,16 @@ open class MobileAuthentication: MobileAuthenticationProtocol, @unchecked Sendab
|
||||
|
||||
|
||||
|
||||
open func acknowledgeWatchSnapshot(receipt: Data)throws -> MobileWatchSnapshotStatus {
|
||||
return try FfiConverterTypeMobileWatchSnapshotStatus_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) {
|
||||
uniffiCallStatus in
|
||||
uniffi_ironstorage_apple_fn_method_mobileauthentication_acknowledge_watch_snapshot(
|
||||
self.uniffiCloneHandle(),
|
||||
FfiConverterData.lower(receipt),uniffiCallStatus
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
open func addEntryEditorField(editor: UInt64, kind: MobileEntryEditorFieldKind, name: String?, value: String)throws -> MobileEntryEditorPage {
|
||||
return try FfiConverterTypeMobileEntryEditorPage_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) {
|
||||
uniffiCallStatus in
|
||||
@@ -833,6 +853,17 @@ open func entryPage(path: String, unixSeconds: UInt64)throws -> MobileEntryPres
|
||||
})
|
||||
}
|
||||
|
||||
open func failWatchSnapshot(revision: UInt64, detail: String)throws -> MobileWatchSnapshotStatus {
|
||||
return try FfiConverterTypeMobileWatchSnapshotStatus_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) {
|
||||
uniffiCallStatus in
|
||||
uniffi_ironstorage_apple_fn_method_mobileauthentication_fail_watch_snapshot(
|
||||
self.uniffiCloneHandle(),
|
||||
FfiConverterUInt64.lower(revision),
|
||||
FfiConverterString.lower(detail),uniffiCallStatus
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
open func generateEntryEditorPassword(editor: UInt64, length: UInt32?, noSymbols: Bool)throws -> MobileEntryEditorPage {
|
||||
return try FfiConverterTypeMobileEntryEditorPage_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) {
|
||||
uniffiCallStatus in
|
||||
@@ -904,6 +935,16 @@ open func prepareEntryMutation(path: String, action: MobileMutationAction)throws
|
||||
})
|
||||
}
|
||||
|
||||
open func prepareWatchSnapshot(platformPairingIdentity: String)throws -> MobileWatchSnapshotTransfer {
|
||||
return try FfiConverterTypeMobileWatchSnapshotTransfer_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) {
|
||||
uniffiCallStatus in
|
||||
uniffi_ironstorage_apple_fn_method_mobileauthentication_prepare_watch_snapshot(
|
||||
self.uniffiCloneHandle(),
|
||||
FfiConverterString.lower(platformPairingIdentity),uniffiCallStatus
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
open func removeApplicationToken()throws {try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) {
|
||||
uniffiCallStatus in
|
||||
uniffi_ironstorage_apple_fn_method_mobileauthentication_remove_application_token(
|
||||
@@ -1030,6 +1071,17 @@ open func setTotpWatchShared(path: String, shared: Bool, unixSeconds: UInt64)thr
|
||||
})
|
||||
}
|
||||
|
||||
open func setWatchSnapshotUnavailable(unpaired: Bool, detail: String)throws -> MobileWatchSnapshotStatus {
|
||||
return try FfiConverterTypeMobileWatchSnapshotStatus_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) {
|
||||
uniffiCallStatus in
|
||||
uniffi_ironstorage_apple_fn_method_mobileauthentication_set_watch_snapshot_unavailable(
|
||||
self.uniffiCloneHandle(),
|
||||
FfiConverterBool.lower(unpaired),
|
||||
FfiConverterString.lower(detail),uniffiCallStatus
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
open func state()throws -> MobileAuthenticationState {
|
||||
return try FfiConverterTypeMobileAuthenticationState_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) {
|
||||
uniffiCallStatus in
|
||||
@@ -1100,6 +1152,15 @@ open func updateEntryEditor(editor: UInt64, fields: [MobileEntryEditorInput])thr
|
||||
})
|
||||
}
|
||||
|
||||
open func watchSnapshotStatus()throws -> MobileWatchSnapshotStatus {
|
||||
return try FfiConverterTypeMobileWatchSnapshotStatus_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) {
|
||||
uniffiCallStatus in
|
||||
uniffi_ironstorage_apple_fn_method_mobileauthentication_watch_snapshot_status(
|
||||
self.uniffiCloneHandle(),uniffiCallStatus
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -4453,12 +4514,14 @@ public func FfiConverterTypeMobileTotpRow_lower(_ value: MobileTotpRow) -> RustB
|
||||
|
||||
public struct MobileWatchSnapshotStatus: Equatable, Hashable {
|
||||
public var state: MobileWatchSnapshotState
|
||||
public var revision: UInt64?
|
||||
public var detail: String
|
||||
|
||||
// Default memberwise initializers are never public by default, so we
|
||||
// declare one manually.
|
||||
public init(state: MobileWatchSnapshotState, detail: String) {
|
||||
public init(state: MobileWatchSnapshotState, revision: UInt64?, detail: String) {
|
||||
self.state = state
|
||||
self.revision = revision
|
||||
self.detail = detail
|
||||
}
|
||||
|
||||
@@ -4479,12 +4542,14 @@ public struct FfiConverterTypeMobileWatchSnapshotStatus: FfiConverterRustBuffer
|
||||
return
|
||||
try MobileWatchSnapshotStatus(
|
||||
state: FfiConverterTypeMobileWatchSnapshotState.read(from: &buf),
|
||||
revision: FfiConverterOptionUInt64.read(from: &buf),
|
||||
detail: FfiConverterString.read(from: &buf)
|
||||
)
|
||||
}
|
||||
|
||||
public static func write(_ value: MobileWatchSnapshotStatus, into buf: inout [UInt8]) {
|
||||
FfiConverterTypeMobileWatchSnapshotState.write(value.state, into: &buf)
|
||||
FfiConverterOptionUInt64.write(value.revision, into: &buf)
|
||||
FfiConverterString.write(value.detail, into: &buf)
|
||||
}
|
||||
}
|
||||
@@ -4505,6 +4570,68 @@ public func FfiConverterTypeMobileWatchSnapshotStatus_lower(_ value: MobileWatch
|
||||
}
|
||||
|
||||
|
||||
public struct MobileWatchSnapshotTransfer: Equatable, Hashable {
|
||||
public var revision: UInt64
|
||||
public var selectedEntries: UInt32
|
||||
public var snapshot: Data
|
||||
public var deliveredReceipt: Data
|
||||
|
||||
// Default memberwise initializers are never public by default, so we
|
||||
// declare one manually.
|
||||
public init(revision: UInt64, selectedEntries: UInt32, snapshot: Data, deliveredReceipt: Data) {
|
||||
self.revision = revision
|
||||
self.selectedEntries = selectedEntries
|
||||
self.snapshot = snapshot
|
||||
self.deliveredReceipt = deliveredReceipt
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#if compiler(>=6)
|
||||
extension MobileWatchSnapshotTransfer: Sendable {}
|
||||
#endif
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public struct FfiConverterTypeMobileWatchSnapshotTransfer: FfiConverterRustBuffer {
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileWatchSnapshotTransfer {
|
||||
return
|
||||
try MobileWatchSnapshotTransfer(
|
||||
revision: FfiConverterUInt64.read(from: &buf),
|
||||
selectedEntries: FfiConverterUInt32.read(from: &buf),
|
||||
snapshot: FfiConverterData.read(from: &buf),
|
||||
deliveredReceipt: FfiConverterData.read(from: &buf)
|
||||
)
|
||||
}
|
||||
|
||||
public static func write(_ value: MobileWatchSnapshotTransfer, into buf: inout [UInt8]) {
|
||||
FfiConverterUInt64.write(value.revision, into: &buf)
|
||||
FfiConverterUInt32.write(value.selectedEntries, into: &buf)
|
||||
FfiConverterData.write(value.snapshot, into: &buf)
|
||||
FfiConverterData.write(value.deliveredReceipt, into: &buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileWatchSnapshotTransfer_lift(_ buf: RustBuffer) throws -> MobileWatchSnapshotTransfer {
|
||||
return try FfiConverterTypeMobileWatchSnapshotTransfer.lift(buf)
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileWatchSnapshotTransfer_lower(_ value: MobileWatchSnapshotTransfer) -> RustBuffer {
|
||||
return FfiConverterTypeMobileWatchSnapshotTransfer.lower(value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
public enum MobileAppearance: Equatable, Hashable {
|
||||
|
||||
@@ -6618,6 +6745,9 @@ public enum MobileWatchPreferenceState: Equatable, Hashable {
|
||||
case appNotInstalled
|
||||
case ready
|
||||
case pending
|
||||
case delivered
|
||||
case current
|
||||
case failed
|
||||
|
||||
|
||||
|
||||
@@ -6649,6 +6779,12 @@ public struct FfiConverterTypeMobileWatchPreferenceState: FfiConverterRustBuffer
|
||||
|
||||
case 5: return .pending
|
||||
|
||||
case 6: return .delivered
|
||||
|
||||
case 7: return .current
|
||||
|
||||
case 8: return .failed
|
||||
|
||||
default: throw UniffiInternalError.unexpectedEnumCase
|
||||
}
|
||||
}
|
||||
@@ -6676,6 +6812,18 @@ public struct FfiConverterTypeMobileWatchPreferenceState: FfiConverterRustBuffer
|
||||
case .pending:
|
||||
writeInt(&buf, Int32(5))
|
||||
|
||||
|
||||
case .delivered:
|
||||
writeInt(&buf, Int32(6))
|
||||
|
||||
|
||||
case .current:
|
||||
writeInt(&buf, Int32(7))
|
||||
|
||||
|
||||
case .failed:
|
||||
writeInt(&buf, Int32(8))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6701,7 +6849,11 @@ public func FfiConverterTypeMobileWatchPreferenceState_lower(_ value: MobileWatc
|
||||
public enum MobileWatchSnapshotState: Equatable, Hashable {
|
||||
|
||||
case unavailable
|
||||
case unpaired
|
||||
case pending
|
||||
case delivered
|
||||
case current
|
||||
case failed
|
||||
|
||||
|
||||
|
||||
@@ -6725,7 +6877,15 @@ public struct FfiConverterTypeMobileWatchSnapshotState: FfiConverterRustBuffer {
|
||||
|
||||
case 1: return .unavailable
|
||||
|
||||
case 2: return .pending
|
||||
case 2: return .unpaired
|
||||
|
||||
case 3: return .pending
|
||||
|
||||
case 4: return .delivered
|
||||
|
||||
case 5: return .current
|
||||
|
||||
case 6: return .failed
|
||||
|
||||
default: throw UniffiInternalError.unexpectedEnumCase
|
||||
}
|
||||
@@ -6739,9 +6899,25 @@ public struct FfiConverterTypeMobileWatchSnapshotState: FfiConverterRustBuffer {
|
||||
writeInt(&buf, Int32(1))
|
||||
|
||||
|
||||
case .pending:
|
||||
case .unpaired:
|
||||
writeInt(&buf, Int32(2))
|
||||
|
||||
|
||||
case .pending:
|
||||
writeInt(&buf, Int32(3))
|
||||
|
||||
|
||||
case .delivered:
|
||||
writeInt(&buf, Int32(4))
|
||||
|
||||
|
||||
case .current:
|
||||
writeInt(&buf, Int32(5))
|
||||
|
||||
|
||||
case .failed:
|
||||
writeInt(&buf, Int32(6))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6786,6 +6962,30 @@ fileprivate struct FfiConverterOptionUInt32: FfiConverterRustBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
fileprivate struct FfiConverterOptionUInt64: FfiConverterRustBuffer {
|
||||
typealias SwiftType = UInt64?
|
||||
|
||||
public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
|
||||
guard let value = value else {
|
||||
writeInt(&buf, Int8(0))
|
||||
return
|
||||
}
|
||||
writeInt(&buf, Int8(1))
|
||||
FfiConverterUInt64.write(value, into: &buf)
|
||||
}
|
||||
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
|
||||
switch try readInt(&buf) as Int8 {
|
||||
case 0: return nil
|
||||
case 1: return try FfiConverterUInt64.read(from: &buf)
|
||||
default: throw UniffiInternalError.unexpectedOptionalTag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
@@ -7497,6 +7697,9 @@ private let initializationResult: InitializationResult = {
|
||||
if (uniffi_ironstorage_apple_checksum_func_set_selected_mobile_tab() != 65280) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobileauthentication_acknowledge_watch_snapshot() != 12885) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobileauthentication_add_entry_editor_field() != 58790) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
@@ -7527,6 +7730,9 @@ private let initializationResult: InitializationResult = {
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobileauthentication_entry_page() != 9073) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobileauthentication_fail_watch_snapshot() != 56313) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobileauthentication_generate_entry_editor_password() != 34289) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
@@ -7548,6 +7754,9 @@ private let initializationResult: InitializationResult = {
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobileauthentication_prepare_entry_mutation() != 6234) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobileauthentication_prepare_watch_snapshot() != 14176) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobileauthentication_remove_application_token() != 11452) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
@@ -7584,6 +7793,9 @@ private let initializationResult: InitializationResult = {
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobileauthentication_set_totp_watch_shared() != 57472) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobileauthentication_set_watch_snapshot_unavailable() != 49094) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobileauthentication_state() != 60826) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
@@ -7605,6 +7817,9 @@ private let initializationResult: InitializationResult = {
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobileauthentication_update_entry_editor() != 30139) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobileauthentication_watch_snapshot_status() != 17344) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobilehomeoperation_cached() != 32436) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
|
||||
@@ -253,6 +253,11 @@ uint64_t uniffi_ironstorage_apple_fn_clone_mobileauthentication(uint64_t handle,
|
||||
void uniffi_ironstorage_apple_fn_free_mobileauthentication(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_ACKNOWLEDGE_WATCH_SNAPSHOT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_ACKNOWLEDGE_WATCH_SNAPSHOT
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_acknowledge_watch_snapshot(uint64_t ptr, RustBuffer receipt, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_ADD_ENTRY_EDITOR_FIELD
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_ADD_ENTRY_EDITOR_FIELD
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_add_entry_editor_field(uint64_t ptr, uint64_t editor, RustBuffer kind, RustBuffer name, RustBuffer value, RustCallStatus *_Nonnull out_status
|
||||
@@ -303,6 +308,11 @@ RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_entry_editor(
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_entry_page(uint64_t ptr, RustBuffer path, uint64_t unix_seconds, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_FAIL_WATCH_SNAPSHOT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_FAIL_WATCH_SNAPSHOT
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_fail_watch_snapshot(uint64_t ptr, uint64_t revision, RustBuffer detail, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_GENERATE_ENTRY_EDITOR_PASSWORD
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_GENERATE_ENTRY_EDITOR_PASSWORD
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_generate_entry_editor_password(uint64_t ptr, uint64_t editor, RustBuffer length, int8_t no_symbols, RustCallStatus *_Nonnull out_status
|
||||
@@ -338,6 +348,11 @@ RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_preferences(u
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_prepare_entry_mutation(uint64_t ptr, RustBuffer path, RustBuffer action, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_PREPARE_WATCH_SNAPSHOT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_PREPARE_WATCH_SNAPSHOT
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_prepare_watch_snapshot(uint64_t ptr, RustBuffer platform_pairing_identity, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_REMOVE_APPLICATION_TOKEN
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_REMOVE_APPLICATION_TOKEN
|
||||
void uniffi_ironstorage_apple_fn_method_mobileauthentication_remove_application_token(uint64_t ptr, RustCallStatus *_Nonnull out_status
|
||||
@@ -398,6 +413,11 @@ void uniffi_ironstorage_apple_fn_method_mobileauthentication_set_mobile_appearan
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_set_totp_watch_shared(uint64_t ptr, RustBuffer path, int8_t shared, uint64_t unix_seconds, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_SET_WATCH_SNAPSHOT_UNAVAILABLE
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_SET_WATCH_SNAPSHOT_UNAVAILABLE
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_set_watch_snapshot_unavailable(uint64_t ptr, int8_t unpaired, RustBuffer detail, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_STATE
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_STATE
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_state(uint64_t ptr, RustCallStatus *_Nonnull out_status
|
||||
@@ -433,6 +453,11 @@ RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_unlock_totp(u
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_update_entry_editor(uint64_t ptr, uint64_t editor, RustBuffer fields, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_WATCH_SNAPSHOT_STATUS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEAUTHENTICATION_WATCH_SNAPSHOT_STATUS
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobileauthentication_watch_snapshot_status(uint64_t ptr, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_CLONE_MOBILEHOMEOPERATION
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_CLONE_MOBILEHOMEOPERATION
|
||||
uint64_t uniffi_ironstorage_apple_fn_clone_mobilehomeoperation(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
@@ -973,6 +998,12 @@ uint16_t uniffi_ironstorage_apple_checksum_func_replace_configured_mobile_applic
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_FUNC_SET_SELECTED_MOBILE_TAB
|
||||
uint16_t uniffi_ironstorage_apple_checksum_func_set_selected_mobile_tab(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_ACKNOWLEDGE_WATCH_SNAPSHOT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_ACKNOWLEDGE_WATCH_SNAPSHOT
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobileauthentication_acknowledge_watch_snapshot(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_ADD_ENTRY_EDITOR_FIELD
|
||||
@@ -1033,6 +1064,12 @@ uint16_t uniffi_ironstorage_apple_checksum_method_mobileauthentication_entry_edi
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_ENTRY_PAGE
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobileauthentication_entry_page(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_FAIL_WATCH_SNAPSHOT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_FAIL_WATCH_SNAPSHOT
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobileauthentication_fail_watch_snapshot(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_GENERATE_ENTRY_EDITOR_PASSWORD
|
||||
@@ -1075,6 +1112,12 @@ uint16_t uniffi_ironstorage_apple_checksum_method_mobileauthentication_preferenc
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_PREPARE_ENTRY_MUTATION
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobileauthentication_prepare_entry_mutation(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_PREPARE_WATCH_SNAPSHOT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_PREPARE_WATCH_SNAPSHOT
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobileauthentication_prepare_watch_snapshot(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_REMOVE_APPLICATION_TOKEN
|
||||
@@ -1147,6 +1190,12 @@ uint16_t uniffi_ironstorage_apple_checksum_method_mobileauthentication_set_mobil
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_SET_TOTP_WATCH_SHARED
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobileauthentication_set_totp_watch_shared(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_SET_WATCH_SNAPSHOT_UNAVAILABLE
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_SET_WATCH_SNAPSHOT_UNAVAILABLE
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobileauthentication_set_watch_snapshot_unavailable(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_STATE
|
||||
@@ -1189,6 +1238,12 @@ uint16_t uniffi_ironstorage_apple_checksum_method_mobileauthentication_unlock_to
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_UPDATE_ENTRY_EDITOR
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobileauthentication_update_entry_editor(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_WATCH_SNAPSHOT_STATUS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_WATCH_SNAPSHOT_STATUS
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobileauthentication_watch_snapshot_status(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEHOMEOPERATION_CACHED
|
||||
|
||||
@@ -14,11 +14,161 @@ extension Notification.Name {
|
||||
static let ironStorageWatchSnapshotDidChange = Notification.Name(
|
||||
"de.rfc1437.ironstorage.watch-snapshot-did-change"
|
||||
)
|
||||
static let ironStorageWatchStatusDidChange = Notification.Name(
|
||||
"de.rfc1437.ironstorage.watch-status-did-change"
|
||||
)
|
||||
static let ironStorageKeyMaterialDidChange = Notification.Name(
|
||||
"de.rfc1437.ironstorage.key-material-did-change"
|
||||
)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private final class WatchSnapshotCoordinator: NSObject, WCSessionDelegate {
|
||||
private static let snapshotKey = "de.rfc1437.ironstorage.watch.snapshot"
|
||||
private static let deliveredReceiptKey = "de.rfc1437.ironstorage.watch.delivered"
|
||||
|
||||
private let authentication: MobileAuthentication?
|
||||
private var session: WCSession?
|
||||
private var syncTask: Task<Void, Never>?
|
||||
private var generation = 0
|
||||
|
||||
var isSupported: Bool { WCSession.isSupported() }
|
||||
var isPaired: Bool { session?.isPaired ?? false }
|
||||
var isWatchAppInstalled: Bool { session?.isWatchAppInstalled ?? false }
|
||||
|
||||
init(authentication: MobileAuthentication?) {
|
||||
self.authentication = authentication
|
||||
super.init()
|
||||
for name in [
|
||||
Notification.Name.ironStorageWatchSnapshotDidChange,
|
||||
.ironStorageLocalStoreDidChange,
|
||||
.ironStorageAuthenticationDidChange,
|
||||
] {
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(synchronize),
|
||||
name: name,
|
||||
object: nil
|
||||
)
|
||||
}
|
||||
guard WCSession.isSupported() else {
|
||||
recordUnavailable(unpaired: false, detail: "Apple Watch connectivity is unavailable on this device.")
|
||||
return
|
||||
}
|
||||
let session = WCSession.default
|
||||
self.session = session
|
||||
session.delegate = self
|
||||
session.activate()
|
||||
}
|
||||
|
||||
deinit {
|
||||
syncTask?.cancel()
|
||||
session?.delegate = nil
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
@objc func synchronize() {
|
||||
generation += 1
|
||||
syncTask?.cancel()
|
||||
guard let authentication else { return }
|
||||
guard let session, session.activationState == .activated else { return }
|
||||
guard session.isPaired else {
|
||||
try? session.updateApplicationContext([:])
|
||||
recordUnavailable(unpaired: true, detail: "Pair an Apple Watch to synchronize selected TOTP entries.")
|
||||
return
|
||||
}
|
||||
guard session.isWatchAppInstalled, let directory = session.watchDirectoryURL else {
|
||||
try? session.updateApplicationContext([:])
|
||||
recordUnavailable(unpaired: false, detail: "Install IronStorage on the paired Apple Watch.")
|
||||
return
|
||||
}
|
||||
let pairingIdentity = directory.lastPathComponent
|
||||
let current = generation
|
||||
syncTask = Task { [weak self] in
|
||||
let result = await Task.detached(priority: .utility) {
|
||||
Result { try authentication.prepareWatchSnapshot(platformPairingIdentity: pairingIdentity) }
|
||||
}.value
|
||||
guard !Task.isCancelled, let self, current == generation else { return }
|
||||
switch result {
|
||||
case var .success(transfer):
|
||||
var snapshot = Data(transfer.snapshot)
|
||||
defer {
|
||||
snapshot.resetBytes(in: 0..<snapshot.count)
|
||||
transfer.snapshot.removeAll(keepingCapacity: false)
|
||||
}
|
||||
do {
|
||||
try session.updateApplicationContext([
|
||||
Self.snapshotKey: snapshot,
|
||||
Self.deliveredReceiptKey: Data(transfer.deliveredReceipt),
|
||||
])
|
||||
} catch {
|
||||
_ = try? authentication.failWatchSnapshot(
|
||||
revision: transfer.revision,
|
||||
detail: "The latest Apple Watch snapshot could not be queued."
|
||||
)
|
||||
}
|
||||
notifyStatusChanged()
|
||||
case .failure:
|
||||
// A locked GPG key leaves the previous replacement pending. Authentication
|
||||
// changes trigger a fresh attempt without persisting snapshot bytes in Swift.
|
||||
notifyStatusChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func recordUnavailable(unpaired: Bool, detail: String) {
|
||||
guard let authentication else { return }
|
||||
_ = try? authentication.setWatchSnapshotUnavailable(unpaired: unpaired, detail: detail)
|
||||
notifyStatusChanged()
|
||||
}
|
||||
|
||||
private func notifyStatusChanged() {
|
||||
NotificationCenter.default.post(name: .ironStorageWatchStatusDidChange, object: nil)
|
||||
}
|
||||
|
||||
nonisolated func session(
|
||||
_ session: WCSession,
|
||||
activationDidCompleteWith activationState: WCSessionActivationState,
|
||||
error: Error?
|
||||
) {
|
||||
Task { @MainActor [weak self] in self?.synchronize() }
|
||||
}
|
||||
|
||||
nonisolated func sessionDidBecomeInactive(_ session: WCSession) {
|
||||
Task { @MainActor [weak self] in
|
||||
try? session.updateApplicationContext([:])
|
||||
self?.recordUnavailable(unpaired: true, detail: "The active Apple Watch changed; a fresh snapshot is required.")
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated func sessionDidDeactivate(_ session: WCSession) {
|
||||
session.activate()
|
||||
}
|
||||
|
||||
nonisolated func sessionWatchStateDidChange(_ session: WCSession) {
|
||||
Task { @MainActor [weak self] in self?.synchronize() }
|
||||
}
|
||||
|
||||
nonisolated private func acknowledge(_ userInfo: [String: Any]) {
|
||||
guard
|
||||
let receipt = userInfo["de.rfc1437.ironstorage.watch.delivered"] as? Data
|
||||
else { return }
|
||||
Task { @MainActor [weak self] in
|
||||
guard let self, let authentication else { return }
|
||||
_ = try? authentication.acknowledgeWatchSnapshot(receipt: receipt)
|
||||
notifyStatusChanged()
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated func session(_ session: WCSession, didReceiveUserInfo userInfo: [String: Any] = [:]) {
|
||||
acknowledge(userInfo)
|
||||
}
|
||||
|
||||
nonisolated func session(_ session: WCSession, didReceiveMessage message: [String: Any]) {
|
||||
acknowledge(message)
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
final class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
var window: UIWindow?
|
||||
@@ -73,6 +223,7 @@ private protocol MobileTabRoot: AnyObject {
|
||||
private final class AppContext: NSObject, UITabBarControllerDelegate {
|
||||
private let tabs = UITabBarController()
|
||||
private let authentication = try? mobileAuthentication()
|
||||
private lazy var watchSnapshot = WatchSnapshotCoordinator(authentication: authentication)
|
||||
private var navigationControllers: [UINavigationController] = []
|
||||
private var restoreTask: Task<Void, Never>?
|
||||
private var authenticationMonitor: Task<Void, Never>?
|
||||
@@ -83,6 +234,7 @@ private final class AppContext: NSObject, UITabBarControllerDelegate {
|
||||
}
|
||||
|
||||
func makeRootController() -> UIViewController {
|
||||
watchSnapshot.synchronize()
|
||||
let shell = mobileShellFixture(state: .loading)
|
||||
navigationControllers = shell.pages.map { page in
|
||||
let root: UIViewController = switch page.tab {
|
||||
@@ -93,7 +245,11 @@ private final class AppContext: NSObject, UITabBarControllerDelegate {
|
||||
case .totp:
|
||||
TotpListViewController(shellPage: page, authentication: authentication)
|
||||
case .preferences:
|
||||
PreferencesViewController(page: page, authentication: authentication)
|
||||
PreferencesViewController(
|
||||
page: page,
|
||||
authentication: authentication,
|
||||
watchSnapshot: watchSnapshot
|
||||
)
|
||||
case .home:
|
||||
ShellViewController(page: page, authentication: authentication)
|
||||
}
|
||||
@@ -852,9 +1008,7 @@ private final class ShellViewController: UITableViewController, MobileTabRoot {
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private final class PreferencesViewController: UITableViewController, MobileTabRoot,
|
||||
WCSessionDelegate
|
||||
{
|
||||
private final class PreferencesViewController: UITableViewController, MobileTabRoot {
|
||||
fileprivate let shellTab = MobileTab.preferences
|
||||
private var page: MobilePage
|
||||
private let authentication: MobileAuthentication?
|
||||
@@ -862,21 +1016,20 @@ private final class PreferencesViewController: UITableViewController, MobileTabR
|
||||
private var state: MobileAuthenticationState?
|
||||
private var preferences: MobilePreferences?
|
||||
private var gitIdentity: MobileGitIdentity?
|
||||
private var watchSession: WCSession?
|
||||
private let watchSnapshot: WatchSnapshotCoordinator
|
||||
private var preferenceTask: Task<Void, Never>?
|
||||
private var loadTask: Task<Void, Never>?
|
||||
private var loadGeneration = 0
|
||||
|
||||
init(page: MobilePage, authentication: MobileAuthentication?) {
|
||||
init(
|
||||
page: MobilePage,
|
||||
authentication: MobileAuthentication?,
|
||||
watchSnapshot: WatchSnapshotCoordinator
|
||||
) {
|
||||
self.page = page
|
||||
self.authentication = authentication
|
||||
self.watchSnapshot = watchSnapshot
|
||||
super.init(style: .insetGrouped)
|
||||
if WCSession.isSupported() {
|
||||
let session = WCSession.default
|
||||
watchSession = session
|
||||
session.delegate = self
|
||||
session.activate()
|
||||
}
|
||||
title = page.title
|
||||
navigationItem.largeTitleDisplayMode = .always
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||
@@ -891,6 +1044,12 @@ private final class PreferencesViewController: UITableViewController, MobileTabR
|
||||
name: .ironStorageAuthenticationDidChange,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(authenticationDidChange),
|
||||
name: .ironStorageWatchStatusDidChange,
|
||||
object: nil
|
||||
)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
@@ -899,7 +1058,6 @@ private final class PreferencesViewController: UITableViewController, MobileTabR
|
||||
}
|
||||
|
||||
deinit {
|
||||
watchSession?.delegate = nil
|
||||
preferenceTask?.cancel()
|
||||
loadTask?.cancel()
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
@@ -1380,8 +1538,19 @@ private final class PreferencesViewController: UITableViewController, MobileTabR
|
||||
|
||||
private func openWatchPreference() {
|
||||
switch preferences?.watchState {
|
||||
case .ready, .pending:
|
||||
case .ready, .pending, .delivered, .current:
|
||||
selectTotpTab()
|
||||
case .failed:
|
||||
let alert = UIAlertController(
|
||||
title: "Apple Watch Synchronization Failed",
|
||||
message: preferences?.watchDetail ?? "The latest snapshot could not be synchronized.",
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
||||
alert.addAction(UIAlertAction(title: "Retry", style: .default) { [weak self] _ in
|
||||
self?.watchSnapshot.synchronize()
|
||||
})
|
||||
present(alert, animated: true)
|
||||
case .notPaired:
|
||||
presentWatchGuidance(
|
||||
title: "Pair an Apple Watch",
|
||||
@@ -1429,6 +1598,9 @@ private final class PreferencesViewController: UITableViewController, MobileTabR
|
||||
switch state {
|
||||
case .ready: "applewatch.radiowaves.left.and.right"
|
||||
case .pending: "arrow.triangle.2.circlepath"
|
||||
case .delivered: "arrow.down.circle"
|
||||
case .current: "checkmark.circle.fill"
|
||||
case .failed: "exclamationmark.triangle"
|
||||
case .notPaired, .appNotInstalled: "applewatch.slash"
|
||||
default: "applewatch"
|
||||
}
|
||||
@@ -1480,9 +1652,9 @@ private final class PreferencesViewController: UITableViewController, MobileTabR
|
||||
state = try? authentication?.state()
|
||||
gitIdentity = try? authentication?.gitIdentity()
|
||||
preferences = try? authentication?.preferences(
|
||||
watchSupported: WCSession.isSupported(),
|
||||
watchPaired: watchSession?.isPaired ?? false,
|
||||
watchAppInstalled: watchSession?.isWatchAppInstalled ?? false
|
||||
watchSupported: watchSnapshot.isSupported,
|
||||
watchPaired: watchSnapshot.isPaired,
|
||||
watchAppInstalled: watchSnapshot.isWatchAppInstalled
|
||||
)
|
||||
if let appearance = preferences?.appearance {
|
||||
tabBarController?.overrideUserInterfaceStyle = switch appearance {
|
||||
@@ -1494,23 +1666,6 @@ private final class PreferencesViewController: UITableViewController, MobileTabR
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
nonisolated func session(
|
||||
_ session: WCSession,
|
||||
activationDidCompleteWith activationState: WCSessionActivationState,
|
||||
error: Error?
|
||||
) {
|
||||
Task { @MainActor [weak self] in self?.refreshState() }
|
||||
}
|
||||
|
||||
nonisolated func sessionDidBecomeInactive(_ session: WCSession) {}
|
||||
|
||||
nonisolated func sessionDidDeactivate(_ session: WCSession) {
|
||||
session.activate()
|
||||
}
|
||||
|
||||
nonisolated func sessionWatchStateDidChange(_ session: WCSession) {
|
||||
Task { @MainActor [weak self] in self?.refreshState() }
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@@ -2220,6 +2375,12 @@ private final class TotpListViewController: UITableViewController, MobileTabRoot
|
||||
name: .ironStorageWatchSnapshotDidChange,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(localStoreDidChange),
|
||||
name: .ironStorageWatchStatusDidChange,
|
||||
object: nil
|
||||
)
|
||||
refreshState()
|
||||
}
|
||||
|
||||
@@ -2691,8 +2852,6 @@ private final class TotpListViewController: UITableViewController, MobileTabRoot
|
||||
object: authentication
|
||||
)
|
||||
refreshState()
|
||||
} else {
|
||||
showUnavailable(title: failure.title, detail: failure.detail, image: "exclamationmark.triangle")
|
||||
}
|
||||
presentAuthenticationFailure(failure)
|
||||
}
|
||||
@@ -2870,6 +3029,12 @@ private final class TotpDetailViewController: UITableViewController {
|
||||
name: .ironStorageLocalStoreDidChange,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(entryDidChange),
|
||||
name: .ironStorageWatchStatusDidChange,
|
||||
object: nil
|
||||
)
|
||||
apply(detail)
|
||||
startTimer()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,54 @@
|
||||
import SwiftUI
|
||||
import WatchConnectivity
|
||||
|
||||
private final class WatchSnapshotTransport: NSObject, ObservableObject, WCSessionDelegate {
|
||||
private static let snapshotKey = "de.rfc1437.ironstorage.watch.snapshot"
|
||||
private static let deliveredReceiptKey = "de.rfc1437.ironstorage.watch.delivered"
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
guard WCSession.isSupported() else { return }
|
||||
let session = WCSession.default
|
||||
session.delegate = self
|
||||
session.activate()
|
||||
}
|
||||
|
||||
private func receive(_ applicationContext: [String: Any], session: WCSession) {
|
||||
guard
|
||||
applicationContext[Self.snapshotKey] is Data,
|
||||
let receipt = applicationContext[Self.deliveredReceiptKey] as? Data
|
||||
else { return }
|
||||
let acknowledgement = [Self.deliveredReceiptKey: receipt]
|
||||
if session.isReachable {
|
||||
session.sendMessage(acknowledgement, replyHandler: nil) { _ in
|
||||
session.transferUserInfo(acknowledgement)
|
||||
}
|
||||
} else {
|
||||
session.transferUserInfo(acknowledgement)
|
||||
}
|
||||
}
|
||||
|
||||
func session(
|
||||
_ session: WCSession,
|
||||
activationDidCompleteWith activationState: WCSessionActivationState,
|
||||
error: Error?
|
||||
) {
|
||||
guard activationState == .activated, error == nil else { return }
|
||||
receive(session.receivedApplicationContext, session: session)
|
||||
}
|
||||
|
||||
func session(
|
||||
_ session: WCSession,
|
||||
didReceiveApplicationContext applicationContext: [String: Any]
|
||||
) {
|
||||
receive(applicationContext, session: session)
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
struct IronStorageWatchApp: App {
|
||||
@StateObject private var transport = WatchSnapshotTransport()
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentUnavailableView("No TOTP Codes", systemImage: "timer")
|
||||
|
||||
Reference in New Issue
Block a user