This post was posted by Yison on the ScalaCool team blog.
As Shuidi plans to develop business applications on mobile terminals, the author started the overall scheme design on iOS terminals.
With no historical baggage and a team willing to try something different, after two weeks of focused study and research, we didn’t go with the mainstream MVVM architecture. Instead, based on the features of ReSwift and Swift language (the core of which is Extension), the author builds a scheme similar to Vue + Vuex. The author intends to share this idea through four articles.
It should be noted that the author is also the first time to contact Swift and iOS. To some extent, he is also a rookie of iOS. It is inevitable that there are some imprecise points in the article, and I hope to correct them. But at the same time, the author also has Scala and many years of Web front-end development experience, different platforms and languages, there will be similar thinking and knowledge structure, so when entering the mobile terminal native application development, also found a lot of common.
Here is an outline of this series:
- ReSwift
- Coordinator
- extension
- VueLike
Evolution of the architectural approach
Before introducing ReSwift, let’s take a quick look at the evolution of iOS (loosely, mobile app development) architecture.
There are plenty of good articles on this topic, but I’d like to recommend @Bohdan Orlov’s iOS Architecture Patterns, which are very systematic and easy to understand.
Massive View Controller
MVC is one of the most mentioned patterns when discussing architectural patterns. As we all know, the MVC introduced by Apple is different from the traditional MVC in software engineering.
Many people have a misunderstanding of Model in classic MVC, thinking that it represents only an entity Model. In fact, its exact concept should also include a lot of business logic processing, as opposed to Controller, which is just a bridge between the View and Model layers.
Note: In the development process of the industry, there are also many extended discussions around MVC, such as the typical “fat Model and thin Model” problem, even more than a decade ago, there was a heated discussion on the design of Model on JavaEye, the post is still there.
Apple’s MVC adopts the thin Model design, and the ViewController carries a lot of logic processing. There’s a reason why it’s designed this way.
If you compare the iOS platform to the browser, there’s a lot of analogies, but one thing that’s very different about the iOS platform is that iOS, like Android, has a very distinct lifecycle, and the lifecycle approach is in the ViewController.
So the initial iOS architecture problem was obvious: the bloated View Controller layer greatly reduced the maintainability and testability of the project.
Good iOS Application Architecture: MVVM vs. MVC vs. VIPER by @Krzysztof Zabłocki, who not only explained his understanding of different architectures, but also proposed his own criteria for a Good Architecture.
MVP
The cure for Massive View Controller came from MVP. The core of this design approach is the Presenter layer, which acts as a bridge between the View layer and the Model layer and handles the business logic. This meets our ideal single responsibility principle.
MVVM
In my opinion, MVVM is very similar to MVP. This design solves the problem of Massive View Controller and also introduces “two-way data binding”. MVVM is also a concept familiar to Web front-end students.
MVVM is arguably the most popular architecture for iOS and Android right now.
VIPER
VIPER is a View + Interactor + Presenter + Entity + Router. This architecture seems to be more popular on iOS than on Android, but overall there aren’t many designs that use it. Theoretically, this is a very good architectural idea inspired by The so-called Clean Architecture.
However, the finer modular design of the VIPER has made it criticized by many as over-engineering. For those interested, check out ObjC. IO’s Architecting iOS Apps with VIPER.
ReSwift
Inside The drop, we used Angular 1.x for business development, so we are familiar with the concept of two-way data binding. With the needs of our business, we transitioned to the more mature Vue 2 + WebPack to organize the development of the Web front end. After experimenting with different data flow schemes, in terms of preference, we still prefer the “one-way data flow” approach because it is simpler and more testable.
Therefore, after learning the mature solution of MVVM, the author also began to seek a one-way data flow solution for iOS, and later found ReSwift. After two weeks of experience and testing, we found that this might be an architectural design more in line with the aesthetic preference of the team.
Redux
All you need to learn about “one-way data flow” is Redux. In 2014, Facebook proposed the concept of Flux architecture. In 2015, Redux appeared, combining Flux with functional programming, and became the hottest Web front-end architecture in a short time.
The core design
Based on the classic Redux model, ReSwift also follows the following design:
-
The Store: The state of The entire APP is managed in a single data structure. The state can only be modified by dispatching Actions. Once the state in the Store changes, it notifies all observers.
-
Actions: Describes a state change in the form of a statement, which contains no code, is stored in the Store and forwarded to the Reducers. Reducers will take these Actions and make the corresponding state logic changes.
-
Reducers: Return a new app state through pure functions based on the current action and app state.
combineReducers
The authors found that the corresponding combineReducers implementation in Redux is not provided in the current version of ReSwift. I suspect that this is due to the fact that Swift has static typing as opposed to JavaScript, which is a dynamic language. But the problem can be solved in other ways.
The wheel test
Now let’s look at how to create an iOS project based on ReSwift.
The first is to design the project structure, assuming that this is a multi-functional business requirement, to see if ReSwift can organize a relatively complex project.
The project structure
- App
- AppReducer.swift
- AppState.swift
- Modules
- Module1
- Actions
- Reducers
- State
- Module2
- …
- Module1
- Views
- AppDelegte.swift
- …
AppDelegate.swift
import UIKit
import ReSwift
let mainStore = Store<AppState>(
reducer: appReducer,
state: nil
)
@UIApplicationMain
class AppDelegate: UIResponder.UIApplicationDelegate {
……
}
Copy the code
App/AppState.swift
import ReSwift
struct AppState: StateType {
var module1State: Module1State
var module2State: Module2State
}
Copy the code
App/AppReducer.swift
import ReSwift
import ReSwiftRouter
func appReducer(action: Action, state: AppState?) -> AppState {
return AppState( module1State: module1Reducer(action: action, module1State: state? .module1State), module2State: module2Reducer(action: action, module2State: state? .module2State) ) }Copy the code
Modules/Module1/State/Module1State.swift
import ReSwift
struct Module1State {
……
}
Copy the code
Modules/Module1/Reducers/Module1Reducer.swift
import ReSwift
func module1Reducer(action: Action, module1State: Module1State?) -> Module1State {
return doSomething(module1State) ?? Module1State()}Copy the code
Modules/Module1/Actions/Module1Action.swift
import ReSwift
struct Module1Action {
func action1(params: Int) -> Action {
return Action1(params: params)
}
}
extension Module1Action {
struct Action1: Action {
let params: Int}}Copy the code
So, we’ve finished designing the structure for Redux, and how Redux interacts with the ViewController layer. We will cover more about Coordinators in our next article.