Added more robust app documentation. Started working on an issue where entering zero as the first number in the Distance text field causes the app to crash.
This commit is contained in:
parent
7d6643112d
commit
b6677c2053
7 changed files with 108 additions and 41 deletions
|
|
@ -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 = "<group>"; };
|
||||
850F8025285437500094580D /* isaac.greene.clouds.heic */ = {isa = PBXFileReference; lastKnownFileType = file; name = isaac.greene.clouds.heic; path = Assets.xcassets/isaac.greene.clouds.heic; sourceTree = "<group>"; };
|
||||
850F8026285437500094580D /* jake.zimmerman.group.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; name = jake.zimmerman.group.jpg; path = Assets.xcassets/jake.zimmerman.group.jpg; sourceTree = "<group>"; };
|
||||
859298C828592F1F00D9D6CB /* Documentation.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = Documentation.docc; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
85AAA0D727FA2DD600F4B9A1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -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 */,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
14
Splits/Documentation.docc/Documentation.md
Normal file
14
Splits/Documentation.docc/Documentation.md
Normal file
|
|
@ -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
|
||||
|
||||
<!--@START_MENU_TOKEN@-->Text<!--@END_MENU_TOKEN@-->
|
||||
|
||||
## Topics
|
||||
|
||||
### Getting Started
|
||||
|
||||
- <!--@START_MENU_TOKEN@-->``Symbol``<!--@END_MENU_TOKEN@-->
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue