diff --git a/BeeSwift.xcodeproj/project.pbxproj b/BeeSwift.xcodeproj/project.pbxproj index 1d26997c..2c8c65bc 100644 --- a/BeeSwift.xcodeproj/project.pbxproj +++ b/BeeSwift.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 9B65F2322CFA6427009674A7 /* DeeplinkGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B65F2312CFA6418009674A7 /* DeeplinkGenerator.swift */; }; 9B8CA57D24B120CA009C86C2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9B8CA57C24B120CA009C86C2 /* LaunchScreen.storyboard */; }; + 9BD4C4E82D45A09F00B03E99 /* MainCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BD4C4E72D45A09F00B03E99 /* MainCoordinator.swift */; }; 9BFB27E92CFE770F0056D10D /* FreshnessIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BFB27E82CFE770F0056D10D /* FreshnessIndicatorView.swift */; }; A10D4E931B07948500A72D29 /* DatapointsTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A10D4E921B07948500A72D29 /* DatapointsTableView.swift */; }; A10DC2DF207BFCBA00FB7B3A /* RemoveHKMetricViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A10DC2DE207BFCBA00FB7B3A /* RemoveHKMetricViewController.swift */; }; @@ -218,6 +219,7 @@ /* Begin PBXFileReference section */ 9B65F2312CFA6418009674A7 /* DeeplinkGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeeplinkGenerator.swift; sourceTree = ""; }; 9B8CA57C24B120CA009C86C2 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + 9BD4C4E72D45A09F00B03E99 /* MainCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainCoordinator.swift; sourceTree = ""; }; 9BFB27E82CFE770F0056D10D /* FreshnessIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FreshnessIndicatorView.swift; sourceTree = ""; }; A10D4E921B07948500A72D29 /* DatapointsTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatapointsTableView.swift; sourceTree = ""; }; A10DC2DE207BFCBA00FB7B3A /* RemoveHKMetricViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveHKMetricViewController.swift; sourceTree = ""; }; @@ -473,6 +475,7 @@ A196CB161AE4142E00B90A3E /* BeeSwift */ = { isa = PBXGroup; children = ( + 9BD4C4E72D45A09F00B03E99 /* MainCoordinator.swift */, 9B65F2312CFA6418009674A7 /* DeeplinkGenerator.swift */, A1E618E51E79E01900D8ED93 /* Cells */, E46071002B43DA7100305DB4 /* Gallery */, @@ -1023,6 +1026,7 @@ A10DC2DF207BFCBA00FB7B3A /* RemoveHKMetricViewController.swift in Sources */, E412DAE12B86A8F70099E483 /* GoalImageView.swift in Sources */, A1619EA41BEECC1500E14B3A /* EditDefaultNotificationsViewController.swift in Sources */, + 9BD4C4E82D45A09F00B03E99 /* MainCoordinator.swift in Sources */, A149B3701AEF528C00F19A09 /* SettingsViewController.swift in Sources */, E46DC80F2AA58DF20059FDFE /* PullToRefreshHint.swift in Sources */, A1E618E21E78158700D8ED93 /* HealthKitConfigViewController.swift in Sources */, diff --git a/BeeSwift/Gallery/GalleryViewController.swift b/BeeSwift/Gallery/GalleryViewController.swift index 1f9415d2..7412929c 100644 --- a/BeeSwift/Gallery/GalleryViewController.swift +++ b/BeeSwift/Gallery/GalleryViewController.swift @@ -19,6 +19,7 @@ import BeeKit class GalleryViewController: UIViewController { + private weak var coordinator: MainCoordinator? let logger = Logger(subsystem: "com.beeminder.beeminder", category: "GalleryViewController") public enum NotificationName { @@ -131,13 +132,15 @@ class GalleryViewController: UIViewController { versionManager: VersionManager, goalManager: GoalManager, healthStoreManager: HealthStoreManager, - requestManager: RequestManager) { + requestManager: RequestManager, + coordinator: MainCoordinator) { self.currentUserManager = currentUserManager self.viewContext = viewContext self.versionManager = versionManager self.goalManager = goalManager self.healthStoreManager = healthStoreManager self.requestManager = requestManager + self.coordinator = coordinator let fetchRequest = Goal.fetchRequest() as! NSFetchRequest fetchRequest.sortDescriptors = Self.preferredSort @@ -158,8 +161,6 @@ class GalleryViewController: UIViewController { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(self.handleSignIn), name: CurrentUserManager.NotificationName.signedIn, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(self.handleSignOut), name: CurrentUserManager.NotificationName.signedOut, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(self.openGoalFromNotification(_:)), name: GalleryViewController.NotificationName.openGoal, object: nil) self.view.addSubview(self.stackView) stackView.snp.makeConstraints { (make) -> Void in @@ -274,20 +275,8 @@ class GalleryViewController: UIViewController { } } - override func viewDidAppear(_ animated: Bool) { - if !currentUserManager.signedIn(context: viewContext) { - let signInVC = SignInViewController(currentUserManager: currentUserManager) - signInVC.modalPresentationStyle = .fullScreen - self.present(signInVC, animated: true, completion: nil) - } - } - @objc func settingsButtonPressed() { - self.navigationController?.pushViewController(SettingsViewController( - currentUserManager: currentUserManager, - viewContext: viewContext, - goalManager: goalManager, - requestManager: requestManager), animated: true) + coordinator?.showSettings() } @objc func searchButtonPressed() { @@ -307,23 +296,13 @@ class GalleryViewController: UIViewController { } @objc func handleSignIn() { - self.dismiss(animated: true, completion: nil) self.fetchGoals() UNUserNotificationCenter.current().requestAuthorization(options: UNAuthorizationOptions([.alert, .badge, .sound])) { [weak self] (success, error) in - self?.logger.info("Requested person’s authorization upon signin to allow local and remote notifications; successful? \(success)") + self?.logger.info("Requested person's authorization upon signin to allow local and remote notifications; successful? \(success)") } } - @objc func handleSignOut() { - if self.presentedViewController != nil { - if type(of: self.presentedViewController!) == SignInViewController.self { return } - } - let signInVC = SignInViewController(currentUserManager: currentUserManager) - signInVC.modalPresentationStyle = .fullScreen - self.present(signInVC, animated: true, completion: nil) - } - func updateDeadbeatVisibility() { self.deadbeatView.isHidden = !isUserKnownDeadbeat } @@ -415,35 +394,8 @@ class GalleryViewController: UIViewController { }) } - @objc func openGoalFromNotification(_ notification: Notification) { - guard let notif = notification as NSNotification? else { return } - var matchingGoal: Goal? - - if let identifier = notif.userInfo?["identifier"] as? String { - if let url = URL(string: identifier), let objectID = viewContext.persistentStoreCoordinator?.managedObjectID(forURIRepresentation: url) { - matchingGoal = viewContext.object(with: objectID) as? Goal - } - } - else if let slug = notif.userInfo?["slug"] as? String { - matchingGoal = self.filteredGoals.filter({ (goal) -> Bool in - return goal.slug == slug - }).last - } - if matchingGoal != nil { - self.navigationController?.popToRootViewController(animated: false) - self.openGoal(matchingGoal!) - } - } - func openGoal(_ goal: Goal) { - let goalViewController = GoalViewController( - goal: goal, - healthStoreManager: healthStoreManager, - goalManager: goalManager, - requestManager: requestManager, - currentUserManager: currentUserManager, - viewContext: viewContext) - self.navigationController?.pushViewController(goalViewController, animated: true) + coordinator?.showGoal(goal) } private func configureDataSource() { diff --git a/BeeSwift/GoalViewController.swift b/BeeSwift/GoalViewController.swift index 19a42e70..29bdb954 100644 --- a/BeeSwift/GoalViewController.swift +++ b/BeeSwift/GoalViewController.swift @@ -31,6 +31,7 @@ class GoalViewController: UIViewController, UIScrollViewDelegate, DatapointTabl private let requestManager: RequestManager private let currentUserManager: CurrentUserManager private let viewContext: NSManagedObjectContext + private weak var coordinator: MainCoordinator? private let timeElapsedView = FreshnessIndicatorView() fileprivate var goalImageView = GoalImageView(isThumbnail: false) @@ -56,13 +57,15 @@ class GoalViewController: UIViewController, UIScrollViewDelegate, DatapointTabl goalManager: GoalManager, requestManager: RequestManager, currentUserManager: CurrentUserManager, - viewContext: NSManagedObjectContext) { + viewContext: NSManagedObjectContext, + coordinator: MainCoordinator) { self.goal = goal self.healthStoreManager = healthStoreManager self.goalManager = goalManager self.requestManager = requestManager self.currentUserManager = currentUserManager self.viewContext = viewContext + self.coordinator = coordinator super.init(nibName: nil, bundle: nil) } @@ -347,9 +350,7 @@ class GoalViewController: UIViewController, UIScrollViewDelegate, DatapointTabl } @objc func timerButtonPressed() { - let controller = TimerViewController(goal: self.goal, requestManager: self.requestManager) - controller.modalPresentationStyle = .fullScreen - self.present(controller, animated: true, completion: nil) + coordinator?.showTimerForGoal(goal) } @objc func refreshButtonPressed() { @@ -387,10 +388,7 @@ class GoalViewController: UIViewController, UIScrollViewDelegate, DatapointTabl guard !self.goal.hideDataEntry else { return } guard let existingDatapoint = datapoint as? DataPoint else { return } - let editDatapointViewController = EditDatapointViewController(goal: goal, datapoint: existingDatapoint, requestManager: self.requestManager, goalManager: self.goalManager) - let navigationController = UINavigationController(rootViewController: editDatapointViewController) - navigationController.modalPresentationStyle = .formSheet - self.present(navigationController, animated: true, completion: nil) + coordinator?.showEditDatapointForGoal(goal: goal, datapoint: existingDatapoint) } @objc func dateStepperValueChanged() { diff --git a/BeeSwift/MainCoordinator.swift b/BeeSwift/MainCoordinator.swift new file mode 100644 index 00000000..3e5d8a4c --- /dev/null +++ b/BeeSwift/MainCoordinator.swift @@ -0,0 +1,240 @@ +import UIKit +import BeeKit +import CoreData + +class MainCoordinator { + private let navigationController: UINavigationController + private let currentUserManager: CurrentUserManager + private let viewContext: NSManagedObjectContext + private let versionManager: VersionManager + private let goalManager: GoalManager + private let healthStoreManager: HealthStoreManager + private let requestManager: RequestManager + + init(navigationController: UINavigationController, + currentUserManager: CurrentUserManager, + viewContext: NSManagedObjectContext, + versionManager: VersionManager, + goalManager: GoalManager, + healthStoreManager: HealthStoreManager, + requestManager: RequestManager) { + self.navigationController = navigationController + self.currentUserManager = currentUserManager + self.viewContext = viewContext + self.versionManager = versionManager + self.goalManager = goalManager + self.healthStoreManager = healthStoreManager + self.requestManager = requestManager + + setUpNotifications() + } + + private func setUpNotifications() { + NotificationCenter.default.addObserver(self, + selector: #selector(handleSignIn), + name: CurrentUserManager.NotificationName.signedIn, + object: nil) + NotificationCenter.default.addObserver(self, + selector: #selector(handleSignOut), + name: CurrentUserManager.NotificationName.signedOut, + object: nil) + NotificationCenter.default.addObserver(self, + selector: #selector(openGoalFromNotification(_:)), + name: GalleryViewController.NotificationName.openGoal, + object: nil) + } + + func start() { + let galleryVC = GalleryViewController( + currentUserManager: currentUserManager, + viewContext: viewContext, + versionManager: versionManager, + goalManager: goalManager, + healthStoreManager: healthStoreManager, + requestManager: requestManager, + coordinator: self + ) + + navigationController.setViewControllers([galleryVC], animated: false) + navigationController.navigationBar.isTranslucent = false + navigationController.navigationBar.barStyle = .black + navigationController.navigationBar.tintColor = .white + + if !currentUserManager.signedIn(context: viewContext) { + showSignIn() + } + } + + func showGoal(_ goal: Goal) { + let goalViewController = GoalViewController( + goal: goal, + healthStoreManager: healthStoreManager, + goalManager: goalManager, + requestManager: requestManager, + currentUserManager: currentUserManager, + viewContext: viewContext, + coordinator: self) + navigationController.pushViewController(goalViewController, animated: true) + } + + func showSettings() { + let settingsVC = SettingsViewController( + currentUserManager: currentUserManager, + viewContext: viewContext, + goalManager: goalManager, + requestManager: requestManager, + coordinator: self) + navigationController.pushViewController(settingsVC, animated: true) + } + + func showSignIn() { + let signInVC = SignInViewController(currentUserManager: currentUserManager, coordinator: self) + signInVC.modalPresentationStyle = .fullScreen + navigationController.present(signInVC, animated: true) + } + + func showTimerForGoal(_ goal: Goal) { + let controller = TimerViewController(goal: goal, requestManager: requestManager) + controller.modalPresentationStyle = .fullScreen + navigationController.present(controller, animated: true, completion: nil) + } + + func showChooseGallerySortAlgorithm() { + let controller = ChooseGoalSortViewController() + + navigationController.pushViewController(controller, + animated: true) + } + + func showConfigureNotifications() { + let controller = ConfigureNotificationsViewController( + goalManager: goalManager, + viewContext: viewContext, + currentUserManager: currentUserManager, + requestManager: requestManager, + coordinator: self) + + navigationController.pushViewController(controller, + animated: true) + } + + func showConfigureHealthKitIntegration() { + let controller = HealthKitConfigViewController( + goalManager: goalManager, + viewContext: viewContext, + healthStoreManager: ServiceLocator.healthStoreManager, + requestManager: requestManager, + coordinator: self) + + navigationController.pushViewController(controller, + animated: true) + } + + func showRemoveHealthKitIntegrationFromGoal(_ goal: Goal) { + let controller = RemoveHKMetricViewController( + goal: goal, + requestManager: requestManager, + goalManager: goalManager) + + navigationController.pushViewController(controller, + animated: true) + } + + func showAssociateHealthKitWithGoal(_ goal: Goal) { + let chooseHKMetricViewController = ChooseHKMetricViewController( + goal: goal, + healthStoreManager: healthStoreManager, + requestManager: requestManager, + coordinator: self) + + navigationController.pushViewController(chooseHKMetricViewController, + animated: true) + } + + func showConfigureNotificationsForGoal(_ goal: Goal) { + let controller = EditGoalNotificationsViewController( + goal: goal, + currentUserManager: currentUserManager, + requestManager: requestManager, + goalManager: goalManager, + viewContext: viewContext) + + navigationController.pushViewController(controller, + animated: true) + } + + func showConfigureDefaultNotifications() { + let controller = EditDefaultNotificationsViewController( + currentUserManager: currentUserManager, + requestManager: requestManager, + goalManager: goalManager, + viewContext: viewContext) + + navigationController.pushViewController(controller, + animated: true) + } + + func showConfigureHKMetricForGoal(_ goal: Goal, _ metric: HealthKitMetric) { + let controller = ConfigureHKMetricViewController( + goal: goal, + metric: metric, + healthStoreManager: healthStoreManager, + requestManager: requestManager + ) + + navigationController.pushViewController(controller, + animated: true) + } + + func showEditDatapointForGoal(goal: Goal, datapoint: DataPoint) { + let editDatapointViewController = EditDatapointViewController(goal: goal, + datapoint: datapoint, + requestManager: self.requestManager, + goalManager: self.goalManager) + + let navigationController = UINavigationController(rootViewController: editDatapointViewController) + navigationController.modalPresentationStyle = .formSheet + self.navigationController.present(navigationController, animated: true, completion: nil) + } + + func showLogs() { + let controller = LogsViewController() + navigationController.pushViewController(controller, + animated: true) + } + + @objc private func handleSignIn() { + navigationController.dismiss(animated: true) + navigationController.popToRootViewController(animated: true) + start() + } + + @objc private func handleSignOut() { + navigationController.popToRootViewController(animated: true) + showSignIn() + } + + @objc private func openGoalFromNotification(_ notification: Notification) { + var goalFromID: Goal? { + guard let identifier = notification.userInfo?["identifier"] as? String else { return nil } + guard + let url = URL(string: identifier), + let objectID = viewContext.persistentStoreCoordinator?.managedObjectID(forURIRepresentation: url) + else { return nil } + return viewContext.object(with: objectID) as? Goal + } + + var goalFromSlug: Goal? { + guard + let slug = notification.userInfo?["slug"] as? String, + let user = currentUserManager.user(context: viewContext) + else { return nil } + return user.goals.first { $0.slug == slug } + } + + if let goal = goalFromID ?? goalFromSlug { + navigationController.popToRootViewController(animated: false) + showGoal(goal) + } + } +} diff --git a/BeeSwift/SceneDelegate.swift b/BeeSwift/SceneDelegate.swift index 123cfbcd..3cba6dae 100644 --- a/BeeSwift/SceneDelegate.swift +++ b/BeeSwift/SceneDelegate.swift @@ -7,12 +7,16 @@ import BeeKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? + + private var coordinator: MainCoordinator? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: .all)) - let galleryVC = GalleryViewController( + let navigationController = UINavigationController() + self.coordinator = MainCoordinator( + navigationController: navigationController, currentUserManager: ServiceLocator.currentUserManager, viewContext: ServiceLocator.persistentContainer.viewContext, versionManager: ServiceLocator.versionManager, @@ -21,13 +25,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { requestManager: ServiceLocator.requestManager ) - let navigationController = UINavigationController(rootViewController: galleryVC) - navigationController.navigationBar.isTranslucent = false - navigationController.navigationBar.barStyle = .black - navigationController.navigationBar.tintColor = .white - window = UIWindow(windowScene: windowScene) window?.rootViewController = navigationController window?.makeKeyAndVisible() + + coordinator?.start() } } diff --git a/BeeSwift/Settings/ChooseHKMetricViewController.swift b/BeeSwift/Settings/ChooseHKMetricViewController.swift index f5b2b0fd..7376f5f1 100644 --- a/BeeSwift/Settings/ChooseHKMetricViewController.swift +++ b/BeeSwift/Settings/ChooseHKMetricViewController.swift @@ -19,11 +19,13 @@ class ChooseHKMetricViewController: UIViewController { let goal: Goal private let healthStoreManager: HealthStoreManager private let requestManager: RequestManager + private weak var coordinator: MainCoordinator? - init(goal: Goal, healthStoreManager: HealthStoreManager, requestManager: RequestManager) { + init(goal: Goal, healthStoreManager: HealthStoreManager, requestManager: RequestManager, coordinator: MainCoordinator) { self.goal = goal self.healthStoreManager = healthStoreManager self.requestManager = requestManager + self.coordinator = coordinator super.init(nibName: nil, bundle: nil) } @@ -142,14 +144,7 @@ extension ChooseHKMetricViewController : UITableViewDelegate, UITableViewDataSou return } - self.navigationController?.pushViewController( - ConfigureHKMetricViewController( - goal: self.goal, - metric: metric, - healthStoreManager: self.healthStoreManager, - requestManager: self.requestManager - ), - animated: true) + coordinator?.showConfigureHKMetricForGoal(goal, metric) } } diff --git a/BeeSwift/Settings/ConfigureNotificationsViewController.swift b/BeeSwift/Settings/ConfigureNotificationsViewController.swift index 225048be..9b31c80f 100644 --- a/BeeSwift/Settings/ConfigureNotificationsViewController.swift +++ b/BeeSwift/Settings/ConfigureNotificationsViewController.swift @@ -25,12 +25,18 @@ class ConfigureNotificationsViewController: UIViewController { private let viewContext: NSManagedObjectContext private let currentUserManager: CurrentUserManager private let requestManager: RequestManager + private weak var coordinator: MainCoordinator? - init(goalManager: GoalManager, viewContext: NSManagedObjectContext, currentUserManager: CurrentUserManager, requestManager: RequestManager) { + init(goalManager: GoalManager, + viewContext: NSManagedObjectContext, + currentUserManager: CurrentUserManager, + requestManager: RequestManager, + coordinator: MainCoordinator) { self.goalManager = goalManager self.viewContext = viewContext self.currentUserManager = currentUserManager self.requestManager = requestManager + self.coordinator = coordinator super.init(nibName: nil, bundle: nil) } @@ -193,34 +199,19 @@ extension ConfigureNotificationsViewController : UITableViewDataSource, UITableV } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let editNotificationsVC: UIViewController + self.tableView.deselectRow(at: indexPath, animated: true) switch indexPath.section { case 0: - editNotificationsVC = EditDefaultNotificationsViewController( - currentUserManager: currentUserManager, - requestManager: requestManager, - goalManager: goalManager, - viewContext: viewContext) + coordinator?.showConfigureDefaultNotifications() case 1: let goal = self.goalsUsingDefaultNotifications[indexPath.row] - editNotificationsVC = EditGoalNotificationsViewController( - goal: goal, - currentUserManager: currentUserManager, - requestManager: requestManager, - goalManager: goalManager, - viewContext: viewContext) - default: + coordinator?.showConfigureNotificationsForGoal(goal) + case 2: let goal = self.goalsUsingNonDefaultNotifications[indexPath.row] - editNotificationsVC = EditGoalNotificationsViewController( - goal: goal, - currentUserManager: currentUserManager, - requestManager: requestManager, - goalManager: goalManager, - viewContext: viewContext) + coordinator?.showConfigureNotificationsForGoal(goal) + default: + break } - - self.navigationController?.pushViewController(editNotificationsVC, animated: true) - self.tableView.deselectRow(at: indexPath, animated: true) } } diff --git a/BeeSwift/Settings/HealthKitConfigViewController.swift b/BeeSwift/Settings/HealthKitConfigViewController.swift index d16c28c6..f4e6ef88 100644 --- a/BeeSwift/Settings/HealthKitConfigViewController.swift +++ b/BeeSwift/Settings/HealthKitConfigViewController.swift @@ -27,15 +27,18 @@ class HealthKitConfigViewController: UIViewController { private let viewContext: NSManagedObjectContext private let healthStoreManager: HealthStoreManager private let requestManager: RequestManager + private weak var coordinator: MainCoordinator? init(goalManager: GoalManager, viewContext: NSManagedObjectContext, healthStoreManager: HealthStoreManager, - requestManager: RequestManager) { + requestManager: RequestManager, + coordinator: MainCoordinator) { self.goalManager = goalManager self.viewContext = viewContext self.healthStoreManager = healthStoreManager self.requestManager = requestManager + self.coordinator = coordinator super.init(nibName: nil, bundle: nil) } @@ -166,17 +169,9 @@ extension HealthKitConfigViewController: UITableViewDelegate, UITableViewDataSou let goal = self.goalAt(indexPath) if !goal.isDataProvidedAutomatically { - let chooseHKMetricViewController = ChooseHKMetricViewController( - goal: goal, - healthStoreManager: healthStoreManager, - requestManager: requestManager) - self.navigationController?.pushViewController(chooseHKMetricViewController, animated: true) + coordinator?.showAssociateHealthKitWithGoal(goal) } else if goal.autodata == "apple" { - let controller = RemoveHKMetricViewController( - goal: goal, - requestManager: requestManager, - goalManager: goalManager) - self.navigationController?.pushViewController(controller, animated: true) + coordinator?.showRemoveHealthKitIntegrationFromGoal(goal) } else { let alert: UIAlertController = { let alert = UIAlertController(title: "Autodata Goal", message: "At the moment we don't have a way for you to swap data sources here yourself for autodata goals", preferredStyle: .alert) diff --git a/BeeSwift/Settings/SettingsViewController.swift b/BeeSwift/Settings/SettingsViewController.swift index f84b5a01..3a71cd73 100644 --- a/BeeSwift/Settings/SettingsViewController.swift +++ b/BeeSwift/Settings/SettingsViewController.swift @@ -21,15 +21,18 @@ class SettingsViewController: UIViewController { private let viewContext: NSManagedObjectContext private let goalManager: GoalManager private let requestManager: RequestManager + private weak var coordinator: MainCoordinator? init(currentUserManager: CurrentUserManager, viewContext: NSManagedObjectContext, goalManager: GoalManager, - requestManager: RequestManager) { + requestManager: RequestManager, + coordinator: MainCoordinator) { self.currentUserManager = currentUserManager self.viewContext = viewContext self.goalManager = goalManager self.requestManager = requestManager + self.coordinator = coordinator super.init(nibName: nil, bundle: nil) } @@ -108,7 +111,7 @@ class SettingsViewController: UIViewController { @objc func showLogsTapped() { - self.navigationController?.pushViewController(LogsViewController(), animated: true) + coordinator?.showLogs() } @objc private func fetchUser() { @@ -128,45 +131,43 @@ class SettingsViewController: UIViewController { extension SettingsViewController : UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { - return UIView() + UIView() } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - return UIView() + UIView() } func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { - return CGFloat.leastNormalMagnitude + CGFloat.leastNormalMagnitude } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { - return 20 + 20 } func numberOfSections(in tableView: UITableView) -> Int { - return HKHealthStore.isHealthDataAvailable() ? 5 : 4 + 5 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 1 + 1 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - var section = indexPath.section - guard let cell = tableView.dequeueReusableCell(withIdentifier: self.cellReuseIdentifier) as? SettingsTableViewCell else { return UITableViewCell() } - if HKHealthStore.isHealthDataAvailable() { - if section == 0 { - cell.title = "Health app integration" - cell.imageName = "Health" - cell.accessoryType = .disclosureIndicator - return cell - } - section = section - 1 - } + guard let cell = tableView.dequeueReusableCell(withIdentifier: self.cellReuseIdentifier, for: indexPath) as? SettingsTableViewCell + else { return UITableViewCell() } - switch section { + + switch indexPath.section { case 0: + cell.title = "Health app integration" + cell.imageName = "Health" + cell.accessoryType = .disclosureIndicator + cell.isUserInteractionEnabled = HKHealthStore.isHealthDataAvailable() + return cell + case 1: let selectedGoalSort = UserDefaults.standard.value(forKey: Constants.selectedGoalSortKey) as? String cell.title = "Sort goals by: \(selectedGoalSort ?? "")" cell.imageName = "arrow.up.arrow.down" cell.accessoryType = .disclosureIndicator - case 1: + case 2: UNUserNotificationCenter.current().getNotificationSettings { (settings) in DispatchQueue.main.async { cell.title = "Emergency notifications: \(settings.authorizationStatus == .authorized ? "on" : "off")" @@ -174,13 +175,13 @@ extension SettingsViewController : UITableViewDataSource, UITableViewDelegate { } cell.imageName = "app.badge" cell.accessoryType = .disclosureIndicator - case 2: + case 3: let user = currentUserManager.user(context: viewContext) let timezone = user?.timezone ?? "Unknown" cell.title = "Time zone: \(timezone)" cell.imageName = "clock" cell.accessoryType = .none - case 3: + case 4: cell.title = "Sign out" cell.imageName = "rectangle.portrait.and.arrow.right" cell.accessoryType = .none @@ -191,31 +192,16 @@ extension SettingsViewController : UITableViewDataSource, UITableViewDelegate { } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - var section = indexPath.section - if HKHealthStore.isHealthDataAvailable() { - if section == 0 { - self.navigationController?.pushViewController(HealthKitConfigViewController( - goalManager: goalManager, - viewContext: viewContext, - healthStoreManager: ServiceLocator.healthStoreManager, - requestManager: requestManager), animated: true) - return - } - section = section - 1 - } - - switch section { - case 0: - self.navigationController?.pushViewController(ChooseGoalSortViewController(), animated: true) + switch indexPath.section { + case 0 where HKHealthStore.isHealthDataAvailable(): + coordinator?.showConfigureHealthKitIntegration() case 1: - self.navigationController?.pushViewController(ConfigureNotificationsViewController( - goalManager: goalManager, - viewContext: viewContext, - currentUserManager: currentUserManager, - requestManager: requestManager), animated: true) + coordinator?.showChooseGallerySortAlgorithm() case 2: - print("nothing") + coordinator?.showConfigureNotifications() case 3: + print("nothing") + case 4: self.signOutButtonPressed() default: break diff --git a/BeeSwift/SignInViewController.swift b/BeeSwift/SignInViewController.swift index 606283cf..9320f17e 100644 --- a/BeeSwift/SignInViewController.swift +++ b/BeeSwift/SignInViewController.swift @@ -21,9 +21,13 @@ class SignInViewController : UIViewController, UITextFieldDelegate { var signInButton = BSButton() var divider = UIView() private let currentUserManager: CurrentUserManager + private weak var coordinator: MainCoordinator? - init(currentUserManager: CurrentUserManager) { + init(currentUserManager: CurrentUserManager, + coordinator: MainCoordinator?) { self.currentUserManager = currentUserManager + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) } @@ -129,15 +133,20 @@ class SignInViewController : UIViewController, UITextFieldDelegate { return lackOfCredentials } + private var couldNotSignInAlertController: UIAlertController { + let controller = UIAlertController(title: "Could not sign in", message: "Invalid credentials", preferredStyle: .alert) + controller.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil)) + return controller + } + @objc func handleFailedSignIn(_ notification : Notification) { - let failureAC = UIAlertController(title: "Could not sign in", message: "Invalid credentials", preferredStyle: .alert) - failureAC.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil)) - self.present(failureAC, animated: true, completion: nil) + self.present(couldNotSignInAlertController, animated: true, completion: nil) MBProgressHUD.hide(for: self.view, animated: true) } @objc func handleSignedIn(_ notification : Notification) { MBProgressHUD.hide(for: self.view, animated: true) + coordinator?.start() } @objc func signInButtonPressed() {