mirror of
https://github.com/zhigang1992/GitHawk.git
synced 2026-04-24 04:05:16 +08:00
Huge refactor of AppDelegate and authentication routing (#2238)
* refactor with new app controller, replace root nav mgr * finish refactoring out root nav mgr * remove root mgr
This commit is contained in:
@@ -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) {}
|
||||
|
||||
}
|
||||
|
||||
@@ -116,7 +116,6 @@ final class SettingsAccountsViewController: UITableViewController, GitHubSession
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
func didReceiveRedirect(manager: GitHubSessionManager, code: String) {}
|
||||
func didLogout(manager: GitHubSessionManager) {}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
appController.performFetch(application: application, with: 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)
|
||||
}
|
||||
|
||||
func didReceiveRedirect(manager: GitHubSessionManager, code: String) {}
|
||||
func didLogout(manager: GitHubSessionManager) {}
|
||||
}
|
||||
|
||||
142
Classes/Systems/AppRouter/AppController.swift
Normal file
142
Classes/Systems/AppRouter/AppController.swift
Normal file
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
46
Classes/Systems/AppRouter/AppSplitViewController.swift
Normal file
46
Classes/Systems/AppRouter/AppSplitViewController.swift
Normal file
@@ -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()]
|
||||
}
|
||||
|
||||
}
|
||||
28
Classes/Systems/Client+GithubUserSession.swift
Normal file
28
Classes/Systems/Client+GithubUserSession.swift
Normal file
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 = "<group>"; };
|
||||
290744BD1F268F8700FD9E48 /* UserAutocomplete+GraphQL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserAutocomplete+GraphQL.swift"; sourceTree = "<group>"; };
|
||||
2908C5881F6F3EB00071C39D /* IssueLocalReaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueLocalReaction.swift; sourceTree = "<group>"; };
|
||||
290CA7632169799600DE04F8 /* AppController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppController.swift; sourceTree = "<group>"; };
|
||||
290CA76521697A7900DE04F8 /* AppSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSplitViewController.swift; sourceTree = "<group>"; };
|
||||
290CA767216984F000DE04F8 /* Client+GithubUserSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+GithubUserSession.swift"; sourceTree = "<group>"; };
|
||||
290D2A3C1F044CB20082E6CC /* UIViewController+SmartDeselection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+SmartDeselection.swift"; sourceTree = "<group>"; };
|
||||
290D2A411F04D3470082E6CC /* IssueStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueStatus.swift; sourceTree = "<group>"; };
|
||||
290EF5691F06A7E1006A2160 /* Notification+NotificationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+NotificationViewModel.swift"; sourceTree = "<group>"; };
|
||||
@@ -640,8 +644,6 @@
|
||||
2930F2721F8A27750082BA26 /* WidthCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidthCache.swift; sourceTree = "<group>"; };
|
||||
29316DB41ECC7DEB007CAE3F /* ButtonCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ButtonCell.swift; sourceTree = "<group>"; };
|
||||
29316DBE1ECC95DB007CAE3F /* RootViewControllers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootViewControllers.swift; sourceTree = "<group>"; };
|
||||
29316DC21ECC981D007CAE3F /* RootNavigationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootNavigationManager.swift; sourceTree = "<group>"; };
|
||||
29316DC41ECC9841007CAE3F /* Alamofire+GithubAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Alamofire+GithubAPI.swift"; sourceTree = "<group>"; };
|
||||
293189271F5391F700EF0911 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
|
||||
2931892A1F5397E400EF0911 /* IssueMilestoneCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueMilestoneCell.swift; sourceTree = "<group>"; };
|
||||
2931892E1F539C0E00EF0911 /* IssueMilestoneSectionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueMilestoneSectionController.swift; sourceTree = "<group>"; };
|
||||
@@ -1080,6 +1082,15 @@
|
||||
path = Autocomplete;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
290CA7622169798B00DE04F8 /* AppRouter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
290CA7632169799600DE04F8 /* AppController.swift */,
|
||||
290CA76521697A7900DE04F8 /* AppSplitViewController.swift */,
|
||||
);
|
||||
path = AppRouter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
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 */,
|
||||
|
||||
@@ -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<V3StatusCodeResponse<V3StatusCode200or201>>?
|
||||
|
||||
@@ -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<V3StatusCodeResponse<V3StatusCode200or201>>?
|
||||
|
||||
|
||||
@@ -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" : {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="EPN-4V-9Hj">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="EPN-4V-9Hj">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Notifications-->
|
||||
<scene sceneID="5Pm-pl-ZQT">
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="V24-0g-MmQ" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<navigationController id="AKn-8S-pzd" sceneMemberID="viewController">
|
||||
<tabBarItem key="tabBarItem" title="Notifications" image="inbox" id="KQ4-AC-rKh"/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="crz-Uj-ajM">
|
||||
@@ -25,7 +27,6 @@
|
||||
<segue destination="icu-Mt-euN" kind="relationship" relationship="rootViewController" id="DU7-Bf-pgd"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="V24-0g-MmQ" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="268" y="536.58170914542734"/>
|
||||
</scene>
|
||||
@@ -92,10 +93,10 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="167" y="1321"/>
|
||||
</scene>
|
||||
<!--Split View Controller-->
|
||||
<!--App Split View Controller-->
|
||||
<scene sceneID="0c8-No-DSu">
|
||||
<objects>
|
||||
<splitViewController id="EPN-4V-9Hj" sceneMemberID="viewController">
|
||||
<splitViewController id="EPN-4V-9Hj" customClass="AppSplitViewController" customModule="Freetime" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<connections>
|
||||
<segue destination="f5k-7G-hod" kind="relationship" relationship="masterViewController" id="aFN-Se-rpt"/>
|
||||
<segue destination="D4E-Yy-PTN" kind="relationship" relationship="detailViewController" id="gsk-8r-3gM"/>
|
||||
|
||||
Reference in New Issue
Block a user