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"),