new routing library and refactor shortcuts (#2241)

This commit is contained in:
Ryan Nystrom
2018-10-07 23:46:57 -04:00
committed by GitHub
parent c45b35e721
commit 64f920a3f0
16 changed files with 294 additions and 107 deletions

View File

@@ -104,6 +104,10 @@ SearchResultSectionControllerDelegate {
}
}
func searchBarBecomeFirstResponder() {
searchBar.becomeFirstResponder()
}
// MARK: Data Loading/Paging
private func update(animated: Bool) {

View File

@@ -23,6 +23,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
appController.appDidFinishLaunching(with: window)
appController.setupRoutes()
// setup fabric
Fabric.with([Crashlytics.self])
@@ -49,11 +50,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
guard let route = Route(shortcutItem: shortcutItem) else {
completionHandler(false)
return
}
completionHandler(appController.handle(route: route))
appController.handle(path: shortcutItem.type, params: shortcutItem.params)
}
func applicationDidBecomeActive(_ application: UIApplication) {

View File

@@ -0,0 +1,19 @@
//
// AppController+SetupRoutes.swift
// Freetime
//
// Created by Ryan Nystrom on 10/7/18.
// Copyright © 2018 Ryan Nystrom. All rights reserved.
//
import Foundation
extension AppController {
func setupRoutes() {
register(route: BookmarkShortcutRoute.self)
register(route: SwitchAccountShortcutRoute.self)
register(route: SearchShortcutRoute.self)
}
}

View File

@@ -18,6 +18,7 @@ final class AppController: LoginSplashViewControllerDelegate, GitHubSessionListe
private var appClient: GithubClient?
private var settingsNavigationController: UINavigationController?
private var watchAppSync: WatchAppUserSessionSync?
private var routes = [String: (Routable & RoutePerformable).Type]()
init() {
sessionManager.addListener(listener: self)
@@ -51,18 +52,30 @@ final class AppController: LoginSplashViewControllerDelegate, GitHubSessionListe
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
@discardableResult
func handle(url: URL) -> Bool {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
else { return false }
var params = [String: String]()
for item in components.queryItems ?? [] {
params[item.name] = item.value
}
return handle(path: url.path, params: params)
}
@discardableResult
func handle(path: String, params: [String: String]) -> Bool {
guard let routeType = routes[path],
let route = routeType.from(params: params)
else { return false }
return route.perform(
sessionManager: sessionManager,
splitViewController: splitViewController
)
}
func register<T: Routable & RoutePerformable>(route: T.Type) {
routes[T.path] = T.self
}
private func resetWatchSync(userSession: GitHubUserSession) {

View File

@@ -20,18 +20,14 @@ final class AppSplitViewController: UISplitViewController {
preferredDisplayMode = .allVisible
}
private var detailNavigationController: UINavigationController? {
var detailNavigationController: UINavigationController? {
return viewControllers.last as? UINavigationController
}
private var masterTabBarController: UITabBarController? {
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

View File

@@ -0,0 +1,28 @@
//
// BookmarkShortcutRoute.swift
// Freetime
//
// Created by Ryan Nystrom on 10/7/18.
// Copyright © 2018 Ryan Nystrom. All rights reserved.
//
import Foundation
import GitHubSession
struct BookmarkShortcutRoute: Routable {
static func from(params: [String : String]) -> BookmarkShortcutRoute? {
return BookmarkShortcutRoute()
}
static var path: String {
return "com.githawk.shortcut.bookmark"
}
}
extension BookmarkShortcutRoute: RoutePerformable {
func perform(
sessionManager: GitHubSessionManager,
splitViewController: AppSplitViewController
) -> Bool {
return splitViewController.masterTabBarController?.selectTab(of: BookmarkViewController.self) != nil
}
}

View File

@@ -0,0 +1,19 @@
//
// Routable.swift
// Freetime
//
// Created by Ryan Nystrom on 10/7/18.
// Copyright © 2018 Ryan Nystrom. All rights reserved.
//
import Foundation
protocol Routable {
static func from(params: [String: String]) -> Self?
var encoded: [String: String] { get }
static var path: String { get }
}
extension Routable {
var encoded: [String: String] { return [:] }
}

View File

@@ -0,0 +1,18 @@
//
// RoutePerformable.swift
// Freetime
//
// Created by Ryan Nystrom on 10/7/18.
// Copyright © 2018 Ryan Nystrom. All rights reserved.
//
import Foundation
import GitHubSession
protocol RoutePerformable {
@discardableResult
func perform(
sessionManager: GitHubSessionManager,
splitViewController: AppSplitViewController
) -> Bool
}

View File

@@ -0,0 +1,31 @@
//
// SearchShortcutRoute.swift
// Freetime
//
// Created by Ryan Nystrom on 10/7/18.
// Copyright © 2018 Ryan Nystrom. All rights reserved.
//
import UIKit
import GitHubSession
struct SearchShortcutRoute: Routable {
static func from(params: [String : String]) -> SearchShortcutRoute? {
return SearchShortcutRoute()
}
static var path: String {
return "com.githawk.shortcut.search"
}
}
extension SearchShortcutRoute: RoutePerformable {
func perform(
sessionManager: GitHubSessionManager,
splitViewController: AppSplitViewController
) -> Bool {
guard let controller = splitViewController.masterTabBarController?.selectTab(of: SearchViewController.self)
else { return false }
controller.searchBarBecomeFirstResponder()
return true
}
}

View File

@@ -0,0 +1,38 @@
//
// SwitchAccountShortcutRoute.swift
// Freetime
//
// Created by Ryan Nystrom on 10/7/18.
// Copyright © 2018 Ryan Nystrom. All rights reserved.
//
import Foundation
import GitHubSession
struct SwitchAccountShortcutRoute: Routable {
let username: String
static func from(params: [String : String]) -> SwitchAccountShortcutRoute? {
guard let username = params["username"] else { return nil }
return SwitchAccountShortcutRoute(username: username)
}
var encoded: [String : String] {
return ["username": username]
}
static var path: String {
return "com.githawk.shortcut.switch"
}
}
extension SwitchAccountShortcutRoute: RoutePerformable {
func perform(
sessionManager: GitHubSessionManager,
splitViewController: AppSplitViewController
) -> Bool {
let userSessions = sessionManager.userSessions
guard let needle = userSessions.first(where: { username == $0.username })
else { return false }
sessionManager.focus(needle, dismiss: false)
splitViewController.masterTabBarController?.selectTab(of: NotificationsViewController.self)
return true
}
}

View File

@@ -1,23 +0,0 @@
//
// Navigation.swift
// Freetime
//
// Created by Rizwan on 26/02/18.
// Copyright © 2018 Ryan Nystrom. All rights reserved.
//
import Foundation
enum Route {
case tab(TabBarController.Tab)
case switchAccount(sessionIndex: Int?)
init?(shortcutItem: UIApplicationShortcutItem) {
guard let itemType = ShortcutHandler.Items(rawValue: shortcutItem.type) else { return nil }
switch itemType {
case .search: self = .tab(.search)
case .bookmarks: self = .tab(.bookmarks)
case .switchAccount: self = .switchAccount(sessionIndex: shortcutItem.userInfo?["sessionIndex"] as? Int)
}
}
}

View File

@@ -9,52 +9,67 @@
import Foundation
import GitHubSession
struct ShortcutHandler {
extension UIApplicationShortcutItem {
enum Items: String {
case search
case bookmarks
case switchAccount
var params: [String: String] {
var params = [String: String]()
userInfo?.forEach {
if let value = $1 as? String {
params[$0] = value
}
}
return params
}
static func from<T: Routable>(
route: T,
localizedTitle: String,
localizedSubtitle: String? = nil,
icon: UIApplicationShortcutIcon? = nil
) -> UIApplicationShortcutItem {
return UIApplicationShortcutItem(
type: T.path,
localizedTitle: localizedTitle,
localizedSubtitle: localizedSubtitle,
icon: icon,
userInfo: route.encoded
)
}
}
struct ShortcutHandler {
static func configure(sessionUsernames: [String]) {
UIApplication.shared.shortcutItems = generateItems(sessionUsernames: sessionUsernames)
}
private static func generateItems(sessionUsernames: [String]) -> [UIApplicationShortcutItem] {
var items: [UIApplicationShortcutItem] = []
var items = [
UIApplicationShortcutItem.from(
route: SearchShortcutRoute(),
localizedTitle: Constants.Strings.search,
icon: UIApplicationShortcutIcon(templateImageName: "search")
),
UIApplicationShortcutItem.from(
route: BookmarkShortcutRoute(),
localizedTitle: Constants.Strings.bookmarks,
icon: UIApplicationShortcutIcon(templateImageName: "bookmark")
)
]
// Search
let searchIcon = UIApplicationShortcutIcon(templateImageName: Items.search.rawValue)
let searchItem = UIApplicationShortcutItem(type: Items.search.rawValue,
localizedTitle: Constants.Strings.search,
localizedSubtitle: nil,
icon: searchIcon)
items.append(searchItem)
// Bookmarks
let bookmarkIcon = UIApplicationShortcutIcon(templateImageName: "bookmark")
let bookmarkItem = UIApplicationShortcutItem(type: Items.bookmarks.rawValue,
localizedTitle: Constants.Strings.bookmarks,
localizedSubtitle: nil,
icon: bookmarkIcon)
items.append(bookmarkItem)
// Switchuser
if sessionUsernames.count > 1 {
let username = sessionUsernames[1]
let userIcon = UIApplicationShortcutIcon(templateImageName: "organization")
let userItem = UIApplicationShortcutItem(
type: Items.switchAccount.rawValue,
items.append(UIApplicationShortcutItem.from(
route: SwitchAccountShortcutRoute(username: username),
localizedTitle: NSLocalizedString("Switch Account", comment: ""),
localizedSubtitle: username,
icon: userIcon,
userInfo: ["sessionIndex": 1]
)
items.append(userItem)
icon: UIApplicationShortcutIcon(templateImageName: "organization")
))
}
return items
}
}

View File

@@ -1,22 +0,0 @@
//
// TabBarController.swift
// Freetime
//
// Created by Rizwan on 26/02/18.
// Copyright © 2018 Ryan Nystrom. All rights reserved.
//
import Foundation
final class TabBarController: UITabBarController {
enum Tab: Int {
case inbox = 0
case search = 1
case bookmarks = 2
case settings = 3
}
func showTab(_ tab: Tab) {
selectedIndex = tab.rawValue
}
}

View File

@@ -0,0 +1,34 @@
//
// UITabBarController+SelectType.swift
// Freetime
//
// Created by Ryan Nystrom on 10/7/18.
// Copyright © 2018 Ryan Nystrom. All rights reserved.
//
import UIKit
extension UITabBarController {
@discardableResult
func selectTab<T: UIViewController>(of type: T.Type) -> T? {
for viewController in viewControllers ?? [] {
let checkController: T?
let selectController: UIViewController
if let navigationController = viewController as? UINavigationController,
let topController = navigationController.topViewController {
checkController = topController as? T
selectController = navigationController
} else {
checkController = viewController as? T
selectController = viewController
}
if checkController != nil {
selectedViewController = selectController
return checkController
}
}
return nil
}
}

View File

@@ -25,6 +25,13 @@
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 */; };
290CA76A216AC82700DE04F8 /* SearchShortcutRoute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290CA769216AC82700DE04F8 /* SearchShortcutRoute.swift */; };
290CA76E216AE8FA00DE04F8 /* Routable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290CA76D216AE8FA00DE04F8 /* Routable.swift */; };
290CA770216AE91300DE04F8 /* UITabBarController+SelectType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290CA76F216AE91300DE04F8 /* UITabBarController+SelectType.swift */; };
290CA772216AE93E00DE04F8 /* SwitchAccountShortcutRoute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290CA771216AE93E00DE04F8 /* SwitchAccountShortcutRoute.swift */; };
290CA774216AE94D00DE04F8 /* BookmarkShortcutRoute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290CA773216AE94D00DE04F8 /* BookmarkShortcutRoute.swift */; };
290CA778216AFAE600DE04F8 /* RoutePerformable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290CA777216AFAE600DE04F8 /* RoutePerformable.swift */; };
290CA77A216AFC1300DE04F8 /* AppController+SetupRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290CA779216AFC1300DE04F8 /* AppController+SetupRoutes.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 */; };
@@ -401,8 +408,6 @@
49FFF4341F9FC83200335568 /* HapticFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49FFF4331F9FC83200335568 /* HapticFeedback.swift */; };
54AD5E8E1F24D953004A4BD6 /* FeedSelectionProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54AD5E8D1F24D953004A4BD6 /* FeedSelectionProviding.swift */; };
5DB4DD471FC5C10000DF7ABF /* Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB4DD461FC5C10000DF7ABF /* Accessibility.swift */; };
65A315292044369D0074E3B6 /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65A315282044369D0074E3B6 /* TabBarController.swift */; };
65A3152B2044376D0074E3B6 /* Route.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65A3152A2044376D0074E3B6 /* Route.swift */; };
7509865A2048959D00D1E37A /* RepositoryWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 750986592048959D00D1E37A /* RepositoryWebViewController.swift */; };
754488B11F7ADF8D0032D08C /* UIAlertController+Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 754488B01F7ADF8D0032D08C /* UIAlertController+Action.swift */; };
75468F7A1F7AFBC800F2BC19 /* AlertActionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75468F791F7AFBC800F2BC19 /* AlertActionBuilder.swift */; };
@@ -548,6 +553,13 @@
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>"; };
290CA769216AC82700DE04F8 /* SearchShortcutRoute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchShortcutRoute.swift; sourceTree = "<group>"; };
290CA76D216AE8FA00DE04F8 /* Routable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Routable.swift; sourceTree = "<group>"; };
290CA76F216AE91300DE04F8 /* UITabBarController+SelectType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITabBarController+SelectType.swift"; sourceTree = "<group>"; };
290CA771216AE93E00DE04F8 /* SwitchAccountShortcutRoute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchAccountShortcutRoute.swift; sourceTree = "<group>"; };
290CA773216AE94D00DE04F8 /* BookmarkShortcutRoute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkShortcutRoute.swift; sourceTree = "<group>"; };
290CA777216AFAE600DE04F8 /* RoutePerformable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutePerformable.swift; sourceTree = "<group>"; };
290CA779216AFC1300DE04F8 /* AppController+SetupRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppController+SetupRoutes.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>"; };
@@ -939,8 +951,6 @@
54AD5E8D1F24D953004A4BD6 /* FeedSelectionProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedSelectionProviding.swift; sourceTree = "<group>"; };
5DB4DD461FC5C10000DF7ABF /* Accessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Accessibility.swift; sourceTree = "<group>"; };
63A98647141C04CBCFCA7923 /* Pods-Freetime.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Freetime.release.xcconfig"; path = "Pods/Target Support Files/Pods-Freetime/Pods-Freetime.release.xcconfig"; sourceTree = "<group>"; };
65A315282044369D0074E3B6 /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = "<group>"; };
65A3152A2044376D0074E3B6 /* Route.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Route.swift; sourceTree = "<group>"; };
750986592048959D00D1E37A /* RepositoryWebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepositoryWebViewController.swift; sourceTree = "<group>"; };
754488B01F7ADF8D0032D08C /* UIAlertController+Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Action.swift"; sourceTree = "<group>"; };
75468F791F7AFBC800F2BC19 /* AlertActionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertActionBuilder.swift; sourceTree = "<group>"; };
@@ -1087,6 +1097,12 @@
children = (
290CA7632169799600DE04F8 /* AppController.swift */,
290CA76521697A7900DE04F8 /* AppSplitViewController.swift */,
290CA773216AE94D00DE04F8 /* BookmarkShortcutRoute.swift */,
290CA76D216AE8FA00DE04F8 /* Routable.swift */,
290CA779216AFC1300DE04F8 /* AppController+SetupRoutes.swift */,
290CA777216AFAE600DE04F8 /* RoutePerformable.swift */,
290CA769216AC82700DE04F8 /* SearchShortcutRoute.swift */,
290CA771216AE93E00DE04F8 /* SwitchAccountShortcutRoute.swift */,
);
path = AppRouter;
sourceTree = "<group>";
@@ -1678,7 +1694,6 @@
292CD3D31F0DC12100D3D57B /* PhotoViewHandler.swift */,
2980033A1F51E82400BE90F4 /* Rating */,
293189271F5391F700EF0911 /* Result.swift */,
65A3152A2044376D0074E3B6 /* Route.swift */,
294A3D751FB29843000E81A4 /* ScrollViewKeyboardAdjuster.swift */,
9870B9021FC73EE70009719C /* Secrets.swift */,
3E79A2FE1F8A7DA700E1126B /* ShortcutHandler.swift */,
@@ -1703,7 +1718,6 @@
29316DBE1ECC95DB007CAE3F /* RootViewControllers.swift */,
29C33FDA1F127DBB00EC8D40 /* SplitPlaceholderViewController.swift */,
29AC90E41F00A7C8000B80E4 /* SplitViewControllerDelegate.swift */,
65A315282044369D0074E3B6 /* TabBarController.swift */,
294B111F1F7B07DD00E04F2D /* TabBarControllerDelegate.swift */,
294B111D1F7B07BB00E04F2D /* TabNavRootViewControllerType.swift */,
29D548CA1FA27FE900F8E46F /* UINavigationItem+TitleSubtitle.swift */,
@@ -1721,6 +1735,7 @@
290D2A3C1F044CB20082E6CC /* UIViewController+SmartDeselection.swift */,
299F63E3205E1CAB0015D901 /* UIViewController+StyledTextViewCellDelegate.swift */,
4920F1A71F72E27200131E9D /* UIViewController+UserActivity.swift */,
290CA76F216AE91300DE04F8 /* UITabBarController+SelectType.swift */,
);
path = "View Controllers";
sourceTree = "<group>";
@@ -2759,6 +2774,7 @@
29F3A18620CBF99E00645CB7 /* NotificationModelController.swift in Sources */,
29EB1EEF1F425E5100A200B4 /* ForegroundHandler.swift in Sources */,
29C167741ECA0DBB00439D62 /* GithubAPIDateFormatter.swift in Sources */,
290CA770216AE91300DE04F8 /* UITabBarController+SelectType.swift in Sources */,
294434E11FB1F2DA00050C06 /* BookmarkNavigationController.swift in Sources */,
29BCA7FE212137E100753A3C /* IssueTargetBranchModel.swift in Sources */,
29EE44461F19D5C100B05ED3 /* GithubClient+Issues.swift in Sources */,
@@ -2875,6 +2891,7 @@
DCA5ED141FAEE8030072F074 /* Bookmark.swift in Sources */,
299F4A89204CEDDC004BA4F0 /* Client+AccessToken.swift in Sources */,
2931892F1F539C0E00EF0911 /* IssueMilestoneSectionController.swift in Sources */,
290CA77A216AFC1300DE04F8 /* AppController+SetupRoutes.swift in Sources */,
29BBD82920CAC7D5004D62FE /* NotificationViewModel.swift in Sources */,
299F63E2205DE1470015D901 /* UIView+DateDetails.swift in Sources */,
9870B9031FC73EE70009719C /* Secrets.swift in Sources */,
@@ -2903,6 +2920,7 @@
2928C78C1F15D80E0000D06D /* IssueRenamedSectionController.swift in Sources */,
2928C78E1F15DF1B0000D06D /* IssueRenamedString.swift in Sources */,
297A372E1F17018F0081C04E /* IssueRequestCell.swift in Sources */,
290CA774216AE94D00DE04F8 /* BookmarkShortcutRoute.swift in Sources */,
297A372C1F1700BC0081C04E /* IssueRequestModel.swift in Sources */,
297A37301F1704C10081C04E /* IssueRequestSectionController.swift in Sources */,
29F7F05C1F2A751B00F6075D /* IssueResult.swift in Sources */,
@@ -2923,7 +2941,6 @@
DCA5ED101FAEDF290072F074 /* BookmarkStore.swift in Sources */,
295C31CD1F0AA55400521CED /* IssueStatusEvent.swift in Sources */,
295840671EE89FE4007723C6 /* IssueStatusEventCell.swift in Sources */,
65A315292044369D0074E3B6 /* TabBarController.swift in Sources */,
295840651EE89F28007723C6 /* IssueStatusEventModel.swift in Sources */,
295840691EE8A328007723C6 /* IssueStatusEventSectionController.swift in Sources */,
29F3A18C20CD790F00645CB7 /* UIViewController+CommonActionItems.swift in Sources */,
@@ -2967,6 +2984,7 @@
29D548CB1FA27FE900F8E46F /* UINavigationItem+TitleSubtitle.swift in Sources */,
299A04A11FAE86B0003C2450 /* IssueReviewViewCommentsCell.swift in Sources */,
7BF2239D1F91056C006CC9A2 /* File+Filename.swift in Sources */,
290CA76E216AE8FA00DE04F8 /* Routable.swift in Sources */,
2924C18120D5B29800FCFCFF /* MilestonesViewController.swift in Sources */,
29A1950A1EC78B4800C3E289 /* NotificationType+Icon.swift in Sources */,
29A1950C1EC7901400C3E289 /* NotificationType.swift in Sources */,
@@ -3018,10 +3036,13 @@
2905AFAF1F7357FA0015AE32 /* RepositoryViewController.swift in Sources */,
2963A9341EE2118E0066509C /* ResponderButton.swift in Sources */,
BDB6AA69215FBC35009BB73C /* RepositoryBranchesViewController.swift in Sources */,
290CA772216AE93E00DE04F8 /* SwitchAccountShortcutRoute.swift in Sources */,
293189281F5391F700EF0911 /* Result.swift in Sources */,
290CA778216AFAE600DE04F8 /* RoutePerformable.swift in Sources */,
295B51421FC26B8100C3993B /* PeopleCell.swift in Sources */,
29316DBF1ECC95DB007CAE3F /* RootViewControllers.swift in Sources */,
29DA1E791F5DEE8F0050C64B /* SearchLoadingView.swift in Sources */,
290CA76A216AC82700DE04F8 /* SearchShortcutRoute.swift in Sources */,
986B872D1F2C846700AAB55C /* SearchNoResultsCell.swift in Sources */,
DC60C6DC1F99414E00241271 /* IsCancellationError.swift in Sources */,
29CC29311FF421DC006B6DE7 /* PullRequestReviewCommentsViewController.swift in Sources */,
@@ -3087,7 +3108,6 @@
2924C18320D5B2BF00FCFCFF /* MilestoneCell.swift in Sources */,
29C8F9AB208BF64D0075931C /* BaseListViewController2.swift in Sources */,
29AC90E51F00A7C8000B80E4 /* SplitViewControllerDelegate.swift in Sources */,
65A3152B2044376D0074E3B6 /* Route.swift in Sources */,
2977788A20B306F200F2AFC2 /* LabelLayoutManager.swift in Sources */,
29DB264A1FCA10A800C3D0C9 /* GithubHighlighting.swift in Sources */,
2950AB1D2083B1E400C6F19A /* EmptyLoadingView.swift in Sources */,

View File

@@ -12,7 +12,6 @@
<!--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">
@@ -27,6 +26,7 @@
<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>
@@ -109,7 +109,7 @@
<!--Tab Bar Controller-->
<scene sceneID="gc3-YD-4oz">
<objects>
<tabBarController automaticallyAdjustsScrollViewInsets="NO" id="f5k-7G-hod" customClass="TabBarController" customModule="Freetime" customModuleProvider="target" sceneMemberID="viewController">
<tabBarController automaticallyAdjustsScrollViewInsets="NO" id="f5k-7G-hod" sceneMemberID="viewController">
<toolbarItems/>
<tabBar key="tabBar" contentMode="scaleToFill" id="FXG-86-zX1">
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>