diff --git a/Compass.xcodeproj/project.pbxproj b/Compass.xcodeproj/project.pbxproj index 3a73039..fcc4d3a 100644 --- a/Compass.xcodeproj/project.pbxproj +++ b/Compass.xcodeproj/project.pbxproj @@ -15,6 +15,9 @@ BDF7B4111C3FF43F00576737 /* TestCompass.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDF7B40F1C3FF43F00576737 /* TestCompass.swift */; }; BDF7B4151C3FF51100576737 /* Sugar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BDF7B4141C3FF51100576737 /* Sugar.framework */; }; BDF7B4171C3FF51800576737 /* Sugar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BDF7B4161C3FF51800576737 /* Sugar.framework */; }; + D5A2A7931C4CEB1C00B51356 /* Routable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A2A7921C4CEB1C00B51356 /* Routable.swift */; }; + D5A2A7971C4CEB7C00B51356 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A2A7961C4CEB7C00B51356 /* Router.swift */; }; + D5A2A79A1C4CEDDA00B51356 /* TestRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A2A7991C4CEDDA00B51356 /* TestRouter.swift */; }; D5B2E8AA1C3A780C00C0327D /* Compass.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D5B2E89F1C3A780C00C0327D /* Compass.framework */; }; D5C6294A1C3A7FAA007F7B7C /* Compass.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D5C629401C3A7FAA007F7B7C /* Compass.framework */; }; /* End PBXBuildFile section */ @@ -43,6 +46,9 @@ BDF7B40F1C3FF43F00576737 /* TestCompass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestCompass.swift; sourceTree = ""; }; BDF7B4141C3FF51100576737 /* Sugar.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sugar.framework; path = Carthage/Build/Mac/Sugar.framework; sourceTree = ""; }; BDF7B4161C3FF51800576737 /* Sugar.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sugar.framework; path = Carthage/Build/iOS/Sugar.framework; sourceTree = ""; }; + D5A2A7921C4CEB1C00B51356 /* Routable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Routable.swift; sourceTree = ""; }; + D5A2A7961C4CEB7C00B51356 /* Router.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; + D5A2A7991C4CEDDA00B51356 /* TestRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TestRouter.swift; path = Sources/iOS/TestRouter.swift; sourceTree = SOURCE_ROOT; }; D5B2E89F1C3A780C00C0327D /* Compass.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Compass.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D5B2E8A91C3A780C00C0327D /* Compass-iOS-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Compass-iOS-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; D5C629401C3A7FAA007F7B7C /* Compass.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Compass.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -134,7 +140,9 @@ D5C6296A1C3A809D007F7B7C /* iOS */ = { isa = PBXGroup; children = ( + D5A2A7921C4CEB1C00B51356 /* Routable.swift */, BDF7B40D1C3FF41500576737 /* Compass+Navigate.swift */, + D5A2A7961C4CEB7C00B51356 /* Router.swift */, ); path = iOS; sourceTree = ""; @@ -170,6 +178,7 @@ D5C629921C3A8BDA007F7B7C /* iOS */ = { isa = PBXGroup; children = ( + D5A2A7991C4CEDDA00B51356 /* TestRouter.swift */, ); path = iOS; sourceTree = ""; @@ -396,7 +405,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D5A2A7971C4CEB7C00B51356 /* Router.swift in Sources */, BDF7B4091C3FF40300576737 /* Compass.swift in Sources */, + D5A2A7931C4CEB1C00B51356 /* Routable.swift in Sources */, BDF7B40E1C3FF41500576737 /* Compass+Navigate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -405,6 +416,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D5A2A79A1C4CEDDA00B51356 /* TestRouter.swift in Sources */, BDF7B4101C3FF43F00576737 /* TestCompass.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/README.md b/README.md index aefa223..c213778 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,46 @@ Preferably you would add your own global function that you use for internal navi ## Compass life hacks -##### Tip 1. NavigationHandler.swift +##### Tip 1. Router +We also have some conventional tools for you that could be used to organize your +route handling code and avoid huge `switch` cases. + +- Implement `Routable` protocol to keep your single route navigation code +in one place: +```swift +struct ProfileRoute: Routable { + func resolve(arguments: [String: String], navigationController: UINavigationController?) { + guard let username = arguments["username"] else { return } + + let profileController = profileController(title: username) + navigationController?.pushViewController(profileController, animated: true) + } +} +``` + +- Create a `Router` instance and register your routes: +```swift +let router = Router() +router.routes = [ + "profile:{username}" : ProfileRoute(), + // "logout" : LogoutRoute() +] +``` + +- Parse URL with **Compass** and navigate to the route with a help of your +`Router` instance. +```swift +func application(app: UIApplication, + openURL url: NSURL, + options: [String : AnyObject]) -> Bool { + return Compass.parse(url) { route, arguments in + router.navigate(route, arguments: arguments, + navigationController: navigationController) + } +} +``` + +##### Tip 2. Navigation handler You could have multiple handlers depending on if a user is logged in or not. ```swift struct NavigationHandler { @@ -102,7 +141,24 @@ struct NavigationHandler { } ``` -##### Tip 2. Compass.swift +If you use `Router`-based approach you could set up 2 routers depending on the +auth state. +```swift +let routerPreLogin = Router() +routerPreLogin.routes = [ + "profile:{username}" : ProfileRoute() +] + +let routerPostLogin = Router() +routerPostLogin.routes = [ + "login:{username}" : LoginRoute() +] + +let router = isLoggedIn ? routerPostLogin : routerPreLogin +router.navigate(route, arguments: arguments, navigationController: navigationController) +``` + +##### Tip 3. Global function Add your own global function to easily navigate internally ``` swift import Compass diff --git a/Sources/iOS/Routable.swift b/Sources/iOS/Routable.swift new file mode 100644 index 0000000..4681f8c --- /dev/null +++ b/Sources/iOS/Routable.swift @@ -0,0 +1,6 @@ +import UIKit + +public protocol Routable { + + func resolve(arguments: [String: String], navigationController: UINavigationController?) +} diff --git a/Sources/iOS/Router.swift b/Sources/iOS/Router.swift new file mode 100644 index 0000000..e5165b0 --- /dev/null +++ b/Sources/iOS/Router.swift @@ -0,0 +1,14 @@ +import UIKit + +public struct Router { + + public var routes = [String: Routable]() + + public init() {} + + public func navigate(route: String, arguments: [String: String], navigationController: UINavigationController?) { + guard let route = routes[route] else { return } + + route.resolve(arguments, navigationController: navigationController) + } +} diff --git a/Sources/iOS/TestRouter.swift b/Sources/iOS/TestRouter.swift new file mode 100644 index 0000000..9d53ec6 --- /dev/null +++ b/Sources/iOS/TestRouter.swift @@ -0,0 +1,37 @@ +import Foundation +import XCTest +import Compass + +class TestRoute: Routable { + + var resolved = false + + func resolve(arguments: [String: String], navigationController: UINavigationController?) { + resolved = true + } +} + +class TestRouter: XCTestCase { + + var router: Router! + var navigationController = UINavigationController() + var route: TestRoute! + + override func setUp() { + router = Router() + route = TestRoute() + } + + func testNavigateIfRouteRegistered() { + router.routes["test"] = route + router.navigate("test", arguments: [:], navigationController: navigationController) + + XCTAssertTrue(route.resolved) + } + + func testNavigateIfRouteNotRegistered() { + router.navigate("test", arguments: [:], navigationController: navigationController) + + XCTAssertFalse(route.resolved) + } +}