Marshroute is an iOS Library for making your Routers simple but extremely powerful

Overview

Marshroute

GitHub license GitHub release Swift 5.0 support cocoapods compatible carthage compatible Build Status Code Coverage

Contents

Overview

Marshroute is a library that will encourage you to locate all the navigation logic in the Router layer, no matter which architecture you prefer. Marshroute helps make your Routers syntactically compact and clear.

Key features:

Details

Every Router-driven transition is always forwarded to the topmost UIViewController to make it super easy to support DeepLinks and for example present Authorization module from any point of your application. I prefer doing this right from my root application's Router.

This repo allows you to drive your transitions in a super clean, descriptive and flexible fashion. For example pretend the following code is taken from your root application's Router:

func showAuthorization() {
    pushViewControllerDerivedFrom { routerSeed -> UIViewController in
        let authorizationAssembly = assemblyFactory.authorizationAssembly()

        let viewController = authorizationAssembly.module(
            routerSeed: routerSeed
        )

        return viewController
    }
}

This code pushes an Authorization view controller to the top UINavigationController's stack. The routerSeed parameter is only used to create a Router for the Authorization module.

The magic here is in this line of code:

pushViewControllerDerivedFrom { routerSeed -> UIViewController in

You can easily change the presentation style in favor of a modal transition by simply changing it to:

presentModalNavigationControllerWithRootViewControllerDerivedFrom { routerSeed -> UIViewController in

If for some reason you do not need a UINavigationController for your Authorization module, you may accomplish this by:

presentModalViewControllerDerivedFrom { routerSeed -> UIViewController in

Once again, the transition will be forwarded to the top, keeping the Router very plain and straightforward. So that, the Router keeps being responsible for only one thing: selecting the style of a transition.

Tuning the transition animation

You may add an animator to customize the way your transition looks like. For example

func showCategories() {
    presentModalNavigationControllerWithRootViewControllerDerivedFrom( { routerSeed -> UIViewController in
        let categoriesAssembly = assemblyFactory.categoriesAssembly()

        let viewController = categoriesAssembly.module(
            routerSeed: routerSeed
        )

        return viewController
    }, animator: RecursionAnimator())
}

The key line here is

}, animator: RecursionAnimator())

So the syntax remains clean and it is super easy to switch back to the original animation style.

3d touch support

PeekAndPopUtility

Want to add fancy peek and pop previews? Easy peasy! Just use PeekAndPopUtility from the MarshrouteStack and register your view controller as capable of previewing other controllers!

peekAndPopUtility.register(
    viewController: self,
    forPreviewingInSourceView: peekSourceView,
    onPeek: { [weak self] (previewingContext, location) in
        self?.startPeekWith(
            previewingContext: previewingContext,
            location: location
        )
    },
    onPreviewingContextChange: nil
)

peekSourceView is used by UIKit during preview animations to take screenshots from. You can register single view controller for previewing in many source views (e.g.: in a table view and in a navigation bar).

onPeek closure will get called every time a force touch gesture occurs in a peekSourceView. In your startPeekWith(previewingContext:location:) method you should do the following:

  1. Find a view which a user interacts with (interactable view). You should use a specified location in previewingContext.sourceView's coordinate system.

  2. Adjust sourceRect of a previewingContext. You should convert a frame of that interactable view to previewingContext.sourceView's coordinate system. UIKit uses sourceRect to keep it visually sharp when a user presses it, while blurring all surrounding content.

  3. Invoke the transition, that will normally occur if a user simply taps at a same location. For example, it a user presses a UIControl, you may call sendActions(for: .touchUpInside) to invoke that UIControl's an action handler.

Lets pretend the above-mentioned action handler ends up with some router calling pushViewControllerDerivedFrom(_:) to push a new view controller. In this case no pushing will actually occur. Instead of this, Marshroute will freeze a transition and present a target view controller in a preview mode. The transition will eventually get performed only if a user commits the preview (i.e. pops).

The above described behavior takes place only during active peek requests (when UIViewControllerPreviewingDelegate requests a view controller to be previewed). In all other situations, pushViewControllerDerivedFrom(_:) will push immediately as expected.

Important note: if you invoke no transition within onPeek closure, or invoke an asynchronous transition, no peek will occur. This behavior is a result of UIKit Api restrictions: UIViewControllerPreviewingDelegate is required to return a previewing view controller synchronously.

You can also use onPreviewingContextChange closure to set up your gesture recognizer failure relationships.

Peek and pop state observing

You can use PeekAndPopStateObservable from the MarshrouteStack to observe any view controller's peek and pop state changes. This may be useful for analytics purposes.

peekAndPopStateObservable.addObserver(
    disposable: self,
    onPeekAndPopStateChange: { viewController, peekAndPopState in
        debugPrint("viewController: \(viewController) changed `peek and pop` state: \(peekAndPopState)")
    }
)

You can also use PeekAndPopStateViewControllerObservable to observe your particular view controller's peek and pop state changes. This may be useful for adjusting view controller's appearance in peek and popped modes.

peekAndPopStateViewControllerObservable.addObserver(
    disposableViewController: self,
    onPeekAndPopStateChange: { [weak self] peekAndPopState in
        switch peekAndPopState {
        case .inPeek:
            self?.onPeek?()
        case .popped:
            self?.onPop?()
        case .interrupted:
            break
        }
    }
)

Here in onPeek and onPop closures your Presenter may force a view to update its UI accordingly

view?.onPeek = { [weak self] in
    self?.view?.setSimilarSearchResultsHidden(true)
}

view?.onPop = { [weak self] in
    self?.view?.setSimilarSearchResultsHidden(false)
}

Demo

Check out the demo project. This demo is written in Swift using VIPER architecture and shows all the capabilities which Routers are now full of.

Run this demo on a simulator and check out what happens if you simulate a memory warning or a device shake. You will see several types of transitions driven by the root module's Router (i.e. a UITabBarController's Router).

