Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Biometrics lock (Lock app with FaceID/TouchID/iOS Password) #2398

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1afab5a
Biometrics lock concept
bgoncal Aug 17, 2023
a7564fb
Improvements on authentication handler and protection overlay
bgoncal Aug 21, 2023
1d757ca
Add localized placeholders
bgoncal Aug 21, 2023
c56f638
Lint autocorrect
bgoncal Nov 1, 2023
1335665
PR improvements and macOS taking in consideration
bgoncal Nov 2, 2023
f85e6ae
Linter autocorrect
bgoncal Nov 2, 2023
9fd70fa
Add missing self reference
bgoncal Nov 7, 2023
5850f35
Fix typo and move biometrics logic
bgoncal Nov 16, 2023
ce1f140
Added new UI for the "lock" screen, moved some code around to a prope…
bgoncal Nov 16, 2023
96cdfac
Biometrics lock concept
bgoncal Aug 17, 2023
fc15eac
Improvements on authentication handler and protection overlay
bgoncal Aug 21, 2023
b728292
Add localized placeholders
bgoncal Aug 21, 2023
fe07834
Lint autocorrect
bgoncal Nov 1, 2023
6ffe64c
PR improvements and macOS taking in consideration
bgoncal Nov 2, 2023
2a9ddda
Linter autocorrect
bgoncal Nov 2, 2023
1f0b692
Add missing self reference
bgoncal Nov 7, 2023
e051dd4
Fix typo and move biometrics logic
bgoncal Nov 16, 2023
63c9c46
Added new UI for the "lock" screen, moved some code around to a prope…
bgoncal Nov 16, 2023
8c656d3
Merge branch 'Biometric-lock-concept' of github.com:bgoncal/HA-iOS in…
bgoncal Dec 4, 2023
9f1a418
Merge branch 'master' into Biometric-lock-concept
bgoncal Dec 5, 2023
75982af
Removed mac implementation and moved to SwiftUI for iOS
bgoncal Dec 5, 2023
27cf8ac
Present on top most controller
bgoncal Dec 5, 2023
97839c3
Linter
bgoncal Dec 5, 2023
77e5077
Present biometric lock on window level
bgoncal Dec 5, 2023
27e2a63
Remove unneeded check
bgoncal Dec 6, 2023
2bc8f60
Improve logic to hide security toggle for mac
bgoncal Dec 6, 2023
072af4e
Use camel case for spacing values
bgoncal Dec 6, 2023
6674456
Auto disable biometric lock when delete all servers
bgoncal Dec 6, 2023
eec44b0
Re-add hebrew and estonian languages
bgoncal Dec 6, 2023
5dbb72e
Merge branch 'master' into Biometric-lock-concept
bgoncal Dec 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
295 changes: 168 additions & 127 deletions HomeAssistant.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

