• Stars
    star
    224
  • Rank 177,792 (Top 4 %)
  • Language
    Swift
  • License
    MIT License
  • Created over 8 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

Marshroute

GitHub license GitHub release Swift 5.0 support cocoapods compatible

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:

  • Every Router-driven transition is always forwarded to the topmost UIViewController. This means you can ask Marshroute to present a view controller from any point in your program and it will simply work!
  • No matter how you module was presented, you can simply ask your module's Router to dismiss this module via calling router.dismissCurrentModule() and it will simply work! Your parent module can change presentation style in the future (e.g. present modally instead of pushing), but router.dismissCurrentModule() will work anyway!
  • No matter how your module presents subsequent modules, you can simply ask your module's Router to return to this module via calling router.focusOnCurrentModule() and it will also simply work!
  • Marshroute allows changing transition animations in just 1 line of code (see Tuning the transition animation for more details)
  • Marshroute supports 3d touch transitions
  • Marshroute detects view controller retain cycles and notifies you about them via assertions API. You can override default assertions with your implementation: e.g. print assertions to the output or do some advanced analytics (see plugin-customization for more details)
  • Marshroute features a detailed demo project describing key navigation principles on both iPhone and iPad

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 9.0+
  • Xcode 14.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, '9.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:

github "avito-tech/Marshroute" ~> 1.0.0

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).

More Repositories

1

playbook

AvitoTech team playbook
1,443
star
2

Paparazzo

Custom iOS camera and photo picker with editing capabilities
Swift
769
star
3

avito-android

Infrastructure of Avito android
Kotlin
356
star
4

Emcee

Emcee is a tool that runs Android and iOS tests in parallel using multiple simulators and emulators across many servers
Swift
331
star
5

bioyino

High performance and high-precision multithreaded StatsD server
Rust
229
star
6

netramesh

Ultra light service mesh for any orchestrator
Go
228
star
7

go-transaction-manager

Transaction manager for GoLang
Go
227
star
8

deepsecrets

Secrets scanner that understands code
Python
180
star
9

aqueduct

Framework for create performance-efficient prediction
Python
171
star
10

Mixbox

iOS UI testing framework https://t.me/mixbox_english https://t.me/mixbox_russian
Swift
152
star
11

go-mutesting

Mutation testing for Go source code. Fork from https://github.com/zimmski/go-mutesting
Go
145
star
12

krop

Small widget for image cropping in Instagram-like style
Kotlin
126
star
13

avitotech-presentations

Go
112
star
14

autumn-2021-intern-assignment

98
star
15

internship_backend_2022

Тестовое задание на позицию стажера-бэкендера
Go
84
star
16

Calcifer

Calcifer
Swift
72
star
17

nginx-log-collector

nginx-log-collector
Go
55
star
18

sx-frontend-trainee-assignment

Тестовое задание для стажёра Frontend в команду Seller Experience
53
star
19

Konveyor

Kotlin
48
star
20

auto-backend-trainee-assignment

Тестовое задание на позицию бекенд разработчика в юнит Авто
42
star
21

navigator

Multicluster service mesh solution based on envoy
Go
39
star
22

pulemet

Controlled RPS for interservice communication
Python
39
star
23

android-ui-testing

Kotlin
39
star
24

python-trainee-assignment

Тестовое задание по python
37
star
25

normalize

Go
35
star
26

adv-backend-trainee-assignment

Тестовое задание для стажёра Backend в команду Advertising
29
star
27

frontend-trainee-assignment-2023

27
star
28

internship_frontend_2022

Тестовое задание на позицию стажера-фронтендера
TypeScript
26
star
29

job-backend-trainee-assignment

Тестовое задание на позицию стажера-бекендера в юнит "Работа"
26
star
30

safedeal-frontend-trainee

22
star
31

pg_reindex

Console utility for rebuilding indexes and primary keys for PostgreSQL in automatic mode with analysis of index bloating and without table locking
Python
21
star
32

msg-backend-trainee-assignment

В ДАННЫЙ МОМЕНТ НЕ АКТУАЛЬНО! Тестовое задание на позицию стажера-бекендера
21
star
33

internship

Тестовое задание для iOS-стажировки
20
star
34

geo-backend-trainee-assignment

20
star
35

android-trainee-task-2021

20
star
36

blur-layout

Support for blurred semitransparent backgrounds in Android.
Assembly
19
star
37

verticals

Публичный репозиторий кластера Verticals
19
star
38

pro-fe-trainee-task

