Compare commits
154 Commits
experimental
...
account
| Author | SHA1 | Date | |
|---|---|---|---|
| 441dae5caf | |||
| 74870197f9 | |||
| b71a2463c9 | |||
| 1cd6e73ac4 | |||
| e18edd26d2 | |||
| e0c0e24188 | |||
| 26c2b2832e | |||
| 4ff0455afd | |||
| 34cc04cd63 | |||
| 74c2c76af9 | |||
| e6817bd795 | |||
| 148a104a4c | |||
| 2a9cb8e677 | |||
| ff22e44140 | |||
| 5b576a7912 | |||
| 5a6534b1f1 | |||
| 219de54856 | |||
| 5800cd9774 | |||
| e461dae024 | |||
| f8cac67055 | |||
| 72b0cc92b6 | |||
| 7435c04348 | |||
| a774446a30 | |||
| 16046f7375 | |||
| 39dd83f287 | |||
| 3e601e474e | |||
| 6695f264ae | |||
| 9054f13af7 | |||
| 3ceeac07bc | |||
| e1ff034115 | |||
| aa602051d1 | |||
| 5835006985 | |||
| e9196bb249 | |||
| e32b3a4db9 | |||
| a505a8e553 | |||
| 5ee0ee4f4e | |||
| 4e4ea76d7e | |||
| b3053d2673 | |||
| 9bb4d0ccf1 | |||
| b1e11b8bc2 | |||
| 520d4bb6a3 | |||
| 325792ff13 | |||
| 746d3ff229 | |||
| 33d3739005 | |||
| 4a635aadce | |||
| e53cc408e2 | |||
| 45cf2b768e | |||
| 6cb4b75f82 | |||
| 1aeb6fb666 | |||
| 2ced920106 | |||
| 021368d7bb | |||
| 306d4bfaf1 | |||
| f37a12c963 | |||
| e790bca040 | |||
| 8f829217c1 | |||
| facf8eaeec | |||
| a375f16947 | |||
| 788eb4973a | |||
| 9ee1132d7d | |||
| a3dfff1e38 | |||
| 6f76ff90cd | |||
| cd6d81c3b3 | |||
| 6d0aa461c3 | |||
| bf7e746aff | |||
| 1223e42c46 | |||
| 083b5a05e0 | |||
| 259137170d | |||
| e165beb3aa | |||
| e255f8811e | |||
| 2cbfa70658 | |||
| c187403e29 | |||
| f8b78cfbc4 | |||
| 386e3f988e | |||
| 1bc2f95749 | |||
| da46725420 | |||
| 751c92fe14 | |||
| e41c0ea067 | |||
| ecd15bb30a | |||
| 431650c366 | |||
| acfcdbed24 | |||
| d200984eaf | |||
| d999944dfa | |||
| 634ad5aaf8 | |||
| 2eeb560fbb | |||
| 9312184177 | |||
| 17b300e74c | |||
| 996a1b343b | |||
| 03e52c7cb4 | |||
| d9a1fe4649 | |||
| 950e86302c | |||
| b987823a8e | |||
| 5a64dab670 | |||
| ef72aa8b91 | |||
| e9d0500132 | |||
| d5cfddb3fb | |||
| 84e4358fbc | |||
| f9c1af7fd8 | |||
| 54dad7f1d4 | |||
| 56fb9b6a3d | |||
| e0fd1e758d | |||
| 26752f607e | |||
| a57ab9c2f8 | |||
| 2c5f28c97b | |||
| 7e4dc51da8 | |||
| 2cd464c890 | |||
| d01d56b6ef | |||
| 788d43e333 | |||
| a493bfe6e8 | |||
| c54e18ad64 | |||
| ce9cbd7127 | |||
| 33c6173785 | |||
| cb80b2925d | |||
| 06d639c912 | |||
| d90c792436 | |||
| f88c48b9ad | |||
| ce758633b0 | |||
| d0ab0ba81d | |||
| a4de8274d4 | |||
| 31afd97850 | |||
| a3252603fb | |||
| e0b6eb41fd | |||
| ba50233bd2 | |||
| 79202c71f3 | |||
| 7e17405858 | |||
| c3c2142418 | |||
| 251b281a04 | |||
| ce9cf95e47 | |||
| 3cc0672d26 | |||
| fd332ad9f4 | |||
| c1e49ed66f | |||
| bf37dd657c | |||
| 395a7ecdec | |||
| 650c48ade9 | |||
| 6b89b930fc | |||
| c30f78efbb | |||
| c3fc3e827f | |||
| 02d8b40831 | |||
| 0571645df7 | |||
| bbd1520f5d | |||
| 04b4798ac3 | |||
| 8a90bf21bf | |||
| f33c1d8d1c | |||
| 1284b4ec36 | |||
| 19388c2db0 | |||
| 879689a10a | |||
| 30756d32aa | |||
| cb5515e54a | |||
| 27184eff9d | |||
| 702f4135a6 | |||
| 7be4b6ab97 | |||
| a1d149b1ee | |||
| f6d2fb66ed | |||
| fc2792ec37 | |||
| bd663772fb |
@@ -9,11 +9,12 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
4F509BD225AE22D100726227 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F509BD125AE22D100726227 /* Models.swift */; };
|
||||
4F8A607125A9160400D88DC3 /* AddAlarmViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F8A607025A9160400D88DC3 /* AddAlarmViewController.swift */; };
|
||||
4F8A607525A919E600D88DC3 /* Logic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F8A607425A919E600D88DC3 /* Logic.swift */; };
|
||||
4F98955225A9260400F51321 /* Net.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F98955125A9260400F51321 /* Net.swift */; };
|
||||
4FA419AF25AF93EC004CE0FC /* AlarmViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FA419AE25AF93EC004CE0FC /* AlarmViewController.swift */; };
|
||||
4FD642D325B48C380069171E /* AlarmActivator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FD642D225B48C380069171E /* AlarmActivator.swift */; };
|
||||
4FD642DB25B4B7F60069171E /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FD642DA25B4B7F60069171E /* Utils.swift */; };
|
||||
4FD642E025B4D5F30069171E /* AlarmActivationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FD642DF25B4D5F30069171E /* AlarmActivationViewController.swift */; };
|
||||
4FF0683F25A5F18700304E6B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF0683E25A5F18700304E6B /* AppDelegate.swift */; };
|
||||
4FF0684125A5F18700304E6B /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF0684025A5F18700304E6B /* SceneDelegate.swift */; };
|
||||
4FF0684325A5F18700304E6B /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF0684225A5F18700304E6B /* AccountViewController.swift */; };
|
||||
4FF0684625A5F18700304E6B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4FF0684425A5F18700304E6B /* Main.storyboard */; };
|
||||
4FF0684825A5F18800304E6B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4FF0684725A5F18800304E6B /* Assets.xcassets */; };
|
||||
@@ -21,18 +22,20 @@
|
||||
7C5DAE9C25AF812200E44C52 /* clock.png in Resources */ = {isa = PBXBuildFile; fileRef = 7C5DAE9B25AF812200E44C52 /* clock.png */; };
|
||||
7C83963625AF375B0027A94C /* NotificationLogic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C83963525AF375B0027A94C /* NotificationLogic.swift */; };
|
||||
7C83963925AF68980027A94C /* TestingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C83963825AF68980027A94C /* TestingViewController.swift */; };
|
||||
7C83963C25AF6B6B0027A94C /* Alarm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C83963B25AF6B6B0027A94C /* Alarm.swift */; };
|
||||
C7E638E825B88F8B00799512 /* MathExpressions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7E638E725B88F8B00799512 /* MathExpressions.swift */; };
|
||||
F0DF7C0725BCD9FC0064A26B /* StopwatchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DF7C0625BCD9FC0064A26B /* StopwatchViewController.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
4F509BD125AE22D100726227 /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = "<group>"; };
|
||||
4F8A607025A9160400D88DC3 /* AddAlarmViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAlarmViewController.swift; sourceTree = "<group>"; };
|
||||
4F8A607425A919E600D88DC3 /* Logic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logic.swift; sourceTree = "<group>"; };
|
||||
4F98955125A9260400F51321 /* Net.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Net.swift; sourceTree = "<group>"; };
|
||||
4FA419AE25AF93EC004CE0FC /* AlarmViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmViewController.swift; sourceTree = "<group>"; };
|
||||
4FD642D225B48C380069171E /* AlarmActivator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmActivator.swift; sourceTree = "<group>"; };
|
||||
4FD642DA25B4B7F60069171E /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
|
||||
4FD642DF25B4D5F30069171E /* AlarmActivationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmActivationViewController.swift; sourceTree = "<group>"; };
|
||||
4FF0683B25A5F18700304E6B /* ProjectClock.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProjectClock.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4FF0683E25A5F18700304E6B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
4FF0684025A5F18700304E6B /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
4FF0684225A5F18700304E6B /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = "<group>"; };
|
||||
4FF0684525A5F18700304E6B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
4FF0684725A5F18800304E6B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
@@ -43,7 +46,8 @@
|
||||
7C83962F25AF34F10027A94C /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; };
|
||||
7C83963525AF375B0027A94C /* NotificationLogic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationLogic.swift; sourceTree = "<group>"; };
|
||||
7C83963825AF68980027A94C /* TestingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestingViewController.swift; sourceTree = "<group>"; };
|
||||
7C83963B25AF6B6B0027A94C /* Alarm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alarm.swift; sourceTree = "<group>"; };
|
||||
C7E638E725B88F8B00799512 /* MathExpressions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MathExpressions.swift; sourceTree = "<group>"; };
|
||||
F0DF7C0625BCD9FC0064A26B /* StopwatchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StopwatchViewController.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -77,23 +81,25 @@
|
||||
4FF0683D25A5F18700304E6B /* ProjectClock */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4FF0683E25A5F18700304E6B /* AppDelegate.swift */,
|
||||
4FF0684025A5F18700304E6B /* SceneDelegate.swift */,
|
||||
7C5DAE9B25AF812200E44C52 /* clock.png */,
|
||||
4FF0684C25A5F18800304E6B /* Info.plist */,
|
||||
7C83962D25AF34F00027A94C /* ProjectClock.entitlements */,
|
||||
4FF0684925A5F18800304E6B /* LaunchScreen.storyboard */,
|
||||
4FF0684725A5F18800304E6B /* Assets.xcassets */,
|
||||
4FF0683E25A5F18700304E6B /* AppDelegate.swift */,
|
||||
4FF0684225A5F18700304E6B /* AccountViewController.swift */,
|
||||
4FA419AE25AF93EC004CE0FC /* AlarmViewController.swift */,
|
||||
4F8A607025A9160400D88DC3 /* AddAlarmViewController.swift */,
|
||||
7C83963825AF68980027A94C /* TestingViewController.swift */,
|
||||
4FD642DF25B4D5F30069171E /* AlarmActivationViewController.swift */,
|
||||
4FD642D225B48C380069171E /* AlarmActivator.swift */,
|
||||
4FF0684425A5F18700304E6B /* Main.storyboard */,
|
||||
4FF0684725A5F18800304E6B /* Assets.xcassets */,
|
||||
4FF0684925A5F18800304E6B /* LaunchScreen.storyboard */,
|
||||
4FF0684C25A5F18800304E6B /* Info.plist */,
|
||||
4F8A607425A919E600D88DC3 /* Logic.swift */,
|
||||
F0DF7C0625BCD9FC0064A26B /* StopwatchViewController.swift */,
|
||||
7C83963525AF375B0027A94C /* NotificationLogic.swift */,
|
||||
4F98955125A9260400F51321 /* Net.swift */,
|
||||
7C5DAE9B25AF812200E44C52 /* clock.png */,
|
||||
4F509BD125AE22D100726227 /* Models.swift */,
|
||||
7C83963825AF68980027A94C /* TestingViewController.swift */,
|
||||
7C83963B25AF6B6B0027A94C /* Alarm.swift */,
|
||||
C7E638E725B88F8B00799512 /* MathExpressions.swift */,
|
||||
4FD642DA25B4B7F60069171E /* Utils.swift */,
|
||||
);
|
||||
path = ProjectClock;
|
||||
sourceTree = "<group>";
|
||||
@@ -182,15 +188,17 @@
|
||||
files = (
|
||||
4F8A607125A9160400D88DC3 /* AddAlarmViewController.swift in Sources */,
|
||||
4F98955225A9260400F51321 /* Net.swift in Sources */,
|
||||
4F8A607525A919E600D88DC3 /* Logic.swift in Sources */,
|
||||
7C83963925AF68980027A94C /* TestingViewController.swift in Sources */,
|
||||
4FF0684325A5F18700304E6B /* AccountViewController.swift in Sources */,
|
||||
F0DF7C0725BCD9FC0064A26B /* StopwatchViewController.swift in Sources */,
|
||||
4FF0683F25A5F18700304E6B /* AppDelegate.swift in Sources */,
|
||||
4FF0684125A5F18700304E6B /* SceneDelegate.swift in Sources */,
|
||||
4FD642D325B48C380069171E /* AlarmActivator.swift in Sources */,
|
||||
4FD642DB25B4B7F60069171E /* Utils.swift in Sources */,
|
||||
4FA419AF25AF93EC004CE0FC /* AlarmViewController.swift in Sources */,
|
||||
7C83963C25AF6B6B0027A94C /* Alarm.swift in Sources */,
|
||||
4F509BD225AE22D100726227 /* Models.swift in Sources */,
|
||||
C7E638E825B88F8B00799512 /* MathExpressions.swift in Sources */,
|
||||
7C83963625AF375B0027A94C /* NotificationLogic.swift in Sources */,
|
||||
4FD642E025B4D5F30069171E /* AlarmActivationViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -7,10 +7,173 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
Account view controller controlling the two separate view controllers
|
||||
*/
|
||||
class AccountViewController: UIViewController
|
||||
{
|
||||
@IBOutlet var vLogin: UIView!
|
||||
@IBOutlet var vManage: UIView!
|
||||
|
||||
// For instance references
|
||||
static var this: AccountViewController!
|
||||
|
||||
/**
|
||||
Called when the user switch to this tab
|
||||
*/
|
||||
override func viewDidLoad()
|
||||
{
|
||||
// Static instance reference
|
||||
AccountViewController.this = self
|
||||
|
||||
// Check if already registered/logged in
|
||||
if localStorage.string(forKey: "id") != nil { login() }
|
||||
|
||||
super.viewDidLoad()
|
||||
}
|
||||
|
||||
/**
|
||||
Login from the account page
|
||||
*/
|
||||
func login()
|
||||
{
|
||||
vLogin.isHidden = true
|
||||
vManage.isHidden = false
|
||||
ManageVC.this.display()
|
||||
}
|
||||
|
||||
/**
|
||||
Logout
|
||||
*/
|
||||
func logout()
|
||||
{
|
||||
// Remove login info
|
||||
["id", "user", "pass"].forEach { localStorage.removeObject(forKey: $0) }
|
||||
|
||||
// Switch UI
|
||||
vLogin.isHidden = false
|
||||
vManage.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
View controller for registration and login
|
||||
*/
|
||||
class LoginVC: UIViewController
|
||||
{
|
||||
@IBOutlet weak var username: UITextField!
|
||||
@IBOutlet weak var password: UITextField!
|
||||
|
||||
/**
|
||||
Send user login/registration request
|
||||
|
||||
- Parameter login: True: Login, False: Register
|
||||
*/
|
||||
func userRequest(login: Bool)
|
||||
{
|
||||
// Verify username and password
|
||||
guard let name = username.text, name ~= "[A-Za-z0-9_-]{3,16}" else
|
||||
{
|
||||
msg("Username Invalid", "Username must be 3 to 16 characters long, and must only contain a-z, 0-9, underscore, and minus signs (-).")
|
||||
return
|
||||
}
|
||||
guard let pass = password.text, pass.count >= 8 else
|
||||
{
|
||||
msg("Password Invalid", "Password must be more than or equal to 8 characters long")
|
||||
return
|
||||
}
|
||||
|
||||
// Error messages
|
||||
let errors = ["409 - [\"A0111\"]": "Account already exists, please login instead.",
|
||||
"401 -": "Incorrect username/password",
|
||||
"404 -": "Username does not exist in the database",
|
||||
"406 - [\"A0101\"]": "Username invalid."
|
||||
]
|
||||
|
||||
// Send register request
|
||||
sendReq(login ? APIs.login : APIs.register,
|
||||
title: login ? "Logging in..." : "Registering...", errors: errors,
|
||||
params: ["username": name, "password": pass.sha256])
|
||||
{
|
||||
// Store username and password
|
||||
localStorage["name"] = name
|
||||
localStorage["pass"] = pass.sha256
|
||||
localStorage["id"] = $0
|
||||
|
||||
// Send feedback
|
||||
if login { self.msg("Login success!", "Now you can use account features, yay!") }
|
||||
else { self.msg("Registration success!", "Now you have an account, yay!") }
|
||||
|
||||
// Hide registration and show account detail view
|
||||
AccountViewController.this.login()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks register
|
||||
*/
|
||||
@IBAction func register(_ sender: Any)
|
||||
{
|
||||
userRequest(login: false)
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks login
|
||||
*/
|
||||
@IBAction func login(_ sender: Any)
|
||||
{
|
||||
userRequest(login: true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Account manage view controller
|
||||
*/
|
||||
class ManageVC: UIViewController
|
||||
{
|
||||
static var this: ManageVC!
|
||||
|
||||
@IBOutlet weak var lUsername: UILabel!
|
||||
@IBOutlet weak var lJoinDate: UILabel!
|
||||
|
||||
/**
|
||||
Called when the user switched to the account tab (whether the view container is hidden or not)
|
||||
*/
|
||||
override func viewDidLoad()
|
||||
{
|
||||
// Static reference
|
||||
ManageVC.this = self
|
||||
super.viewDidLoad()
|
||||
}
|
||||
|
||||
/**
|
||||
Display account info
|
||||
*/
|
||||
func display()
|
||||
{
|
||||
lUsername.text = localStorage.string(forKey: "name")
|
||||
// TODO: Correct join date
|
||||
lJoinDate.text = localStorage.string(forKey: "id")
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks the logout button
|
||||
*/
|
||||
@IBAction func logout(_ sender: Any)
|
||||
{
|
||||
AccountViewController.this.logout()
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks the delete account button
|
||||
*/
|
||||
@IBAction func deleteAccount(_ sender: Any)
|
||||
{
|
||||
sendReq(APIs.delete, title: "Deleting...")
|
||||
{
|
||||
print("Deleted! \($0)")
|
||||
self.msg("Deleted!", "You are erased from our database, you no longer exist.")
|
||||
self.logout(sender)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,14 +17,55 @@ class AddAlarmViewController: UIViewController
|
||||
scrollView.addSubview(scrollViewInner)
|
||||
scrollView.contentSize = scrollViewInner.frame.size
|
||||
}
|
||||
|
||||
|
||||
// Pickers
|
||||
@IBOutlet weak var timePicker: UIDatePicker!
|
||||
@IBOutlet weak var wvmPicker: UIPickerView!
|
||||
|
||||
override func viewDidLoad()
|
||||
// UI Elements
|
||||
@IBOutlet weak var repeatWeekdaysSwitch: UISwitch!
|
||||
@IBOutlet weak var repeatWeekendsSwitch: UISwitch!
|
||||
@IBOutlet weak var alarmNameTextField: UITextField!
|
||||
|
||||
@IBAction func defaultRingtonesButton(_ sender: Any)
|
||||
{
|
||||
super.viewDidLoad()
|
||||
|
||||
}
|
||||
|
||||
@IBAction func soundLibraryButton(_ sender: Any)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks the remove button and brings them back to the home page
|
||||
*/
|
||||
@IBAction func cancelAlarmButton(_ sender: Any) {
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
//might need to reset all UI elements
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks Add Alarm
|
||||
*/
|
||||
@IBAction func addAlarmButton(_ sender: Any) {
|
||||
|
||||
let (h, m, _) = timePicker.date.getHMS()
|
||||
|
||||
// Create the alarm
|
||||
let alarm = Alarm(hour: h, minute: m,
|
||||
text: alarmNameTextField.text ?? "Alarm",
|
||||
wakeMethod: wvms[wvmPicker.selectedRow(inComponent: 0)],
|
||||
lastActivate: Date())
|
||||
|
||||
// TODO: Set alarm.repeats to correspond with what the user selects
|
||||
|
||||
|
||||
// Add the alarm to the list and save the list
|
||||
Alarms.fromLocal().apply { $0.list.append(alarm) }.localSave();
|
||||
|
||||
// Dismiss this view
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
//
|
||||
// Alarm.swift
|
||||
// ProjectClock
|
||||
//
|
||||
// Created by Aaron Saporito on 1/13/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class Alarm {
|
||||
var alarmTime: Date
|
||||
var text: String
|
||||
var wakeMethod: WVM
|
||||
|
||||
init(alarmTime: Date, text: String, wakeMethod: WVM) {
|
||||
|
||||
self.alarmTime = alarmTime
|
||||
self.text = text
|
||||
self.wakeMethod = wakeMethod
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// AlarmActivationViewController.swift
|
||||
// ProjectClock
|
||||
//
|
||||
// Created by Hykilpikonna on 1/17/21.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
class AlarmActivationViewController: UIViewController
|
||||
{
|
||||
var timer: Timer?
|
||||
var currentAlarm: Alarm?
|
||||
|
||||
//Puzzle outlets
|
||||
@IBOutlet weak var puzzleView: UIView!
|
||||
@IBOutlet weak var puzzleQuestionLabel: UILabel!
|
||||
@IBOutlet weak var puzzleAnswerInput: UITextField!
|
||||
var puzzleAnswers: [Int] = []
|
||||
|
||||
//RPS Outlets
|
||||
@IBOutlet weak var rpsView: UIView!
|
||||
@IBOutlet weak var rpsResult: UILabel!
|
||||
|
||||
|
||||
init?(coder: NSCoder, currentAlarm: Alarm)
|
||||
{
|
||||
self.currentAlarm = currentAlarm
|
||||
//print(currentAlarm.wakeMethod)
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad()
|
||||
{
|
||||
super.viewDidLoad()
|
||||
//Hide all inactive wakemethods
|
||||
puzzleView.isHidden = true
|
||||
rpsView.isHidden = true
|
||||
|
||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(AlarmActivationViewController.playSound), userInfo: nil, repeats: true)
|
||||
setAlarmType()
|
||||
//print(MathExpression.random())
|
||||
}
|
||||
|
||||
@objc func playSound()
|
||||
{
|
||||
AudioServicesPlayAlertSound(SystemSoundID(1005))
|
||||
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate)
|
||||
}
|
||||
|
||||
func setAlarmType()
|
||||
{
|
||||
if let alarm = currentAlarm
|
||||
{
|
||||
switch alarm.wakeMethod.name {
|
||||
case "Walk":
|
||||
walkAction()
|
||||
case "Jump":
|
||||
jumpAction()
|
||||
case "Factor":
|
||||
self.puzzleAnswers = factorAction(puzzleQuestionLabel: puzzleQuestionLabel)
|
||||
puzzleView.isHidden = false
|
||||
case "Smash":
|
||||
print("")
|
||||
case "RPS":
|
||||
rpsView.isHidden = false
|
||||
//Get Choice here
|
||||
//rpsAction(choice: choice)
|
||||
default:
|
||||
print("Invalid alarm type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Verfies and ends factoring WVM
|
||||
@IBAction func checkBinomialSolution(_ sender: Any) {
|
||||
if let input = puzzleAnswerInput.text {
|
||||
if let numericalInput = Int(input) {
|
||||
if puzzleAnswers.contains(numericalInput) {
|
||||
endAlarm()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Gets RPS choice
|
||||
@IBAction func rockChoice(_ sender: Any) {
|
||||
if rpsAction(choice: .rock)! {
|
||||
endAlarm()
|
||||
} else {
|
||||
rpsResult.text = "Paper: You lost, try again"
|
||||
}
|
||||
}
|
||||
@IBAction func paperChoice(_ sender: Any) {
|
||||
if rpsAction(choice: .paper)! {
|
||||
endAlarm()
|
||||
} else {
|
||||
rpsResult.text = "Scissors: You lost, try again"
|
||||
}
|
||||
}
|
||||
@IBAction func scissorChoice(_ sender: Any) {
|
||||
if rpsAction(choice: .scissors)! {
|
||||
endAlarm()
|
||||
} else {
|
||||
rpsResult.text = "Rock: You lost, try again"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Standard way to turn off and close the alarm
|
||||
func endAlarm() {
|
||||
timer?.invalidate()
|
||||
print("Alarm solved")
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// AlarmActivator.swift
|
||||
// ProjectClock
|
||||
//
|
||||
// Created by Hykilpikonna on 1/17/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
Class to activate alarms when the user is inside the app
|
||||
|
||||
Note: This will not run when app is switched to the background or when the display is turned off, but it will run right after the user switched back to the app.
|
||||
*/
|
||||
class AlarmActivator: UITabBarController
|
||||
{
|
||||
/// Interval in seconds
|
||||
static var interval = 2.0
|
||||
|
||||
/// Timer for scheduled calls
|
||||
var timer: Timer?
|
||||
var alarm: Alarm?
|
||||
|
||||
override func viewDidLoad()
|
||||
{
|
||||
start()
|
||||
}
|
||||
|
||||
/**
|
||||
Start detecting alarms
|
||||
*/
|
||||
func start()
|
||||
{
|
||||
if timer != nil { return }
|
||||
timer = Timer.scheduledTimer(timeInterval: AlarmActivator.interval, target: self, selector: #selector(AlarmActivator.check), userInfo: nil, repeats: true)
|
||||
}
|
||||
|
||||
/**
|
||||
Stop detecting alarms
|
||||
*/
|
||||
func stop()
|
||||
{
|
||||
timer?.invalidate()
|
||||
timer = nil
|
||||
}
|
||||
|
||||
/**
|
||||
Check alarm
|
||||
*/
|
||||
@objc func check()
|
||||
{
|
||||
//NSLog("Check")
|
||||
|
||||
// Get the alarm to activate
|
||||
let alarms = Alarms.fromLocal()
|
||||
guard let alarm = alarms.listActivating.first else { return }
|
||||
|
||||
// Update alarm info
|
||||
alarm.apply {
|
||||
$0.lastActivate = Date()
|
||||
if $0.oneTime { $0.enabled = false }
|
||||
}
|
||||
|
||||
alarms.localSave()
|
||||
self.alarm = alarm
|
||||
// Segue
|
||||
//NSLog(JSON.stringify(alarm)!)
|
||||
performSegue(withIdentifier: "activate-alarm", sender: alarm)
|
||||
}
|
||||
|
||||
@IBSegueAction func sendAlarm(_ coder: NSCoder) -> AlarmActivationViewController? {
|
||||
return AlarmActivationViewController(coder: coder, currentAlarm: alarm!)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,15 +3,14 @@ import UIKit
|
||||
class AlarmViewController: UIViewController
|
||||
{
|
||||
@IBOutlet weak var table: UITableView!
|
||||
|
||||
// TODO: Remove example and use localStorage
|
||||
var data: [Alarm] = [Alarm(alarmTime: Date(), text: "Wake up lol", wakeMethod: wvms[0])]
|
||||
static var staticTable: UITableView?
|
||||
|
||||
override func viewDidLoad()
|
||||
{
|
||||
super.viewDidLoad()
|
||||
|
||||
// Assign table delegate and data source
|
||||
AlarmViewController.staticTable = table
|
||||
table.delegate = self
|
||||
table.dataSource = self
|
||||
}
|
||||
@@ -26,7 +25,7 @@ extension AlarmViewController: UITableViewDelegate, UITableViewDataSource
|
||||
func numberOfSections(in: UITableView) -> Int { return 1 }
|
||||
|
||||
/// How many rows are there
|
||||
func tableView(_ v: UITableView, numberOfRowsInSection s: Int) -> Int { return data.count }
|
||||
func tableView(_ v: UITableView, numberOfRowsInSection s: Int) -> Int { return Alarms.fromLocal().list.count }
|
||||
|
||||
/// Configure each cell
|
||||
func tableView(_ v: UITableView, cellForRowAt i: IndexPath) -> UITableViewCell
|
||||
@@ -34,7 +33,7 @@ extension AlarmViewController: UITableViewDelegate, UITableViewDataSource
|
||||
// Get the cell and item at index i
|
||||
let rawCell = v.dequeueReusableCell(withIdentifier: "alarm", for: i)
|
||||
guard let cell = rawCell as? AlarmTableCell else { return rawCell }
|
||||
let item = data[i.row]
|
||||
let item = Alarms.fromLocal().list[i.row]
|
||||
|
||||
// Set the content of the cell to the content of the item.
|
||||
cell.setData(item)
|
||||
@@ -62,8 +61,33 @@ class AlarmTableCell: UITableViewCell
|
||||
@IBOutlet weak var goingOffText: UILabel!
|
||||
|
||||
/// Update information on the cell to information in the alarm object
|
||||
//WARNING:Terrible code lies ahead! You WILL be dissapointed! But it works, and that is all that matters.
|
||||
|
||||
func setData(_ alarm: Alarm)
|
||||
{
|
||||
descriptionText.text = "- " + alarm.text
|
||||
|
||||
// Display Hour, Minute, and AM or PM
|
||||
ampm.text = alarm.hour < 12 || alarm.hour == 24 ? "AM" : "PM"
|
||||
let hour = alarm.hour <= 12 ? alarm.hour : alarm.hour - 12
|
||||
time.text = alarm.minute < 10 ? "\(hour):0\(alarm.minute)" : "\(hour):\(alarm.minute)"
|
||||
|
||||
// displays the specific days alarm is activated
|
||||
let daysDict = ["Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"]
|
||||
var daysActive : [String] = []
|
||||
if alarm.oneTime {repeatText.text = "No Repeat"}
|
||||
else {
|
||||
for (index, element) in alarm.repeats.enumerated() {
|
||||
if element {
|
||||
daysActive.append(daysDict[index])
|
||||
}
|
||||
}
|
||||
repeatText.text = daysActive.joined(separator: ", ")
|
||||
}
|
||||
|
||||
// Show next activation date
|
||||
if alarm.enabled, let n = alarm.nextActivate {
|
||||
goingOffText.text = "(Going off in \(n.timeIntervalSince(Date()).str()))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,29 +8,76 @@
|
||||
import UIKit
|
||||
|
||||
@main
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate
|
||||
{
|
||||
/// Override point for customization after application launch.
|
||||
func application(_ app: UIApplication, didFinishLaunchingWithOptions op: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
|
||||
{
|
||||
// Init default settings
|
||||
localStorage.register(defaults: [
|
||||
"alarms": JSON.stringify([Alarm(hour: 7, minute: 20, text: "Wake up lol", wakeMethod: wvms[0])])!
|
||||
])
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: UISceneSession Lifecycle
|
||||
|
||||
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
||||
func application(_ app: UIApplication, configurationForConnecting session: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration
|
||||
{
|
||||
// Called when a new scene session is being created.
|
||||
// Use this method to select a configuration to create the new scene with.
|
||||
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
|
||||
return UISceneConfiguration(name: "Default Configuration", sessionRole: session.role)
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
|
||||
func application(_ app: UIApplication, didDiscardSceneSessions sessions: Set<UISceneSession>)
|
||||
{
|
||||
// Called when the user discards a scene session.
|
||||
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
|
||||
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate
|
||||
{
|
||||
var window: UIWindow?
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
|
||||
{
|
||||
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
|
||||
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
|
||||
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
||||
guard let _ = (scene as? UIWindowScene) else { return }
|
||||
}
|
||||
|
||||
func sceneDidDisconnect(_ scene: UIScene)
|
||||
{
|
||||
// Called as the scene is being released by the system.
|
||||
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
||||
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
||||
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
|
||||
}
|
||||
|
||||
func sceneDidBecomeActive(_ scene: UIScene)
|
||||
{
|
||||
// Called when the scene has moved from an inactive state to an active state.
|
||||
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
||||
}
|
||||
|
||||
func sceneWillResignActive(_ scene: UIScene)
|
||||
{
|
||||
// Called when the scene will move from an active state to an inactive state.
|
||||
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
||||
}
|
||||
|
||||
func sceneWillEnterForeground(_ scene: UIScene)
|
||||
{
|
||||
// Called as the scene transitions from the background to the foreground.
|
||||
// Use this method to undo the changes made on entering the background.
|
||||
}
|
||||
|
||||
func sceneDidEnterBackground(_ scene: UIScene)
|
||||
{
|
||||
// Called as the scene transitions from the foreground to the background.
|
||||
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
||||
// to restore the scene back to its current state.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="s8U-C8-W2S">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="s8U-C8-W2S">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
@@ -18,7 +18,7 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="VaN-9W-uoG">
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" rowHeight="44" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="VaN-9W-uoG">
|
||||
<rect key="frame" x="0.0" y="98" width="414" height="798"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<button key="tableFooterView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" id="DQJ-kT-79q">
|
||||
@@ -26,7 +26,7 @@
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<state key="normal" title="Add Alarm"/>
|
||||
<connections>
|
||||
<segue destination="Mki-dC-5Kc" kind="presentation" id="Qim-4Q-43N"/>
|
||||
<segue destination="Mki-dC-5Kc" kind="presentation" identifier="alarm-edit" id="Qim-4Q-43N"/>
|
||||
</connections>
|
||||
</button>
|
||||
<prototypes>
|
||||
@@ -61,22 +61,24 @@
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="| Going off in 23 hr 22 m" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EPJ-eL-Kek">
|
||||
<rect key="frame" x="179" y="70" width="215" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yra-wA-O7B">
|
||||
<rect key="frame" x="345" y="26" width="51" height="31"/>
|
||||
</switch>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="(Going off in 23 hr 22 m)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EPJ-eL-Kek">
|
||||
<rect key="frame" x="179" y="74.5" width="215" height="14.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yra-wA-O7B">
|
||||
<rect key="frame" x="345" y="26" width="49" height="31"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="dWq-jh-9Ue" firstAttribute="baseline" secondItem="4hj-fb-FF3" secondAttribute="baseline" id="4H2-o6-GfV"/>
|
||||
<constraint firstItem="YhV-X3-y1v" firstAttribute="leading" secondItem="Wl6-IQ-lVM" secondAttribute="leadingMargin" constant="10" id="Hur-MO-GTA"/>
|
||||
<constraint firstItem="yra-wA-O7B" firstAttribute="top" secondItem="Wl6-IQ-lVM" secondAttribute="topMargin" constant="15" id="Jvt-p2-9cx"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="EPJ-eL-Kek" secondAttribute="trailing" id="LhS-n6-zst"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="yra-wA-O7B" secondAttribute="trailing" id="R9H-AC-mf6"/>
|
||||
<constraint firstItem="mjO-SX-f31" firstAttribute="leading" secondItem="Wl6-IQ-lVM" secondAttribute="leadingMargin" constant="10" id="VOR-Go-oCw"/>
|
||||
<constraint firstItem="EPJ-eL-Kek" firstAttribute="top" secondItem="4hj-fb-FF3" secondAttribute="bottom" constant="13" id="je6-UM-5Kc"/>
|
||||
<constraint firstItem="EPJ-eL-Kek" firstAttribute="baseline" secondItem="YhV-X3-y1v" secondAttribute="firstBaseline" id="ilR-ei-lE6"/>
|
||||
<constraint firstItem="dWq-jh-9Ue" firstAttribute="leading" secondItem="mjO-SX-f31" secondAttribute="trailing" constant="8" symbolic="YES" id="ocx-bN-3M8"/>
|
||||
<constraint firstItem="EPJ-eL-Kek" firstAttribute="leading" secondItem="YhV-X3-y1v" secondAttribute="trailing" constant="8" symbolic="YES" id="po7-2V-fZ6"/>
|
||||
<constraint firstItem="4hj-fb-FF3" firstAttribute="top" secondItem="Wl6-IQ-lVM" secondAttribute="topMargin" constant="25" id="r6f-mB-n9x"/>
|
||||
@@ -133,7 +135,7 @@
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="LKF-bm-JFg" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-494" y="869"/>
|
||||
<point key="canvasLocation" x="-494.20289855072468" y="868.52678571428567"/>
|
||||
</scene>
|
||||
<!--Account-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
@@ -149,194 +151,57 @@
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="sXr-25-joh">
|
||||
<rect key="frame" x="20" y="336" width="374" height="224"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" text="Account" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Yoz-zy-c7V">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="41"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle0"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="xzo-A1-wgr">
|
||||
<rect key="frame" x="0.0" y="61" width="374" height="41"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lPF-ex-duz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="78.5" height="41"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="hbK-Dt-9tn">
|
||||
<rect key="frame" x="98.5" y="0.0" width="275.5" height="41"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="yvB-Yi-jqQ">
|
||||
<rect key="frame" x="0.0" y="122" width="374" height="41"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Password" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="twj-MC-8je">
|
||||
<rect key="frame" x="0.0" y="0.0" width="73.5" height="41"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="97S-CL-8Wq">
|
||||
<rect key="frame" x="93.5" y="0.0" width="280.5" height="41"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" secureTextEntry="YES"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="htn-tb-R70">
|
||||
<rect key="frame" x="0.0" y="183" width="374" height="41"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WK1-Pr-EyJ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="177" height="41"/>
|
||||
<state key="normal" title="Register"/>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Exj-c9-ucM">
|
||||
<rect key="frame" x="197" y="0.0" width="177" height="41"/>
|
||||
<state key="normal" title="Login"/>
|
||||
</button>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eS2-n9-bE3" userLabel="Divider Line">
|
||||
<rect key="frame" x="20" y="92" width="374" height="1"/>
|
||||
<color key="backgroundColor" systemColor="labelColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="Da5-Yc-aYa"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="hkW-EE-XPg">
|
||||
<rect key="frame" x="20" y="109" width="374" height="321"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3Wi-Rg-PJy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="170"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ZOn-IP-cwj">
|
||||
<rect key="frame" x="132" y="0.0" width="110" height="110"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="110" id="6IV-Ay-C4i"/>
|
||||
<constraint firstAttribute="height" constant="110" id="xCo-yb-dyX"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Username 233" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0xm-Sd-S80">
|
||||
<rect key="frame" x="20" y="118" width="334" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Joined on Jan 8, 2021" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yeb-TX-Edr">
|
||||
<rect key="frame" x="20" y="138.5" width="334" height="14.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="yeb-TX-Edr" secondAttribute="trailing" constant="20" id="E18-zy-QnK"/>
|
||||
<constraint firstItem="0xm-Sd-S80" firstAttribute="leading" secondItem="3Wi-Rg-PJy" secondAttribute="leading" constant="20" id="KTh-5Z-Icc"/>
|
||||
<constraint firstItem="ZOn-IP-cwj" firstAttribute="top" secondItem="3Wi-Rg-PJy" secondAttribute="top" id="aUR-fX-A3S"/>
|
||||
<constraint firstItem="ZOn-IP-cwj" firstAttribute="centerX" secondItem="3Wi-Rg-PJy" secondAttribute="centerX" id="awM-Rr-agJ"/>
|
||||
<constraint firstAttribute="height" constant="170" id="e42-Ry-PNW"/>
|
||||
<constraint firstAttribute="trailing" secondItem="0xm-Sd-S80" secondAttribute="trailing" constant="20" id="hXl-GG-yik"/>
|
||||
<constraint firstItem="0xm-Sd-S80" firstAttribute="top" secondItem="ZOn-IP-cwj" secondAttribute="bottom" constant="8" symbolic="YES" id="lkR-sx-rEQ"/>
|
||||
<constraint firstItem="yeb-TX-Edr" firstAttribute="leading" secondItem="3Wi-Rg-PJy" secondAttribute="leading" constant="20" id="nH7-Kg-HzV"/>
|
||||
<constraint firstItem="yeb-TX-Edr" firstAttribute="top" secondItem="0xm-Sd-S80" secondAttribute="bottom" id="pUg-uh-chQ"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="3aB-bs-ULc">
|
||||
<rect key="frame" x="0.0" y="190" width="374" height="31"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Auto Backup" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Syv-4b-ZfJ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="305" height="31"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ebp-xK-if6">
|
||||
<rect key="frame" x="325" y="0.0" width="51" height="31"/>
|
||||
</switch>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="q7T-mb-6ax">
|
||||
<rect key="frame" x="0.0" y="241" width="374" height="30"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5a1-FL-1KK">
|
||||
<rect key="frame" x="0.0" y="0.0" width="187" height="30"/>
|
||||
<color key="backgroundColor" red="0.9137254901960784" green="0.95294117647058818" blue="0.99607843137254903" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<state key="normal" title="Backup Config"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
|
||||
<integer key="value" value="5"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="layer.borderWidth">
|
||||
<integer key="value" value="2"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="layer.borderColor">
|
||||
<color key="value" name="AccentColor"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1on-v1-fQc">
|
||||
<rect key="frame" x="187" y="0.0" width="187" height="30"/>
|
||||
<state key="normal" title="Restore Backup"/>
|
||||
</button>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="l9d-j4-iaa">
|
||||
<rect key="frame" x="0.0" y="291" width="374" height="30"/>
|
||||
<state key="normal" title="Delete Account">
|
||||
<color key="titleColor" systemColor="systemRedColor"/>
|
||||
</state>
|
||||
</button>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Manage Account and Settings" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cPO-q9-S2N">
|
||||
<rect key="frame" x="20" y="74.5" width="374" height="14.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uu6-RV-xw3">
|
||||
<rect key="frame" x="0.0" y="109" width="414" height="200"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="200" id="Kko-qD-93X"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<segue destination="ZAC-hn-zGl" kind="embed" id="foq-Zo-2nt"/>
|
||||
</connections>
|
||||
</containerView>
|
||||
<containerView hidden="YES" opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="RSY-ha-JUW">
|
||||
<rect key="frame" x="0.0" y="109" width="414" height="704"/>
|
||||
<connections>
|
||||
<segue destination="3gV-kF-UbK" kind="embed" id="dNK-l6-bgo"/>
|
||||
</connections>
|
||||
</containerView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="hkW-EE-XPg" firstAttribute="top" secondItem="cPO-q9-S2N" secondAttribute="bottom" constant="20" id="6om-ac-MMy"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="eS2-n9-bE3" secondAttribute="trailing" constant="20" id="7G3-4D-wJh"/>
|
||||
<constraint firstItem="sXr-25-joh" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="Jul-zo-yqj"/>
|
||||
<constraint firstItem="sXr-25-joh" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="WM6-BP-vb6"/>
|
||||
<constraint firstItem="RSY-ha-JUW" firstAttribute="top" secondItem="cPO-q9-S2N" secondAttribute="bottom" constant="20" id="40e-vU-3lH"/>
|
||||
<constraint firstItem="RSY-ha-JUW" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="FTb-v0-N63"/>
|
||||
<constraint firstItem="cPO-q9-S2N" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="Wio-lA-v4A"/>
|
||||
<constraint firstItem="uu6-RV-xw3" firstAttribute="top" secondItem="cPO-q9-S2N" secondAttribute="bottom" constant="20" id="b4k-8J-XlA"/>
|
||||
<constraint firstItem="RSY-ha-JUW" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="bap-y2-ZtA"/>
|
||||
<constraint firstItem="cPO-q9-S2N" firstAttribute="top" secondItem="xUt-DT-D6S" secondAttribute="bottom" id="d6f-SJ-atD"/>
|
||||
<constraint firstItem="uu6-RV-xw3" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="e63-Mb-cxd"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="xUt-DT-D6S" secondAttribute="trailing" constant="20" id="h0T-oL-Nsj"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="cPO-q9-S2N" secondAttribute="trailing" constant="20" id="mC6-HG-Sid"/>
|
||||
<constraint firstItem="eS2-n9-bE3" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="mgE-TM-WQ6"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="hkW-EE-XPg" secondAttribute="trailing" constant="20" id="olr-Sd-MDD"/>
|
||||
<constraint firstItem="hkW-EE-XPg" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="pm5-wU-KhI"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="sXr-25-joh" secondAttribute="trailing" constant="20" id="qEv-DD-dGy"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="uu6-RV-xw3" secondAttribute="trailing" id="nQR-3Z-BvQ"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="RSY-ha-JUW" secondAttribute="bottom" id="oyr-RU-84k"/>
|
||||
<constraint firstItem="xUt-DT-D6S" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="10" id="vI0-ip-gSt"/>
|
||||
<constraint firstItem="xUt-DT-D6S" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="yqR-Rd-3o7"/>
|
||||
</constraints>
|
||||
<variation key="default">
|
||||
<mask key="subviews">
|
||||
<exclude reference="sXr-25-joh"/>
|
||||
<exclude reference="eS2-n9-bE3"/>
|
||||
</mask>
|
||||
</variation>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" title="Account" image="person.crop.circle" catalog="system" selectedImage="person.crop.circle" id="pml-pe-eBz"/>
|
||||
<connections>
|
||||
<outlet property="vLogin" destination="uu6-RV-xw3" id="HDR-hN-k2a"/>
|
||||
<outlet property="vManage" destination="RSY-ha-JUW" id="gYK-lZ-5iB"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="242" y="869"/>
|
||||
<point key="canvasLocation" x="897" y="869"/>
|
||||
</scene>
|
||||
<!--Item-->
|
||||
<!--Debug-->
|
||||
<scene sceneID="x23-kV-b5u">
|
||||
<objects>
|
||||
<viewController id="r8W-6e-Hn2" customClass="TestingViewController" customModule="ProjectClock" customModuleProvider="target" sceneMemberID="viewController">
|
||||
@@ -344,33 +209,204 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GhB-GA-PWg">
|
||||
<rect key="frame" x="93" y="128" width="229" height="40"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="23"/>
|
||||
<state key="normal" title="Send Accel. to Console"/>
|
||||
<connections>
|
||||
<action selector="getAccel:" destination="r8W-6e-Hn2" eventType="touchUpInside" id="hDu-Hj-vnU"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8o4-jF-mCC">
|
||||
<rect key="frame" x="154" y="232" width="110" height="41"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="24"/>
|
||||
<state key="normal" title="Send Notif"/>
|
||||
<connections>
|
||||
<action selector="sendNotification:" destination="r8W-6e-Hn2" eventType="touchUpInside" id="ry0-VK-YRb"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Debug" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="w0P-Jb-Y3X">
|
||||
<rect key="frame" x="20" y="54" width="374" height="20.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="What is this?" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tcW-ZW-HgZ">
|
||||
<rect key="frame" x="20" y="74.5" width="374" height="14.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="oTa-Ok-U9h">
|
||||
<rect key="frame" x="20" y="109" width="374" height="128"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8o4-jF-mCC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="36"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="20"/>
|
||||
<state key="normal" title="Send Notification"/>
|
||||
<connections>
|
||||
<action selector="sendNotification:" destination="r8W-6e-Hn2" eventType="touchUpInside" id="ry0-VK-YRb"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="oK7-CW-NUv">
|
||||
<rect key="frame" x="0.0" y="46" width="374" height="36"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="20"/>
|
||||
<state key="normal" title="Add an alarm activating one minute later"/>
|
||||
<connections>
|
||||
<action selector="addAlarm:" destination="r8W-6e-Hn2" eventType="touchUpInside" id="Ctz-XA-1z2"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="VdA-Ax-Nzf">
|
||||
<rect key="frame" x="0.0" y="92" width="374" height="36"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="20"/>
|
||||
<state key="normal" title="Delete All Alarms"/>
|
||||
<connections>
|
||||
<action selector="deleteAlarm:" destination="r8W-6e-Hn2" eventType="touchUpInside" id="gqI-7A-W9C"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="hX1-uy-i87"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="w0P-Jb-Y3X" firstAttribute="top" secondItem="hX1-uy-i87" secondAttribute="top" constant="10" id="42B-VU-jVH"/>
|
||||
<constraint firstItem="hX1-uy-i87" firstAttribute="trailing" secondItem="oTa-Ok-U9h" secondAttribute="trailing" constant="20" id="BZf-0X-jDK"/>
|
||||
<constraint firstItem="oTa-Ok-U9h" firstAttribute="leading" secondItem="hX1-uy-i87" secondAttribute="leading" constant="20" id="DjJ-yy-Jc0"/>
|
||||
<constraint firstItem="tcW-ZW-HgZ" firstAttribute="leading" secondItem="hX1-uy-i87" secondAttribute="leading" constant="20" id="MMc-9X-wep"/>
|
||||
<constraint firstItem="hX1-uy-i87" firstAttribute="trailing" secondItem="tcW-ZW-HgZ" secondAttribute="trailing" constant="20" id="TuV-bF-NqF"/>
|
||||
<constraint firstItem="oTa-Ok-U9h" firstAttribute="top" secondItem="tcW-ZW-HgZ" secondAttribute="bottom" constant="20" id="Xiz-02-dUA"/>
|
||||
<constraint firstItem="tcW-ZW-HgZ" firstAttribute="top" secondItem="w0P-Jb-Y3X" secondAttribute="bottom" id="i43-E7-hoY"/>
|
||||
<constraint firstItem="w0P-Jb-Y3X" firstAttribute="leading" secondItem="hX1-uy-i87" secondAttribute="leading" constant="20" id="mKZ-sC-tZt"/>
|
||||
<constraint firstItem="hX1-uy-i87" firstAttribute="trailing" secondItem="w0P-Jb-Y3X" secondAttribute="trailing" constant="20" id="zYy-da-HnI"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" title="Item" id="QZ2-fJ-hLg"/>
|
||||
<tabBarItem key="tabBarItem" title="Debug" image="hammer.fill" catalog="system" selectedImage="hammer.fill" id="QZ2-fJ-hLg"/>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="mZu-oh-Lmb" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="952" y="869"/>
|
||||
<point key="canvasLocation" x="1652" y="869"/>
|
||||
</scene>
|
||||
<!--Stopwatch-->
|
||||
<scene sceneID="HUO-7o-NzS">
|
||||
<objects>
|
||||
<viewController id="PYt-Lf-JNp" customClass="StopwatchViewController" customModule="ProjectClock" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="wac-VZ-OTJ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="51" translatesAutoresizingMaskIntoConstraints="NO" id="Apb-MM-jue">
|
||||
<rect key="frame" x="20" y="89" width="374" height="714"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="34" translatesAutoresizingMaskIntoConstraints="NO" id="Lnz-vd-hkl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="60"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="00" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="54s-5X-INP">
|
||||
<rect key="frame" x="0.0" y="0.0" width="100" height="60"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="ultraLight" pointSize="50"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=":" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="W12-19-4Jh">
|
||||
<rect key="frame" x="134" y="0.0" width="10.5" height="60"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="ultraLight" pointSize="50"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rpt-j6-UiB">
|
||||
<rect key="frame" x="178.5" y="0.0" width="58.5" height="60"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="ultraLight" pointSize="50"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=":" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rGI-kH-dgk">
|
||||
<rect key="frame" x="271" y="0.0" width="10.5" height="60"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="ultraLight" pointSize="50"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SdB-cz-eTv">
|
||||
<rect key="frame" x="315.5" y="0.0" width="58.5" height="60"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="ultraLight" pointSize="50"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillProportionally" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="4j4-zF-LWF">
|
||||
<rect key="frame" x="0.0" y="111" width="374" height="150"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Jse-AL-cOs">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="30"/>
|
||||
<color key="backgroundColor" name="AccentColor"/>
|
||||
<state key="normal" title="Start">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="start:" destination="PYt-Lf-JNp" eventType="touchUpInside" id="ucf-fN-SgP"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="O4A-eq-kEz">
|
||||
<rect key="frame" x="0.0" y="40" width="374" height="30"/>
|
||||
<color key="backgroundColor" name="AccentColor"/>
|
||||
<state key="normal" title="Stop">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="stop:" destination="PYt-Lf-JNp" eventType="touchUpInside" id="ncg-GB-bAj"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Q7i-3U-NbI">
|
||||
<rect key="frame" x="0.0" y="80" width="374" height="30"/>
|
||||
<color key="backgroundColor" name="AccentColor"/>
|
||||
<state key="normal" title="Reset">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="reset:" destination="PYt-Lf-JNp" eventType="touchUpInside" id="3ff-of-rJl"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="794-VN-m96">
|
||||
<rect key="frame" x="0.0" y="120" width="374" height="30"/>
|
||||
<color key="backgroundColor" name="AccentColor"/>
|
||||
<state key="normal" title="Lap">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="lap:" destination="PYt-Lf-JNp" eventType="touchUpInside" id="UT9-pP-BWA"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="O7B-Tj-vT0">
|
||||
<rect key="frame" x="0.0" y="312" width="374" height="402"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="lapCell" id="2Je-Lw-ZAb">
|
||||
<rect key="frame" x="0.0" y="28" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="2Je-Lw-ZAb" id="HNi-3j-MIV">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="PYt-Lf-JNp" id="HRZ-MF-SqM"/>
|
||||
<outlet property="delegate" destination="PYt-Lf-JNp" id="uCp-Op-uuX"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="0Lp-Fy-R0s"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="0Lp-Fy-R0s" firstAttribute="bottom" secondItem="Apb-MM-jue" secondAttribute="bottom" constant="10" id="4Rv-Vr-MaH"/>
|
||||
<constraint firstItem="0Lp-Fy-R0s" firstAttribute="trailing" secondItem="Apb-MM-jue" secondAttribute="trailing" constant="20" id="ENw-M0-fRs"/>
|
||||
<constraint firstItem="Apb-MM-jue" firstAttribute="top" secondItem="0Lp-Fy-R0s" secondAttribute="top" constant="45" id="RHG-Fs-SLZ"/>
|
||||
<constraint firstItem="Apb-MM-jue" firstAttribute="leading" secondItem="0Lp-Fy-R0s" secondAttribute="leading" constant="20" id="l5v-Y0-GXP"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" title="Stopwatch" image="clock.arrow.circlepath" catalog="system" selectedImage="clock.arrow.circlepath" id="hNc-UI-vIQ"/>
|
||||
<connections>
|
||||
<outlet property="hourLabel" destination="54s-5X-INP" id="Ugg-ul-ebm"/>
|
||||
<outlet property="lapButton" destination="794-VN-m96" id="1aX-pS-Cjr"/>
|
||||
<outlet property="minuteLabel" destination="rpt-j6-UiB" id="Qaf-xg-UCX"/>
|
||||
<outlet property="resetButton" destination="Q7i-3U-NbI" id="gYT-DB-XpP"/>
|
||||
<outlet property="secondLabel" destination="SdB-cz-eTv" id="syA-P8-NFX"/>
|
||||
<outlet property="startButton" destination="Jse-AL-cOs" id="iq2-xE-Nir"/>
|
||||
<outlet property="stopButton" destination="O4A-eq-kEz" id="dd7-v0-fqc"/>
|
||||
<outlet property="tableView" destination="O7B-Tj-vT0" id="kfu-vG-sgE"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="ZlX-5W-Pjn" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="192.75362318840581" y="868.52678571428567"/>
|
||||
</scene>
|
||||
<!--Add Alarm View Controller-->
|
||||
<scene sceneID="sJi-AZ-FUw">
|
||||
@@ -388,6 +424,9 @@
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="30"/>
|
||||
<state key="normal" image="checkmark" catalog="system"/>
|
||||
<connections>
|
||||
<action selector="addAlarmButton:" destination="Mki-dC-5Kc" eventType="touchUpInside" id="AiG-Cc-DlR"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6d8-Gi-Ipa">
|
||||
<rect key="frame" x="20" y="20" width="30" height="30"/>
|
||||
@@ -397,6 +436,9 @@
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="30"/>
|
||||
<state key="normal" image="trash.fill" catalog="system"/>
|
||||
<connections>
|
||||
<action selector="cancelAlarmButton:" destination="Mki-dC-5Kc" eventType="touchUpInside" id="4zo-hG-Mnk"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Going off in 23 hr 59 min" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xqs-n0-H9G">
|
||||
<rect key="frame" x="20" y="40.5" width="374" height="14.5"/>
|
||||
@@ -436,10 +478,16 @@
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cZ8-yN-fp6">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="30"/>
|
||||
<state key="normal" title="Pick From iOS Default Ringtones"/>
|
||||
<connections>
|
||||
<action selector="defaultRingtonesButton:" destination="Mki-dC-5Kc" eventType="touchUpInside" id="0oM-fE-Gyh"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="A3Z-Qp-XmN">
|
||||
<rect key="frame" x="0.0" y="30" width="374" height="30"/>
|
||||
<state key="normal" title="Pick From Sound Library"/>
|
||||
<connections>
|
||||
<action selector="soundLibraryButton:" destination="Mki-dC-5Kc" eventType="touchUpInside" id="5G8-FV-bDx"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</stackView>
|
||||
@@ -462,7 +510,7 @@
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="e8v-cM-bxf">
|
||||
<rect key="frame" x="20" y="553.5" width="334" height="72"/>
|
||||
<rect key="frame" x="20" y="553.5" width="334" height="116"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="iAX-G9-qxh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="334" height="31"/>
|
||||
@@ -492,6 +540,22 @@
|
||||
</switch>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="gdH-Ok-RIH">
|
||||
<rect key="frame" x="0.0" y="82" width="334" height="34"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Alarm Name:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5LP-Zk-4Sh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="98.5" height="34"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="bJg-zp-Aan">
|
||||
<rect key="frame" x="118.5" y="0.0" width="215.5" height="34"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
@@ -553,6 +617,9 @@
|
||||
</view>
|
||||
<navigationItem key="navigationItem" id="Ydw-dw-vLC"/>
|
||||
<connections>
|
||||
<outlet property="alarmNameTextField" destination="bJg-zp-Aan" id="Itt-3v-GJB"/>
|
||||
<outlet property="repeatWeekdaysSwitch" destination="5oN-BL-Xtu" id="gqb-l3-1jZ"/>
|
||||
<outlet property="repeatWeekendsSwitch" destination="WPM-Fh-sRB" id="LB7-zW-jpC"/>
|
||||
<outlet property="scrollView" destination="ybc-8d-6pJ" id="m1B-ff-zeC"/>
|
||||
<outlet property="scrollViewInner" destination="N7f-vi-SFU" id="LQW-6T-f6x"/>
|
||||
<outlet property="timePicker" destination="qSt-1V-2DQ" id="aDM-jE-0OP"/>
|
||||
@@ -561,12 +628,137 @@
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="0OD-e8-Pfh" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-494" y="1576"/>
|
||||
<point key="canvasLocation" x="-494.20289855072468" y="1575.6696428571429"/>
|
||||
</scene>
|
||||
<!--Tab Bar Controller-->
|
||||
<!--Alarm Activation View Controller-->
|
||||
<scene sceneID="21H-AI-pzd">
|
||||
<objects>
|
||||
<viewController id="hDW-11-g9U" customClass="AlarmActivationViewController" customModule="ProjectClock" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="BZn-UX-vps">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="17:40" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vmg-Oj-8tB">
|
||||
<rect key="frame" x="20" y="20" width="374" height="137.5"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="thin" pointSize="115"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Jan 17, Sunday" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="42Q-0v-ZS3">
|
||||
<rect key="frame" x="20" y="152.5" width="374" height="25.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="21"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OMQ-qK-fej" userLabel="factorView">
|
||||
<rect key="frame" x="20" y="249" width="374" height="343"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Solve: " lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CZ0-R6-FDq">
|
||||
<rect key="frame" x="39" y="35" width="300" height="33"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="27"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<textField opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="eTb-g7-8x3">
|
||||
<rect key="frame" x="39" y="154" width="309" height="34"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" textContentType="cc-number"/>
|
||||
<connections>
|
||||
<action selector="checkBinomialSolution:" destination="hDW-11-g9U" eventType="editingChanged" id="nuZ-mN-UR3"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Input 1 answer:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vHe-pz-8w2">
|
||||
<rect key="frame" x="39" y="113" width="174" height="33"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="27"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemGray6Color"/>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Smash your phone to dismiss" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mjE-Hr-RIc">
|
||||
<rect key="frame" x="95.5" y="411" width="223.5" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NmX-iJ-1D0" userLabel="rpsView">
|
||||
<rect key="frame" x="20" y="261" width="374" height="343"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" spacing="64" translatesAutoresizingMaskIntoConstraints="NO" id="213-rS-oYj">
|
||||
<rect key="frame" x="19" y="134" width="336" height="42"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ndj-R4-AKk">
|
||||
<rect key="frame" x="0.0" y="0.0" width="55" height="42"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="25"/>
|
||||
<state key="normal" title="Rock"/>
|
||||
<connections>
|
||||
<action selector="rockChoice:" destination="hDW-11-g9U" eventType="touchUpInside" id="w4O-MC-ohr"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Iiv-Co-j8G">
|
||||
<rect key="frame" x="119" y="0.0" width="62" height="42"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="25"/>
|
||||
<state key="normal" title="Paper"/>
|
||||
<connections>
|
||||
<action selector="paperChoice:" destination="hDW-11-g9U" eventType="touchUpInside" id="giR-wy-jqv"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="U4y-ls-yuK">
|
||||
<rect key="frame" x="245" y="0.0" width="91" height="42"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="25"/>
|
||||
<state key="normal" title="Scissors"/>
|
||||
<connections>
|
||||
<action selector="scissorChoice:" destination="hDW-11-g9U" eventType="touchUpInside" id="BVu-ST-Hfr"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="RPS: What do you choose?" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HEP-oh-VSY">
|
||||
<rect key="frame" x="31" y="8" width="313" height="33"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="27"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemGray4Color"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="hS6-fS-yWp"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="vmg-Oj-8tB" firstAttribute="top" secondItem="hS6-fS-yWp" secondAttribute="top" constant="20" id="5lz-9m-i9F"/>
|
||||
<constraint firstItem="vmg-Oj-8tB" firstAttribute="leading" secondItem="hS6-fS-yWp" secondAttribute="leading" constant="20" id="7fm-Y8-FaE"/>
|
||||
<constraint firstItem="42Q-0v-ZS3" firstAttribute="top" secondItem="vmg-Oj-8tB" secondAttribute="bottom" constant="-5" id="GNM-uI-sQt"/>
|
||||
<constraint firstItem="hS6-fS-yWp" firstAttribute="trailing" secondItem="42Q-0v-ZS3" secondAttribute="trailing" constant="20" id="L1F-Bv-r5M"/>
|
||||
<constraint firstItem="42Q-0v-ZS3" firstAttribute="leading" secondItem="hS6-fS-yWp" secondAttribute="leading" constant="20" id="Nbf-jB-Uq5"/>
|
||||
<constraint firstItem="mjE-Hr-RIc" firstAttribute="centerY" secondItem="BZn-UX-vps" secondAttribute="centerY" id="SFw-9a-XAc"/>
|
||||
<constraint firstItem="mjE-Hr-RIc" firstAttribute="centerX" secondItem="BZn-UX-vps" secondAttribute="centerX" id="oBA-RV-cEO"/>
|
||||
<constraint firstItem="hS6-fS-yWp" firstAttribute="trailing" secondItem="vmg-Oj-8tB" secondAttribute="trailing" constant="20" id="vVE-ID-dgj"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="puzzleAnswerInput" destination="eTb-g7-8x3" id="xIh-TJ-vhq"/>
|
||||
<outlet property="puzzleQuestionLabel" destination="CZ0-R6-FDq" id="gon-N6-XTD"/>
|
||||
<outlet property="puzzleView" destination="OMQ-qK-fej" id="KLT-oe-RJX"/>
|
||||
<outlet property="rpsResult" destination="HEP-oh-VSY" id="W2G-eZ-mzI"/>
|
||||
<outlet property="rpsView" destination="NmX-iJ-1D0" id="Rhg-Hm-2Xx"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="78q-sr-CNz" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2391" y="1102"/>
|
||||
</scene>
|
||||
<!--Alarm Activator-->
|
||||
<scene sceneID="c7a-VN-D68">
|
||||
<objects>
|
||||
<tabBarController automaticallyAdjustsScrollViewInsets="NO" id="s8U-C8-W2S" sceneMemberID="viewController">
|
||||
<tabBarController automaticallyAdjustsScrollViewInsets="NO" id="s8U-C8-W2S" customClass="AlarmActivator" customModule="ProjectClock" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<toolbarItems/>
|
||||
<tabBar key="tabBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="203-Gr-Mvz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
|
||||
@@ -577,30 +769,214 @@
|
||||
<segue destination="Bqt-du-DT2" kind="relationship" relationship="viewControllers" id="ZTh-ke-ZIV"/>
|
||||
<segue destination="BYZ-38-t0r" kind="relationship" relationship="viewControllers" destinationCreationSelector="Account" id="NN6-JO-Hmi"/>
|
||||
<segue destination="r8W-6e-Hn2" kind="relationship" relationship="viewControllers" destinationCreationSelector="Testing" id="SLn-vq-UnH"/>
|
||||
<segue destination="hDW-11-g9U" kind="showDetail" identifier="activate-alarm" destinationCreationSelector="sendAlarm:" id="z9o-Jh-fnM"/>
|
||||
<segue destination="PYt-Lf-JNp" kind="relationship" relationship="viewControllers" id="uMq-nh-i7c"/>
|
||||
</connections>
|
||||
</tabBarController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="S6R-Xr-Nfe" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-1406" y="1102"/>
|
||||
</scene>
|
||||
<!--LoginVC-->
|
||||
<scene sceneID="erw-PT-N27">
|
||||
<objects>
|
||||
<viewController id="ZAC-hn-zGl" customClass="LoginVC" customModule="ProjectClock" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="ikD-PZ-wEA">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="200"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="bey-TF-Fqa">
|
||||
<rect key="frame" x="20" y="29" width="374" height="142"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="4Rf-oJ-54p">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="34"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2A7-sm-WU9">
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="34"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="80" id="sge-J8-P4B"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="1M0-Wo-TZA">
|
||||
<rect key="frame" x="100" y="0.0" width="274" height="34"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no" textContentType="username"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="8Az-dM-tXe">
|
||||
<rect key="frame" x="0.0" y="54" width="374" height="34"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Password" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vtI-qz-pCA">
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="34"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="80" id="CfI-BL-Ifg"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="3pJ-zP-vKy">
|
||||
<rect key="frame" x="100" y="0.0" width="274" height="34"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" secureTextEntry="YES"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="2Vc-MS-Vmc">
|
||||
<rect key="frame" x="0.0" y="108" width="374" height="34"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DpZ-cR-QRd">
|
||||
<rect key="frame" x="0.0" y="0.0" width="177" height="34"/>
|
||||
<state key="normal" title="Register"/>
|
||||
<connections>
|
||||
<action selector="register:" destination="ZAC-hn-zGl" eventType="touchUpInside" id="t9e-S1-E1e"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="C8G-k3-WeR">
|
||||
<rect key="frame" x="197" y="0.0" width="177" height="34"/>
|
||||
<state key="normal" title="Login"/>
|
||||
<connections>
|
||||
<action selector="login:" destination="ZAC-hn-zGl" eventType="touchUpInside" id="hda-pr-3rN"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="bky-eU-lyX"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="bey-TF-Fqa" firstAttribute="centerY" secondItem="ikD-PZ-wEA" secondAttribute="centerY" id="61k-0f-XAy"/>
|
||||
<constraint firstAttribute="trailing" secondItem="bey-TF-Fqa" secondAttribute="trailing" constant="20" id="6q6-l9-TNU"/>
|
||||
<constraint firstItem="bey-TF-Fqa" firstAttribute="leading" secondItem="ikD-PZ-wEA" secondAttribute="leading" constant="20" id="Juc-Oa-X2W"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="password" destination="3pJ-zP-vKy" id="Cnb-HO-uMj"/>
|
||||
<outlet property="username" destination="1M0-Wo-TZA" id="rfc-YG-20V"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="M0T-zq-vZ0" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="949" y="1342"/>
|
||||
</scene>
|
||||
<!--ManageVC-->
|
||||
<scene sceneID="jRZ-qD-YDj">
|
||||
<objects>
|
||||
<viewController id="3gV-kF-UbK" customClass="ManageVC" customModule="ProjectClock" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="yUd-Ri-s9C">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="704"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="gYR-mG-zfS">
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="270"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="gUf-g1-1af">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="170"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="person.crop.circle.badge.checkmark" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="yVW-fL-SIQ">
|
||||
<rect key="frame" x="132" y="0.0" width="110" height="109.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="110" id="So8-k5-nLS"/>
|
||||
<constraint firstAttribute="height" constant="110" id="inA-vb-CrL"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Username 233" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kAN-ll-4wh">
|
||||
<rect key="frame" x="20" y="118" width="334" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Joined on Jan 8, 2021" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cT0-Ux-t7H">
|
||||
<rect key="frame" x="20" y="138.5" width="334" height="14.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="cT0-Ux-t7H" firstAttribute="leading" secondItem="gUf-g1-1af" secondAttribute="leading" constant="20" id="1Ik-pd-j1d"/>
|
||||
<constraint firstItem="cT0-Ux-t7H" firstAttribute="top" secondItem="kAN-ll-4wh" secondAttribute="bottom" id="8Tf-2A-QR5"/>
|
||||
<constraint firstItem="yVW-fL-SIQ" firstAttribute="centerX" secondItem="gUf-g1-1af" secondAttribute="centerX" id="BgL-qD-g20"/>
|
||||
<constraint firstItem="kAN-ll-4wh" firstAttribute="top" secondItem="yVW-fL-SIQ" secondAttribute="bottom" constant="8" symbolic="YES" id="Bt4-2a-aPr"/>
|
||||
<constraint firstAttribute="trailing" secondItem="kAN-ll-4wh" secondAttribute="trailing" constant="20" id="DNM-FH-WaB"/>
|
||||
<constraint firstItem="yVW-fL-SIQ" firstAttribute="top" secondItem="gUf-g1-1af" secondAttribute="top" id="bfq-gx-ofm"/>
|
||||
<constraint firstItem="kAN-ll-4wh" firstAttribute="leading" secondItem="gUf-g1-1af" secondAttribute="leading" constant="20" id="dqc-ZN-G6v"/>
|
||||
<constraint firstAttribute="height" constant="170" id="xvU-zX-6BQ"/>
|
||||
<constraint firstAttribute="trailing" secondItem="cT0-Ux-t7H" secondAttribute="trailing" constant="20" id="yRX-lY-fXX"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="3pG-br-PVl">
|
||||
<rect key="frame" x="0.0" y="190" width="374" height="30"/>
|
||||
<state key="normal" title="Log Out">
|
||||
<color key="titleColor" systemColor="systemOrangeColor"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="logout:" destination="3gV-kF-UbK" eventType="touchUpInside" id="dyQ-AR-u9Y"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="awy-Du-cqu">
|
||||
<rect key="frame" x="0.0" y="240" width="374" height="30"/>
|
||||
<state key="normal" title="Delete Account">
|
||||
<color key="titleColor" systemColor="systemRedColor"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="deleteAccount:" destination="3gV-kF-UbK" eventType="touchUpInside" id="iQ9-m9-VIm"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="xo1-tu-MEz"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="gYR-mG-zfS" firstAttribute="leading" secondItem="yUd-Ri-s9C" secondAttribute="leading" constant="20" id="LuV-cO-Q8K"/>
|
||||
<constraint firstItem="gYR-mG-zfS" firstAttribute="top" secondItem="yUd-Ri-s9C" secondAttribute="top" id="aTB-LN-BGK"/>
|
||||
<constraint firstAttribute="trailing" secondItem="gYR-mG-zfS" secondAttribute="trailing" constant="20" id="fxQ-LY-4QQ"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="lJoinDate" destination="cT0-Ux-t7H" id="1XG-r8-Qr4"/>
|
||||
<outlet property="lUsername" destination="kAN-ll-4wh" id="3Ta-rI-wql"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="pLc-eQ-yBW" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="948" y="1676"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="alarm.fill" catalog="system" width="124" height="128"/>
|
||||
<image name="checkmark" catalog="system" width="128" height="114"/>
|
||||
<image name="clock.arrow.circlepath" catalog="system" width="128" height="110"/>
|
||||
<image name="hammer.fill" catalog="system" width="128" height="117"/>
|
||||
<image name="person.crop.circle" catalog="system" width="128" height="121"/>
|
||||
<image name="person.crop.circle.badge.checkmark" catalog="system" width="128" height="113"/>
|
||||
<image name="trash.fill" catalog="system" width="121" height="128"/>
|
||||
<namedColor name="AccentColor">
|
||||
<color red="0.0" green="0.46000000000000002" blue="0.89000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
<systemColor name="labelColor">
|
||||
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<systemColor name="secondaryLabelColor">
|
||||
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemGray4Color">
|
||||
<color red="0.81960784313725488" green="0.81960784313725488" blue="0.83921568627450982" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemGray6Color">
|
||||
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemOrangeColor">
|
||||
<color red="1" green="0.58431372549019611" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemRedColor">
|
||||
<color red="1" green="0.23137254901960785" blue="0.18823529411764706" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
|
||||
@@ -2,6 +2,19 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
//
|
||||
// ProjectClock
|
||||
//
|
||||
// Created by Hykilpikonna on 1/8/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct WVM
|
||||
{
|
||||
let name: String
|
||||
let desc: String
|
||||
}
|
||||
|
||||
let wvms = [
|
||||
WVM(name: "Walk", desc: "Walk a few steps"),
|
||||
WVM(name: "Jump", desc: "Make a few jumps"),
|
||||
WVM(name: "Puzzle", desc: "Complete a simple puzzle"),
|
||||
WVM(name: "Smash", desc: "It'll never truns off"),
|
||||
]
|
||||
@@ -0,0 +1,198 @@
|
||||
//
|
||||
// MathExpressions.swift
|
||||
// ProjectClock
|
||||
//
|
||||
// Puzzles to complete for task (math or RPS)
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Math element for problem generation (Credit: https://stackoverflow.com/a/43132311/7346633)
|
||||
*/
|
||||
enum MathElement : CustomStringConvertible {
|
||||
case Integer(value: Int)
|
||||
case Percentage(value: Int)
|
||||
case Expression(expression: MathExpression)
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .Integer(let value): return "\(value)"
|
||||
case .Percentage(let percentage): return "\(percentage)%"
|
||||
case .Expression(let expr): return expr.description
|
||||
}
|
||||
}
|
||||
|
||||
var nsExpressionFormatString : String {
|
||||
switch self {
|
||||
case .Integer(let value): return "\(value).0"
|
||||
case .Percentage(let percentage): return "\(Double(percentage) / 100)"
|
||||
case .Expression(let expr): return "(\(expr.description))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Math operator for problem generation (Credit: https://stackoverflow.com/a/43132311/7346633)
|
||||
*/
|
||||
enum MathOperator : String {
|
||||
case plus = "+"
|
||||
case minus = "-"
|
||||
case multiply = "*"
|
||||
case divide = "/"
|
||||
case power = "**"
|
||||
|
||||
static func random() -> MathOperator {
|
||||
let allMathOperators: [MathOperator] = [.plus, .minus, .multiply, .divide, .power]
|
||||
let index = Int(arc4random_uniform(UInt32(allMathOperators.count)))
|
||||
|
||||
return allMathOperators[index]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Math expressions for problem generation (Credit: https://stackoverflow.com/a/43132311/7346633)
|
||||
*/
|
||||
class MathExpression : CustomStringConvertible {
|
||||
var lhs: MathElement
|
||||
var rhs: MathElement
|
||||
var op: MathOperator
|
||||
|
||||
init(lhs: MathElement, rhs: MathElement, op: MathOperator) {
|
||||
self.lhs = lhs
|
||||
self.rhs = rhs
|
||||
self.op = op
|
||||
}
|
||||
|
||||
var description: String {
|
||||
var leftString = ""
|
||||
var rightString = ""
|
||||
|
||||
if case .Expression(_) = lhs {
|
||||
leftString = "(\(lhs))"
|
||||
} else {
|
||||
leftString = lhs.description
|
||||
}
|
||||
if case .Expression(_) = rhs {
|
||||
rightString = "(\(rhs))"
|
||||
} else {
|
||||
rightString = rhs.description
|
||||
}
|
||||
|
||||
return "\(leftString) \(self.op.rawValue) \(rightString)"
|
||||
}
|
||||
|
||||
var result : Int? {
|
||||
let format = "\(lhs.nsExpressionFormatString) \(op.rawValue) \(rhs.nsExpressionFormatString)"
|
||||
let expr = NSExpression(format: format)
|
||||
return expr.expressionValue(with: nil, context: nil) as? Int
|
||||
}
|
||||
|
||||
static func random() -> MathExpression {
|
||||
let lhs = MathElement.Integer(value: Int(arc4random_uniform(10)))
|
||||
let rhs = MathElement.Integer(value: Int(arc4random_uniform(10)))
|
||||
|
||||
return MathExpression(lhs: lhs, rhs: rhs, op: .random())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Generate simple problem - 2 expressions
|
||||
*/
|
||||
class AlgProb2 : MathExpression {
|
||||
let a = MathExpression.random()
|
||||
let b = MathExpression.random()
|
||||
|
||||
func getProblem() -> String {
|
||||
return "\(a) + \(b)"
|
||||
}
|
||||
|
||||
func getAnswer() -> String {
|
||||
return "\(a.result! + b.result!)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Generate simple problem - 3 expressions
|
||||
*/
|
||||
class AlgProb3 : MathExpression {
|
||||
let a = MathExpression.random()
|
||||
let b = MathExpression.random()
|
||||
let c = MathExpression.random()
|
||||
|
||||
func getProblem() -> String {
|
||||
return "\(a) + \(b) + \(c)"
|
||||
}
|
||||
|
||||
func getAnswer() -> String {
|
||||
return "\(a.result! + b.result! + c.result!)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Generate quadratic factorization problem
|
||||
*/
|
||||
class QuadraticProb {
|
||||
// Generates the roots
|
||||
let root1 = Int.random(in: 1...10)
|
||||
let root2 = Int.random(in: 1...10)
|
||||
|
||||
/**
|
||||
Generate problem description
|
||||
*/
|
||||
func getProblem() -> String {
|
||||
//a is 1
|
||||
let b = root1 + root2 //bx
|
||||
let c = root1 * root2 //x
|
||||
|
||||
return "x^2 + \(b)x + \(c)"
|
||||
}
|
||||
|
||||
/**
|
||||
Finds the roots of the quadratic **NOTE**: the return type is [Int], not a String
|
||||
*/
|
||||
func getAnswer() -> [Int] {
|
||||
return [root1, root2]
|
||||
}
|
||||
}
|
||||
|
||||
class RPS {
|
||||
//@IBOutlet weak var resultsLabel: UILabel!
|
||||
|
||||
enum Choice: String {
|
||||
case rock = "ROCK"
|
||||
case paper = "PAPER"
|
||||
case scissors = "SCISSORS"
|
||||
}
|
||||
|
||||
static func randomComputerChoice() -> Choice {
|
||||
let choices: [Choice] = [.rock, .paper, .scissors]
|
||||
return choices[Int.random(in: 0...2)]
|
||||
}
|
||||
|
||||
func playRPS(you: Choice, computer: Choice) -> Bool? {
|
||||
if you == .rock && computer == .scissors { return true }
|
||||
else if you == .paper && computer == .rock { return true}
|
||||
else if you == .scissors && computer == .paper { return true }
|
||||
else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@IBAction func rock(_ sender: UIButton) {
|
||||
let computerChoice = Choice.randomComputerChoice()
|
||||
resultsLabel.text = playRPS(you: .rock, computer: computerChoice)
|
||||
}
|
||||
|
||||
@IBAction func paper(_ sender: UIButton) {
|
||||
let computerChoice = Choice.randomComputerChoice()
|
||||
resultsLabel.text = playRPS(you: .paper, computer: computerChoice)
|
||||
}
|
||||
|
||||
@IBAction func scissors(_ sender: UIButton) {
|
||||
let computerChoice = Choice.randomComputerChoice()
|
||||
resultsLabel.text = playRPS(you: .scissors, computer: computerChoice)
|
||||
}
|
||||
*/
|
||||
}
|
||||
+112
-1
@@ -7,10 +7,121 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct User: Decodable
|
||||
struct User: Codable
|
||||
{
|
||||
var id: Int
|
||||
var name: String
|
||||
var email: String
|
||||
var pass: String
|
||||
}
|
||||
|
||||
struct Family: Codable
|
||||
{
|
||||
var fid: Int
|
||||
var fname: String
|
||||
var members: [String]
|
||||
// And a hidden field: admin pin
|
||||
}
|
||||
|
||||
struct WVM: Codable
|
||||
{
|
||||
let name: String
|
||||
let desc: String
|
||||
}
|
||||
|
||||
let wvms = [
|
||||
WVM(name: "Factor", desc: "Factor a binomial"),
|
||||
WVM(name: "RPS", desc: "Win a game of rock paper scissors"),
|
||||
WVM(name: "Smash", desc: "It'll never turn off"),
|
||||
WVM(name: "Walk", desc: "Walk a few steps"),
|
||||
WVM(name: "Jump", desc: "Make a few jumps")
|
||||
]
|
||||
|
||||
class Alarm: Codable
|
||||
{
|
||||
var enabled: Bool
|
||||
var hour: Int // Hour (24)
|
||||
var minute: Int
|
||||
var text: String
|
||||
var wakeMethod: WVM
|
||||
|
||||
/// What days does it repeat (Sun, Mon, Tue, Wed, Thu, Fri, Sat)
|
||||
var repeats: [Bool]
|
||||
|
||||
/// Does it automatically disable after activating once
|
||||
var oneTime: Bool
|
||||
|
||||
/// When is the last time that the alarm went off
|
||||
var lastActivate: Date
|
||||
|
||||
/// Constructor
|
||||
init(enabled: Bool = true,
|
||||
hour: Int, minute: Int, text: String, wakeMethod: WVM,
|
||||
repeats: [Bool] = [false, true, true, true, true, true, false],
|
||||
oneTime: Bool = false, lastActivate: Date = Date()
|
||||
)
|
||||
{
|
||||
self.enabled = enabled
|
||||
self.hour = hour
|
||||
self.minute = minute
|
||||
self.text = text
|
||||
self.wakeMethod = wakeMethod
|
||||
self.repeats = repeats
|
||||
self.oneTime = oneTime
|
||||
self.lastActivate = lastActivate
|
||||
}
|
||||
|
||||
/// When should the alarm activate next since lastActivate?
|
||||
var nextActivate: Date?
|
||||
{
|
||||
let (y, m, d) = lastActivate.getYMD()
|
||||
let (nh, nm, _) = lastActivate.getHMS()
|
||||
|
||||
// Create activation date
|
||||
var date = Date.create(y, m, d, hour, minute)
|
||||
|
||||
// If it will activate tomorrow
|
||||
if nh > hour || (nh == hour && nm >= minute) { date = date.added(.day, 1) }
|
||||
|
||||
// If it's one-time, don't have to check for repeating date
|
||||
if oneTime { return date }
|
||||
|
||||
// Make sure it's repeating
|
||||
guard (repeats.contains { $0 }) else { return nil }
|
||||
|
||||
// If the day is not one of the "repeat" days, keep adding 1 until it is
|
||||
while !repeats[date.get(.weekday) - 1] { date = date.added(.day, 1) }
|
||||
|
||||
return date
|
||||
}
|
||||
}
|
||||
|
||||
class Alarms: Codable
|
||||
{
|
||||
var list: [Alarm] = []
|
||||
|
||||
/// Save alarms to local storage
|
||||
func localSave()
|
||||
{
|
||||
localStorage.setValue(JSON.stringify(list)!, forKey: "alarms")
|
||||
|
||||
// Reload table view
|
||||
if let table = AlarmViewController.staticTable { table.reloadData() }
|
||||
}
|
||||
|
||||
/// Read alarms from local storage
|
||||
func localRead() { list = JSON.parse([Alarm].self, localStorage.string(forKey: "alarms")!)! }
|
||||
|
||||
/// Read an alarm object from local storage
|
||||
static func fromLocal() -> Alarms { return Alarms().apply { $0.localRead() } }
|
||||
|
||||
/// Get enabled alarms
|
||||
var listEnabled: [Alarm] { return list.filter { $0.enabled } }
|
||||
|
||||
/// Get alarms that should be activating now
|
||||
var listActivating: [Alarm]
|
||||
{
|
||||
let now = Date()
|
||||
return listEnabled.filter { guard let n = $0.nextActivate else { return false }; return n < now }
|
||||
}
|
||||
}
|
||||
|
||||
+119
-24
@@ -7,8 +7,27 @@
|
||||
import Foundation
|
||||
|
||||
/// Base URL of the HTTP server
|
||||
let baseUrl = "http://localhost:8080/api" // TODO: Production settings
|
||||
let JSON = JSONDecoder()
|
||||
let baseUrl = "https://alarm-clock-api.hydev.org"
|
||||
|
||||
/// Json class
|
||||
class JSON
|
||||
{
|
||||
static let decoder = JSONDecoder()
|
||||
static let encoder = JSONEncoder()
|
||||
|
||||
static func stringify<T: Encodable>(_ o: T) -> String?
|
||||
{
|
||||
guard let jsonData = try? encoder.encode(o) else { return nil }
|
||||
return String(data: jsonData, encoding: String.Encoding.utf8)
|
||||
}
|
||||
|
||||
static func parse<T: Decodable>(_ type: T.Type, _ j: String) -> T?
|
||||
{
|
||||
return try? decoder.decode(type, from: j.data(using: .utf8)!)
|
||||
}
|
||||
}
|
||||
|
||||
/// Local storage
|
||||
let localStorage = UserDefaults(suiteName: "group.org.hydev.alarm.clock")!
|
||||
|
||||
/// API class
|
||||
@@ -24,21 +43,32 @@ class APIs
|
||||
Register the user in the database.
|
||||
|
||||
## Parameters
|
||||
- name: The user's name (this is not username because it doesn't have to be unique)
|
||||
- email: The user's email (this does have to be unique)
|
||||
- pass: Password (initial hash)
|
||||
- username: The user's unique username
|
||||
- password: Password hash
|
||||
|
||||
## Returns
|
||||
Success or error
|
||||
*/
|
||||
static let register = API<String>(loc: "/user/register")
|
||||
|
||||
/**
|
||||
Verify password and login.
|
||||
|
||||
## Parameters
|
||||
- username: The user's unique username
|
||||
- password: Password hash
|
||||
|
||||
## Returns
|
||||
Success or error
|
||||
*/
|
||||
static let login = API<String>(loc: "/user/login")
|
||||
|
||||
/**
|
||||
Delete a user from the database.
|
||||
|
||||
## Parameters
|
||||
- email: The user's email
|
||||
- pass: Password (initial hash)
|
||||
- username: The user's unique username
|
||||
- password: Password hash
|
||||
|
||||
## Returns
|
||||
Success or error
|
||||
@@ -48,7 +78,7 @@ class APIs
|
||||
/**
|
||||
Upload curent config to the cloud.
|
||||
|
||||
## Parameters
|
||||
## Parameters (Besides from username and password)
|
||||
- config: The config json
|
||||
|
||||
## Returns
|
||||
@@ -59,7 +89,7 @@ class APIs
|
||||
/**
|
||||
Download the config from the cloud.
|
||||
|
||||
## Parameters
|
||||
## Parameters (Besides from username and password)
|
||||
None
|
||||
|
||||
## Returns
|
||||
@@ -67,24 +97,88 @@ class APIs
|
||||
*/
|
||||
static let downloadConfig = API<String>(loc: "/backup/download")
|
||||
|
||||
/**
|
||||
Create a family
|
||||
|
||||
## Parameters (Besides from username and password)
|
||||
- fname: Family name
|
||||
- pin: Admin pin
|
||||
|
||||
## Returns
|
||||
Family object
|
||||
*/
|
||||
static let familyCreate = API<Family>(loc: "/family/create")
|
||||
|
||||
/**
|
||||
Delete a family
|
||||
|
||||
## Parameters (Besides from username and password)
|
||||
- fid: Family ID
|
||||
- pin: Admin pin
|
||||
|
||||
## Returns
|
||||
Success or not
|
||||
*/
|
||||
static let familyDelete = API<String>(loc: "/family/delete")
|
||||
|
||||
/**
|
||||
Change a family's admin pin
|
||||
|
||||
## Parameters (Besides from username and password)
|
||||
- fid: Family ID
|
||||
- orig_pin: Original admin pin
|
||||
- new_pin: New admin pin
|
||||
|
||||
## Returns
|
||||
Success or not
|
||||
*/
|
||||
static let familyChangePin = API<String>(loc: "/family/update_pin")
|
||||
|
||||
/**
|
||||
Join family
|
||||
|
||||
## Parameters (Besides from username and password)
|
||||
- fid: Family ID
|
||||
- pin: Admin pin
|
||||
|
||||
## Returns
|
||||
Family object
|
||||
*/
|
||||
static let familyJoin = API<Family>(loc: "/family/join")
|
||||
|
||||
/**
|
||||
Leave family
|
||||
|
||||
## Parameters (Besides from username and password)
|
||||
- fid: Family ID
|
||||
- pin: Admin pin
|
||||
|
||||
## Returns
|
||||
Success or not
|
||||
*/
|
||||
static let familyLeave = API<String>(loc: "/family/leave")
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
/**
|
||||
Build a URL with the node path and params
|
||||
Build a URLRequest with the node path and params
|
||||
|
||||
- Parameter api: API Node (Eg. APIs.register)
|
||||
- Parameter params: Parameters to send to the server (Check the documentation of the API node to see which parameters you need)
|
||||
- Returns: URL
|
||||
- Returns: URLRequest
|
||||
*/
|
||||
func createUrl(_ node: String, _ params: [String: String]? = [:]) -> URL
|
||||
func createRequest(_ node: String, _ params: [String: String]? = [:]) -> URLRequest
|
||||
{
|
||||
var url = URLComponents(string: baseUrl + node)
|
||||
if let params = params
|
||||
{
|
||||
url?.queryItems = params.map { URLQueryItem(name: $0, value: $1) }
|
||||
}
|
||||
return url!.url!
|
||||
// Create URL and request
|
||||
let url = URLComponents(string: baseUrl + node)
|
||||
var request = URLRequest(url: url!.url!)
|
||||
request.httpMethod = "POST"
|
||||
|
||||
// Put parameters inside headers
|
||||
params?.forEach { request.setValue($1, forHTTPHeaderField: $0) }
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,14 +195,12 @@ func send<T: Decodable>(_ api: API<T>, _ params: [String: String]? = [:], _ succ
|
||||
var params = params
|
||||
if params != nil
|
||||
{
|
||||
if params!["email"] == nil { params!["email"] = localStorage.string(forKey: "email") }
|
||||
if params!["pass"] == nil { params!["pass"] = localStorage.string(forKey: "pass") }
|
||||
if params!["username"] == nil { params!["username"] = localStorage.string(forKey: "name") }
|
||||
if params!["password"] == nil { params!["password"] = localStorage.string(forKey: "pass") }
|
||||
}
|
||||
|
||||
let url = createUrl(api.loc, params)
|
||||
|
||||
// Create task
|
||||
let task = URLSession.shared.dataTask(with: url) { (raw, response, error) in
|
||||
let task = URLSession.shared.dataTask(with: createRequest(api.loc, params)) { (raw, response, error) in
|
||||
|
||||
// Check if raw data exists
|
||||
guard let response = response as? HTTPURLResponse, let raw = raw else { err("Data doesn't exist"); return }
|
||||
@@ -116,8 +208,11 @@ func send<T: Decodable>(_ api: API<T>, _ params: [String: String]? = [:], _ succ
|
||||
// If success
|
||||
if (200...299).contains(response.statusCode)
|
||||
{
|
||||
// If the desired type is string, it doesn't have to parse json.
|
||||
if T.self == String.self, let msg = String(data: raw, encoding: .utf8) { success(msg as! T); return }
|
||||
|
||||
// Parse JSON
|
||||
guard let obj = try? JSON.decode(T.self, from: raw) else { err("JSON cannot be parsed"); return }
|
||||
guard let obj = try? JSON.decoder.decode(T.self, from: raw) else { err("JSON cannot be parsed"); return }
|
||||
|
||||
// Call callback
|
||||
success(obj)
|
||||
|
||||
@@ -8,16 +8,7 @@
|
||||
import Foundation
|
||||
import CoreMotion
|
||||
import UserNotifications
|
||||
|
||||
let motionManager = CMMotionManager()
|
||||
|
||||
func getAccelerometer() {
|
||||
motionManager.startAccelerometerUpdates()
|
||||
//print(motionManager.accelerometerData)
|
||||
if let accelerometerData = motionManager.accelerometerData {
|
||||
print("Acclerometer: \(accelerometerData)")
|
||||
}
|
||||
}
|
||||
import UIKit
|
||||
|
||||
func walkAction() {
|
||||
|
||||
@@ -27,10 +18,25 @@ func jumpAction() {
|
||||
|
||||
}
|
||||
|
||||
func puzzleAction() {
|
||||
func rpsAction(choice: RPS.Choice) -> Bool? {
|
||||
let rps = RPS()
|
||||
return rps.playRPS(you: choice, computer: RPS.randomComputerChoice())
|
||||
|
||||
}
|
||||
|
||||
// Handles the core logic behind the factoring alarm
|
||||
func factorAction(puzzleQuestionLabel: UILabel) -> [Int] {
|
||||
let problem = QuadraticProb()
|
||||
|
||||
let answer = problem.getAnswer()
|
||||
let problemString = problem.getProblem()
|
||||
|
||||
puzzleQuestionLabel.text = "Solve: \(problemString)"
|
||||
print("Answer: \(answer)")
|
||||
return answer
|
||||
}
|
||||
|
||||
func smashAction() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
//
|
||||
// SceneDelegate.swift
|
||||
// ProjectClock
|
||||
//
|
||||
// Created by Hykilpikonna on 1/6/21.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
|
||||
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
|
||||
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
||||
guard let _ = (scene as? UIWindowScene) else { return }
|
||||
}
|
||||
|
||||
func sceneDidDisconnect(_ scene: UIScene) {
|
||||
// Called as the scene is being released by the system.
|
||||
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
||||
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
||||
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
|
||||
}
|
||||
|
||||
func sceneDidBecomeActive(_ scene: UIScene) {
|
||||
// Called when the scene has moved from an inactive state to an active state.
|
||||
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
||||
}
|
||||
|
||||
func sceneWillResignActive(_ scene: UIScene) {
|
||||
// Called when the scene will move from an active state to an inactive state.
|
||||
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
||||
}
|
||||
|
||||
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||
// Called as the scene transitions from the background to the foreground.
|
||||
// Use this method to undo the changes made on entering the background.
|
||||
}
|
||||
|
||||
func sceneDidEnterBackground(_ scene: UIScene) {
|
||||
// Called as the scene transitions from the foreground to the background.
|
||||
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
||||
// to restore the scene back to its current state.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// StopwatchViewController.swift
|
||||
// ProjectClock
|
||||
//
|
||||
// Created by Dallon Archibald on 1/23/21.
|
||||
// Reference: https://youtu.be/H691qFRpaWA
|
||||
|
||||
import UIKit
|
||||
|
||||
class StopwatchViewController: UIViewController {
|
||||
|
||||
@IBOutlet weak var hourLabel: UILabel!
|
||||
@IBOutlet weak var minuteLabel: UILabel!
|
||||
@IBOutlet weak var secondLabel: UILabel!
|
||||
|
||||
@IBOutlet weak var startButton: UIButton!
|
||||
@IBOutlet weak var stopButton: UIButton!
|
||||
@IBOutlet weak var resetButton: UIButton!
|
||||
@IBOutlet weak var lapButton: UIButton!
|
||||
|
||||
@IBOutlet weak var tableView: UITableView!
|
||||
|
||||
var hours = 0
|
||||
var minutes = 0
|
||||
var seconds = 0
|
||||
|
||||
var lappedTimes: [String] = []
|
||||
var timer = Timer()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
//lapButton.isHidden = true
|
||||
}
|
||||
|
||||
@IBAction func start(_ sender: UIButton) {
|
||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(count), userInfo: nil, repeats: true)
|
||||
//startButton.isHidden = true
|
||||
//lapButton.isHidden = false
|
||||
}
|
||||
|
||||
@objc fileprivate func count() {
|
||||
seconds += 1
|
||||
|
||||
if seconds == 60 {
|
||||
minutes += 1
|
||||
seconds = 0
|
||||
}
|
||||
if minutes == 60 {
|
||||
hours += 1
|
||||
minutes = 0
|
||||
}
|
||||
if hours == 24 {
|
||||
resetTimes()
|
||||
}
|
||||
|
||||
if seconds >= 10 { secondLabel.text = "\(seconds)" }
|
||||
else { secondLabel.text = "0\(seconds)" }
|
||||
if minutes >= 10 { minuteLabel.text = "\(minutes)" }
|
||||
else { minuteLabel.text = "0\(minutes)" }
|
||||
if hours >= 10 { hourLabel.text = "\(hours)" }
|
||||
else { hourLabel.text = "0\(hours)" }
|
||||
}
|
||||
|
||||
@IBAction func stop(_ sender: UIButton) {
|
||||
timer.invalidate()
|
||||
//startButton.isHidden = false
|
||||
}
|
||||
|
||||
@IBAction func reset(_ sender: UIButton) {
|
||||
resetTimes()
|
||||
}
|
||||
|
||||
func resetTimes() {
|
||||
seconds = 0
|
||||
minutes = 0
|
||||
seconds = 0
|
||||
lappedTimes = []
|
||||
timer.invalidate()
|
||||
secondLabel.text = "00"
|
||||
minuteLabel.text = "00"
|
||||
hourLabel.text = "00"
|
||||
tableView.reloadData()
|
||||
//startButton.isHidden = false
|
||||
//lapButton.isHidden = true
|
||||
}
|
||||
|
||||
@IBAction func lap(_ sender: UIButton) {
|
||||
var currentSec = ""
|
||||
if seconds >= 10 { currentSec = "\(seconds)" }
|
||||
else { currentSec = "0\(seconds)" }
|
||||
|
||||
var currentMin = ""
|
||||
if minutes >= 10 { currentMin = "\(minutes)" }
|
||||
else { currentMin = "0\(minutes)" }
|
||||
|
||||
var currentHour = ""
|
||||
if hours >= 10 { currentHour = "\(hours)" }
|
||||
else { currentHour = "0\(hours)" }
|
||||
|
||||
let currentTime = "\(currentHour):\(currentMin):\(currentSec)" //CHECK THIS
|
||||
lappedTimes.append(currentTime)
|
||||
|
||||
let indexPath = IndexPath(row: lappedTimes.count - 1, section: 0)
|
||||
tableView.insertRows(at: [indexPath], with: .automatic)
|
||||
}
|
||||
}
|
||||
|
||||
extension StopwatchViewController: UITableViewDelegate, UITableViewDataSource {
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return lappedTimes.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "lapCell", for: indexPath)
|
||||
cell.textLabel?.text = lappedTimes[indexPath.row]
|
||||
cell.selectionStyle = .none
|
||||
return cell
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
|
||||
if editingStyle == .delete {
|
||||
lappedTimes.remove(at: indexPath.row)
|
||||
|
||||
tableView.deleteRows(at: [indexPath], with: .automatic)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,30 +8,26 @@
|
||||
import UIKit
|
||||
import UserNotifications
|
||||
|
||||
class TestingViewController: UIViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
class TestingViewController: UIViewController
|
||||
{
|
||||
override func viewDidLoad()
|
||||
{
|
||||
super.viewDidLoad()
|
||||
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) {
|
||||
(granted, error) in
|
||||
if granted {
|
||||
print("Authorized Notifications")
|
||||
} else {
|
||||
print("Error: No notification access")
|
||||
}
|
||||
}
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
@IBAction func getAccel(_ sender: Any) {
|
||||
getAccelerometer()
|
||||
// Request notification permission
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
|
||||
if success {
|
||||
print("All set!")
|
||||
} else if let error = error {
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Sends a test notification
|
||||
@IBAction func sendNotification(_ sender: Any) {
|
||||
let alarm = Alarm(alarmTime: Date(), text: "Good morning!", wakeMethod: WVM(name: "walking", desc: "Walk"))
|
||||
@IBAction func sendNotification(_ sender: Any)
|
||||
{
|
||||
let alarm = Alarm(hour: 7, minute: 20, text: "Good morning!", wakeMethod: WVM(name: "walking", desc: "Walk"))
|
||||
|
||||
let content = UNMutableNotificationContent()
|
||||
|
||||
@@ -51,7 +47,6 @@ class TestingViewController: UIViewController {
|
||||
let attachment = try! UNNotificationAttachment(identifier: imageName, url: imageURL, options: .none)
|
||||
content.attachments = [attachment]
|
||||
|
||||
|
||||
// Readies notification to be sent
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
|
||||
let request = UNNotificationRequest(identifier: "notification.id.01", content: content, trigger: trigger)
|
||||
@@ -60,14 +55,14 @@ class TestingViewController: UIViewController {
|
||||
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
|
||||
}
|
||||
|
||||
/*
|
||||
// MARK: - Navigation
|
||||
|
||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
// Get the new view controller using segue.destination.
|
||||
// Pass the selected object to the new view controller.
|
||||
@IBAction func addAlarm(_ sender: Any)
|
||||
{
|
||||
let (h, m, _) = Date().getHMS()
|
||||
Alarms.fromLocal().apply { $0.list.append(Alarm(hour: h, minute: m, text: "Test alarm - \(h * m)", wakeMethod: wvms[1], repeats: [true, true, true, true, true, true, true], oneTime: true, lastActivate: Date().added(.minute, -1))) }.localSave()
|
||||
}
|
||||
|
||||
@IBAction func deleteAlarm(_ sender: Any)
|
||||
{
|
||||
Alarms.fromLocal().apply { $0.list.removeAll() }.localSave()
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
//
|
||||
// Utils.swift
|
||||
// ProjectClock
|
||||
//
|
||||
// Created by Hykilpikonna on 1/17/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
import UIKit
|
||||
|
||||
/// Date manipulations
|
||||
extension Date
|
||||
{
|
||||
/// Add toString to Date
|
||||
func str() -> String
|
||||
{
|
||||
let f = DateFormatter()
|
||||
f.dateFormat = "yyyy-MM-dd hh:mm:ss"
|
||||
return f.string(from: self)
|
||||
}
|
||||
|
||||
/// Constructor from components
|
||||
static func create(_ year: Int, _ month: Int, _ day: Int, _ hour: Int, _ minute: Int) -> Date
|
||||
{
|
||||
var c = DateComponents()
|
||||
c.year = year
|
||||
c.month = month
|
||||
c.day = day
|
||||
c.hour = hour
|
||||
c.minute = minute
|
||||
let cal = Calendar(identifier: .gregorian)
|
||||
return cal.date(from: c)!
|
||||
}
|
||||
|
||||
/// Get year, month, day
|
||||
func getYMD() -> (y: Int, m: Int, d: Int)
|
||||
{
|
||||
let calendar = Calendar.current
|
||||
let comp = calendar.dateComponents([.year, .month, .day], from: self)
|
||||
return (comp.year!, comp.month!, comp.day!)
|
||||
}
|
||||
|
||||
/// Get hour, minute, seconds
|
||||
func getHMS() -> (h: Int, m: Int, s: Int)
|
||||
{
|
||||
let calendar = Calendar.current
|
||||
let comp = calendar.dateComponents([.hour, .minute, .second], from: self)
|
||||
return (comp.hour!, comp.minute!, comp.second!)
|
||||
}
|
||||
|
||||
/// Get another component
|
||||
func get(_ c: Calendar.Component) -> Int
|
||||
{
|
||||
let calendar = Calendar.current
|
||||
let comp = calendar.dateComponents([c], from: self)
|
||||
return comp.value(for: c)!
|
||||
}
|
||||
|
||||
/// Return a new modified date
|
||||
func added(_ c: Calendar.Component, _ v: Int) -> Date
|
||||
{
|
||||
return Calendar.current.date(byAdding: c, value: v, to: self)!
|
||||
}
|
||||
}
|
||||
|
||||
extension TimeInterval
|
||||
{
|
||||
var seconds: Int { return Int(self) % 60 }
|
||||
var minutes: Int { return (Int(self) / 60) % 60 }
|
||||
var hours: Int { return (Int(self) / 3600) % 24 }
|
||||
var days: Int { return Int(self) / (3600 * 24) }
|
||||
|
||||
/// Add toString to time interval
|
||||
func str() -> String
|
||||
{
|
||||
if days != 0 { return "\(days)d \(hours)h \(minutes)m \(seconds)s" }
|
||||
else if hours != 0 { return "\(hours)h \(minutes)m \(seconds)s" }
|
||||
else if minutes != 0 { return "\(minutes)m \(seconds)s" }
|
||||
else { return "\(seconds)s" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Apply like Kotlin
|
||||
protocol HasApply {}
|
||||
extension HasApply
|
||||
{
|
||||
@discardableResult
|
||||
func apply(_ c: (Self) -> ()) -> Self
|
||||
{
|
||||
c(self)
|
||||
return self
|
||||
}
|
||||
}
|
||||
extension Alarm: HasApply {}
|
||||
extension Alarms: HasApply {}
|
||||
|
||||
|
||||
/// Hashing
|
||||
extension Digest
|
||||
{
|
||||
var bytes: [UInt8] { Array(makeIterator()) }
|
||||
var b64: String { Data(bytes).base64EncodedString() }
|
||||
}
|
||||
|
||||
extension String
|
||||
{
|
||||
var sha256: String { SHA256.hash(data: self.data(using: .utf8)!).b64 }
|
||||
}
|
||||
|
||||
|
||||
/// UI Extensions
|
||||
extension UIViewController
|
||||
{
|
||||
/**
|
||||
Send an alert
|
||||
|
||||
- Parameter title: Title of the alert
|
||||
- Parameter message: Body message of the alert
|
||||
- Parameter okayable: Whether the alert can be okayed
|
||||
*/
|
||||
@discardableResult
|
||||
func alert(_ title: String, _ message: String, okayable: Bool = false) -> UIAlertController
|
||||
{
|
||||
// Create alert
|
||||
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
|
||||
// Add okay button if it's okayable
|
||||
if okayable { alert.addAction(UIAlertAction(title: "OK", style: .default)) }
|
||||
|
||||
// Display alert
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
return alert
|
||||
}
|
||||
|
||||
/// A message is an okayable alert
|
||||
@discardableResult
|
||||
func msg(_ title: String, _ message: String) -> UIAlertController { alert(title, message, okayable: true) }
|
||||
|
||||
/// More convenient dismiss function
|
||||
func dismiss(_ completion: (() -> Void)? = nil) { ui { self.dismiss(animated: false, completion: completion) } }
|
||||
|
||||
/**
|
||||
Send a http request even more conveniently
|
||||
*/
|
||||
func sendReq<T: Decodable>(_ api: API<T>, title: String, errors: [String: String] = [:], params: [String: String]? = [:], _ success: @escaping (T) -> Void, err: @escaping (String) -> Void = {it in})
|
||||
{
|
||||
// Send request
|
||||
let a = alert(title, "Please Wait")
|
||||
send(api, params) { it in a.dismiss { success(it) } }
|
||||
err:
|
||||
{
|
||||
// Display error message
|
||||
print("===== Error: \($0) =====")
|
||||
let message = errors[$0.trimmingCharacters(in: .whitespaces)]
|
||||
?? "Maybe the server is on fire, just wait a few hours."
|
||||
a.dismiss { self.msg("An error occurred", message) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Regex Matching (Credit: https://www.hackingwithswift.com/articles/108/how-to-use-regular-expressions-in-swift)
|
||||
extension NSRegularExpression
|
||||
{
|
||||
convenience init(_ pattern: String)
|
||||
{
|
||||
do { try self.init(pattern: pattern) }
|
||||
catch { preconditionFailure("Illegal regular expression: \(pattern).") }
|
||||
}
|
||||
|
||||
func matches(_ string: String) -> Bool
|
||||
{
|
||||
let range = NSRange(location: 0, length: string.utf16.count)
|
||||
return firstMatch(in: string, options: [], range: range) != nil
|
||||
}
|
||||
}
|
||||
|
||||
extension String
|
||||
{
|
||||
static func ~= (lhs: String, rhs: String) -> Bool
|
||||
{
|
||||
guard let regex = try? NSRegularExpression(pattern: rhs) else { return false }
|
||||
let range = NSRange(location: 0, length: lhs.utf16.count)
|
||||
return regex.firstMatch(in: lhs, options: [], range: range) != nil
|
||||
}
|
||||
}
|
||||
|
||||
/// More convenient ui update closure
|
||||
func ui(closure: @escaping () -> Void) { DispatchQueue.main.async { closure() } }
|
||||
|
||||
/// More convenient UserDefaults access (Credit: https://gist.github.com/Otbivnoe/04b8bd7984fba0cb58ca7f136fd95582)
|
||||
extension UserDefaults
|
||||
{
|
||||
subscript<T>(key: String) -> T?
|
||||
{
|
||||
get { return value(forKey: key) as? T }
|
||||
set { set(newValue, forKey: key) }
|
||||
}
|
||||
|
||||
subscript<T: RawRepresentable>(key: String) -> T?
|
||||
{
|
||||
get
|
||||
{
|
||||
if let rawValue = value(forKey: key) as? T.RawValue { return T(rawValue: rawValue) }
|
||||
return nil
|
||||
}
|
||||
set { self[key] = newValue?.rawValue }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user