The demo project targets both iPhone and iPad and adds some minor differences to their navigation behaviors by creating distinct Router implementations for every supported device idiom, thus highlighting the value of moving the navigation logic from the View layer in favor of a Router layer.

When you tap a blue timer tile, you schedule a reverse transition to the module that tile belongs to. To see this effect taking place, you should make several transitions deeper into the navigation stack.

Starting with 0.4.0 the demo project was updated to show PeekAndPopUtility in action: you can press on any table view cell and navigation bar button to get a preview of an underlying transition. You can also learn how to use PeekAndPopStateViewControllerObservable to adjust AdvertisementViewController's appearance in peek and popped modes: in a peek mode you will see only a fullscreen colored image pattern, while in a popped mode you will also see a similar advertisements section.

Requirements

  • iOS 8.0+
  • Xcode 9.0+

Note: peek and pop is supported only for iOS 9.0+

Installation

Cocoapods

To install Marshroute using CocoaPods, add the following lines to your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!

pod 'Marshroute'

Then run pod install command. For details of the installation and usage of CocoaPods, visit its official website.

Carthage

To install Marshroute using Carthage, add the following lines to your Cartfile:

0.4.4 ">
github "avito-tech/Marshroute" ~> 0.4.4

Then run carthage update --platform iOS command. For details of the installation and usage of Carthage, visit its repo website.

Customization

You can provide custom print and assert realization using MarshroutePrintPlugin and MarshrouteAssertionPlugin. This is as easy as:

MarshroutePrintManager.setUpPrintPlugin(YourPrintPlugin())
MarshrouteAssertionManager.setUpAssertionPlugin(YourAssertionPlugin())

Licence

MIT

Objective-c support

The framework is written in pure Swift using its latest features, so if you want to use Marshroute in your Objective-c application you will have to write your Routers in Swift.

Useful links

You can watch this video to get a closer look at the reasons and ideas which formed the basis of Marshroute (in Russian).

You can also read this guide of using Marshroute when implementing DeepLinks support in your application.

Authors

