From 9e1efcf8b383b0c35c0386502a17d4aa41ab3665 Mon Sep 17 00:00:00 2001 From: fhasse95 <49185957+fhasse95@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:18:15 +0200 Subject: [PATCH 1/5] Added Biometric Authentication Playground --- Android/app/src/main/AndroidManifest.xml | 5 +- Package.swift | 2 +- .../BiometricAuthenticationPlayground.swift | 85 +++++++++++++++++++ Sources/Showcase/PlaygroundListView.swift | 5 ++ .../Showcase/Resources/Localizable.xcstrings | 56 +++++++++++- 5 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 Sources/Showcase/BiometricAuthenticationPlayground.swift diff --git a/Android/app/src/main/AndroidManifest.xml b/Android/app/src/main/AndroidManifest.xml index 298b859..61b720e 100644 --- a/Android/app/src/main/AndroidManifest.xml +++ b/Android/app/src/main/AndroidManifest.xml @@ -20,7 +20,7 @@ - + @@ -34,6 +34,9 @@ + + + diff --git a/Package.swift b/Package.swift index da9a616..903584d 100644 --- a/Package.swift +++ b/Package.swift @@ -22,7 +22,7 @@ let package = Package( fuse ? .package(url: "https://source.skip.tools/skip-fuse-ui.git", from: "1.0.0") : .package(url: "https://source.skip.tools/skip-ui.git", from: "1.0.0"), .package(url: "https://source.skip.tools/skip.git", from: "1.7.7"), - .package(url: "https://source.skip.tools/skip-kit.git", from: "1.0.0"), + .package(url: "https://github.com/fhasse95/skip-kit.git", branch: "Biometric-Authentication-Support"), .package(url: "https://source.skip.tools/skip-av.git", "0.6.2"..<"2.0.0"), .package(url: "https://source.skip.tools/skip-web.git", "0.9.1"..<"2.0.0"), .package(url: "https://source.skip.tools/skip-sql.git", "0.16.0"..<"2.0.0"), diff --git a/Sources/Showcase/BiometricAuthenticationPlayground.swift b/Sources/Showcase/BiometricAuthenticationPlayground.swift new file mode 100644 index 0000000..59c27fc --- /dev/null +++ b/Sources/Showcase/BiometricAuthenticationPlayground.swift @@ -0,0 +1,85 @@ +// Copyright 2023–2026 Skip +import SwiftUI +import SkipKit + +struct BiometricAuthenticationPlayground: View { + private var canAuthenticate: Bool { BiometricAuthentication.canAuthenticate } + @State private var authenticationType = BiometricAuthentication.authenticationType + @State private var result = "Not authenticated" + @State private var authenticationAttempt = 0 + + private var resultColor: Color { + switch self.result { + case "Authenticated": .green + case "Cancelled": .orange + case "Failed", "Unavailable": .red + default: + .secondary + } + } + + var body: some View { + List { + Section("Availability") { + LabeledContent("Type", + value: { + switch self.authenticationType { + case .fingerprint: "Fingerprint" + case .facialRecognition: "Facial Recognition" + case .unspecified: "Unspecified Biometric" + case .none: "None" + } + }() + ) + LabeledContent("Can Authenticate") { + Text(self.canAuthenticate ? "Yes" : "No") + .foregroundStyle(self.canAuthenticate ? Color.green : Color.red) + } + } + + if self.canAuthenticate { + Section("Authentication") { + Button("Authenticate") { self.authenticate() } + + Text(self.result) + .foregroundStyle(self.resultColor) + } + } + } + .onAppear { self.refresh() } + .onDisappear { self.cancelAuthentication() } + .toolbar { + PlaygroundSourceLink(file: "BiometricAuthenticationPlayground.swift") + } + } + + private func authenticate() { + self.authenticationAttempt += 1 + let authenticationAttempt = self.authenticationAttempt + self.result = "Authenticating..." + + BiometricAuthentication.authenticate( + localizedReason: "Authenticate with biometrics" + ) { authenticationResult in + guard authenticationAttempt == self.authenticationAttempt else { + return + } + + self.result = switch authenticationResult { + case .success: "Authenticated" + case .cancelled: "Cancelled" + case .failed: "Failed" + case .unavailable: "Unavailable" + } + self.refresh() + } + } + + private func refresh() { + self.authenticationType = BiometricAuthentication.authenticationType + } + + private func cancelAuthentication() { + self.authenticationAttempt += 1 + } +} diff --git a/Sources/Showcase/PlaygroundListView.swift b/Sources/Showcase/PlaygroundListView.swift index b3d10b7..89e6c15 100644 --- a/Sources/Showcase/PlaygroundListView.swift +++ b/Sources/Showcase/PlaygroundListView.swift @@ -8,6 +8,7 @@ enum PlaygroundType: CaseIterable, View { case animation case audio case background + case biometricAuthentication case blendMode case blur case border @@ -113,6 +114,8 @@ enum PlaygroundType: CaseIterable, View { return LocalizedStringResource("Audio", comment: "Title of Audio playground") case .background: return LocalizedStringResource("Background", comment: "Title of Background playground") + case .biometricAuthentication: + return LocalizedStringResource("Biometric Authentication", comment: "Title of Biometric Authentication playground") case .blendMode: return LocalizedStringResource("BlendMode", comment: "Title of BlendMode playground") case .blur: @@ -316,6 +319,8 @@ enum PlaygroundType: CaseIterable, View { #endif case .background: BackgroundPlayground() + case .biometricAuthentication: + BiometricAuthenticationPlayground() case .blendMode: BlendModePlayground() case .blur: diff --git a/Sources/Showcase/Resources/Localizable.xcstrings b/Sources/Showcase/Resources/Localizable.xcstrings index f9cf7c8..0224a8a 100644 --- a/Sources/Showcase/Resources/Localizable.xcstrings +++ b/Sources/Showcase/Resources/Localizable.xcstrings @@ -1002,12 +1002,30 @@ }, "AsyncImage" : { + }, + "Authenticate" : { + + }, + "Authenticate with biometrics" : { + + }, + "Authenticated" : { + + }, + "Authenticating..." : { + + }, + "Authentication" : { + }, "Audio" : { "comment" : "Title of Audio playground" }, "AudioPlayground is Lite-only until ported to SkipAV" : { + }, + "Availability" : { + }, "B" : { @@ -1077,6 +1095,9 @@ }, "Binding View" : { + }, + "Biometric Authentication" : { + "comment" : "Title of Biometric Authentication playground" }, "BipBop" : { @@ -1182,6 +1203,12 @@ }, "Cancel" : { + }, + "Cancelled" : { + + }, + "Can Authenticate" : { + }, "Capsule" : { @@ -1673,6 +1700,12 @@ }, "F2" : { + }, + "Facial Recognition" : { + + }, + "Failed" : { + }, "Favorite" : { @@ -1700,6 +1733,9 @@ }, "fill(.red)\n .stroke(.green, lineWidth: 10)" : { + }, + "Fingerprint" : { + }, "First" : { @@ -2647,6 +2683,9 @@ }, "NO" : { + }, + "No" : { + }, "No badge (count 0)" : { @@ -2665,6 +2704,12 @@ }, "Normal" : { + }, + "None" : { + + }, + "Not authenticated" : { + }, "Not supported on macOS" : { @@ -4225,6 +4270,9 @@ }, "Title Only" : { + }, + "Type" : { + }, "To:" : { @@ -4306,6 +4354,9 @@ }, "Unavailable" : { + }, + "Unspecified Biometric" : { + }, "Underline" : { @@ -4719,6 +4770,9 @@ }, "Yellow" : { + }, + "Yes" : { + }, "YES" : { @@ -4734,4 +4788,4 @@ } }, "version" : "1.0" -} \ No newline at end of file +} From 0a72a06f66e47add93c6deb026cc8da95a09681e Mon Sep 17 00:00:00 2001 From: fhasse95 <49185957+fhasse95@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:41:55 +0200 Subject: [PATCH 2/5] Update BiometricAuthenticationPlayground.swift --- Sources/Showcase/BiometricAuthenticationPlayground.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Showcase/BiometricAuthenticationPlayground.swift b/Sources/Showcase/BiometricAuthenticationPlayground.swift index 59c27fc..fb68371 100644 --- a/Sources/Showcase/BiometricAuthenticationPlayground.swift +++ b/Sources/Showcase/BiometricAuthenticationPlayground.swift @@ -4,9 +4,9 @@ import SkipKit struct BiometricAuthenticationPlayground: View { private var canAuthenticate: Bool { BiometricAuthentication.canAuthenticate } - @State private var authenticationType = BiometricAuthentication.authenticationType - @State private var result = "Not authenticated" - @State private var authenticationAttempt = 0 + @State var authenticationType = BiometricAuthentication.authenticationType + @State var result = "Not authenticated" + @State var authenticationAttempt = 0 private var resultColor: Color { switch self.result { From 55e3511ba92f66e4cbb0d1a4762da635157b842b Mon Sep 17 00:00:00 2001 From: fhasse95 <49185957+fhasse95@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:45:05 +0200 Subject: [PATCH 3/5] Update BiometricAuthenticationPlayground.swift --- Sources/Showcase/BiometricAuthenticationPlayground.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/Showcase/BiometricAuthenticationPlayground.swift b/Sources/Showcase/BiometricAuthenticationPlayground.swift index fb68371..08c06b7 100644 --- a/Sources/Showcase/BiometricAuthenticationPlayground.swift +++ b/Sources/Showcase/BiometricAuthenticationPlayground.swift @@ -2,6 +2,7 @@ import SwiftUI import SkipKit +/// This component uses the `SkipKit` module from https://source.skip.tools/skip-kit struct BiometricAuthenticationPlayground: View { private var canAuthenticate: Bool { BiometricAuthentication.canAuthenticate } @State var authenticationType = BiometricAuthentication.authenticationType From 6edba25e56b820395456bb51f9ba89438811f1e2 Mon Sep 17 00:00:00 2001 From: fhasse95 <49185957+fhasse95@users.noreply.github.com> Date: Wed, 15 Jul 2026 17:45:39 +0200 Subject: [PATCH 4/5] Update BiometricAuthenticationPlayground.swift --- Sources/Showcase/BiometricAuthenticationPlayground.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Showcase/BiometricAuthenticationPlayground.swift b/Sources/Showcase/BiometricAuthenticationPlayground.swift index 08c06b7..5971161 100644 --- a/Sources/Showcase/BiometricAuthenticationPlayground.swift +++ b/Sources/Showcase/BiometricAuthenticationPlayground.swift @@ -4,11 +4,12 @@ import SkipKit /// This component uses the `SkipKit` module from https://source.skip.tools/skip-kit struct BiometricAuthenticationPlayground: View { - private var canAuthenticate: Bool { BiometricAuthentication.canAuthenticate } @State var authenticationType = BiometricAuthentication.authenticationType @State var result = "Not authenticated" @State var authenticationAttempt = 0 + private var canAuthenticate: Bool { BiometricAuthentication.canAuthenticate } + private var resultColor: Color { switch self.result { case "Authenticated": .green From e94c42c89cb56e427937e0c681e68374882e60a8 Mon Sep 17 00:00:00 2001 From: Marc Prud'hommeaux Date: Sat, 18 Jul 2026 00:11:38 -0400 Subject: [PATCH 5/5] Update skip-kit dependency URL and version --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 903584d..da9a616 100644 --- a/Package.swift +++ b/Package.swift @@ -22,7 +22,7 @@ let package = Package( fuse ? .package(url: "https://source.skip.tools/skip-fuse-ui.git", from: "1.0.0") : .package(url: "https://source.skip.tools/skip-ui.git", from: "1.0.0"), .package(url: "https://source.skip.tools/skip.git", from: "1.7.7"), - .package(url: "https://github.com/fhasse95/skip-kit.git", branch: "Biometric-Authentication-Support"), + .package(url: "https://source.skip.tools/skip-kit.git", from: "1.0.0"), .package(url: "https://source.skip.tools/skip-av.git", "0.6.2"..<"2.0.0"), .package(url: "https://source.skip.tools/skip-web.git", "0.9.1"..<"2.0.0"), .package(url: "https://source.skip.tools/skip-sql.git", "0.16.0"..<"2.0.0"),