Giter VIP home page Giter VIP logo

xrouter's Introduction

XRouter

A simple routing library for iOS projects.

Codacy Badge CodeCov Badge Build Status Docs Badge Version License Platform

XRouter

Usage

Basic Usage

Creating a Router

// Create a router
let router = Router<MyRoutes>()

// Navigate to a route
router.navigate(to: .loginFlow)

// ... or open a route from a URL
router.openURL(url)

Configuring Routes

Define your routes, like so:

enum AppRoute {
    case home
    case profile(withID: Int)
}
  • Note: By default, enum properties dont factor into equality checks/comparisons. You can provide your own implemention of var name: String or static func == (_:_:) if you would like to override this.

Implement the protocol stubs:

extension AppRoute: RouteProvider {

    /// Configure the transitions
    var transition: RouteTransition {
        switch self {
        case .home:
            return .push
        case .profile:
            return .modal
        }
    }

    /// Prepare the view controller for the route
    /// - You can use this to configure entry points into flow coordinators
    /// - You can throw errors here to cancel the navigation
    func prepareForTransition(from currentViewController: UIViewController) throws {
        switch self {
        case .home:
            return HomeCoordinator.shared.navigationController
        case .profile(let profileID):
            let myProfile = try Profile.load(withID: profileID)
            return ProfileViewController(profile: myProfile)
        }
    }

}

Advanced Usage

URL Support

You only need to do two things to add URL support to your routes.

First, implement the static method registerURLs in your RouteProvider.

Here is an example with a single host:

extension MyRoute: RouteProvider {

    static func registerURLs() -> Router<MyRoute>.URLMatcherGroup? {
        return .group("store.example.com") {
            $0.map("products") { .allProducts }
            $0.map("products/{category}/view") { try .products(catagory: $0.param("category")) }
            $0.map("user/{id}/profile") { try .viewProfile(withID: $0.param("id")) }
            $0.map("user/*/logout") { .logout }
        }
    }

}

Here is an example with multiple domains configured:

extension MyRoute: RouteProvider {

    static func registerURLs() -> Router<MyRoute>.URLMatcherGroup? {
        return .init(matchers: [
            .group(["example.com", "store.example.com"]) {
                $0.map("products/") { .allProducts }
                $0.map("products/{category}/view") { try .products(catagory: $0.param("category")) }
                $0.map("user/{id}/profile") { try .viewProfile(withID: $0.param("id")) }
                $0.map("user/*/logout") { .logout }
            },
            .group("affiliate.website.net.au") {
                $0.map("*/referral/") { .openReferralProgram(for: $0.rawURL) }
            }
        ])
    }

}

Then call the openURL method inside your URL handler. Here is Universal Links for example:

extension AppDelegate {

    /// Open Universal Links
    func application(_ application: UIApplication,
                 continue userActivity: NSUserActivity,
                 restorationHandler: @escaping ([Any]?) -> Void) -> Bool
    {
        guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
            let url = userActivity.webpageURL,
            let handledURL = router.openURL(url) else {
            return false // Unrecognized URL
        }

        return handledURL
    }

}

Handling errors

It can be messy trying to handle errors in every place you call navigate.

You can set a completion handler for a navigation action:

router.navigate(to: .profilePage(id: "12")) { optionalError in
    if let error = optionalError {
        print("Oh no, there was an unexpected error!")
    } else {
        print("Success!")
    }
}

You could wrap or override the navigate functions to provide a default completion handler, if you handle all navigation errors in the same way. For example, something like this:

class MyRouter<Route: RouteProvider>: XRouter.Router<Route> {

    /// Navigate to a route. Logs errors.
    override func navigate(to route: Route, animated: Bool = true, completion: ((Error?) -> Void)? = nil) {
        super.navigate(to: route, animated: animated, completion: completion ?? logErrors)
    }

    /// Open a URL to a route. Logs errors.
    @discardableResult
    override func openURL(_ url: URL, animated: Bool = true, completion: ((Error?) -> Void)? = nil) -> Bool {
        return openURL(url, animated: animated, completion: completion ?? logErrors)
    }

    /// Completion handler for `navigate(...)`/`openURL(...)`.
    private func logErrors(_ error: Error?) {
        guard let error = error else { return }

        // Log errors here
    }

}

Custom Transitions

Here is an example using the popular Hero Transitions library.

Set the customTransitionDelegate for the Router:

router.customTransitionDelegate = self

(Optional) Define your custom transitions in an extension so you can refer to them statically

extension RouteTransition {
    static var myHeroFade: RouteTransition {
        return .custom(identifier: "heroFade")
    }
}

Implement the delegate method performTransition(...):

extension AppDelegate: RouterCustomTransitionDelegate {
    
    /// Perform a custom transition
    func performTransition(to newViewController: UIViewController,
                           from oldViewController: UIViewController,
                           transition: RouteTransition,
                           animated: Bool) {
        if transition == .myHeroFade {
            oldViewController.hero.isEnabled = true
            newViewController.hero.isEnabled = true
            newViewController.hero.modalAnimationType = .fade
            
            // Creates a container nav stack
            let containerNavController = UINavigationController()
            containerNavController.hero.isEnabled = true
            containerNavController.setViewControllers([newViewController], animated: false)
            
            // Present the hero animation
            oldViewController.present(containerNavController, animated: animated)
        }
    }
    
}

And set the transition to .custom in your Routes.swift file:

    var transition: RouteTransition {
        switch self {
            case .myRoute:
                return .myHeroFade
        }
    }

Documentation

Complete documentation is available here.

Example

To run the example project, clone the repo, and run it in Xcode 10.

Requirements

Installation

CocoaPods

XRouter is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'XRouter'

Author

Reece Como, [email protected]

License

XRouter is available under the MIT license. See the LICENSE file for more info.

xrouter's People

Contributors

reececomo avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.