128 changes: 65 additions & 63 deletions Sources/App/Resources/Info.plist
bgoncal marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -547,41 +547,6 @@
</array>
</dict>
</array>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>WebView</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).WebViewSceneDelegate</string>
</dict>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SettingsSceneDelegate</string>
<key>UISceneConfigurationName</key>
<string>Settings</string>
</dict>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).AboutSceneDelegate</string>
<key>UISceneConfigurationName</key>
<string>About</string>
</dict>
</array>
</dict>
</dict>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>FIREBASE_ANALYTICS_COLLECTION_ENABLED</key>
Expand Down Expand Up @@ -623,6 +588,8 @@
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NFCReaderUsageDescription</key>
<string>Reading and writing NFC tags allows you to trigger events.</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
Expand All @@ -634,21 +601,36 @@
<string>_home-assistant._tcp</string>
<string>_matter._tcp</string>
</array>
<key>NSCameraUsageDescription</key>
<string>Take photos and send them to your Home Assistant server.</string>
<key>NSCrossWebsiteTrackingUsageDescription</key>
<string>Optionally enable cross-website tracking if your configuration requires it.</string>
<key>NSFaceIDUsageDescription</key>
<string>We use FaceID to protect the App access.</string>
<key>NSFocusStatusUsageDescription</key>
<string>Report your focus status as a sensor.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Locate and communicate with your Home Assistant instance.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We suggest selecting &quot;Always Allow&quot; for the best location experience.
Selecting &quot;Only While Using the App&quot; will disable iBeacons, geofences,
<string>We suggest selecting "Always Allow" for the best location experience.
Selecting "Only While Using the App" will disable iBeacons, geofences,
background location updates and accurate reporting.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>We always need access to your location for features like iBeacons, geofences,
background location updates and accurate reporting.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Only allowing location access while app is in use will disable iBeacons, geofences,
background location updates and accurate reporting.</string>
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
<key>TemporaryFullAccuracyReasonManualUpdate</key>
<string>[localized in strings file]</string>
</dict>
<key>NSLocationUsageDescription</key>
<string>When enabled, your device location is sent to your Home Assistant server as a device
tracker.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Only allowing location access while app is in use will disable iBeacons, geofences,
background location updates and accurate reporting.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Record audio using your Home Assistant frontend.</string>
<key>NSMotionUsageDescription</key>
<string>Motion is used to improve location updates with current motion type, as well as
provide basic pedometer data.</string>
Expand All @@ -658,24 +640,8 @@
<string>Photo Library access is needed to allow saving photos from the web view.</string>
<key>NSSiriUsageDescription</key>
<string>We use Siri to allow created shortcuts to interact with the app.</string>
<key>NFCReaderUsageDescription</key>
<string>Reading and writing NFC tags allows you to trigger events.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>Used to dictate text to Assist.</string>
<key>NSCameraUsageDescription</key>
<string>Take photos and send them to your Home Assistant server.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Record audio using your Home Assistant frontend.</string>
<key>NSFocusStatusUsageDescription</key>
<string>Report your focus status as a sensor.</string>
<key>com.apple.developer.nfc.readersession.felica.systemcodes</key>
<array>
<string>12FC</string>
</array>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>D2760000850101</string>
</array>
<key>NSUserActivityTypes</key>
<array>
<string>CallServiceIntent</string>
Expand All @@ -690,6 +656,41 @@
<string>WidgetOpenPageIntent</string>
<string>AssistIntent</string>
</array>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>WebView</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).WebViewSceneDelegate</string>
</dict>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>Settings</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SettingsSceneDelegate</string>
</dict>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>About</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).AboutSceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationShortcutItems</key>
<array>
<dict>
Expand Down Expand Up @@ -731,12 +732,13 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSCrossWebsiteTrackingUsageDescription</key>
<string>Optionally enable cross-website tracking if your configuration requires it.</string>
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
<key>TemporaryFullAccuracyReasonManualUpdate</key>
<string>[localized in strings file]</string>
</dict>
<key>com.apple.developer.nfc.readersession.felica.systemcodes</key>
<array>
<string>12FC</string>
</array>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>D2760000850101</string>
</array>
</dict>
</plist>
5 changes: 5 additions & 0 deletions Sources/App/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,11 @@ Home Assistant is free and open source home automation software with a focus on
"settings_details.general.visibility.options.dock_and_menu_bar" = "Dock and Menu Bar";
"settings_details.general.visibility.options.menu_bar" = "Menu Bar";
"settings_details.general.visibility.title" = "Show App In…";
"settings_details.general.security.title" = "Security";
"settings_details.general.security.action" = "Enable biometric lock";
"settings_details.general.security.footer" = "You will be required to authenticate with biometrics every time you open the app.";
"settings_details.general.security.authentication_policy" = "Authentication required";
"settings_details.general.security.unlock_button" = "Unlock";
"settings_details.location.background_refresh.disabled" = "Disabled";
"settings_details.location.background_refresh.enabled" = "Enabled";
"settings_details.location.background_refresh.title" = "Background Refresh";
Expand Down
11 changes: 11 additions & 0 deletions Sources/App/Resources/et.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ Home Assistant is free and open source home automation software with a focus on
"settings.developer.crashlytics_test.non_fatal.notification.body" = "When you press OK, a non-fatal error will be sent to Crashlytics. It may take up to 5 minutes to appear in the console.";
"settings.developer.crashlytics_test.non_fatal.notification.title" = "About to submit a non-fatal error";
"settings.developer.crashlytics_test.non_fatal.title" = "Test Crashlytics Non-Fatal Error";
"settings.developer.mock_thread_credentials_sharing.title" = "Simulator Thread Credentials Sharing";
"settings.developer.debug_strings.title" = "Debug strings";
"settings.developer.export_log_files.title" = "Export log files";
"settings.developer.footer" = "Don't use these if you don't know what you are doing!";
Expand Down Expand Up @@ -411,6 +412,11 @@ Home Assistant is free and open source home automation software with a focus on
"settings_details.general.visibility.options.dock_and_menu_bar" = "Dock and Menu Bar";
"settings_details.general.visibility.options.menu_bar" = "Menu Bar";
"settings_details.general.visibility.title" = "Show App In…";
"settings_details.general.security.title" = "Security";
"settings_details.general.security.action" = "Enable biometric lock";
"settings_details.general.security.footer" = "You will be required to authenticate with biometrics every time you open the app.";
"settings_details.general.security.authentication_policy" = "Authentication required";
"settings_details.general.security.unlock_button" = "Unlock";
"settings_details.location.background_refresh.disabled" = "Disabled";
"settings_details.location.background_refresh.enabled" = "Enabled";
"settings_details.location.background_refresh.title" = "Background Refresh";
Expand Down Expand Up @@ -769,3 +775,8 @@ Home Assistant is free and open source home automation software with a focus on
"widgets.open_page.not_configured" = "No Pages Available";
"widgets.open_page.title" = "Open Page";
"yes_label" = "Yes";
"thread.credentials.network_name_title" = "Network Name";
"thread.credentials.network_key_title" = "Network Key";
"thread.credentials.border_agent_id_title" = "Border Agent ID";
"thread.credentials.share_credentials_button_title" = "Share credential with Home Assistant";
"thread.credentials.screen_title" = "Thread Credentials";
Comment on lines +778 to +782
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bad merge?

