diff --git a/Splits.xcodeproj/project.pbxproj b/Splits.xcodeproj/project.pbxproj index bf9fb1a..ba0e9b6 100644 --- a/Splits.xcodeproj/project.pbxproj +++ b/Splits.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 850F8028285437500094580D /* aiden.moore.states.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 850F8024285437500094580D /* aiden.moore.states.jpg */; }; 850F8029285437500094580D /* isaac.greene.clouds.heic in Resources */ = {isa = PBXBuildFile; fileRef = 850F8025285437500094580D /* isaac.greene.clouds.heic */; }; 850F802A285437500094580D /* jake.zimmerman.group.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 850F8026285437500094580D /* jake.zimmerman.group.jpg */; }; + 859298C928592F1F00D9D6CB /* Documentation.docc in Sources */ = {isa = PBXBuildFile; fileRef = 859298C828592F1F00D9D6CB /* Documentation.docc */; }; 85AAA0D627FA2DD600F4B9A1 /* SplitsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AAA0D527FA2DD600F4B9A1 /* SplitsApp.swift */; }; 85AAA0D827FA2DD600F4B9A1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AAA0D727FA2DD600F4B9A1 /* ContentView.swift */; }; 85AAA0DA27FA2DDA00F4B9A1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 85AAA0D927FA2DDA00F4B9A1 /* Assets.xcassets */; }; @@ -39,6 +40,7 @@ 850F8024285437500094580D /* aiden.moore.states.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; name = aiden.moore.states.jpg; path = Assets.xcassets/aiden.moore.states.jpg; sourceTree = ""; }; 850F8025285437500094580D /* isaac.greene.clouds.heic */ = {isa = PBXFileReference; lastKnownFileType = file; name = isaac.greene.clouds.heic; path = Assets.xcassets/isaac.greene.clouds.heic; sourceTree = ""; }; 850F8026285437500094580D /* jake.zimmerman.group.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; name = jake.zimmerman.group.jpg; path = Assets.xcassets/jake.zimmerman.group.jpg; sourceTree = ""; }; + 859298C828592F1F00D9D6CB /* Documentation.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = Documentation.docc; sourceTree = ""; }; 85AAA0D227FA2DD600F4B9A1 /* Splits.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Splits.app; sourceTree = BUILT_PRODUCTS_DIR; }; 85AAA0D527FA2DD600F4B9A1 /* SplitsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitsApp.swift; sourceTree = ""; }; 85AAA0D727FA2DD600F4B9A1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -80,6 +82,7 @@ isa = PBXGroup; children = ( 85AAA0D527FA2DD600F4B9A1 /* SplitsApp.swift */, + 859298C828592F1F00D9D6CB /* Documentation.docc */, 8502250D27FA5D0800FE8E95 /* ChangeLogData.swift */, 85AAA0D727FA2DD600F4B9A1 /* ContentView.swift */, 850062C82802328F0095F121 /* TabViewData.swift */, @@ -195,6 +198,7 @@ 8502250E27FA5D0800FE8E95 /* ChangeLogData.swift in Sources */, 850F8014284A7F6A0094580D /* DocsView.swift in Sources */, 850F8016284A815C0094580D /* KnownIssues.swift in Sources */, + 859298C928592F1F00D9D6CB /* Documentation.docc in Sources */, 85AAA0E827FA2F1600F4B9A1 /* ChangeLogView.swift in Sources */, 85AAA0E627FA2EB100F4B9A1 /* ModalView.swift in Sources */, 850F80202853F7790094580D /* LicenseView.swift in Sources */, diff --git a/Splits/ChangeLogData.swift b/Splits/ChangeLogData.swift index c28c9af..ae67f20 100644 --- a/Splits/ChangeLogData.swift +++ b/Splits/ChangeLogData.swift @@ -6,12 +6,23 @@ // import SwiftUI -// this file will not have comments. -// it is considered self-explanatory and best viewed in-app struct June2022: View { var body: some View { ScrollView { + //2022-06-14 + HStack { + VStack (alignment: .leading) { + Text("2022-06-14") + .font(.title2) + Text("Version Prerelease Build LVSXT10a.3\n") + .font(.footnote) + Text("\u{2022} Optimized some code to allow for future development\n\u{2022} Starting on fixing an issue where putting a zero as the first number in the Distance field causes the app to crash\n\u{2022} Partially added more robust app documentation") + } + Spacer() + } + .padding(30) + //2022-06-11 HStack { VStack (alignment: .leading) { @@ -43,7 +54,7 @@ struct June2022: View { VStack (alignment: .leading) { Text("2022-06-09") .font(.title2) - Text("Version Prerelease LVSXT10c.2\n") + Text("Version Prerelease Build LVSXT10c.2\n") .font(.footnote) Text("\u{2022} Worked on a simple login page to view contact info\n\t\u{2022} Spent *way* too long on it") } @@ -63,6 +74,7 @@ struct June2022: View { Spacer() } .padding(30) + //2022-06-05 HStack { VStack (alignment: .leading) { @@ -70,9 +82,11 @@ struct June2022: View { .font(.title2) Text("Version Prerelease Build LVSXT10d.3\n") .font(.footnote) - Text("\u{2022} Started work on converting the pace to a more understandable format\n\u{2022} ") + Text("\u{2022} Started work on converting the pace to a more understandable format") } } + .padding(30) + //2022-06-04 HStack { VStack (alignment: .leading) { @@ -118,6 +132,7 @@ struct June2022: View { } } +// done adding content to these struct May2022: View { var body: some View { ScrollView { diff --git a/Splits/ContentView.swift b/Splits/ContentView.swift index bfd424b..89677f5 100644 --- a/Splits/ContentView.swift +++ b/Splits/ContentView.swift @@ -8,6 +8,9 @@ import SwiftUI import Foundation +/// Creates the primary tab of ``SplitsApp`` +/// +/// e struct ContentView: View { @FocusState private var nameIsFocused: Bool var SISystem = ["km","mi"] @@ -101,8 +104,10 @@ struct PaceResults: View { 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:String = { + let convertedDistance = distanceDub * multiplier + return (distance == "" || distance.starts(with: "0") ? "0" : String(format: "%.3f", convertedDistance)) + }() let convertedSeconds:Double = (Double(timeSeconds) ?? 0) * (1.6666666666666666666666666) let timeSecondsInt:Int = Int(timeSeconds) ?? 0 @@ -141,22 +146,27 @@ struct PaceResults: View { // a single value in terms of minutes, so that 1:08:45 // becomes 68.75 which is a nice number we can do math on // and this number is never directly seen by the user + let pace = actualTime / distanceDub - let paceOpposite = (selectedSystem == "km" ? (pace * 1.609344) : (pace * 0.6213711922)) - let paceSeconds = pace.truncatingRemainder(dividingBy: 1.0) - let paceMinutes = (pace.truncatingRemainder(dividingBy: 60.0) - paceSeconds) - let paceHours = String(format: "%.0f", ((pace - paceMinutes) / 60)) - let reConvertedSeconds = (paceSeconds / 1.666666666666666666) * 100 + let paceFormatted:String = { + let paceSeconds = pace.truncatingRemainder(dividingBy: 1.0) + let paceMinutes = (pace.truncatingRemainder(dividingBy: 60.0) - paceSeconds) + let paceHours = String(format: "%.0f", ((pace - paceMinutes) / 60)) + let reConvertedSeconds = (paceSeconds / 1.666666666666666666) * 100 + let properTimeMS = String(format: "%02d:%02d", Int(paceMinutes), Int(reConvertedSeconds.rounded())) + return (pace >= 60 ? "\(paceHours):\(properTimeMS)" : "\(properTimeMS)") + }() - let properTimeMS = String(format: "%02d:%02d", Int(paceMinutes), Int(reConvertedSeconds.rounded())) - - let paceSecondsOpposite = paceOpposite.truncatingRemainder(dividingBy: 1.0) - let paceMinutesOpposite = (paceOpposite.truncatingRemainder(dividingBy: 60.0) - paceSecondsOpposite) - let paceHoursOpposite = String(format: "%.0f", ((paceOpposite - paceMinutesOpposite) / 60)) - let reConvertedSecondsOpposite = (paceSecondsOpposite / 1.666666666666666666) * 100 - - let properTimeMSOpposite = String(format: "%02d:%02d", Int(paceMinutesOpposite), Int(reConvertedSecondsOpposite.rounded())) + let paceFormattedOpposite:String = { + let paceOpposite = (selectedSystem == "km" ? (pace * 1.609344) : (pace * 0.6213711922)) + let paceSecondsOpposite = paceOpposite.truncatingRemainder(dividingBy: 1.0) + let paceMinutesOpposite = (paceOpposite.truncatingRemainder(dividingBy: 60.0) - paceSecondsOpposite) + let paceHoursOpposite = String(format: "%.0f", ((paceOpposite - paceMinutesOpposite) / 60)) + let reConvertedSecondsOpposite = (paceSecondsOpposite / 1.666666666666666666) * 100 + let properTimeMSOpposite = String(format: "%02d:%02d", Int(paceMinutesOpposite), Int(reConvertedSecondsOpposite.rounded())) + return (paceOpposite >= 60 ? "\(paceHoursOpposite):\(properTimeMSOpposite)" : "\(properTimeMSOpposite)") + }() //let paceString:String = String(format: "%.2f", pace) let totalHours:Double = Double(timeMinutesToHours) + (Double(timeHours) ?? 0) @@ -164,18 +174,16 @@ 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 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 // zeros to them, so that 5 minutes 7 seconds will show // as 05:07 which is a format most people, and certainly // the people this app is intended for, will understand - + HStack { - VStack { + VStack (alignment: .leading) { Text("\(distance)\(selectedSystem)") Text("\(convertedDistanceString)\(notSelectedSystem)") } diff --git a/Splits/DocsView.swift b/Splits/DocsView.swift index 183935c..13848aa 100644 --- a/Splits/DocsView.swift +++ b/Splits/DocsView.swift @@ -13,9 +13,25 @@ import LocalAuthentication // the reader has a basic understanding of // Swift and/or SwiftUI. +/// The system username 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 = "admin" + +/// 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 = "123" +/// 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 @@ -46,8 +62,8 @@ struct DocsView: View { } Section(header: Text("App Information")) { NavigationLink("Software License", destination: LicenseView()) - Text("Version: Release Candidate 2 (1.0.0)") - Text("Release date: 2022-06-11") + Text("Version: Prerelease Build LVSXT10a.3") + Text("Release date: 2022-06-14") Text("Start date: 2022-03-25") Link("Built with SwiftUI \(Image(systemName: "swift"))", destination: URL(string: "https://developer.apple.com/xcode/swiftui")!) } @@ -60,21 +76,21 @@ struct DocsView: View { isUnlocked = false } } else { - if !isUnlocked { - Button("Log in with biometrics") { - authenticate() - } + 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()) } @@ -90,11 +106,19 @@ struct DocsView: View { default: () } - if (pass == password && user == username) { + 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 (pass == password && user == username) { return true @@ -102,11 +126,15 @@ struct DocsView: View { return false } } - func checkIfUnlocked() { - if !isUnlocked { - authenticate() - } - } + + /// 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? diff --git a/Splits/Documentation.docc/Documentation.md b/Splits/Documentation.docc/Documentation.md new file mode 100644 index 0000000..9711ca9 --- /dev/null +++ b/Splits/Documentation.docc/Documentation.md @@ -0,0 +1,14 @@ +# ``Splits`` + +Splits is a revolutionary new app to calculate paces. +It takes a value, in kilometers or miles, and converts + +## Overview + +Text + +## Topics + +### Getting Started + +- ``Symbol`` diff --git a/Splits/KnownIssues.swift b/Splits/KnownIssues.swift index 5ee79af..8b17a94 100644 --- a/Splits/KnownIssues.swift +++ b/Splits/KnownIssues.swift @@ -25,10 +25,7 @@ struct HighPriority: View { var body: some View { ScrollView { VStack { - Image(systemName: "checkmark.shield.fill") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 70) + Text("\u{2022} Putting a zero as the first number in the Distance text field causes the app to crash\n\t\u{2022} **Workaround:** Just don't put zero first. You can still use decimals (like .7km and .24mi)") } .padding(30) } @@ -55,7 +52,7 @@ struct LowPriority: View { var body: some View { ScrollView { VStack(alignment: .leading) { - Text("\u{2022} Space between the equation and explanation in the \"Help\" tab is too large\n\u{2022} App has no custom icon") + Text("\u{2022} Space between the equation and explanation in the \"Help\" tab is too large\n\u{2022} App has no custom icon\n\u{2022} In some circumstances, the pace will show xx:60/unit as a pace, when the 60 should equal one minute") } .padding(30) } diff --git a/Splits/SplitsApp.swift b/Splits/SplitsApp.swift index efe2243..e16ccf3 100644 --- a/Splits/SplitsApp.swift +++ b/Splits/SplitsApp.swift @@ -13,6 +13,7 @@ import SwiftUI // Swift and/or SwiftUI. @main +/// Builds the main view of the app (``TabViewData``) struct SplitsApp: App { var body: some Scene { WindowGroup {