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 24 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
281 changes: 152 additions & 129 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
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
9 changes: 9 additions & 0 deletions Sources/App/Settings/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,19 @@
}

if contentSections.contains(.general) {
#if targetEnvironment(macCatalyst)

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
form +++ Section()
bgoncal marked this conversation as resolved.
Show resolved Hide resolved
<<< SettingsRootDataSource.Row.general.row
<<< SettingsRootDataSource.Row.location.row
<<< SettingsRootDataSource.Row.notifications.row
#else
form +++ Section()
<<< SettingsRootDataSource.Row.general.row
<<< SettingsRootDataSource.Row.location.row
<<< SettingsRootDataSource.Row.notifications.row
<<< SettingsRootDataSource.Row.security.row

#endif

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

View check run for this annotation

Codecov / codecov/patch

Sources/App/Settings/SettingsViewController.swift#L110-L117

Added lines #L110 - L117 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
36 changes: 36 additions & 0 deletions Sources/App/WebView/WebViewWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@
restorationActivity = nil
}
}

listenForBiometricsLockRelatedEvents()
}

func present(_ viewController: UIViewController, animated: Bool = true, completion: (() -> Void)? = nil) {
Expand Down Expand Up @@ -469,3 +471,37 @@
}
}
}

// MARK: - Biometrics lock

extension WebViewWindowController {
private func listenForBiometricsLockRelatedEvents() {
Copy link
Member

Choose a reason for hiding this comment

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

what happens if the user gets signed out (e.g. remotely, due to token) while this is happening?

Copy link
Member Author

Choose a reason for hiding this comment

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

How can I easily reproduce that? I think the overlay will stay there, but it's nice to verify

Copy link
Member

Choose a reason for hiding this comment

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

go into your user profile & delete the token for your device in your profile. you can do it from the same machine or other.

if the overlay stays, it may be worth killing it off since there's nothing it's protecting (if it's the last account) and may block signing into something new. there's no way to get to settings during onboarding.

Copy link
Member Author

Choose a reason for hiding this comment

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

Tested. Indeed the "lock screen" was still being shown even with no servers. So I updated it in a way that the user needs to authenticate one last time after the token expires, and then the biometric lock will be disabled and only reenabled if the user sets up a new server and manually enables the toggle.

#if !targetEnvironment(macCatalyst)
if #available(iOS 13.0, *) {
NotificationCenter.default.addObserver(
self,
selector: #selector(checkForBiometrics),
name: UIScene.willEnterForegroundNotification,
object: nil
)

NotificationCenter.default.addObserver(
self,
selector: #selector(protectAppIfNeeded),
name: UIScene.didEnterBackgroundNotification,
object: nil
)
}
#endif
}

@objc private func checkForBiometrics() {
guard let controller = window.rootViewController else { return }
Current.authenticationService.checkForBiometrics(controller: controller)
}

@objc private func protectAppIfNeeded() {
guard let controller = window.rootViewController else { return }
Current.authenticationService.protectAppIfNeeded(controller: controller, completion: nil)
}

Check warning on line 506 in Sources/App/WebView/WebViewWindowController.swift

View check run for this annotation

Codecov / codecov/patch

Sources/App/WebView/WebViewWindowController.swift#L503-L506

Added lines #L503 - L506 were not covered by tests
}
1 change: 0 additions & 1 deletion Sources/Shared/API/Fixtures/ServerFixture.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Foundation
import Shared

struct ServerFixture {
static var standard = Server(identifier: "123", getter: {
Expand All @@ -25,6 +24,6 @@
version: "123"
)
}, setter: { _ in
true
})

Check warning on line 28 in Sources/Shared/API/Fixtures/ServerFixture.swift

View check run for this annotation

Codecov / codecov/patch

Sources/Shared/API/Fixtures/ServerFixture.swift#L27-L28

Added lines #L27 - L28 were not covered by tests
}
1 change: 1 addition & 0 deletions Sources/Shared/Common/Extensions/UIColor+HA.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import UIKit

public extension UIColor {
static let homeAssistant = UIColor(red: 0, green: 0.682, blue: 0.973, alpha: 1)
static let onColor = UIColor(hue: 0.15, saturation: 0.75, brightness: 0.49, alpha: 1.0)
static let defaultEntityColor = UIColor(hue: 0.58, saturation: 0.4, brightness: 0.44, alpha: 1.0)

Expand Down
15 changes: 15 additions & 0 deletions Sources/Shared/DesignSystem/Constants/HASpacing.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation

public enum Spacing {
public static let one: CGFloat = 8
public static let one_and_a_half: CGFloat = 12
bgoncal marked this conversation as resolved.
Show resolved Hide resolved
public static let two: CGFloat = 16
public static let two_and_a_half: CGFloat = 20
public static let three: CGFloat = 24
public static let three_and_a_half: CGFloat = 28
public static let four: CGFloat = 32
public static let four_and_a_half: CGFloat = 36
public static let five: CGFloat = 40
public static let five_and_a_half: CGFloat = 44
public static let six: CGFloat = 48
}
4 changes: 4 additions & 0 deletions Sources/Shared/Environment/Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@ public class AppEnvironment {
}
}

#if !os(watchOS)
public var authenticationService: BiometricsAuthenticationServiceProtocol = BiometricsAuthenticationService()
#endif

public var Log: XCGLogger = {
if NSClassFromString("XCTest") != nil {
let logger = XCGLogger()
Expand Down
Loading
Loading