// // DocsView.swift // Splits // // Created by Isaac Greene on 6/3/22. // import SwiftUI import LocalAuthentication import CryptoKit /// The system username SHA256 hash for the app /// - Note: This is not a secure way to do it /// or in any way good practice /// but this is mainly to test out `SecureField` let username = "c7ad44cbad762a5da0a452f9e854fdc1e0e7a52a38015f23f3eab1d80b931dd472634dfac71cd34ebc35d16ab7fb8a90c81f975113d6c7538dc69dd8de9077ec".utf8 /// The system password associated with ``username`` /// /// I'm thinking about hashing them, but that would take work. /// /// It would be more secure though. /// - Note: Just as with `username` you should not define /// these unencrypted like they are right now. let password = "3c9909afec25354d551dae21590bb26e38d53f2173b8d3dc3eee4c047e7ab1c1eb8b85103e3be7ba613b31bb5c9c36214dc9f14a42fd7a2fdb84856bca5c44c2".utf8 /// Holds the views and login for the Docs tab in-app /// /// Mostly just a long list of sections and calling views /// - Note: New views are best called in this `struct` struct DocsView: View { enum Field: Hashable { case username case password } func hashSHA512(login: String) -> String { let loginAsData = Data(login.utf8) let loginHashHex = SHA512.hash(data: loginAsData) let loginHash = loginHashHex.compactMap { String(format: "%02x", $0) }.joined() return String(loginHash) } @State private var pass: String = "" @State private var user: String = "" @State private var isUnlocked = false @FocusState private var focusedField: Field? var body: some View { NavigationView { List { NavigationLink("Help", destination: HelpView()) NavigationLink("Change Log", destination: ChangeLog()) Section(header: Text("Features")) { NavigationLink("New", destination: NewFeatures()) NavigationLink("In Progress", destination: InProgressFeatures()) NavigationLink("Deprecated", destination: DeprecatedFeatures()) } Section(header: Text("Known Issues")) { NavigationLink("Recently Resolved", destination: RecentlyResolved()) NavigationLink("High Priority", destination: HighPriority()) NavigationLink("Medium Priority", destination: MediumPriority()) NavigationLink("Low Priority", destination: LowPriority()) } Section(header: Text("App Information")) { NavigationLink("Software License", destination: LicenseView()) Text("Version: Prerelease Build LVSXT10a.4") Text("Release date: 2022-07-06") 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")) { if (isUnlocked) { NavigationLink("Contacts", destination: SecretView()) Button("Log out") { pass = "" user = "" isUnlocked = false } } else { Button("Log in with biometrics") { authenticate() } SecureField("Username", text: $user) .keyboardType(.alphabet) .textContentType(.username) .submitLabel(.next) .focused($focusedField, equals: .username) .textContentType(.username) SecureField("Password", text: $pass) .keyboardType(.numbersAndPunctuation) .textContentType(.password) .submitLabel(.done) .focused($focusedField, equals: .password) .textContentType(.password) if checkPassword() { NavigationLink("Contacts", destination: SecretView()) } } } } .navigationTitle("Docs") } .onSubmit { switch focusedField { case .username: focusedField = .password default: () } if checkPassword() { isUnlocked = true } } } /// Compares the `user` SecureField and the `pass` SecureField against ``username`` and ``password`` /// /// - Note: come back to this /// /// /// - Returns: `true` if `user` equals `username` *and* `pass` equals `password`, `false` if one or both checks return false. func checkPassword() -> Bool { if (hashSHA512(login: pass) == String(password) && hashSHA512(login: user) == String(username)) { return true } else { return false } } /// Checks wether the user can use biometrics to sign in /// and (if true) prompts the user to sign with either FaceID or TouchID /// depending on the device. /// /// If the prompt was shown, and the user authenticated correctly /// `isUnlocked` is set to `true` /// /// - Note: `authenticate()` has to be called to show the sign-in sheet 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 { static var previews: some View { DocsView() } }