11 changes: 11 additions & 0 deletions Sources/App/Resources/he.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ Home Assistant is free and open source home automation software with a focus on
"settings.developer.crashlytics_test.non_fatal.notification.body" = "When you press OK, a non-fatal error will be sent to Crashlytics. It may take up to 5 minutes to appear in the console.";
"settings.developer.crashlytics_test.non_fatal.notification.title" = "About to submit a non-fatal error";
"settings.developer.crashlytics_test.non_fatal.title" = "Test Crashlytics Non-Fatal Error";
"settings.developer.mock_thread_credentials_sharing.title" = "Simulator Thread Credentials Sharing";
"settings.developer.debug_strings.title" = "Debug strings";
"settings.developer.export_log_files.title" = "Export log files";
"settings.developer.footer" = "Don't use these if you don't know what you are doing!";
Expand Down Expand Up @@ -411,6 +412,11 @@ Home Assistant is free and open source home automation software with a focus on
"settings_details.general.visibility.options.dock_and_menu_bar" = "Dock and Menu Bar";
"settings_details.general.visibility.options.menu_bar" = "Menu Bar";
"settings_details.general.visibility.title" = "Show App In…";
"settings_details.general.security.title" = "Security";
"settings_details.general.security.action" = "Enable biometric lock";
"settings_details.general.security.footer" = "You will be required to authenticate with biometrics every time you open the app.";
"settings_details.general.security.authentication_policy" = "Authentication required";
"settings_details.general.security.unlock_button" = "Unlock";
"settings_details.location.background_refresh.disabled" = "Disabled";
"settings_details.location.background_refresh.enabled" = "Enabled";
"settings_details.location.background_refresh.title" = "Background Refresh";
Expand Down Expand Up @@ -769,3 +775,8 @@ Home Assistant is free and open source home automation software with a focus on
"widgets.open_page.not_configured" = "No Pages Available";
"widgets.open_page.title" = "Open Page";
"yes_label" = "Yes";
"thread.credentials.network_name_title" = "Network Name";
"thread.credentials.network_key_title" = "Network Key";
"thread.credentials.border_agent_id_title" = "Border Agent ID";
"thread.credentials.share_credentials_button_title" = "Share credential with Home Assistant";
"thread.credentials.screen_title" = "Thread Credentials";
28 changes: 28 additions & 0 deletions Sources/App/Settings/Security/SecurityViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Eureka
import FirebaseInstallations
import FirebaseMessaging
import PromiseKit
import RealmSwift
import Shared
import UIKit

class SecurityViewController: HAFormViewController {
override func viewDidLoad() {
super.viewDidLoad()

title = L10n.SettingsDetails.General.Security.title

form
+++ Section()
<<< SwitchRow("switch", {
$0.title = L10n.SettingsDetails.General.Security.action
$0.value = Current.settingsStore.biometricsRequired
$0.onChange { row in
Current.settingsStore.biometricsRequired = row.value ?? false
bgoncal marked this conversation as resolved.
Show resolved Hide resolved
}
})
+++ Section(
footer: L10n.SettingsDetails.General.Security.footer
)
}

Check warning on line 27 in Sources/App/Settings/Security/SecurityViewController.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/Settings/Security/SecurityViewController.swift#L10-L27

Added lines #L10 - L27 were not covered by tests
}
12 changes: 12 additions & 0 deletions Sources/App/Settings/SettingsRootDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
case servers
case location
case notifications
case security
case actions
case sensors
case complications
Expand All @@ -37,6 +38,7 @@
case .general: return SettingsRootDataSource.general()
case .location: return SettingsRootDataSource.location()
case .notifications: return SettingsRootDataSource.notifications()
case .security: return SettingsRootDataSource.security()

