You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: Carthage/Checkouts/Swift-Flow/README.md
+30-1Lines changed: 30 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -120,13 +120,36 @@ It also leads to code that is difficult to understand at a glance, since depende
120
120
121
121
Swift Flow attempts to solve these problem by placing strong constraints on the way applications can be written. This reduces the room for programmer error and leads to applications that can be easily understood - by inspecting the application state data structure, the actions and the reducers.
122
122
123
+
This architecture provides further benefits beyond improving your code base:
124
+
125
+
- Stores, Reducers, Actions and extensions such as Swift Flow Router are entirely platform independent - you can easily use the same business logic and share it between apps for multiple platforms (iOS, tvOS, etc.)
126
+
- Want to collaborate with a co-worker on fixing an app crash? Use [Swift Flow Recorder](https://github.com/Swift-Flow/Swift-Flow-Recorder) to record the actions that lead up to the crash and send them the JSON file so that they can replay the actions and reproduce the issue right away.
127
+
- Maybe recorded actions can be used to build UI and integration tests?
128
+
129
+
The Swift Flow tooling is still in a very early stage, but aforementioned prospects excite me and hopefully others in the community as well!
130
+
123
131
#Getting Started Guide
124
132
125
133
[A Getting Started Guide that describes the core components of apps built with Swift Flow lives here](Readme/GettingStarted.md). It will be expanded in the next few weeks. To get an understanding of the core principles I recommend reading the brilliant [redux documentation](http://rackt.org/redux/).
126
134
127
135
#Installation
128
136
129
-
You can install SwiftFlow via [Carthage]() by adding the following line to your Cartfile:
137
+
##Cocoapods
138
+
139
+
You can install Swift Flow via CocoaPods by adding it to your `Podfile`:
140
+
141
+
use_frameworks!
142
+
143
+
source 'https://github.com/CocoaPods/Specs.git'
144
+
platform :ios, '8.0'
145
+
146
+
pod 'SwiftFlow'
147
+
148
+
And run `pod install`.
149
+
150
+
##Carthage
151
+
152
+
You can install Swift Flow via [Carthage]() by adding the following line to your Cartfile:
130
153
131
154
github "Swift-Flow/Swift-Flow"
132
155
@@ -156,6 +179,12 @@ This repository contains the core component for Swift Flow, the following extens
156
179
-[CounterExample](https://github.com/Swift-Flow/CounterExample): A very simple counter app implemented with Swift Flow. This app also demonstrates the basics of routing with SwiftFlowRouter.
157
180
-[Meet](https://github.com/Ben-G/Meet): A real world application being built with Swift Flow - currently still very early on.
158
181
182
+
#Contributing
183
+
184
+
There's still a lot of work to do here! I would love to see you involved! Some design decisions for the core of Swift Flow are still up in the air (see issues), there's lots of useful documentation that can be written and a ton of extensions and tools are waiting to be built on top of Swift Flow.
185
+
186
+
I personally think the best way to get started contributing to this library is by using it in one of your projects!
187
+
159
188
#Credits
160
189
161
190
- Thanks a lot to [Dan Abramov](https://github.com/gaearon) for building [Redux](https://github.com/rackt/redux) - all ideas in here and many implementation details were provided by his library.
@@ -28,7 +28,7 @@ There are multiple things to note:
28
28
29
29
##Viewing the State Through a Protocol
30
30
31
-
Protocols are extremely useful for working with this framework. As you can see in the example above, `SwiftRouter` can work with any app state that you define, as long as it conforms to the `HasNavigationState` protocol. The router will only ever access your app state through this protocol - this also means it won't ever see and depend upon any other state that you store within your state struct.
31
+
Protocols are extremely useful for working with this library. As you can see in the example above, `SwiftRouter` can work with any app state that you define, as long as it conforms to the `HasNavigationState` protocol. The router will only ever access your app state through this protocol - this also means it won't ever see and depend upon any other state that you store within your state struct.
32
32
33
33
**You should use this approach as much as possible in your app.** Swift Flow provides some features that make this approach even easier to use. Let's say you want to expand the state above by a simple authentication state. You would do this by first defining a new struct for this sub-state:
34
34
@@ -75,115 +75,45 @@ Note that you don't need to store derived state inside of your app state. E.g. i
75
75
76
76
#Actions
77
77
78
-
Swift Flow provides the `Action` type to create actions that describe state changes. It has the following structure:
78
+
Actions are used to express intented state changes. Actions don't contain functions, instead they provide information about the intended state change, e.g. which user should be deleted.
79
79
80
-
```swift
81
-
structAction : ActionType {
82
-
let type: String
83
-
let payload: [String : AnyObject]?
84
-
}
85
-
```
86
-
This `Action` type is serializable, which is imported for storing past actions to disk and enabling time traveling and hot reloading.
80
+
In your Swift Flow app you will define actions for every possible state change that can happen.
87
81
88
-
For simple actions you can use this type directly; for actions with a complex payload you should make use of Swift's rich type system and create separate type. This type needs to be convertible into plain `Action`s.
82
+
Reducers handle these actions and implement state changes based on the information they provide.
89
83
90
-
##Using Plain Actions
84
+
All actions in Swift Flow conform to the `Action` protocol, which currently is just a marker procotol.
91
85
92
-
The [Counter Example App](https://github.com/Swift-Flow/CounterExample) makes use of plain actions that don't carry any payload. They are simply initialized with a string constant:
86
+
You can either provide custom types as actions, or you can use the built in `StandardAction`.
// provides information that is relevant to processing this action
95
+
// e.g. details about which post should be favorited
96
+
let payload: [String : AnyObject]?
97
+
// this flag is used for serialization when working with Swift Flow Recorder
98
+
let isTypedAction: Bool
150
99
}
151
100
```
152
-
The `CreateContactFromEmail` struct contains a type string that identifies this action (this preserves the type upon serialization and deserialization) and a payload, in this case an email address.
153
-
154
-
In the extension you can see boilerplate code that allows this typed action to be initialized with a plain action and that allows us to convert it into a plain action. [This code will mostly be auto-generated in future, since it is one of the painpoints of working with this framework right now.](https://github.com/Swift-Flow/Swift-Flow/issues/2)
101
+
**For most applications it is recommended to create your own types for actions instead of using `StandardAction`, as this allows you to take advantage of Swift's type system**.
155
102
156
-
###Dispatching a Typed Action
157
-
158
-
The `Store` will accept your typed action directly, as soon as it conforms to the `ActionType` protocol. You can dispatch a typed action the same way as dispatching a plain action:
103
+
To provide your own action, simply create a type that conforms to the `Action` protocol:
In the `handleAction` method of your reducers you will always receive plain `Action`s. This ensure that your reducer works with serialized actions and therefore supports time-traveling and hot-reloading. If your typed action conforms to `ActionConvertible` then it provides a convenience initializer that allows you to easily create a typed action from an untyped one. You should do this as part of your type checking code in the reducers, e.g.:
let newContactID = state.dataState.contacts.count+1
179
-
let newContact =Contact(identifier: newContactID, emailAddress: email)
180
-
state.dataState.contacts.append(newContact)
112
+
The advantage of using a `StandardAction` is that it can be serialized; this is required for using the features provided by [Swift Flow Recorder](https://github.com/Swift-Flow/Swift-Flow-Recorder); such as persisting the application state between app launches.
181
113
182
-
return state
183
-
}
184
-
```
114
+
If you want to use custom types for actions, but still want to be able to make use of the features provided by Swift Flow Recorder, you can implement the `StandardActionConvertible` protocol. This will allow Swift Flow to convert your typed actions to standard actions that can then be serialized.
185
115
186
-
We create a typed action from the plain action and pass it on to a method of the reducer. This way the method has access to the types defined as part of our `CreateContactFromEmail` method.
116
+
Once Swift Flow Recorder's implementation is further along, you will find detailed information on all of this in its documentation.
187
117
188
118
#Reducers
189
119
@@ -193,15 +123,15 @@ This is the only place where you should modify application state! Reducers, just
You typically switch over the types in `handleAction`, then instantiate typed actions from plain actions and finally call a method that implements the actual state mutation.
157
+
You typically switch over the types in `handleAction`, then call a method that implements the actual state mutation.
158
+
159
+
#Store Subscribers
160
+
161
+
Store subscribers are types that are interested in receiving state updates from a store. Whenever the store updates its state it will notify all subscribers by calling the `newState` method on them. Subscribers need to conform to the `StoreSubscriber` protocol:
162
+
163
+
```swift
164
+
protocol StoreSubscriber {
165
+
funcnewState(state: StoreSubscriberStateType)
166
+
}
167
+
```
168
+
169
+
Most of your `StoreSubscriber`s will be in the view layer and update their representation whenever they receive a new state.
228
170
229
171
#Middleware
230
172
231
-
Swift Flow supports middleware in the same way as Redux does, [you can read this great documentation on Redux middleware to get started](http://rackt.org/redux/docs/advanced/Middleware.html).
173
+
Swift Flow supports middleware in the same way as Redux does, [you can read this great documentation on Redux middleware to get started](http://rackt.org/redux/docs/advanced/Middleware.html). Middleware allows developers to provide extensions that wrap the `dispatch` function.
232
174
233
175
Let's take a look at a quick example that shows how Swift Flow supports Redux style middleware.
234
176
235
177
The simplest example of a middleware, is one that prints all actions to the console. Here's how you can implement it:
236
178
237
-
```
179
+
```swift
238
180
let loggingMiddleware: Middleware = { dispatch, getState in
239
181
return { next in
240
182
return { action in
@@ -249,7 +191,8 @@ let loggingMiddleware: Middleware = { dispatch, getState in
249
191
```
250
192
You can define which middleware you would like to use when creating your store:
The actions will pass through the middleware in the order in which they are arranged in the array passed to the store initializer, however ideally middleware should not make any assumptions about when exactly it is called.
0 commit comments