Added TouchID/FaceID to sign in

This commit is contained in:
Isaac Greene 2022-06-11 22:38:35 -04:00
parent 5c4d4f8059
commit 7d6643112d
12 changed files with 239 additions and 52 deletions

View file

@ -335,13 +335,14 @@
DEVELOPMENT_TEAM = UQJ7U8R2CV;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSFaceIDUsageDescription = "We'll need authentication before we can show sensitive data";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UIRequiresFullScreen = NO;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeRight UIInterfaceOrientationLandscapeLeft";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 15.5;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -370,13 +371,14 @@
DEVELOPMENT_TEAM = UQJ7U8R2CV;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSFaceIDUsageDescription = "We'll need authentication before we can show sensitive data";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UIRequiresFullScreen = NO;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeRight UIInterfaceOrientationLandscapeLeft";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 15.5;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",

View file

@ -12,6 +12,19 @@ import SwiftUI
struct June2022: View {
var body: some View {
ScrollView {
//2022-06-11
HStack {
VStack (alignment: .leading) {
Text("2022-06-11")
.font(.title2)
Text("Version Release Candidate 2 (LVSXT10a.2)\n")
.font(.footnote)
Text("\u{2022} Added biometrics to sign in along with option for username/password")
}
Spacer()
}
.padding(30)
//2022-06-10
HStack {
VStack (alignment: .leading) {

View file

@ -21,7 +21,7 @@ struct ContentView: View {
VStack {
Spacer()
VStack {
TextField("Enter distance here", text: $distance)
TextField("Enter distance", text: $distance)
.padding()
.keyboardType(.decimalPad)
.textFieldStyle(.roundedBorder)
@ -39,7 +39,7 @@ struct ContentView: View {
HStack {
VStack {
Text("Hours")
TextField("Enter hours here", text: $timeHours)
TextField("Enter hours", text: $timeHours)
.keyboardType(.numberPad)
.textFieldStyle(.roundedBorder)
.focused($nameIsFocused)
@ -49,7 +49,7 @@ struct ContentView: View {
.padding()
VStack {
Text("Minutes")
TextField("Enter minutes here", text: $timeMinutes)
TextField("Enter minutes", text: $timeMinutes)
.keyboardType(.numberPad)
.textFieldStyle(.roundedBorder)
.focused($nameIsFocused)
@ -60,7 +60,7 @@ struct ContentView: View {
.padding()
VStack {
Text("Seconds")
TextField("Enter seconds here", text: $timeSeconds)
TextField("Enter seconds", text: $timeSeconds)
.keyboardType(.numberPad)
.textFieldStyle(.roundedBorder)
.focused($nameIsFocused)
@ -99,10 +99,10 @@ struct PaceResults: View {
// but then my message would disappear to let you know
// what to enter in that box
let multiplier = {selectedSystem == "mi" ? 1.609344 : 0.6213711922}()
let notSelectedSystem = {selectedSystem == "km" ? "mi" : "km"}()
let multiplier = (selectedSystem == "mi" ? 1.609344 : 0.6213711922)
let notSelectedSystem = (selectedSystem == "km" ? "mi" : "km")
let convertedDistance = distanceDub * multiplier
let convertedDistanceString = {distance == "" ? "" : String(format: "%.2f", convertedDistance)}()
let convertedDistanceString = (distance == "" ? "" : String(format: "%.2f", convertedDistance))
let convertedSeconds:Double = (Double(timeSeconds) ?? 0) * (1.6666666666666666666666666)
let timeSecondsInt:Int = Int(timeSeconds) ?? 0
@ -164,9 +164,9 @@ struct PaceResults: View {
// and the hours calculated in the previous section
// and adds them together to get our total number of hours.
let hoursFormatted:String = String(format: "%.0f", totalHours)
let paceFormatted:String = {pace >= 60 ? "\(paceHours):\(properTimeMS)" : "\(properTimeMS)"}()
let paceFormatted:String = (pace >= 60 ? "\(paceHours):\(properTimeMS)" : "\(properTimeMS)")
let paceFormattedOpposite:String = {paceOpposite >= 60 ? "\(paceHoursOpposite):\(properTimeMSOpposite)" : "\(properTimeMSOpposite)"}()
let paceFormattedOpposite:String = (paceOpposite >= 60 ? "\(paceHoursOpposite):\(properTimeMSOpposite)" : "\(properTimeMSOpposite)")
let leadingZeros:String = String(format: "%02d:%02d", timeMinutesUnderSixty, timeSecondsUnderSixty)
// this takes the minutes and the seconds and adds leading
@ -176,15 +176,18 @@ struct PaceResults: View {
HStack {
VStack {
Text("Distance: \(distance)\(selectedSystem)")
Text("Distance: \(convertedDistanceString)\(notSelectedSystem)")
Text("\(distance)\(selectedSystem)")
Text("\(convertedDistanceString)\(notSelectedSystem)")
}
Text("Total time\n\(hoursFormatted):\(leadingZeros)")
.frame(minWidth: 100)
Text("\(hoursFormatted):\(leadingZeros)")
.padding()
.frame(minWidth: 100)
VStack(alignment: .trailing) {
Text("\(paceFormatted) per \(selectedSystem)")
Text("\(paceFormattedOpposite) per \(notSelectedSystem)")
Text("\(paceFormatted)/\(selectedSystem)")
Text("\(paceFormattedOpposite)/\(notSelectedSystem)")
}
.frame(minWidth: 100)
}
}
}

View file

@ -6,9 +6,10 @@
//
import SwiftUI
// this file will not have comments.
// this code is considered simple enough to be
// human-readable without aid, as long as
import LocalAuthentication
// this file will have some comments.
// Most of this code is considered simple enough
// to be human-readable without aid, as long as
// the reader has a basic understanding of
// Swift and/or SwiftUI.
@ -23,7 +24,8 @@ struct DocsView: View {
@State private var pass: String = ""
@State private var user: String = ""
@FocusState private var focusedField: Field?
@State private var isUnlocked = false
@FocusState private var focusedField: Field?
var body: some View {
NavigationView {
@ -44,24 +46,38 @@ struct DocsView: View {
}
Section(header: Text("App Information")) {
NavigationLink("Software License", destination: LicenseView())
Text("Version: Release Candidate (1.0.0)")
Text("Release date: 2022-06-10")
Text("Version: Release Candidate 2 (1.0.0)")
Text("Release date: 2022-06-11")
Text("Start date: 2022-03-25")
Link("Built with SwiftUI \(Image(systemName: "swift"))", destination: URL(string: "https://developer.apple.com/xcode/swiftui")!)
}
Section(header: Text("Login")) {
SecureField("Username", text: $user)
.keyboardType(.alphabet)
.textContentType(.username)
.submitLabel(.next)
.focused($focusedField, equals: .username)
SecureField("Password", text: $pass)
.keyboardType(.numbersAndPunctuation)
.textContentType(.password)
.submitLabel(.done)
.focused($focusedField, equals: .password)
if (pass == password && user == username) {
if (isUnlocked) {
NavigationLink("Contacts", destination: SecretView())
Button("Log out") {
pass = ""
user = ""
isUnlocked = false
}
} else {
if !isUnlocked {
Button("Log in with biometrics") {
authenticate()
}
}
SecureField("Username", text: $user)
.keyboardType(.alphabet)
.textContentType(.username)
.submitLabel(.next)
.focused($focusedField, equals: .username)
SecureField("Password", text: $pass)
.keyboardType(.numbersAndPunctuation)
.textContentType(.password)
.submitLabel(.done)
.focused($focusedField, equals: .password)
if checkPassword() {
NavigationLink("Contacts", destination: SecretView())
}
}
}
}
@ -74,8 +90,48 @@ struct DocsView: View {
default:
()
}
if (pass == password && user == username) {
isUnlocked = true
}
}
}
func checkPassword() -> Bool {
if (pass == password && user == username) {
return true
} else {
return false
}
}
func checkIfUnlocked() {
if !isUnlocked {
authenticate()
}
}
func authenticate() {
let context = LAContext()
var error: NSError?
// check whether biometric authentication is possible
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
// it's possible, so go ahead and use it
let reason = "We need authentication before we can show you sensitive data"
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
// authentication has now completed
if success {
isUnlocked = true
} else {
()
}
}
} else {
()
}
}
// getting this to work came from
// https://www.hackingwithswift.com/books/ios-swiftui/using-touch-id-and-face-id-with-swiftui
// a truly epic website and it's helped me with
// just about all of my code questions
}
struct DocsView_Previews: PreviewProvider {

View file

@ -11,6 +11,14 @@ struct NewFeatures: View {
var body: some View {
ScrollView {
VStack(alignment: .leading) {
Text("\u{2022} Added TouchID and FaceID to sign in")
Text("Implemented in Version Release Candidate 2\n")
.font(.footnote)
.italic()
Text("\u{2022} Implemented a way to dismiss the keyboard")
Text("Implemented in Version Release Candidate\n")
.font(.footnote)
.italic()
Text("\u{2022} Reformatted the Docs tabs and made the common things up at the top")
Text("Implemented in Version Prerelease LVSXT10d.2\n")
.font(.footnote)
@ -33,15 +41,11 @@ struct NewFeatures: View {
struct InProgressFeatures: View {
var body: some View {
ScrollView {
Text("Features In Progress")
.font(.largeTitle)
.bold()
.padding(.top, 40)
Text("Note: this does not include things I have to fix\n")
.font(.footnote)
.italic()
VStack(alignment: .leading) {
Text("\u{2022} Working on a conversion between measurements for pace and distance\n")
Text("\u{2022} Adding a better-formatted Contacts tab on iPad\n")
}
.padding(30)
}
@ -52,10 +56,6 @@ struct InProgressFeatures: View {
struct DeprecatedFeatures: View {
var body: some View {
ScrollView {
Text("Deprecated Features")
.font(.largeTitle)
.bold()
.padding(.top, 40)
VStack(alignment: .leading) {
Text("\n\u{2022} Removed the picker wheel to enter total time (a truly horrible system)")
Text("Stricken before recorded history\n")

View file

@ -13,7 +13,7 @@ struct RecentlyResolved: View {
var body: some View {
ScrollView {
VStack(alignment: .leading) {
Text("\u{2022} Implemented an easy way to dismiss the keyboard in the main view of Calculator (it only took 2 1/2 months)")
Text("\u{2022} Implemented an easy way to dismiss the keyboard in the main view of Calculator (it only took 2 1/2 months)\n\u{2022} Opening the contacts tab no longer causes the app to crash (RC 2)")
}
.padding(30)
}
@ -24,8 +24,11 @@ struct RecentlyResolved: View {
struct HighPriority: View {
var body: some View {
ScrollView {
VStack(alignment: .leading) {
Text("Tapping on the Contacts tab after entering correct login details causes the app to crash")
VStack {
Image(systemName: "checkmark.shield.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 70)
}
.padding(30)
}
@ -36,9 +39,11 @@ struct HighPriority: View {
struct MediumPriority: View {
var body: some View {
ScrollView {
VStack(alignment: .leading) {
Text("Wow. Such Empty.")
.italic()
VStack {
Image(systemName: "checkmark.shield.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 70)
}
.padding(30)
}

View file

@ -17,7 +17,6 @@ struct LicenseView: View {
// setting fontSize to a value based on that where the whole
// line can be viewed without scrolling horizontally
// or with wrapped text, ruining the required formatting I have.
// As of 2022-06-08, I have not tested this on a phone.
var body: some View {
ScrollView {

View file

@ -14,8 +14,8 @@ import SwiftUI
struct SecretView: View {
var body: some View {
VStack {
ScrollView {
ScrollView {
VStack {
VStack {
Image("jake.zimmerman.group")
.resizable()
@ -101,9 +101,90 @@ Email: greenei@students.lakeviewspartans.org
}
.frame(minWidth: 350, minHeight: 175)
.border(.primary)
VStack {
Image(systemName: "person.crop.circle.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 75, height: 75)
Text("Pryor")
.font(.title2)
VStack (alignment: .leading) {
Text("""
Name: Becky Pryor
Email: bpryor@lakeviewspartans.org
""")
HStack {
Text("Phone:")
Link("(269) 209-9906", destination: URL(string: "tel:2692099906")!)
}
}
}
.frame(minWidth: 350, minHeight: 175)
.border(.primary)
VStack {
Image(systemName: "person.crop.circle.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 75, height: 75)
Text("Paige")
.font(.title2)
VStack (alignment: .leading) {
Text("""
Name: Paige Ratliff
Email: ratliffp@students.lakeviewspartans.org
""")
HStack {
Text("Phone:")
Link("(269) 753-8569", destination: URL(string: "tel:2697538569")!)
}
}
}
.frame(minWidth: 350, minHeight: 175)
.border(.primary)
VStack {
Image(systemName: "person.crop.circle.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 75, height: 75)
Text("Emma")
.font(.title2)
VStack (alignment: .leading) {
Text("""
Name: Emma Kerschbaum
Email: kerschbaume@students.lakeviewspartans.org
""")
HStack {
Text("Phone:")
Link("(269) 419-7880", destination: URL(string: "tel:2694197880")!)
}
}
}
.frame(minWidth: 350, minHeight: 175)
.border(.primary)
VStack {
Image(systemName: "person.crop.circle.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 75, height: 75)
Text("Alyssa")
.font(.title2)
VStack (alignment: .leading) {
Text("""
Name: Alyssa Hinton
Email: hintona2@students.lakeviewspartans.org
""")
HStack {
Text("Phone:")
Link("(269) 589-7609", destination: URL(string: "tel:2695897609")!)
}
}
}
.frame(minWidth: 350, minHeight: 175)
.border(.primary)
}
.frame(maxWidth: .infinity)
.navigationTitle("Contacts")
}
.frame(minWidth: .infinity)
}
}

View file

@ -0,0 +1,6 @@
import UIKit
var selectedSystem = "mi"
let multiplier = (selectedSystem == "mi" ? 1.609344 : 0.6213711922)
print(multiplier)

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='ios' buildActiveScheme='true' importAppTypes='true'>
<timeline fileName='timeline.xctimeline'/>
</playground>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:">
</FileRef>
</Workspace>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<Timeline
version = "3.0">
<TimelineItems>
<LoggerValueHistoryTimelineItem
documentLocation = "file:///Users/ericgreene1/Desktop/IsaacSchool/Xcode/Splits/Splits/qerberymjthnrgbefvdcs.playground#CharacterRangeLen=17&amp;CharacterRangeLoc=110&amp;EndingColumnNumber=0&amp;EndingLineNumber=6&amp;StartingColumnNumber=1&amp;StartingLineNumber=5&amp;Timestamp=676683411.20383"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
</TimelineItems>
</Timeline>