Timur Yusipov ([email protected], [email protected], https://twitter.com/Fizmatchel, https://stackoverflow.com/users/2982854/tim).

You might also like...
RoutingKit - Routing library With Swift

RoutingKit Usage struct MessageBody: Body { typealias Response = String

🎯Linker  Lightweight way to handle internal and external deeplinks in Swift for iOS
🎯Linker Lightweight way to handle internal and external deeplinks in Swift for iOS

Linker Lightweight way to handle internal and external deeplinks in Swift for iOS. Installation Dependency Managers CocoaPods CocoaPods is a dependenc

RxFlow is a navigation framework for iOS applications based on a Reactive Flow Coordinator pattern
RxFlow is a navigation framework for iOS applications based on a Reactive Flow Coordinator pattern

About Navigation concerns RxFlow aims to Installation The key principles How to use RxFlow Tools and dependencies GitHub Actions Frameworks Platform L

SwiftRouter - A URL Router for iOS, written in Swift

SwiftRouter A URL Router for iOS, written in Swift, inspired by HHRouter and JLRoutes. Installation SwiftRouter Version Swift Version Note Before 1.0.

Provides a custom presentation modifier that provides more options including full screen presentations. (iOS)

Presentation Also available as a part of my SwiftUI+ Collection – just add it to Xcode 13+ Provides a custom presentation modifier that provides more

iOS routing done right. Handles both URL recognition and controller displaying with parsed parameters. All in one line, controller stack preserved automatically!
iOS routing done right. Handles both URL recognition and controller displaying with parsed parameters. All in one line, controller stack preserved automatically!

Developed and Maintained by Ipodishima Founder & CTO at Wasappli Inc. (If you need to develop an app, get in touch with our team!) So what is this lib

An easier way to handle third-party URL schemes in iOS apps.
An easier way to handle third-party URL schemes in iOS apps.

IntentKit ========= IntentKit is an easier way to handle third-party URL schemes in iOS apps. Linking to third-party apps is essentially broken on iOS

FSPagerView is an elegant Screen Slide Library. It is extremely helpful for making Banner View、Product Show、Welcome/Guide Pages、Screen/ViewController Sliders.
FSPagerView is an elegant Screen Slide Library. It is extremely helpful for making Banner View、Product Show、Welcome/Guide Pages、Screen/ViewController Sliders.

SWIFT OBJECTIVE-C FSPagerView is an elegant Screen Slide Library implemented primarily with UICollectionView. It is extremely helpful for making Banne

MisterFusion is Swift DSL for AutoLayout. It is the extremely clear, but concise syntax, in addition, can be used in both Swift and Objective-C. Support Safe Area and Size Class.
MisterFusion is Swift DSL for AutoLayout. It is the extremely clear, but concise syntax, in addition, can be used in both Swift and Objective-C. Support Safe Area and Size Class.

MisterFusion MisterFusion makes more easier to use AutoLayout in Swift & Objective-C code. Features Simple And Concise Syntax Use in Swift and Objecti

A simple and easily customizable InputAccessoryView for making powerful input bars with autocomplete and attachments
A simple and easily customizable InputAccessoryView for making powerful input bars with autocomplete and attachments

InputBarAccessoryView Features Autocomplete text with @mention, #hashtag or any other prefix A self-sizing UITextView with an optional fixed height (c

A simple and easily customizable InputAccessoryView for making powerful input bars with autocomplete and attachments
A simple and easily customizable InputAccessoryView for making powerful input bars with autocomplete and attachments

InputBarAccessoryView Features Autocomplete text with @mention, #hashtag or any other prefix A self-sizing UITextView with an optional fixed height (c

ReleaseNotesKit - a brand new, elegant, and extremely simple way to present the recent version’s release notes to your users
ReleaseNotesKit - a brand new, elegant, and extremely simple way to present the recent version’s release notes to your users

ReleaseNotesKit This is ReleaseNotesKit, a brand new, elegant, and extremely simple way to present the recent version’s release notes to your users. R

A simple, but powerful UI patcher for macOS Big Sur designed for everyone.
A simple, but powerful UI patcher for macOS Big Sur designed for everyone.

What is Patched Sur? Patched Sur is a UI patcher for macOS Big Sur, designed to make it easy to run macOS 11 on unsupported Macs. This patcher hopes t

Cybr/Secure - A simple but powerful secure password generator
Cybr/Secure - A simple but powerful secure password generator

A simple but powerful secure password generator. You get the option of password length (10 to 20 characters) and whether you include numbers, symbols, uppercase and/or lowercase letters. Simply tap the lock icon to generate a secure password and then tap to copy the password.

Tranquillity is a lightweight but powerful dependency injection library for swift.
Tranquillity is a lightweight but powerful dependency injection library for swift.

DITranquillity Tranquillity is a lightweight but powerful dependency injection library for swift. The name "Tranquillity" laid the foundation in the b

A lightweight but powerful network library with simplified and expressive syntax based on AFNetworking.
A lightweight but powerful network library with simplified and expressive syntax based on AFNetworking.

XMNetworking English Document XMNetworking 是一个轻量的、简单易用但功能强大的网络库,基于 AFNetworking 3.0+ 封装。 其中,XM 前缀是我们团队 Xcode-Men 的缩写。 简介 如上图所示,XMNetworking 采用中心化的设计思想

iOS / Objective C: an extremely simple UIAlertView alternative
iOS / Objective C: an extremely simple UIAlertView alternative

RKDropdownAlert an extremely simple (and customizeable) alert alternative based on Facebook's app Slingshot, and inspiration from SVProgressHUD (yes,

It is a highly configurable iOS library which allows easy styling with built in styles as well as extra header and footer views so that you can make extremely unique alerts and action sheets.
It is a highly configurable iOS library which allows easy styling with built in styles as well as extra header and footer views so that you can make extremely unique alerts and action sheets.

 CFAlertViewController CFAlertViewController is a library that helps you display and customise Alerts, Action Sheets, and Notifications on iPad and i

QuizButton is a simple buzzer app that is extremely useful when you play quiz games.

QuizButton is a simple buzzer app that is extremely useful when you play quiz games.

Comments
  • Present modal before view appearing

    Present modal before view appearing

    Is there any way to present modal controller before the user will see parent view? I'm using presentModalNavigationControllerWithRootViewControllerDerivedFrom, but it doesn't work in viewWillAppear or viewDidLoad(just nothing happens)

    opened by Xpressive 4
  • The pod has a folder that doesn't compile on a non-UNIX system

    The pod has a folder that doesn't compile on a non-UNIX system

    In this directory: Marshroute/Marshroute/Sources/Transitions/TransitionAnimations/Navigation/, there's a folder called Push/Pop. Cloning a repository on a Windows system (for example) that has this cocoapod, makes the git checkout not work. It complains that the folder Push:Pop is an invalid argument.

    opened by jasperrietrae 3
  • Fix installation using Carthage

    Fix installation using Carthage

    Installing Marshroute with Carthage ended with an error: Dependency "Marshroute" has no shared framework schemes for any of the platforms: iOS

    I replaced application build scheme to framework, made it shared and added imports to test files. Now the installation is performed correctly.

    opened by west0r 0
  • Swift 3 syntax

    Swift 3 syntax

    Xcode migrator handled about 50% of the project. Had to fix all issues manually. Took me a decade to finish. Imagine what will happen with a main repo

    opened by Usipov 0
Owner
avito.tech
avito.ru engineering team open source projects
avito.tech
A simple, powerful and elegant implementation of the coordinator template in Swift for UIKit

A simple, powerful and elegant implementation of the coordinator template in Swift for UIKit Installation Swift Package Manager https://github.com/bar

Aleksei Artemev 22 Oct 16, 2022
URL routing library for iOS with a simple block-based API

JLRoutes What is it? JLRoutes is a URL routing library with a simple block-based API. It is designed to make it very easy to handle complex URL scheme

Joel Levin 5.6k Jan 6, 2023
An App-specific Simple Routing Library

TrieRouter An App-specific Simple Routing Library Usage let r = Router() r.addRoute("tbx://index") { _ in print("root") } r.addRoute("tbx://intTes

TBXark 2 Mar 3, 2022
🛣 Simple Navigation for iOS

Router Reason - Get Started - Installation Why Because classic App Navigation introduces tight coupling between ViewControllers. Complex Apps navigati

Fresh 457 Jan 4, 2023
Eugene Kazaev 713 Dec 25, 2022
LiteRoute is easy transition for your app. Written on Swift 4

LiteRoute Description LiteRoute is easy transition between VIPER modules, who implemented on pure Swift. We can transition between your modules very e

Vladislav Prusakov 90 Mar 12, 2021
A splendid route-matching, block-based way to handle your deep links.

DeepLink Kit Overview DeepLink Kit is a splendid route-matching, block-based way to handle your deep links. Rather than decide how to format your URLs

Button 3.4k Dec 30, 2022
This is OTPView made by HsynLoyiii in order to use in your login flow or whereever need it.

CustomOTPView This is OTPView made by HsynLoyiii in order to use in your login flow or wherever need it. It can use in IOS 15 and later in SwiftUI pro

Mohamad Hosein Hakimi 2 Jul 12, 2022
An open source library for building deep-linkable SwiftUI applications with composition, testing and ergonomics in mind

Composable Navigator An open source library for building deep-linkable SwiftUI applications with composition, testing and ergonomics in mind Vanilla S

Bahn-X 538 Dec 8, 2022
A library for managing complex workflows in Swift

Welcome SwiftCurrent is a library that lets you easily manage journeys through your Swift application. It comes with built-in support for UIKit and Sw

WWT 290 Jan 9, 2023