Тестовое задание для FE стажера в Авито Pro (Команда ARPU)
19
star
39

ios-trainee-problem-2021

Тестовое задание для стажера по направлению iOS
19
star
40

smart-redis-replication

Go
18
star
41

mi-backend-trainee-assignment

Тестовое задание для стажёра Backend в команду MI
17
star
42

dba-utils

Shell
17
star
43

clickhouse-vertica-udx

UDF to seamlessly connect ClickHouse to Vertica using external tables
C++
15
star
44

abito

Python package for hypothesis testing. Suitable for using in A/B-testing software
Python
15
star
45

prop-types-definition

Patch for prop-types to get property type definition in runtime
JavaScript
15
star
46

tm-backend-trainee

Тестовое задание для стажёра Backend в команду Trade Marketing
13
star
47

CommandLineToolkit

Small swift package to create command line tools faster
Swift
13
star
48

antibot-developer-trainee

Тестовая задача для разработчика-стажёра в команду Информационной безопасности Авито для защиты сайта от ботов
13
star
49

bx-backend-trainee-assignment

Тестовое задание на позицию стажера-бекендера в юнит Buyer Experience
12
star
50

mx-backend-trainee-assignment

Тестовое задание для стажёра Backend в команду MX
11
star
51

internship_ios_2022

Тестовое задание на позицию стажёра в iOS
Swift
10
star
52

patterns-and-practices-abstracts

9
star
53

dba-docs

PLpgSQL
9
star
54

qa-trainee-task

Тестовое задание для стажёра-автоматизатора
8
star
55

gravure

Image processing microservice
Rust
8
star
56

ImageSource

Image abstraction toolkit
Swift
8
star
57

pgmock

PostgreSQL 9.4+ extension for unit tests
PLpgSQL
8
star
58

mi-trainee-task-2021

7
star
59

puppet-controlrepo-template

Шаблон control repo для Puppet к статье «Инфраструктура как код в Авито: уроки которые мы извлекли»
Ruby
7
star
60

ap-frontend-trainee-assignment

7
star
61

pro-backend-trainee-assignment

6
star
62

mi-trainee-task

Тестовое задание для стажера в Market Intelligence.
6
star
63

android-trainee-task

6
star
64

ShopX-QA-trainee

задания к собеседованию
6
star
65

ios-trainee-problem

Задача для стажера на платформу iOS
6
star
66

iOS-trainee-assignment-2023

5
star
67

protocol-writer

Simplest of apps to write timed protocols from interviews
JavaScript
5
star
68

bx-android-trainee-assigment

5
star
69

safedeal-backend-trainee

5
star
70

puppet-module-template

Шаблон Puppet модуля к статье «Инфраструктура как код в Авито: уроки которые мы извлекли»
Ruby
5
star
71

android-peerlab-moscow

5
star
72

GraphiteClient

Lightweight Swift framework for feeding data into Graphite and statsD.
Swift
4
star
73

video-course-patterns-and-practices

PHP
4
star
74

xrpcd

PostgreSQL RPC built on top of pgq.
Python
4
star
75

doner

Centralized file downloading service
Rust
4
star
76

trainspotting

Python Dependency Injector based on interface binding
Python
4
star
77

moira

Go
3
star
78

qa-trainee-general

Тестовое задание для QA-cтажёра
3
star
79

aaa-ml-sys-design

ML System Design lectures materials
Python
3
star
80

aaa-ml-datasets-course

Репозиторий курса по созданию датасетов Академии Аналитиков Авито
Jupyter Notebook
3
star
81

vas-frontend-trainee-assignment

Задание для стажёра в команду VAS
2
star
82

Emcee.cloud.action

GItHub action for emcee.cloud
Shell
2
star
83

moira-client

Python
2
star
84

alert-autoconf

Python
2
star
85

homebrew-tap

Homebrew Tap of Avito products and tools
Ruby
2
star
86

avito-pixel

HTML
2
star
87

qa-into-CoE-trainee-task

Тестовое задание для стажёра QA в Центр экспертизы по Обеспечению качества
2
star
88

moira-web

JavaScript
1
star
89

test-asap

Package for easy to start browser testing
JavaScript
1
star
90

EmceePluginSupport

Swift package that allows to extend Emcee with plugins
Swift
1
star
91

avito-vault

Puppet модуль, автоматизирующий выкладку секретов из vault.
Ruby
1
star
92

brave-new-billing

Тестовое задание для backend-стажёра в юнит Billing, Avito
1
star
93

Emcee.cloud-CLI

Shell
1
star