├── Router.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcuserdata
│ └── greg.xcuserdatad
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── Router.xcscheme
└── project.pbxproj
├── Router
├── ViewController.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── ViewController+RouterDelegate.swift
├── Info.plist
├── AppDelegate.swift
└── Router.swift
└── README.markdown
/Router.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Router.xcodeproj/xcuserdata/greg.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Router.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 37E6A4D31D773132000AE454
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Router/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Router
4 | //
5 | // Created by Greg Pierce on 8/31/16.
6 | // Copyright © 2016 Agile Tortoise. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 | }
16 |
17 | override func didReceiveMemoryWarning() {
18 | super.didReceiveMemoryWarning()
19 | }
20 |
21 | //MARK: Actions
22 |
23 | @IBAction func urlExampleButtonTapped(_ sender: AnyObject) {
24 | guard let url = URL(string: "router://x-callback-url/open?identifier=100") else { return }
25 |
26 | UIApplication.shared.open(url, options: [:]) { (didOpen) in
27 | //
28 | }
29 | }
30 |
31 | @IBAction func searchUrlExampleButtonTapped(_ sender: AnyObject) {
32 | guard let url = URL(string: "router://x-callback-url/search?query=test") else { return }
33 |
34 | UIApplication.shared.open(url, options: [:]) { (didOpen) in
35 | //
36 | }
37 | }
38 |
39 |
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | # Router
2 |
3 | There are an increasing array of ways an application on iOS may be launched with a context/command passed to it requiring routing within the app. These methods include:
4 |
5 | - NSUserActivity(s) generated by Handoff or Spotlight.
6 | - UIApplicationShortcutItems generated by 3D Touch shortcuts.
7 | - URL schemes (x-callback-url, univeral app links, or other) for deep linking into the app.
8 |
9 | This is a sample application demonstrating a technique for abstracting routing of similar incoming commands from these sources through a router and router delegate.
10 |
11 | This project was built with Swift 3 on Xcode 8 beta 6 and is not intended for reuse, or meant to be complete, it's just a demostration put out there to solicit feedback on this method.
12 |
13 | The Router object should be created in the AppDelegate, and assigned a router delegate early in the app lifecycle.
14 |
15 | The AppDelegate passes off NSUserActivity(s), UIApplicationShortcutItem(s) and URLs to the router, which handles translating them into a generic RouterRequest struct and asking the router delegate to handle the actual command. In the case of x-callback-url requests the callback URLs are parsed and included with the RouterRequest to be called when appropriate by the router delegate.
16 |
17 | It was made by @agiletortoise. If you have comments, please let me know.
18 |
--------------------------------------------------------------------------------
/Router/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/Router/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Router/ViewController+RouterDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController+RouterDelegate.swift
3 | // Router
4 | //
5 | // Created by Greg Pierce on 8/31/16.
6 | // Copyright © 2016 Agile Tortoise. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension ViewController: RouterDelegate {
12 |
13 | func route(routerRequest: RouterRequest, router: Router) {
14 | // do the actual work required by the command
15 |
16 | switch routerRequest.requestType {
17 | case .open:
18 | let identifier = routerRequest.requestInfo["identifier"]
19 | let alert = UIAlertController(title: "Open", message: "Open action triggered for identifier: \(identifier!)", preferredStyle: .alert)
20 |
21 | alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) in
22 | // action cancelled, call cancel callback
23 | routerRequest.cancelCallback?([:])
24 | }))
25 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler:{ (action) in
26 | // action completed, call success callback
27 | routerRequest.successCallback?([:])
28 | }))
29 |
30 | present(alert, animated: true, completion: nil)
31 | break
32 | case .search:
33 | let query = routerRequest.requestInfo["query"]
34 | let alert = UIAlertController(title: "Search", message: "Search action triggered for query: \(query!)", preferredStyle: .alert)
35 |
36 | alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) in
37 | // action cancelled, call cancel callback
38 | routerRequest.cancelCallback?([:])
39 | }))
40 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler:{ (action) in
41 | // action completed, call success callback
42 | routerRequest.successCallback?([:])
43 | }))
44 |
45 | present(alert, animated: true, completion: nil)
46 | break
47 | }
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/Router/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleURLTypes
20 |
21 |
22 | CFBundleTypeRole
23 | Editor
24 | CFBundleURLName
25 | router
26 | CFBundleURLSchemes
27 |
28 | router
29 |
30 |
31 |
32 | CFBundleVersion
33 | 1
34 | LSRequiresIPhoneOS
35 |
36 | UIApplicationShortcutItems
37 |
38 |
39 | UIApplicationShortcutItemIconFile
40 | shortcut_search
41 | UIApplicationShortcutItemTitle
42 | Search
43 | UIApplicationShortcutItemType
44 | $(PRODUCT_BUNDLE_IDENTIFIER).search
45 | UIApplicationShortcutItemUserInfo
46 |
47 |
48 |
49 | UILaunchStoryboardName
50 | LaunchScreen
51 | UIMainStoryboardFile
52 | Main
53 | UIRequiredDeviceCapabilities
54 |
55 | armv7
56 |
57 | UISupportedInterfaceOrientations
58 |
59 | UIInterfaceOrientationPortrait
60 | UIInterfaceOrientationLandscapeLeft
61 | UIInterfaceOrientationLandscapeRight
62 |
63 | UISupportedInterfaceOrientations~ipad
64 |
65 | UIInterfaceOrientationPortrait
66 | UIInterfaceOrientationPortraitUpsideDown
67 | UIInterfaceOrientationLandscapeLeft
68 | UIInterfaceOrientationLandscapeRight
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/Router/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Router
4 | //
5 | // Created by Greg Pierce on 8/31/16.
6 | // Copyright © 2016 Agile Tortoise. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 | let router = Router()
16 | var performShortcutItem = true
17 |
18 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
19 |
20 | let viewController = window?.rootViewController as! ViewController
21 | // assign router delegate, which will handle the parse router requests
22 | router.delegate = viewController
23 |
24 | // check for and handle shortcut item if needed
25 | performShortcutItem = true
26 | if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsKey.shortcutItem] as? UIApplicationShortcutItem {
27 | handleShortcutItem(shortcutItem, completionHandler: nil)
28 | performShortcutItem = false
29 | }
30 |
31 | return true
32 | }
33 |
34 | //MARK: URLs
35 |
36 | func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
37 | // pass URL to router to be handled
38 | return router.route(url: url, options: options)
39 | }
40 |
41 | //MARK: Shortcuts
42 |
43 | func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Swift.Void) {
44 | guard performShortcutItem else { return }
45 |
46 | handleShortcutItem(shortcutItem, completionHandler: completionHandler)
47 | }
48 |
49 | func handleShortcutItem(_ shortcutItem: UIApplicationShortcutItem, completionHandler: ((Bool) -> Void)?) {
50 | // pass shortcut item to router to be handled
51 | router.route(shortcutItem: shortcutItem) { (success) in
52 | completionHandler?(success)
53 | }
54 | }
55 |
56 | //MARK: NSUserActivity
57 |
58 | func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Swift.Void) -> Bool {
59 | // pass NSUserActivity to router to handle
60 | router.route(userActivity: userActivity) { (success) in
61 | // do nothing - or call restoration handler if necessary
62 | }
63 | return true
64 | }
65 |
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/Router.xcodeproj/xcuserdata/greg.xcuserdatad/xcschemes/Router.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/Router/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
29 |
35 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/Router/Router.swift:
--------------------------------------------------------------------------------
1 | /**
2 | # Router
3 | //
4 | // Created by Greg Pierce on 8/31/16.
5 | // Copyright © 2016 Agile Tortoise. All rights reserved.
6 | */
7 |
8 | import UIKit
9 | import CoreSpotlight
10 |
11 | /**
12 | Defines the types of commands supported by the router. This should be modify for app.
13 | */
14 | public enum RouterRequestType: String {
15 | case open
16 | case search
17 | }
18 |
19 | /**
20 | Holds details pulled from the routing source.
21 | */
22 | public struct RouterRequest {
23 | let uuid: String = UUID().uuidString
24 | let requestType: RouterRequestType
25 | let requestInfo: Dictionary
26 | let successCallback: ((_ params: Dictionary?) -> (Void))?
27 | let errorCallback: ((_ params: Dictionary?) -> (Void))?
28 | let cancelCallback: ((_ params: Dictionary?) -> (Void))?
29 | }
30 |
31 | public protocol RouterDelegate {
32 | func route(routerRequest: RouterRequest, router: Router)
33 | }
34 |
35 | public class Router {
36 |
37 | public var delegate: RouterDelegate?
38 | var lastRouterRequest: RouterRequest?
39 |
40 | public init() {}
41 |
42 | /**
43 | Routes a UIApplicationShortcutItem. Parses and creates generic RouterRequest, and passes to appropriate delegate method.
44 | */
45 | public func route(shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
46 | if shortcutItem.type.hasSuffix("search") {
47 | let request = RouterRequest(requestType: .search, requestInfo: ["query": ""], successCallback: nil, errorCallback: nil, cancelCallback: nil)
48 | delegate?.route(routerRequest: request, router: self)
49 | completionHandler(true)
50 | }
51 | }
52 |
53 | /**
54 | Routes an NSUserActivity. Parses and creates generic RouterRequest, and passes to appropriate delegate method.
55 | */
56 | public func route(userActivity: NSUserActivity, completionHandler: (Bool) -> Void) {
57 | if userActivity.activityType == CSSearchableItemActionType { // Spotlight
58 | if let identifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
59 | let request = RouterRequest(requestType: .open, requestInfo: ["identifier":identifier], successCallback: nil, errorCallback: nil, cancelCallback: nil)
60 | delegate?.route(routerRequest: request, router: self)
61 | completionHandler(true)
62 | }
63 | }
64 | }
65 |
66 | /**
67 | Routes a URL. Parses and creates generic RouterRequest, and passes to appropriate delegate method.
68 | */
69 | public func route(url: URL, options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
70 | guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return false }
71 |
72 | if components.host == "x-callback-url" {
73 | let actionName = components.path.lowercased()
74 | var successCallback: ((_ params: Dictionary?) -> (Void))?
75 | var cancelCallback: ((_ params: Dictionary?) -> (Void))?
76 | var errorCallback: ((_ params: Dictionary?) -> (Void))?
77 | let callbackParams = ["x-success","x-cancel","x-error"]
78 |
79 | if let params = components.queryItems {
80 | for item: URLQueryItem in params {
81 | let paramName = item.name.lowercased()
82 | guard callbackParams.contains(paramName) else { continue }
83 | guard let cbURLString = item.value else { continue }
84 |
85 | if var cbURL = URL(string: cbURLString) {
86 | let callback = { (params: Dictionary?) -> (Void) in
87 | if params != nil {
88 | var cbComponents = URLComponents(url: cbURL, resolvingAgainstBaseURL: false)
89 | for key in params!.keys {
90 | cbComponents?.queryItems?.append(URLQueryItem(name: key, value: params![key]))
91 | }
92 | if let newURL = cbComponents?.url {
93 | cbURL = newURL
94 | }
95 | }
96 | DispatchQueue.main.async {
97 | UIApplication.shared.open(cbURL, options: [:], completionHandler: { (didOpen) in
98 |
99 | })
100 | }
101 | }
102 | if paramName == "x-success" {
103 | successCallback = callback
104 | }
105 | else if paramName == "x-cancel" {
106 | cancelCallback = callback
107 | }
108 | else if paramName == "x-error" {
109 | errorCallback = callback
110 | }
111 | }
112 | }
113 | }
114 |
115 | if actionName == "/open" {
116 | var identifier = ""
117 | if let params = components.queryItems {
118 | for item: URLQueryItem in params {
119 | if item.name.lowercased() == "identifier" {
120 | if let s = item.value {
121 | identifier = s
122 | }
123 | }
124 | }
125 | }
126 | let request = RouterRequest(requestType: .open, requestInfo: ["identifier":identifier], successCallback: successCallback, errorCallback: errorCallback, cancelCallback: cancelCallback)
127 | delegate?.route(routerRequest: request, router: self)
128 | return true
129 | } else if actionName == "/search" {
130 | var query = ""
131 | if let params = components.queryItems {
132 | for item: URLQueryItem in params {
133 | if item.name.lowercased() == "query" {
134 | if let s = item.value {
135 | query = s
136 | }
137 | }
138 | }
139 | }
140 | let request = RouterRequest(requestType: .search, requestInfo: ["query": query], successCallback: successCallback, errorCallback: errorCallback, cancelCallback: cancelCallback)
141 | delegate?.route(routerRequest: request, router: self)
142 | return true
143 | }
144 | }
145 |
146 | return false
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/Router.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 37E6A4D81D773132000AE454 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E6A4D71D773132000AE454 /* AppDelegate.swift */; };
11 | 37E6A4DA1D773132000AE454 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E6A4D91D773132000AE454 /* ViewController.swift */; };
12 | 37E6A4DD1D773132000AE454 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 37E6A4DB1D773132000AE454 /* Main.storyboard */; };
13 | 37E6A4DF1D773132000AE454 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 37E6A4DE1D773132000AE454 /* Assets.xcassets */; };
14 | 37E6A4E21D773132000AE454 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 37E6A4E01D773132000AE454 /* LaunchScreen.storyboard */; };
15 | 37E6A4EA1D773155000AE454 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E6A4E91D773155000AE454 /* Router.swift */; };
16 | 37E6A4EC1D775D2F000AE454 /* ViewController+RouterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E6A4EB1D775D2F000AE454 /* ViewController+RouterDelegate.swift */; };
17 | 37E6A4EE1D77667C000AE454 /* README.markdown in Sources */ = {isa = PBXBuildFile; fileRef = 37E6A4ED1D77667C000AE454 /* README.markdown */; };
18 | /* End PBXBuildFile section */
19 |
20 | /* Begin PBXFileReference section */
21 | 37E6A4D41D773132000AE454 /* Router.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Router.app; sourceTree = BUILT_PRODUCTS_DIR; };
22 | 37E6A4D71D773132000AE454 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
23 | 37E6A4D91D773132000AE454 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
24 | 37E6A4DC1D773132000AE454 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
25 | 37E6A4DE1D773132000AE454 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
26 | 37E6A4E11D773132000AE454 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
27 | 37E6A4E31D773132000AE454 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
28 | 37E6A4E91D773155000AE454 /* Router.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; };
29 | 37E6A4EB1D775D2F000AE454 /* ViewController+RouterDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ViewController+RouterDelegate.swift"; sourceTree = ""; };
30 | 37E6A4ED1D77667C000AE454 /* README.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.markdown; sourceTree = ""; };
31 | /* End PBXFileReference section */
32 |
33 | /* Begin PBXFrameworksBuildPhase section */
34 | 37E6A4D11D773132000AE454 /* Frameworks */ = {
35 | isa = PBXFrameworksBuildPhase;
36 | buildActionMask = 2147483647;
37 | files = (
38 | );
39 | runOnlyForDeploymentPostprocessing = 0;
40 | };
41 | /* End PBXFrameworksBuildPhase section */
42 |
43 | /* Begin PBXGroup section */
44 | 37E6A4CB1D773132000AE454 = {
45 | isa = PBXGroup;
46 | children = (
47 | 37E6A4ED1D77667C000AE454 /* README.markdown */,
48 | 37E6A4D61D773132000AE454 /* Router */,
49 | 37E6A4D51D773132000AE454 /* Products */,
50 | );
51 | sourceTree = "";
52 | };
53 | 37E6A4D51D773132000AE454 /* Products */ = {
54 | isa = PBXGroup;
55 | children = (
56 | 37E6A4D41D773132000AE454 /* Router.app */,
57 | );
58 | name = Products;
59 | sourceTree = "";
60 | };
61 | 37E6A4D61D773132000AE454 /* Router */ = {
62 | isa = PBXGroup;
63 | children = (
64 | 37E6A4D71D773132000AE454 /* AppDelegate.swift */,
65 | 37E6A4D91D773132000AE454 /* ViewController.swift */,
66 | 37E6A4EB1D775D2F000AE454 /* ViewController+RouterDelegate.swift */,
67 | 37E6A4E91D773155000AE454 /* Router.swift */,
68 | 37E6A4DB1D773132000AE454 /* Main.storyboard */,
69 | 37E6A4DE1D773132000AE454 /* Assets.xcassets */,
70 | 37E6A4E01D773132000AE454 /* LaunchScreen.storyboard */,
71 | 37E6A4E31D773132000AE454 /* Info.plist */,
72 | );
73 | path = Router;
74 | sourceTree = "";
75 | };
76 | /* End PBXGroup section */
77 |
78 | /* Begin PBXNativeTarget section */
79 | 37E6A4D31D773132000AE454 /* Router */ = {
80 | isa = PBXNativeTarget;
81 | buildConfigurationList = 37E6A4E61D773132000AE454 /* Build configuration list for PBXNativeTarget "Router" */;
82 | buildPhases = (
83 | 37E6A4D01D773132000AE454 /* Sources */,
84 | 37E6A4D11D773132000AE454 /* Frameworks */,
85 | 37E6A4D21D773132000AE454 /* Resources */,
86 | );
87 | buildRules = (
88 | );
89 | dependencies = (
90 | );
91 | name = Router;
92 | productName = Router;
93 | productReference = 37E6A4D41D773132000AE454 /* Router.app */;
94 | productType = "com.apple.product-type.application";
95 | };
96 | /* End PBXNativeTarget section */
97 |
98 | /* Begin PBXProject section */
99 | 37E6A4CC1D773132000AE454 /* Project object */ = {
100 | isa = PBXProject;
101 | attributes = {
102 | LastSwiftUpdateCheck = 0800;
103 | LastUpgradeCheck = 0800;
104 | ORGANIZATIONNAME = "Agile Tortoise";
105 | TargetAttributes = {
106 | 37E6A4D31D773132000AE454 = {
107 | CreatedOnToolsVersion = 8.0;
108 | DevelopmentTeam = GTFQ98J4YG;
109 | ProvisioningStyle = Automatic;
110 | };
111 | };
112 | };
113 | buildConfigurationList = 37E6A4CF1D773132000AE454 /* Build configuration list for PBXProject "Router" */;
114 | compatibilityVersion = "Xcode 3.2";
115 | developmentRegion = English;
116 | hasScannedForEncodings = 0;
117 | knownRegions = (
118 | en,
119 | Base,
120 | );
121 | mainGroup = 37E6A4CB1D773132000AE454;
122 | productRefGroup = 37E6A4D51D773132000AE454 /* Products */;
123 | projectDirPath = "";
124 | projectRoot = "";
125 | targets = (
126 | 37E6A4D31D773132000AE454 /* Router */,
127 | );
128 | };
129 | /* End PBXProject section */
130 |
131 | /* Begin PBXResourcesBuildPhase section */
132 | 37E6A4D21D773132000AE454 /* Resources */ = {
133 | isa = PBXResourcesBuildPhase;
134 | buildActionMask = 2147483647;
135 | files = (
136 | 37E6A4E21D773132000AE454 /* LaunchScreen.storyboard in Resources */,
137 | 37E6A4DF1D773132000AE454 /* Assets.xcassets in Resources */,
138 | 37E6A4DD1D773132000AE454 /* Main.storyboard in Resources */,
139 | );
140 | runOnlyForDeploymentPostprocessing = 0;
141 | };
142 | /* End PBXResourcesBuildPhase section */
143 |
144 | /* Begin PBXSourcesBuildPhase section */
145 | 37E6A4D01D773132000AE454 /* Sources */ = {
146 | isa = PBXSourcesBuildPhase;
147 | buildActionMask = 2147483647;
148 | files = (
149 | 37E6A4EC1D775D2F000AE454 /* ViewController+RouterDelegate.swift in Sources */,
150 | 37E6A4EE1D77667C000AE454 /* README.markdown in Sources */,
151 | 37E6A4DA1D773132000AE454 /* ViewController.swift in Sources */,
152 | 37E6A4EA1D773155000AE454 /* Router.swift in Sources */,
153 | 37E6A4D81D773132000AE454 /* AppDelegate.swift in Sources */,
154 | );
155 | runOnlyForDeploymentPostprocessing = 0;
156 | };
157 | /* End PBXSourcesBuildPhase section */
158 |
159 | /* Begin PBXVariantGroup section */
160 | 37E6A4DB1D773132000AE454 /* Main.storyboard */ = {
161 | isa = PBXVariantGroup;
162 | children = (
163 | 37E6A4DC1D773132000AE454 /* Base */,
164 | );
165 | name = Main.storyboard;
166 | sourceTree = "";
167 | };
168 | 37E6A4E01D773132000AE454 /* LaunchScreen.storyboard */ = {
169 | isa = PBXVariantGroup;
170 | children = (
171 | 37E6A4E11D773132000AE454 /* Base */,
172 | );
173 | name = LaunchScreen.storyboard;
174 | sourceTree = "";
175 | };
176 | /* End PBXVariantGroup section */
177 |
178 | /* Begin XCBuildConfiguration section */
179 | 37E6A4E41D773132000AE454 /* Debug */ = {
180 | isa = XCBuildConfiguration;
181 | buildSettings = {
182 | ALWAYS_SEARCH_USER_PATHS = NO;
183 | CLANG_ANALYZER_NONNULL = YES;
184 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
185 | CLANG_CXX_LIBRARY = "libc++";
186 | CLANG_ENABLE_MODULES = YES;
187 | CLANG_ENABLE_OBJC_ARC = YES;
188 | CLANG_WARN_BOOL_CONVERSION = YES;
189 | CLANG_WARN_CONSTANT_CONVERSION = YES;
190 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
191 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
192 | CLANG_WARN_EMPTY_BODY = YES;
193 | CLANG_WARN_ENUM_CONVERSION = YES;
194 | CLANG_WARN_INFINITE_RECURSION = YES;
195 | CLANG_WARN_INT_CONVERSION = YES;
196 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
197 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
198 | CLANG_WARN_UNREACHABLE_CODE = YES;
199 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
200 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
201 | COPY_PHASE_STRIP = NO;
202 | DEBUG_INFORMATION_FORMAT = dwarf;
203 | ENABLE_STRICT_OBJC_MSGSEND = YES;
204 | ENABLE_TESTABILITY = YES;
205 | GCC_C_LANGUAGE_STANDARD = gnu99;
206 | GCC_DYNAMIC_NO_PIC = NO;
207 | GCC_NO_COMMON_BLOCKS = YES;
208 | GCC_OPTIMIZATION_LEVEL = 0;
209 | GCC_PREPROCESSOR_DEFINITIONS = (
210 | "DEBUG=1",
211 | "$(inherited)",
212 | );
213 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
214 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
215 | GCC_WARN_UNDECLARED_SELECTOR = YES;
216 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
217 | GCC_WARN_UNUSED_FUNCTION = YES;
218 | GCC_WARN_UNUSED_VARIABLE = YES;
219 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
220 | MTL_ENABLE_DEBUG_INFO = YES;
221 | ONLY_ACTIVE_ARCH = YES;
222 | SDKROOT = iphoneos;
223 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
224 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
225 | TARGETED_DEVICE_FAMILY = "1,2";
226 | };
227 | name = Debug;
228 | };
229 | 37E6A4E51D773132000AE454 /* Release */ = {
230 | isa = XCBuildConfiguration;
231 | buildSettings = {
232 | ALWAYS_SEARCH_USER_PATHS = NO;
233 | CLANG_ANALYZER_NONNULL = YES;
234 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
235 | CLANG_CXX_LIBRARY = "libc++";
236 | CLANG_ENABLE_MODULES = YES;
237 | CLANG_ENABLE_OBJC_ARC = YES;
238 | CLANG_WARN_BOOL_CONVERSION = YES;
239 | CLANG_WARN_CONSTANT_CONVERSION = YES;
240 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
241 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
242 | CLANG_WARN_EMPTY_BODY = YES;
243 | CLANG_WARN_ENUM_CONVERSION = YES;
244 | CLANG_WARN_INFINITE_RECURSION = YES;
245 | CLANG_WARN_INT_CONVERSION = YES;
246 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
247 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
248 | CLANG_WARN_UNREACHABLE_CODE = YES;
249 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
250 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
251 | COPY_PHASE_STRIP = NO;
252 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
253 | ENABLE_NS_ASSERTIONS = NO;
254 | ENABLE_STRICT_OBJC_MSGSEND = YES;
255 | GCC_C_LANGUAGE_STANDARD = gnu99;
256 | GCC_NO_COMMON_BLOCKS = YES;
257 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
258 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
259 | GCC_WARN_UNDECLARED_SELECTOR = YES;
260 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
261 | GCC_WARN_UNUSED_FUNCTION = YES;
262 | GCC_WARN_UNUSED_VARIABLE = YES;
263 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
264 | MTL_ENABLE_DEBUG_INFO = NO;
265 | SDKROOT = iphoneos;
266 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
267 | TARGETED_DEVICE_FAMILY = "1,2";
268 | VALIDATE_PRODUCT = YES;
269 | };
270 | name = Release;
271 | };
272 | 37E6A4E71D773132000AE454 /* Debug */ = {
273 | isa = XCBuildConfiguration;
274 | buildSettings = {
275 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
276 | DEVELOPMENT_TEAM = GTFQ98J4YG;
277 | INFOPLIST_FILE = Router/Info.plist;
278 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
279 | PRODUCT_BUNDLE_IDENTIFIER = com.agiletortoise.Router;
280 | PRODUCT_NAME = "$(TARGET_NAME)";
281 | SWIFT_VERSION = 3.0;
282 | };
283 | name = Debug;
284 | };
285 | 37E6A4E81D773132000AE454 /* Release */ = {
286 | isa = XCBuildConfiguration;
287 | buildSettings = {
288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
289 | DEVELOPMENT_TEAM = GTFQ98J4YG;
290 | INFOPLIST_FILE = Router/Info.plist;
291 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
292 | PRODUCT_BUNDLE_IDENTIFIER = com.agiletortoise.Router;
293 | PRODUCT_NAME = "$(TARGET_NAME)";
294 | SWIFT_VERSION = 3.0;
295 | };
296 | name = Release;
297 | };
298 | /* End XCBuildConfiguration section */
299 |
300 | /* Begin XCConfigurationList section */
301 | 37E6A4CF1D773132000AE454 /* Build configuration list for PBXProject "Router" */ = {
302 | isa = XCConfigurationList;
303 | buildConfigurations = (
304 | 37E6A4E41D773132000AE454 /* Debug */,
305 | 37E6A4E51D773132000AE454 /* Release */,
306 | );
307 | defaultConfigurationIsVisible = 0;
308 | defaultConfigurationName = Release;
309 | };
310 | 37E6A4E61D773132000AE454 /* Build configuration list for PBXNativeTarget "Router" */ = {
311 | isa = XCConfigurationList;
312 | buildConfigurations = (
313 | 37E6A4E71D773132000AE454 /* Debug */,
314 | 37E6A4E81D773132000AE454 /* Release */,
315 | );
316 | defaultConfigurationIsVisible = 0;
317 | };
318 | /* End XCConfigurationList section */
319 | };
320 | rootObject = 37E6A4CC1D773132000AE454 /* Project object */;
321 | }
322 |
--------------------------------------------------------------------------------