Check warning on line 41 in Sources/App/Settings/SettingsRootDataSource.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/Settings/SettingsRootDataSource.swift#L41

Added line #L41 was not covered by tests
case .actions: return SettingsRootDataSource.actions()
case .sensors: return SettingsRootDataSource.sensors()
case .complications: return SettingsRootDataSource.complications()
Expand Down Expand Up @@ -95,6 +97,16 @@
}
}

private static func security() -> SettingsButtonRow {
SettingsButtonRow {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does this work on catalyst? should we hide it or make it available -- if the latter, how do we deal with e.g. the settings scene?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In catalyst it was a bit trickier, because of the "enter background/foreground" thats happens all the time even with the touchID dialog, I treated it with an extra logic to only reauthenticate when the user has reply enter background.

When the user open settings scene, the main web view scene locks again until the user unlocks it.

Screen.Recording.2023-11-16.at.16.33.08.mov
RPReplay_Final1700149090.MP4

$0.title = L10n.SettingsDetails.General.Security.title
$0.icon = .keyChainIcon
$0.presentationMode = .show(controllerProvider: ControllerProvider.callback {
SecurityViewController()
}, onDismiss: nil)
}
}

Check warning on line 108 in Sources/App/Settings/SettingsRootDataSource.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/Settings/SettingsRootDataSource.swift#L100-L108

Added lines #L100 - L108 were not covered by tests

private static func actions() -> SettingsButtonRow {
SettingsButtonRow {
$0.title = L10n.SettingsDetails.Actions.title
Expand Down
6 changes: 5 additions & 1 deletion Sources/App/Settings/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,14 @@
}

if contentSections.contains(.general) {
form +++ Section()
let generalSection = Section()

Check warning on line 105 in Sources/App/Settings/SettingsViewController.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/Settings/SettingsViewController.swift#L105

Added line #L105 was not covered by tests
<<< SettingsRootDataSource.Row.general.row
<<< SettingsRootDataSource.Row.location.row
<<< SettingsRootDataSource.Row.notifications.row
if !Current.isCatalyst {
generalSection <<< SettingsRootDataSource.Row.security.row
}
form +++ generalSection

Check warning on line 112 in Sources/App/Settings/SettingsViewController.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/Settings/SettingsViewController.swift#L109-L112

Added lines #L109 - L112 were not covered by tests
}

if contentSections.contains(.integrations) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@testable import HomeAssistant
@testable import Shared
import XCTest

@available(iOS 13, *)
Expand Down
10 changes: 8 additions & 2 deletions Sources/App/WebView/WebViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
let server: Server

private var urlObserver: NSKeyValueObservation?
private var tokens = [HACancellable]()

Check warning on line 21 in Sources/App/WebView/WebViewController.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/WebView/WebViewController.swift#L21

Added line #L21 was not covered by tests

private let refreshControl = UIRefreshControl()

Check warning on line 23 in Sources/App/WebView/WebViewController.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/WebView/WebViewController.swift#L23

Added line #L23 was not covered by tests
private let sidebarGestureRecognizer: UIScreenEdgePanGestureRecognizer

private var keepAliveTimer: Timer?
Expand All @@ -37,7 +37,7 @@
}
}

private let settingsButton: UIButton! = {

Check warning on line 40 in Sources/App/WebView/WebViewController.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/WebView/WebViewController.swift#L40

Added line #L40 was not covered by tests
let button = UIButton()
button.setImage(
MaterialDesignIcons.cogIcon.image(ofSize: CGSize(width: 36, height: 36), color: .white),
Expand Down Expand Up @@ -322,7 +322,10 @@
}
}

