Skip to content

Commit 1888159

Browse files
committed
readme: make it simpler
1 parent 849d435 commit 1888159

File tree

3 files changed

+1225
-3753
lines changed

3 files changed

+1225
-3753
lines changed

README.md

+42-58
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
| <img alt="RxFlow Logo" src="https://raw.githubusercontent.com/RxSwiftCommunity/RxFlow/develop/Resources/RxFlow_logo.png" width="200"/> | <ul align="left"><li><a href="#about">About</a><li><a href="#navigation-concerns">Navigation concerns</a><li><a href="#rxflow-aims-to">RxFlow aims to</a><li><a href="#installation">Installation</a><li><a href="#the-core-principles">The core principles</a><li><a href="#how-to-use-rxflow">How to use RxFlow</a><li><a href="#tools-and-dependencies">Tools and dependencies</a></ul> |
1+
| <img alt="RxFlow Logo" src="https://raw.githubusercontent.com/RxSwiftCommunity/RxFlow/develop/Resources/RxFlow_logo.png" width="250"/> | <ul align="left"><li><a href="#about">About</a><li><a href="#navigation-concerns">Navigation concerns</a><li><a href="#rxflow-aims-to">RxFlow aims to</a><li><a href="#installation">Installation</a><li><a href="#the-core-principles">The core principles</a><li><a href="#how-to-use-rxflow">How to use RxFlow</a><li><a href="#tools-and-dependencies">Tools and dependencies</a></ul> |
22
| -------------- | -------------- |
33
| Travis CI | [![Build Status](https://travis-ci.org/RxSwiftCommunity/RxFlow.svg?branch=develop)](https://travis-ci.org/RxSwiftCommunity/RxFlow) |
44
| Frameworks | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/RxFlow.svg?style=flat)](http://cocoapods.org/pods/RxFlow) |
@@ -30,8 +30,8 @@ The disadvantage of these two solutions:
3030
- Promote the cutting of storyboards into atomic units to enable collaboration and reusability of UIViewControllers
3131
- Allow the presentation of a UIViewController in different ways according to the navigation context
3232
- Ease the implementation of dependency injection
33-
- Remove every navigation mechanism from UIViewControllers
34-
- Promote reactive programing
33+
- Remove every navigation mechanism from UIViewControllers
34+
- Promote reactive programming
3535
- Express the navigation in a declarative way while addressing the majority of the navigation cases
3636
- Facilitate the cutting of an application into logical blocks of navigation
3737

@@ -43,7 +43,6 @@ In your Cartfile:
4343

4444
```ruby
4545
github "RxSwiftCommunity/RxFlow"
46-
4746
```
4847

4948
## CocoaPods
@@ -52,67 +51,41 @@ In your Podfile:
5251

5352
```ruby
5453
pod 'RxFlow'
55-
5654
```
5755

58-
# The core principles
59-
60-
This is how I imagine the Flow Coordinator pattern in a simple application:
61-
62-
<p align="center">
63-
<img src="https://raw.githubusercontent.com/RxSwiftCommunity/RxFlow/develop/Resources/RxFlow_Coordinate.png" width="480"/>
64-
</p>
65-
66-
How do I read this ?
67-
68-
- Here we have three **Flows**: **Application**, **Onboarding** and **Settings** which describe the three main navigation sections of the application.
69-
- We also have three **Steps**: **Dashboard** (the default navigation state triggered at the application bootstrap), **Set the server** and **Login**.
70-
71-
Each one of these **Steps** will be triggered either because of user actions or because of backend state changes.
72-
The crossing between a **Flow** and a **Step** represented by a colored chip will be a specific navigation action (such as a UIViewController popup).
73-
It will be up to the **Coordinator** engine to trigger the "navigate(to:)" function on the appropriate **Flow**.
74-
75-
## Flow, Step and Flowable
76-
Combinaisons of **Flows** and **Steps** describe all the possible navigation actions within your application.
77-
Each **Flow** defines a clear navigation area (that makes your application divided in well defined parts) in which every **Step** will lead to a specific navigation action (push a VC on a stack, pop up a VC, ...).
56+
# The key principles
7857

79-
In the end, the **Flow.navigate(to:)** function has to return an array of **Flowable**.
58+
The **Coordinator** pattern is a great way to organize the navigation within your application. It allows to:
59+
- remove the navigation code from UIViewControllers
60+
- reuse UIViewControllers in different navigation contexts
61+
- ease the use of dependency injection
8062

81-
A **Flowable** tells the **Coordinator** engine "The next thing that can produce new **Steps** in your Reactive mechanism are":
82-
- this particular next **Presentable**
83-
- this particular next **Stepper**
63+
To learn more about it, I suggest you take a look at this article: ([Coordinator Redux](http://khanlou.com/2015/10/coordinators-redux/)).
8464

85-
In some cases, the **navigate(to:)** function can return an empty array of **Flowable** because we know there won't be any further navigation after the one we are doing.
65+
To me, the Coordinator pattern has some drawbacks:
66+
- you have to write the coordination mechanism each time you bootstrap an application
67+
- there can be a lot of boilerplate code because of the delegation pattern that allows to communicate with Coordinators
8668

87-
For the record, the Demo application shows pretty much every possible cases.
69+
RxFlow is a reactive implementation of the Coordinator pattern. It has all the great features of this architecture, but introduces some improvements:
70+
- makes the navigation more declarative
71+
- provides a built-in Coordinator that handles the navigation flows you've declared
72+
- uses reactive programming to address the communication with Coordinators issue
8873

89-
## Presentable
90-
Presentable is an abstraction of something that can be presented.
91-
Because a **Step** cannot be emitted unless its associated **Presentable** is displayed,
92-
**Presentable** offers Reactive observables that the **Coordinator** will subscribe to (so it will be aware of the presentation state of the **Presentable**).
93-
Therefore there is no risk of firing a new **Step** while its **Presentable** is not yet fully displayed.
94-
95-
## Stepper
96-
A **Stepper** can be anything: a custom UIViewController, a ViewModel, a Presenter…
97-
Once it is registered in the **Coordinator** engine, a **Stepper** can emits new **Steps** via its “steps” property (which is a Rx BehavorSubject).
98-
The **Coordinator** will listen for these **Steps** and call the **Flow**’s “navigate(to:)” function.
99-
100-
A **Step** can even embed inner values (such as Ids, URLs, ...) that will be propagated to screens presented by the **Flows**.
101-
102-
## Coordinator
103-
A **Coordinator** is a just a tool for the developper. Once he has defined the suitable combinations of **Flows** and **Steps** representing the navigation possibilities, the job of the **Coordinator** is to mix these combinaisons into a consistent navigation, according to navigation **Steps** changes induced by **Steppers**.
104-
105-
It is up to the developper to:
106-
- define the **Flows** that represent in the best possible way its application sections (such as Dashboard, Onboarding, Settings, ...) in which significant navigation actions are needed
107-
- provide the **Steppers** that will trigger the **Coordinator** process.
74+
There are 6 terms you have to be familiar with to understand **RxFlow**:
75+
- **Flow**: each **Flow** defines a navigation area within your application. This is the place where you declare the navigation actions (such as presenting a UIViewController or another Flow)
76+
- **Step**: each **Step** is a navigation state in your application. Combinaisons of **Flows** and **Steps** describe all the possible navigation actions. A **Step** can even embed inner values (such as Ids, URLs, ...) that will be propagated to screens declared in the **Flows**
77+
- **Stepper**: it can be anything that can emit **Steps**. **Steppers** will be responsible for triggering every navigation actions within the **Flows**
78+
- **Presentable**: it is an abstraction of something that can be presented (basically **UIViewController** and **Flow** are **Presentable**). **Presentables** offer Reactive observables that the **Coordinator** will subscribe to in order to handle **Flow Steps** in a UIKit compliant way
79+
- **Flowable**: it is a simple data structure that combines a **Presentable** and a **stepper**. It tells the **Coordinator** what will be the next thing that will produce new **Steps** in your Reactive mechanism
80+
- **Coordinator**: once the developer has defined the suitable combinations of **Flows** and **Steps** representing the navigation possibilities, the job of the **Coordinator** is to mix these combinaisons in a consistent way.
10881

10982
# How to use RxFlow
11083

11184
## Code samples
11285

11386
### How to declare **Steps**
11487

115-
As **Steps** are seen like some states spread across the application, it seems pretty obvious to use an enum to declare them
88+
As **Steps** are seen like some navigation states spread across the application, it seems pretty obvious to use an enum to declare them
11689

11790
```swift
11891
enum DemoStep: Step {
@@ -132,7 +105,11 @@ enum DemoStep: Step {
132105

133106
### How to declare a **Flow**
134107

135-
The following **Flow** is used as a Navigation stack.
108+
The following **Flow** is used as a Navigation stack. All you have to do is:
109+
- declare a root UIViewController on which your navigation will be based
110+
- implement the **navigate(to:)** function to transform a **Step** into a navigation action
111+
112+
The **navigate(to:)** function returns an array of **Flowable**. This is how the next navigation actions will be produced (the **Stepper** defined in a **Flowable** will emit the next **Steps**)
136113

137114
```swift
138115
class WatchedFlow: Flow {
@@ -162,8 +139,7 @@ class WatchedFlow: Flow {
162139
return navigateToCastDetailScreen(with: castId)
163140
default:
164141
return Flowable.noFlow
165-
}
166-
142+
}
167143
}
168144

169145
private func navigateToMovieListScreen () -> [Flowable] {
@@ -195,7 +171,7 @@ class WatchedFlow: Flow {
195171
### How to declare a **Stepper**
196172

197173
In theory a **Stepper**, as it is a protocol, can be anything (a UIViewController for instance) by I suggest to isolate that behavior in a ViewModel or so.
198-
For simple cases (for instance when we only need to bootstrap a **Flow** with a first **Step** and don't want to code a basic **Stepper** for that), RxFlow provides a OneStepper class.
174+
For simple cases (for instance when we only need to bootstrap a **Flow** with a first **Step** and don't want to code a basic **Stepper** for that), RxFlow provides a **OneStepper** class.
199175

200176
```swift
201177
class WatchedViewModel: Stepper {
@@ -209,8 +185,10 @@ class WatchedViewModel: Stepper {
209185
})
210186
}
211187

188+
// when a movie is picked, a new Step is emitted.
189+
// That will trigger a navigation action within the WatchedFlow
212190
public func pick (movieId: Int) {
213-
self.steps.onNext(DemoStep.moviePicked(withMovieId: movieId))
191+
self.step.onNext(DemoStep.moviePicked(withMovieId: movieId))
214192
}
215193

216194
}
@@ -228,30 +206,35 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
228206
var coordinator = Coordinator()
229207
let movieService = MoviesService()
230208
lazy var mainFlow = {
231-
return MainFlow(with: self.movieService)
209+
return MainFlow(with: self.movieService)
232210
}()
233211

234212
func application(_ application: UIApplication,
235213
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
236214

237215
guard let window = self.window else { return false }
238216

217+
// listen for Coordinator mechanism is not mandatory
239218
coordinator.rx.didNavigate.subscribe(onNext: { (flow, step) in
240219
print ("did navigate to flow=\(flow) and step=\(step)")
241220
}).disposed(by: self.disposeBag)
242221

222+
// when the MainFlow is ready to be displayed, we assign its root the the Window
243223
Flows.whenReady(flow: mainFlow, block: { [unowned window] (flowRoot) in
244224
window.rootViewController = flowRoot
245225
})
246226

227+
// The navigation begins with the MainFlow at the apiKey Step
228+
// We could also have a specific Stepper that could decide if
229+
// the apiKey should be the fist step or not
247230
coordinator.coordinate(flow: mainFlow, withStepper: OneStepper(withSingleStep: DemoStep.apiKey))
248231

249232
return true
250233
}
251234
}
252235
```
253236

254-
As a bonus, the **Coordinator** offers a Rx extension that allows you to track the navigation actions (Coordinator.rx.willKnit and Coordinator.rx.didKnit).
237+
As a bonus, **Coordinator** offers a Rx extension that allows you to track the navigation actions (Coordinator.rx.willNavigate and Coordinator.rx.didNavigate).
255238

256239
## Demo Application
257240
A demo application is provided to illustrate the core mechanisms. Pretty much every kind of navigation is addressed. The app consists of:
@@ -271,3 +254,4 @@ RxFlow relies on:
271254
- SwiftLint for static code analysis ([Github SwiftLint](https://github.com/realm/SwiftLint))
272255
- RxSwift to expose Wefts into Observables the Loom can react to ([Github RxSwift](https://github.com/ReactiveX/RxSwift))
273256
- Reusable in the Demo App to ease the storyboard cutting into atomic ViewControllers ([Github Reusable](https://github.com/AliSoftware/Reusable))
257+

0 commit comments

Comments
 (0)