diff --git a/Classes/Login/LoginSplashViewController.swift b/Classes/Login/LoginSplashViewController.swift index 28d4bb561..abade6372 100644 --- a/Classes/Login/LoginSplashViewController.swift +++ b/Classes/Login/LoginSplashViewController.swift @@ -14,7 +14,11 @@ import GitHubSession private let loginURL = URL(string: "http://github.com/login/oauth/authorize?client_id=\(Secrets.GitHub.clientId)&scope=user+repo+notifications")! private let callbackURLScheme = "freetime://" -final class LoginSplashViewController: UIViewController, GitHubSessionListener { +protocol LoginSplashViewControllerDelegate: class { + func finishLogin(token: String, authMethod: GitHubUserSession.AuthMethod, username: String) +} + +final class LoginSplashViewController: UIViewController { enum State { case idle @@ -22,12 +26,11 @@ final class LoginSplashViewController: UIViewController, GitHubSessionListener { } private var client: Client! - private var sessionManager: GitHubSessionManager! @IBOutlet weak var splashView: SplashView! @IBOutlet weak var signInButton: UIButton! @IBOutlet weak var activityIndicator: UIActivityIndicatorView! - private weak var safariController: SFSafariViewController? + private weak var delegate: LoginSplashViewControllerDelegate? @available(iOS 11.0, *) private var authSession: SFAuthenticationSession? { @@ -60,7 +63,6 @@ final class LoginSplashViewController: UIViewController, GitHubSessionListener { override func viewDidLoad() { super.viewDidLoad() state = .idle - sessionManager.addListener(listener: self) signInButton.layer.cornerRadius = Styles.Sizes.cardCornerRadius } @@ -72,9 +74,15 @@ final class LoginSplashViewController: UIViewController, GitHubSessionListener { // MARK: Public API - func config(client: Client, sessionManager: GitHubSessionManager) { - self.client = client - self.sessionManager = sessionManager + static func make(client: Client, delegate: LoginSplashViewControllerDelegate) -> LoginSplashViewController? { + let controller = UIStoryboard( + name: "OauthLogin", + bundle: Bundle(for: AppDelegate.self)) + .instantiateInitialViewController() as? LoginSplashViewController + controller?.client = client + controller?.delegate = delegate + controller?.modalPresentationStyle = .formSheet + return controller } // MARK: Private API @@ -88,7 +96,22 @@ final class LoginSplashViewController: UIViewController, GitHubSessionListener { } return } - self?.sessionManager.receivedCodeRedirect(url: callbackUrl) + + guard let items = URLComponents(url: callbackUrl, resolvingAgainstBaseURL: false)?.queryItems, + let index = items.index(where: { $0.name == "code" }), + let code = items[index].value + else { return } + + self?.state = .fetchingToken + + self?.client.requestAccessToken(code: code) { [weak self] result in + switch result { + case .error: + self?.handleError() + case .success(let user): + self?.delegate?.finishLogin(token: user.token, authMethod: .oauth, username: user.username) + } + } }) self.authSession?.start() } @@ -117,7 +140,7 @@ final class LoginSplashViewController: UIViewController, GitHubSessionListener { case .failure: self?.handleError() case .success(let user): - self?.finishLogin(token: token, authMethod: .pat, username: user.data.login) + self?.delegate?.finishLogin(token: token, authMethod: .pat, username: user.data.login) } } }) @@ -138,34 +161,8 @@ final class LoginSplashViewController: UIViewController, GitHubSessionListener { present(alert, animated: trueUnlessReduceMotionEnabled) } - private func finishLogin(token: String, authMethod: GitHubUserSession.AuthMethod, username: String) { - sessionManager.focus( - GitHubUserSession(token: token, authMethod: authMethod, username: username), - dismiss: true - ) - } - private func setupSplashView() { splashView.configureView() } - // MARK: GitHubSessionListener - - func didReceiveRedirect(manager: GitHubSessionManager, code: String) { - safariController?.dismiss(animated: trueUnlessReduceMotionEnabled) - state = .fetchingToken - - client.requestAccessToken(code: code) { [weak self] result in - switch result { - case .error: - self?.handleError() - case .success(let user): - self?.finishLogin(token: user.token, authMethod: .oauth, username: user.username) - } - } - } - - func didFocus(manager: GitHubSessionManager, userSession: GitHubUserSession, dismiss: Bool) {} - func didLogout(manager: GitHubSessionManager) {} - } diff --git a/Classes/Settings/SettingsAccountsViewController.swift b/Classes/Settings/SettingsAccountsViewController.swift index a5d7596dc..62f56b350 100644 --- a/Classes/Settings/SettingsAccountsViewController.swift +++ b/Classes/Settings/SettingsAccountsViewController.swift @@ -116,7 +116,6 @@ final class SettingsAccountsViewController: UITableViewController, GitHubSession tableView.reloadData() } - func didReceiveRedirect(manager: GitHubSessionManager, code: String) {} func didLogout(manager: GitHubSessionManager) {} } diff --git a/Classes/Settings/SettingsViewController.swift b/Classes/Settings/SettingsViewController.swift index c53852781..06a8ef149 100644 --- a/Classes/Settings/SettingsViewController.swift +++ b/Classes/Settings/SettingsViewController.swift @@ -17,8 +17,6 @@ NewIssueTableViewControllerDelegate { // must be injected var sessionManager: GitHubSessionManager! - weak var rootNavigationManager: RootNavigationManager? - var client: GithubClient! @IBOutlet weak var versionLabel: UILabel! @@ -158,7 +156,7 @@ NewIssueTableViewControllerDelegate { func onReportBug() { guard let viewController = NewIssueTableViewController.create( - client: newGithubClient(userSession: sessionManager.focusedUserSession), + client: GithubClient(userSession: sessionManager.focusedUserSession), owner: "GitHawkApp", repo: "GitHawk", signature: .bugReport diff --git a/Classes/Systems/Alamofire+GithubAPI.swift b/Classes/Systems/Alamofire+GithubAPI.swift deleted file mode 100644 index cc9fbf12f..000000000 --- a/Classes/Systems/Alamofire+GithubAPI.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// Alamofire+GithubAPI.swift -// Freetime -// -// Created by Ryan Nystrom on 5/17/17. -// Copyright © 2017 Ryan Nystrom. All rights reserved. -// - -import Foundation -import Alamofire -import Apollo -import GitHubSession -import GitHubAPI - -func newGithubClient( - userSession: GitHubUserSession? = nil - ) -> GithubClient { - let networkingConfigs = userSession?.networkingConfigs - let config = ConfiguredNetworkers( - token: networkingConfigs?.token, - useOauth: networkingConfigs?.useOauth - ) - return GithubClient( - apollo: config.apollo, - networker: config.alamofire, - userSession: userSession - ) -} diff --git a/Classes/Systems/AppDelegate.swift b/Classes/Systems/AppDelegate.swift index e5166e4e6..17f6c2e7c 100644 --- a/Classes/Systems/AppDelegate.swift +++ b/Classes/Systems/AppDelegate.swift @@ -17,28 +17,12 @@ import GitHubSession class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - private var showingLogin = false private let flexController = FlexController() - private let sessionManager = GitHubSessionManager() - private var watchAppSync: WatchAppUserSessionSync? - - private lazy var rootNavigationManager: RootNavigationManager = { - return RootNavigationManager( - sessionManager: self.sessionManager, - rootViewController: self.window?.rootViewController as! UISplitViewController - ) - }() + private let appController = AppController() func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { - sessionManager.addListener(listener: self) - - let focusedSession = sessionManager.focusedUserSession - watchAppSync = WatchAppUserSessionSync(userSession: focusedSession) - watchAppSync?.start() - - // initialize a webview at the start so webview startup later on isn't so slow - _ = UIWebView() + appController.appDidFinishLaunching(with: window) // setup fabric Fabric.with([Crashlytics.self]) @@ -49,10 +33,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // setup FLEX flexController.configureWindow(window) - // setup root VCs - window?.backgroundColor = Styles.Colors.background - rootNavigationManager.resetRootViewController(userSession: focusedSession) - // use Alamofire status bar network activity helper NetworkActivityIndicatorManager.shared.isEnabled = true @@ -73,42 +53,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate { completionHandler(false) return } - completionHandler(ShortcutHandler.handle( - route: route, - sessionManager: sessionManager, - navigationManager: rootNavigationManager)) + completionHandler(appController.handle(route: route)) } func applicationDidBecomeActive(_ application: UIApplication) { - if showingLogin == false && sessionManager.focusedUserSession == nil { - showingLogin = true - rootNavigationManager.showLogin(animated: false) - } - } - - func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { - if let sourceApp = options[.sourceApplication], - String(describing: sourceApp) == "com.apple.SafariViewService" { - sessionManager.receivedCodeRedirect(url: url) - return true - } - return false + appController.appDidBecomeActive() } func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { - rootNavigationManager.client?.badge.fetch(application: application, handler: completionHandler) - } - -} - -extension AppDelegate: GitHubSessionListener { - - // configure 3d touch shortcut handling - func didFocus(manager: GitHubSessionManager, userSession: GitHubUserSession, dismiss: Bool) { - ShortcutHandler.configure(application: UIApplication.shared, sessionManager: sessionManager) - watchAppSync?.sync(userSession: userSession) + appController.performFetch(application: application, with: completionHandler) } - func didReceiveRedirect(manager: GitHubSessionManager, code: String) {} - func didLogout(manager: GitHubSessionManager) {} } diff --git a/Classes/Systems/AppRouter/AppController.swift b/Classes/Systems/AppRouter/AppController.swift new file mode 100644 index 000000000..802115464 --- /dev/null +++ b/Classes/Systems/AppRouter/AppController.swift @@ -0,0 +1,142 @@ +// +// AppController.swift +// Freetime +// +// Created by Ryan Nystrom on 10/6/18. +// Copyright © 2018 Ryan Nystrom. All rights reserved. +// + +import UIKit +import GitHubSession +import GitHubAPI + +final class AppController: LoginSplashViewControllerDelegate, GitHubSessionListener { + + private var splitViewController: AppSplitViewController! + private let sessionManager = GitHubSessionManager() + private weak var loginViewController: LoginSplashViewController? + private var appClient: GithubClient? + private var settingsNavigationController: UINavigationController? + private var watchAppSync: WatchAppUserSessionSync? + + init() { + sessionManager.addListener(listener: self) + } + + func appDidFinishLaunching(with window: UIWindow?) { + guard let controller = window?.rootViewController as? AppSplitViewController else { + fatalError("App must be setup with a split view controller") + } + splitViewController = controller + + if let focused = sessionManager.focusedUserSession { + resetViewControllers(userSession: focused) + resetWatchSync(userSession: focused) + } + } + + func appDidBecomeActive() { + // no need to login if there's a user session + guard sessionManager.focusedUserSession == nil + // avoid stacking logins + && loginViewController == nil + else { return } + showLogin(animated: false) + } + + func performFetch( + application: UIApplication, + with completion: @escaping (UIBackgroundFetchResult) -> Void + ) { + appClient?.badge.fetch(application: application, handler: completion) + } + + func handle(route: Route) -> Bool { + switch route { + case .tab(let tab): + splitViewController.select(tabAt: tab.rawValue) + return true + case .switchAccount(let sessionIndex): + if let index = sessionIndex { + let session = sessionManager.userSessions[index] + sessionManager.focus(session, dismiss: false) + } + return true + } + } + + private func resetWatchSync(userSession: GitHubUserSession) { + watchAppSync = WatchAppUserSessionSync(userSession: userSession) + watchAppSync?.start() + } + + private func resetViewControllers(userSession: GitHubUserSession) { + let appClient = GithubClient(userSession: userSession) + self.appClient = appClient + settingsNavigationController = settingsNavigationController + ?? newSettingsRootViewController(sessionManager: sessionManager) + // keep settings up to date with the most recent client + if let settings = settingsNavigationController?.viewControllers.first as? SettingsViewController { + settings.client = appClient + } + + splitViewController.reset(viewControllers: [ + newNotificationsRootViewController(client: appClient), + newSearchRootViewController(client: appClient), + newBookmarksRootViewController(client: appClient), + settingsNavigationController ?? UINavigationController() // satisfy compiler + ]) + } + + private func showLogin(animated: Bool) { + guard let controller = LoginSplashViewController.make( + client: Client.make(), + delegate: self + ) + else { return } + loginViewController = controller + + let present: () -> Void = { + self.splitViewController.present(controller, animated: animated) + } + if let presented = splitViewController.presentedViewController { + presented.dismiss(animated: animated, completion: present) + } else { + present() + } + + // wipe underlying VCs clean. important for ipad (modal) + splitViewController.resetEmpty() + } + + // MARK: LoginSplashViewControllerDelegate + + func finishLogin(token: String, authMethod: GitHubUserSession.AuthMethod, username: String) { + sessionManager.focus( + GitHubUserSession(token: token, authMethod: authMethod, username: username), + dismiss: true + ) + } + + // MARK: GitHubSessionListener + + func didFocus(manager: GitHubSessionManager, userSession: GitHubUserSession, dismiss: Bool) { + resetViewControllers(userSession: userSession) + + if dismiss { + splitViewController.presentedViewController?.dismiss(animated: trueUnlessReduceMotionEnabled) + } + + ShortcutHandler.configure(sessionUsernames: manager.userSessions.compactMap { $0.username }) + if let watch = watchAppSync { + watch.sync(userSession: userSession) + } else { + resetWatchSync(userSession: userSession) + } + } + + func didLogout(manager: GitHubSessionManager) { + showLogin(animated: trueUnlessReduceMotionEnabled) + } + +} diff --git a/Classes/Systems/AppRouter/AppSplitViewController.swift b/Classes/Systems/AppRouter/AppSplitViewController.swift new file mode 100644 index 000000000..cfe268252 --- /dev/null +++ b/Classes/Systems/AppRouter/AppSplitViewController.swift @@ -0,0 +1,46 @@ +// +// AppSplitViewController.swift +// Freetime +// +// Created by Ryan Nystrom on 10/6/18. +// Copyright © 2018 Ryan Nystrom. All rights reserved. +// + +import UIKit + +final class AppSplitViewController: UISplitViewController { + + private let tabDelegate = TabBarControllerDelegate() + private let appSplitViewDelegate = SplitViewControllerDelegate() + + override func viewDidLoad() { + super.viewDidLoad() + masterTabBarController?.delegate = tabDelegate + delegate = appSplitViewDelegate + preferredDisplayMode = .allVisible + } + + private var detailNavigationController: UINavigationController? { + return viewControllers.last as? UINavigationController + } + + private var masterTabBarController: UITabBarController? { + return viewControllers.first as? UITabBarController + } + + func select(tabAt index: Int) { + masterTabBarController?.selectedIndex = index + } + + func resetEmpty() { + let controller = UIViewController() + controller.view.backgroundColor = Styles.Colors.background + reset(viewControllers: [UINavigationController(rootViewController: controller)]) + } + + func reset(viewControllers: [UIViewController]) { + masterTabBarController?.viewControllers = viewControllers + detailNavigationController?.viewControllers = [SplitPlaceholderViewController()] + } + +} diff --git a/Classes/Systems/Client+GithubUserSession.swift b/Classes/Systems/Client+GithubUserSession.swift new file mode 100644 index 000000000..36b380d0f --- /dev/null +++ b/Classes/Systems/Client+GithubUserSession.swift @@ -0,0 +1,28 @@ +// +// Client+GithubUserSession.swift +// Freetime +// +// Created by Ryan Nystrom on 10/6/18. +// Copyright © 2018 Ryan Nystrom. All rights reserved. +// + +import Foundation +import GitHubSession +import GitHubAPI + +extension Client { + + static func make(userSession: GitHubUserSession? = nil) -> Client { + let networkingConfigs = userSession?.networkingConfigs + let config = ConfiguredNetworkers( + token: networkingConfigs?.token, + useOauth: networkingConfigs?.useOauth + ) + return Client( + httpPerformer: config.alamofire, + apollo: config.apollo, + token: userSession?.token + ) + } + +} diff --git a/Classes/Systems/GithubClient.swift b/Classes/Systems/GithubClient.swift index f0b40b982..5d157d875 100644 --- a/Classes/Systems/GithubClient.swift +++ b/Classes/Systems/GithubClient.swift @@ -21,14 +21,10 @@ struct GithubClient { let client: Client let badge: BadgeNotifications - init( - apollo: ApolloClient, - networker: Alamofire.SessionManager, - userSession: GitHubUserSession? = nil - ) { + init(userSession: GitHubUserSession? = nil) { self.userSession = userSession - self.client = Client(httpPerformer: networker, apollo: apollo, token: userSession?.token) + self.client = Client.make(userSession: userSession) self.badge = BadgeNotifications(client: self.client) if let token = userSession?.token { diff --git a/Classes/Systems/RootNavigationManager.swift b/Classes/Systems/RootNavigationManager.swift deleted file mode 100644 index 4f761d3ef..000000000 --- a/Classes/Systems/RootNavigationManager.swift +++ /dev/null @@ -1,169 +0,0 @@ -// -// RootNavigationManager.swift -// Freetime -// -// Created by Ryan Nystrom on 5/17/17. -// Copyright © 2017 Ryan Nystrom. All rights reserved. -// - -import UIKit -import GitHubAPI -import GitHubSession - -final class RootNavigationManager: GitHubSessionListener { - - private let sessionManager: GitHubSessionManager - private let splitDelegate = SplitViewControllerDelegate() - private let tabDelegate = TabBarControllerDelegate() - - // weak refs to avoid cycles - weak private var rootViewController: UISplitViewController? - - // keep alive between switching accounts - private var settingsRootViewController: UIViewController? - - private(set) var client: GithubClient? - - init( - sessionManager: GitHubSessionManager, - rootViewController: UISplitViewController - ) { - self.sessionManager = sessionManager - self.rootViewController = rootViewController - rootViewController.delegate = splitDelegate - rootViewController.preferredDisplayMode = .allVisible - sessionManager.addListener(listener: self) - - self.tabBarController?.tabBar.tintColor = Styles.Colors.Blue.medium.color - self.tabBarController?.tabBar.unselectedItemTintColor = Styles.Colors.Gray.light.color - self.tabBarController?.delegate = self.tabDelegate - } - - // MARK: Public API - - public func showLogin(animated: Bool = false) { - guard let root = rootViewController else { return } - - let login = newLoginViewController() - login.modalPresentationStyle = .formSheet - - let block: () -> Void = { root.present(login, animated: animated) } - - if let presented = root.presentedViewController { - presented.dismiss(animated: animated, completion: block) - } else { - block() - } - - self.tabBarController?.selectedIndex = 0 - } - - public func resetRootViewController(userSession: GitHubUserSession?) { - guard let userSession = userSession else { return } - - let client = newGithubClient(userSession: userSession) - self.client = client - - fetchUsernameForMigrationIfNecessary(client: client, userSession: userSession, sessionManager: sessionManager) - - // rebuild the settings VC if it doesn't exist - settingsRootViewController = settingsRootViewController ?? newSettingsRootViewController( - sessionManager: sessionManager, - client: client, - rootNavigationManager: self - ) - - tabBarController?.viewControllers = [ - newNotificationsRootViewController(client: client), - newSearchRootViewController(client: client), - newBookmarksRootViewController(client: client), - settingsRootViewController ?? UIViewController() // simply satisfying compiler - ] - } - - public func pushLoginViewController(nav: UINavigationController) { - let login = newLoginViewController() - nav.pushViewController(login, animated: trueUnlessReduceMotionEnabled) - } - - @discardableResult - public func selectViewController(atIndex index: Int) -> UIViewController? { - tabBarController?.selectedIndex = index - return tabBarController?.selectedViewController - } - - @discardableResult - public func selectViewController(atTab tab: TabBarController.Tab) -> UIViewController? { - tabBarController?.showTab(tab) - return tabBarController?.selectedViewController - } - - // MARK: GitHubSessionListener - - func didFocus(manager: GitHubSessionManager, userSession: GitHubUserSession, dismiss: Bool) { - resetRootViewController(userSession: userSession) - - if dismiss { - rootViewController?.presentedViewController?.dismiss(animated: trueUnlessReduceMotionEnabled) - } - } - - func didLogout(manager: GitHubSessionManager) { - settingsRootViewController = nil - - for vc in tabBarController?.viewControllers ?? [] { - if let nav = vc as? UINavigationController { - nav.viewControllers = [SplitPlaceholderViewController()] - } - } - - detailNavigationController?.viewControllers = [SplitPlaceholderViewController()] - showLogin(animated: trueUnlessReduceMotionEnabled) - } - - func didReceiveRedirect(manager: GitHubSessionManager, code: String) {} - - // MARK: Private API - - private func fetchUsernameForMigrationIfNecessary( - client: GithubClient, - userSession: GitHubUserSession, - sessionManager: GitHubSessionManager - ) { - // only required when there is no username - guard userSession.username == nil else { return } - - client.client.send(V3VerifyPersonalAccessTokenRequest(token: userSession.token)) { result in - switch result { - case .success(let user): - userSession.username = user.data.login - - // user session ref is same session that manager should be using - // update w/ mutated session - sessionManager.save() - case .failure: break - } - } - } - - private var detailNavigationController: UINavigationController? { - return rootViewController?.viewControllers.last as? UINavigationController - } - - private var tabBarController: TabBarController? { - return rootViewController?.viewControllers.first as? TabBarController - } - - private func newLoginViewController() -> UIViewController { - let controller = UIStoryboard( - name: "OauthLogin", - bundle: Bundle(for: AppDelegate.self)) - .instantiateInitialViewController() as! LoginSplashViewController - controller.config( - client: newGithubClient().client, - sessionManager: sessionManager - ) - return controller - } - -} diff --git a/Classes/Systems/ShortcutHandler.swift b/Classes/Systems/ShortcutHandler.swift index 8bc4df194..c5b8665e7 100644 --- a/Classes/Systems/ShortcutHandler.swift +++ b/Classes/Systems/ShortcutHandler.swift @@ -17,29 +17,11 @@ struct ShortcutHandler { case switchAccount } - static func configure(application: UIApplication, sessionManager: GitHubSessionManager) { - application.shortcutItems = generateItems(sessionManager: sessionManager) + static func configure(sessionUsernames: [String]) { + UIApplication.shared.shortcutItems = generateItems(sessionUsernames: sessionUsernames) } - static func handle( - route: Route, - sessionManager: GitHubSessionManager, - navigationManager: RootNavigationManager - ) -> Bool { - switch route { - case .tab(let tab): - navigationManager.selectViewController(atTab: tab) - return true - case .switchAccount(let sessionIndex): - if let index = sessionIndex { - let session = sessionManager.userSessions[index] - sessionManager.focus(session, dismiss: false) - } - return true - } - } - - private static func generateItems(sessionManager: GitHubSessionManager) -> [UIApplicationShortcutItem] { + private static func generateItems(sessionUsernames: [String]) -> [UIApplicationShortcutItem] { var items: [UIApplicationShortcutItem] = [] // Search @@ -59,19 +41,17 @@ struct ShortcutHandler { items.append(bookmarkItem) // Switchuser - if sessionManager.userSessions.count > 1 { - let userSession = sessionManager.userSessions[1] - if let username = userSession.username { - let userIcon = UIApplicationShortcutIcon(templateImageName: "organization") - let userItem = UIApplicationShortcutItem( - type: Items.switchAccount.rawValue, - localizedTitle: NSLocalizedString("Switch Account", comment: ""), - localizedSubtitle: username, - icon: userIcon, - userInfo: ["sessionIndex": 1] - ) - items.append(userItem) - } + if sessionUsernames.count > 1 { + let username = sessionUsernames[1] + let userIcon = UIApplicationShortcutIcon(templateImageName: "organization") + let userItem = UIApplicationShortcutItem( + type: Items.switchAccount.rawValue, + localizedTitle: NSLocalizedString("Switch Account", comment: ""), + localizedSubtitle: username, + icon: userIcon, + userInfo: ["sessionIndex": 1] + ) + items.append(userItem) } return items diff --git a/Classes/View Controllers/RootViewControllers.swift b/Classes/View Controllers/RootViewControllers.swift index c7444e54b..49165520e 100644 --- a/Classes/View Controllers/RootViewControllers.swift +++ b/Classes/View Controllers/RootViewControllers.swift @@ -10,24 +10,19 @@ import UIKit import GitHubSession func newSettingsRootViewController( - sessionManager: GitHubSessionManager, - client: GithubClient, - rootNavigationManager: RootNavigationManager - ) -> UIViewController { - guard let controller = UIStoryboard(name: "Settings", bundle: nil).instantiateInitialViewController() + sessionManager: GitHubSessionManager + ) -> UINavigationController { + guard let nav = UIStoryboard(name: "Settings", bundle: nil).instantiateInitialViewController() as? UINavigationController else { fatalError("Could not unpack settings storyboard") } - if let nav = controller as? UINavigationController, - let first = nav.viewControllers.first as? SettingsViewController { - first.client = client + if let first = nav.viewControllers.first as? SettingsViewController { first.sessionManager = sessionManager - first.rootNavigationManager = rootNavigationManager nav.tabBarItem.title = NSLocalizedString("Settings", comment: "") nav.tabBarItem.image = UIImage(named: "tab-gear")?.withRenderingMode(.alwaysOriginal) nav.tabBarItem.selectedImage = UIImage(named: "tab-gear-selected")?.withRenderingMode(.alwaysOriginal) } - return controller + return nav } func newNotificationsRootViewController(client: GithubClient) -> UIViewController { diff --git a/Freetime.xcodeproj/project.pbxproj b/Freetime.xcodeproj/project.pbxproj index c77c17c29..2bd2244c0 100644 --- a/Freetime.xcodeproj/project.pbxproj +++ b/Freetime.xcodeproj/project.pbxproj @@ -22,6 +22,9 @@ 290744BC1F268D8300FD9E48 /* UserAutocomplete.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290744BB1F268D8300FD9E48 /* UserAutocomplete.swift */; }; 290744BE1F268F8700FD9E48 /* UserAutocomplete+GraphQL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290744BD1F268F8700FD9E48 /* UserAutocomplete+GraphQL.swift */; }; 2908C5891F6F3EB00071C39D /* IssueLocalReaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2908C5881F6F3EB00071C39D /* IssueLocalReaction.swift */; }; + 290CA7642169799600DE04F8 /* AppController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290CA7632169799600DE04F8 /* AppController.swift */; }; + 290CA76621697A7900DE04F8 /* AppSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290CA76521697A7900DE04F8 /* AppSplitViewController.swift */; }; + 290CA768216984F000DE04F8 /* Client+GithubUserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290CA767216984F000DE04F8 /* Client+GithubUserSession.swift */; }; 290D2A3D1F044CB20082E6CC /* UIViewController+SmartDeselection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290D2A3C1F044CB20082E6CC /* UIViewController+SmartDeselection.swift */; }; 290D2A421F04D3470082E6CC /* IssueStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290D2A411F04D3470082E6CC /* IssueStatus.swift */; }; 290EF56A1F06A821006A2160 /* Notification+NotificationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290EF5691F06A7E1006A2160 /* Notification+NotificationViewModel.swift */; }; @@ -117,8 +120,6 @@ 2930F2731F8A27750082BA26 /* WidthCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2930F2721F8A27750082BA26 /* WidthCache.swift */; }; 29316DB51ECC7DEB007CAE3F /* ButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29316DB41ECC7DEB007CAE3F /* ButtonCell.swift */; }; 29316DBF1ECC95DB007CAE3F /* RootViewControllers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29316DBE1ECC95DB007CAE3F /* RootViewControllers.swift */; }; - 29316DC31ECC981D007CAE3F /* RootNavigationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29316DC21ECC981D007CAE3F /* RootNavigationManager.swift */; }; - 29316DC51ECC9841007CAE3F /* Alamofire+GithubAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29316DC41ECC9841007CAE3F /* Alamofire+GithubAPI.swift */; }; 293189281F5391F700EF0911 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 293189271F5391F700EF0911 /* Result.swift */; }; 2931892B1F5397E400EF0911 /* IssueMilestoneCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2931892A1F5397E400EF0911 /* IssueMilestoneCell.swift */; }; 2931892F1F539C0E00EF0911 /* IssueMilestoneSectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2931892E1F539C0E00EF0911 /* IssueMilestoneSectionController.swift */; }; @@ -544,6 +545,9 @@ 290744BB1F268D8300FD9E48 /* UserAutocomplete.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserAutocomplete.swift; sourceTree = ""; }; 290744BD1F268F8700FD9E48 /* UserAutocomplete+GraphQL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserAutocomplete+GraphQL.swift"; sourceTree = ""; }; 2908C5881F6F3EB00071C39D /* IssueLocalReaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueLocalReaction.swift; sourceTree = ""; }; + 290CA7632169799600DE04F8 /* AppController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppController.swift; sourceTree = ""; }; + 290CA76521697A7900DE04F8 /* AppSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSplitViewController.swift; sourceTree = ""; }; + 290CA767216984F000DE04F8 /* Client+GithubUserSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+GithubUserSession.swift"; sourceTree = ""; }; 290D2A3C1F044CB20082E6CC /* UIViewController+SmartDeselection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+SmartDeselection.swift"; sourceTree = ""; }; 290D2A411F04D3470082E6CC /* IssueStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueStatus.swift; sourceTree = ""; }; 290EF5691F06A7E1006A2160 /* Notification+NotificationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+NotificationViewModel.swift"; sourceTree = ""; }; @@ -640,8 +644,6 @@ 2930F2721F8A27750082BA26 /* WidthCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidthCache.swift; sourceTree = ""; }; 29316DB41ECC7DEB007CAE3F /* ButtonCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ButtonCell.swift; sourceTree = ""; }; 29316DBE1ECC95DB007CAE3F /* RootViewControllers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootViewControllers.swift; sourceTree = ""; }; - 29316DC21ECC981D007CAE3F /* RootNavigationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootNavigationManager.swift; sourceTree = ""; }; - 29316DC41ECC9841007CAE3F /* Alamofire+GithubAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Alamofire+GithubAPI.swift"; sourceTree = ""; }; 293189271F5391F700EF0911 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; 2931892A1F5397E400EF0911 /* IssueMilestoneCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueMilestoneCell.swift; sourceTree = ""; }; 2931892E1F539C0E00EF0911 /* IssueMilestoneSectionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueMilestoneSectionController.swift; sourceTree = ""; }; @@ -1080,6 +1082,15 @@ path = Autocomplete; sourceTree = ""; }; + 290CA7622169798B00DE04F8 /* AppRouter */ = { + isa = PBXGroup; + children = ( + 290CA7632169799600DE04F8 /* AppController.swift */, + 290CA76521697A7900DE04F8 /* AppSplitViewController.swift */, + ); + path = AppRouter; + sourceTree = ""; + }; 291929431F3EAAAF0012067B /* Files */ = { isa = PBXGroup; children = ( @@ -1645,10 +1656,11 @@ 297AE8651EC0D5C100B44A1F /* Systems */ = { isa = PBXGroup; children = ( - 29316DC41ECC9841007CAE3F /* Alamofire+GithubAPI.swift */, 297AE8671EC0D5C200B44A1F /* AppDelegate.swift */, + 290CA7622169798B00DE04F8 /* AppRouter */, 290744B01F250A1D00FD9E48 /* Autocomplete */, 291929661F3FF9C50012067B /* BadgeNotifications.swift */, + 290CA767216984F000DE04F8 /* Client+GithubUserSession.swift */, DC78570F1F97F546009BADDA /* Debouncer.swift */, 29C167791ECA14F700439D62 /* Feed.swift */, 54AD5E8D1F24D953004A4BD6 /* FeedSelectionProviding.swift */, @@ -1666,7 +1678,6 @@ 292CD3D31F0DC12100D3D57B /* PhotoViewHandler.swift */, 2980033A1F51E82400BE90F4 /* Rating */, 293189271F5391F700EF0911 /* Result.swift */, - 29316DC21ECC981D007CAE3F /* RootNavigationManager.swift */, 65A3152A2044376D0074E3B6 /* Route.swift */, 294A3D751FB29843000E81A4 /* ScrollViewKeyboardAdjuster.swift */, 9870B9021FC73EE70009719C /* Secrets.swift */, @@ -2712,7 +2723,6 @@ D8BAD0601FDA0A1A00C41071 /* LabelListCell.swift in Sources */, 290744A91F24D2DA00FD9E48 /* AddCommentClient.swift in Sources */, 29DAA7AD20202A320029277A /* PullRequestReviewReplyCell.swift in Sources */, - 29316DC51ECC9841007CAE3F /* Alamofire+GithubAPI.swift in Sources */, 75A0ACF51F79A82D0062D99A /* AlertAction.swift in Sources */, 75468F7A1F7AFBC800F2BC19 /* AlertActionBuilder.swift in Sources */, 292FCB2C1EE054900026635E /* API.swift in Sources */, @@ -2767,6 +2777,7 @@ 296B4E311F7C805600C16887 /* GraphQLIDDecode.swift in Sources */, 294B11241F7B37D300E04F2D /* ImageCellHeightCache.swift in Sources */, 294563EC1EE5012100DBCD35 /* Issue+IssueType.swift in Sources */, + 290CA7642169799600DE04F8 /* AppController.swift in Sources */, 29AF1E821F8AAB2B0008A0EF /* EditCommentViewController.swift in Sources */, 297403D71F1851C000ABA95A /* IssueAssigneeAvatarCell.swift in Sources */, 297403D11F184F8D00ABA95A /* IssueAssigneesModel.swift in Sources */, @@ -2815,6 +2826,7 @@ 2974069D1F0EDEAD003A6BFB /* IssueCommentTableCell.swift in Sources */, 29764C141FDC4DB60095FF95 /* SettingsLabel.swift in Sources */, 2974069F1F0EDED3003A6BFB /* IssueCommentTableCollectionCell.swift in Sources */, + 290CA768216984F000DE04F8 /* Client+GithubUserSession.swift in Sources */, 2974069B1F0EDC7C003A6BFB /* IssueCommentTableModel.swift in Sources */, 292FCB0A1EDFCC510026635E /* IssueCommentTextCell.swift in Sources */, 29BE40D32070786400A79C86 /* CMarkParsing.swift in Sources */, @@ -2963,6 +2975,7 @@ BDB6AA68215FBC35009BB73C /* RepositoryBranchesSectionController.swift in Sources */, BDB6AA6B215FBC35009BB73C /* GitHubClient+RepositoryBranches.swift in Sources */, 292CD3D41F0DC12100D3D57B /* PhotoViewHandler.swift in Sources */, + 290CA76621697A7900DE04F8 /* AppSplitViewController.swift in Sources */, 294563EE1EE5012900DBCD35 /* PullRequest+IssueType.swift in Sources */, 298003401F51E93B00BE90F4 /* RatingCell.swift in Sources */, 29351EA22079663800FF8C17 /* String+Shortlink.swift in Sources */, @@ -3006,7 +3019,6 @@ 2963A9341EE2118E0066509C /* ResponderButton.swift in Sources */, BDB6AA69215FBC35009BB73C /* RepositoryBranchesViewController.swift in Sources */, 293189281F5391F700EF0911 /* Result.swift in Sources */, - 29316DC31ECC981D007CAE3F /* RootNavigationManager.swift in Sources */, 295B51421FC26B8100C3993B /* PeopleCell.swift in Sources */, 29316DBF1ECC95DB007CAE3F /* RootViewControllers.swift in Sources */, 29DA1E791F5DEE8F0050C64B /* SearchLoadingView.swift in Sources */, diff --git a/FreetimeTests/NetworkingURLPathTests.swift b/FreetimeTests/NetworkingURLPathTests.swift index 51c2e957b..9e432bafa 100644 --- a/FreetimeTests/NetworkingURLPathTests.swift +++ b/FreetimeTests/NetworkingURLPathTests.swift @@ -23,7 +23,7 @@ class UrlPathComponents: XCTestCase { add: true, people: []) - let githubclient = newGithubClient() + let githubclient = GithubClient() let promise = expectation(description: "completion handler invoked") var requestResponse: GitHubAPI.Result>? @@ -56,7 +56,7 @@ class UrlPathComponents: XCTestCase { add: true, people: []) - let githubclient = newGithubClient() + let githubclient = GithubClient() let promise = expectation(description: "completion handler invoked") var requestResponse: GitHubAPI.Result>? diff --git a/FreetimeWatch/Assets.xcassets/AppIcon.appiconset/Contents.json b/FreetimeWatch/Assets.xcassets/AppIcon.appiconset/Contents.json index 8dbdc5634..f41651f56 100644 --- a/FreetimeWatch/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/FreetimeWatch/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -41,10 +41,16 @@ { "size" : "44x44", "idiom" : "watch", - "filename" : "Icon-HomeScreen-42mm-44x44@2x.png", "scale" : "2x", - "role" : "longLook", - "subtype" : "42mm" + "role" : "appLauncher", + "subtype" : "40mm" + }, + { + "size" : "50x50", + "idiom" : "watch", + "scale" : "2x", + "role" : "appLauncher", + "subtype" : "44mm" }, { "size" : "86x86", @@ -62,11 +68,26 @@ "role" : "quickLook", "subtype" : "42mm" }, + { + "size" : "108x108", + "idiom" : "watch", + "scale" : "2x", + "role" : "quickLook", + "subtype" : "44mm" + }, { "size" : "1024x1024", "idiom" : "watch-marketing", "filename" : "Icon-AppStore-1024x1024.png", "scale" : "1x" + }, + { + "size" : "44x44", + "idiom" : "watch", + "filename" : "Icon-HomeScreen-42mm-44x44@2x.png", + "scale" : "2x", + "role" : "longLook", + "subtype" : "42mm" } ], "info" : { diff --git a/Local Pods/GitHubSession/GitHubSession/GitHubSessionManager.swift b/Local Pods/GitHubSession/GitHubSession/GitHubSessionManager.swift index 1957a63f5..b7e415113 100644 --- a/Local Pods/GitHubSession/GitHubSession/GitHubSessionManager.swift +++ b/Local Pods/GitHubSession/GitHubSession/GitHubSessionManager.swift @@ -9,7 +9,6 @@ import Foundation public protocol GitHubSessionListener: class { - func didReceiveRedirect(manager: GitHubSessionManager, code: String) func didFocus(manager: GitHubSessionManager, userSession: GitHubUserSession, dismiss: Bool) func didLogout(manager: GitHubSessionManager) } @@ -111,14 +110,4 @@ public class GitHubSessionManager: NSObject { } } - public func receivedCodeRedirect(url: URL) { - guard let items = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems, - let index = items.index(where: { $0.name == "code" }), - let code = items[index].value - else { return } - for wrapper in listeners { - wrapper.listener?.didReceiveRedirect(manager: self, code: code) - } - } - } diff --git a/Resources/Base.lproj/Main.storyboard b/Resources/Base.lproj/Main.storyboard index 94f031458..bd7706501 100644 --- a/Resources/Base.lproj/Main.storyboard +++ b/Resources/Base.lproj/Main.storyboard @@ -1,16 +1,18 @@ - + - + + + @@ -25,7 +27,6 @@ - @@ -92,10 +93,10 @@ - + - +