init(server: Server, shouldLoadImmediately: Bool = false) {
init(
server: Server,
shouldLoadImmediately: Bool = false
) {

Check warning on line 328 in Sources/App/WebView/WebViewController.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/WebView/WebViewController.swift#L328

Added line #L328 was not covered by tests
self.server = server
self.sidebarGestureRecognizer = with(UIScreenEdgePanGestureRecognizer()) {
$0.edges = .left
Expand All @@ -342,7 +345,10 @@
}
}

convenience init?(restoring: RestorationType?, shouldLoadImmediately: Bool = false) {
convenience init?(
restoring: RestorationType?,
shouldLoadImmediately: Bool = false
) {

Check warning on line 351 in Sources/App/WebView/WebViewController.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/WebView/WebViewController.swift#L351

Added line #L351 was not covered by tests
if let server = restoring?.server ?? Current.servers.all.first {
self.init(server: server)
} else {
Expand Down Expand Up @@ -996,81 +1002,81 @@

var response: Guarantee<WebSocketMessage>?

if let externalBusMessage = WebViewExternalBusMessage(rawValue: incomingMessage.MessageType) {
switch externalBusMessage {
case .configGet:
response = Guarantee { seal in
DispatchQueue.global(qos: .userInitiated).async {
seal(WebSocketMessage(
id: incomingMessage.ID!,
type: "result",
result: [
"hasSettingsScreen": !Current.isCatalyst,
"canWriteTag": Current.tags.isNFCAvailable,
"canCommissionMatter": Current.matter.isAvailable,
"canImportThreadCredentials": Current.matter.threadCredentialsSharingEnabled,
]
))
}
}
case .configScreenShow:
showSettingsViewController()
case .haptic:
guard let hapticType = incomingMessage.Payload?["hapticType"] as? String else {
Current.Log.error("Received haptic via bus but hapticType was not string! \(incomingMessage)")
return
}
handleHaptic(hapticType)
case .connectionStatus:
guard let connEvt = incomingMessage.Payload?["event"] as? String else {
Current.Log.error("Received connection-status via bus but event was not string! \(incomingMessage)")
return
}
// Possible values: connected, disconnected, auth-invalid
UIView.animate(withDuration: 1.0, delay: 0, options: .curveEaseInOut, animations: {
self.settingsButton.alpha = connEvt == "connected" ? 0.0 : 1.0
}, completion: nil)
case .tagRead:
response = Current.tags.readNFC().map { tag in
WebSocketMessage(id: incomingMessage.ID!, type: "result", result: ["success": true, "tag": tag])
}.recover { _ in
.value(WebSocketMessage(id: incomingMessage.ID!, type: "result", result: ["success": false]))
}
case .tagWrite:
let (promise, seal) = Guarantee<Bool>.pending()
response = promise.map { success in
WebSocketMessage(id: incomingMessage.ID!, type: "result", result: ["success": success])

Check warning on line 1048 in Sources/App/WebView/WebViewController.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/WebView/WebViewController.swift#L1005-L1048

Added lines #L1005 - L1048 were not covered by tests
}

firstly { () throws -> Promise<(tag: String, name: String?)> in
if let tag = incomingMessage.Payload?["tag"] as? String, tag.isEmpty == false {
return .value((tag: tag, name: incomingMessage.Payload?["name"] as? String))
} else {
throw HomeAssistantAPI.APIError.invalidResponse
}
}.then { tagInfo in
Current.tags.writeNFC(value: tagInfo.tag)
}.done { _ in
Current.Log.info("wrote tag via external bus")
seal(true)
}.catch { error in
Current.Log.error("couldn't write tag via external bus: \(error)")
seal(false)

Check warning on line 1064 in Sources/App/WebView/WebViewController.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/WebView/WebViewController.swift#L1051-L1064

Added lines #L1051 - L1064 were not covered by tests
}
case .themeUpdate:
webView.evaluateJavaScript("notifyThemeColors()", completionHandler: nil)
case .matterCommission:
Current.matter.commission(server).done {
Current.Log.info("commission call completed")
}.catch { error in
// we don't show a user-visible error because even a successful operation will return 'cancelled'
// but the errors aren't public, so we can't compare -- the apple ui shows errors visually though
Current.Log.error(error)
}
case .threadImportCredentials:
threadCredentialsRequested()

Check warning on line 1077 in Sources/App/WebView/WebViewController.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/WebView/WebViewController.swift#L1066-L1077

Added lines #L1066 - L1077 were not covered by tests
}
} else {

Check warning on line 1079 in Sources/App/WebView/WebViewController.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/WebView/WebViewController.swift#L1079

Added line #L1079 was not covered by tests
Current.Log.error("unknown: \(incomingMessage.MessageType)")
}

Expand Down Expand Up @@ -1102,15 +1108,15 @@
}
}

private func threadCredentialsRequested() {
if #available(iOS 16.4, *) {
let threadDebugView = UIHostingController(rootView: ThreadCredentialsSharingView(viewModel: .init(
server: server,
threadClient: ThreadClientService()
)))
present(threadDebugView, animated: true)
}
}

Check warning on line 1119 in Sources/App/WebView/WebViewController.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/WebView/WebViewController.swift#L1111-L1119

Added lines #L1111 - L1119 were not covered by tests
}

extension WebViewController: UIScrollViewDelegate {
Expand Down
Loading
Loading