In iOS development, under the MVC architecture mode, the controller will be too bloated, so the MVVM architecture mode is currently more popular. The following is a brief introduction to the practice of MVVM in iOS.
1. iOS MVVM
The following figure is MVVM-C
a structural diagram of the design mode, which C
does not refer to the controller, but as a display or shutdown controller Coordinate
(coordinator). In actual development, we generally Controller
complete 展示或者关闭控制器
tasks in , so we don't pay attention to the coordinator here.
1. Division of responsibilities
In comparison MVC
, a new one has been added VM
, and the following are the responsibilities of each module:
VM : VM
It is a bridge between V
and M
, providing a series of properties for View
the display, and the properties contain the values that should be Model
converted to View
the display when transforming the deformation. In iOS, it is usually also responsible for network requests and Model
updates.
VC : Responsible for establishing the binding relationship VM
between the attributes of the medium View
; responsible for the specific logic of the interaction event response; if the coordinator in the graph is not established, it usually includes the jump logic of the page.
V : The specific creation of the view and user interaction monitoring, the presentation logic of the data in the model.
M : Responsible for storing and managing the data required by the application, and executing related business logic. It should not be coupled with V
either VM
or .控制器
2. Reactive programming RxSwift
for binding
- The above mentioned
VC
is responsible for establishing the binding relationship, we can use itKVO
to achieve it, but it is more troublesome when there is a need for data transformation and transformation, it is not recommended;RxSwift
it is a set of framework specially used for responsive programming, which provides many transformation-related functions, which can help better establish the binding relationship. - Use
RxSwift
, you can achieve one-way or two-way binding according to your needs. When we are proficient inRxSwift
functions, we can improve our code quality and convenience.
2. An example of responsive programming
Here's a simple binding implemented using RxSwift:
var modelObject: ModelObject!
var disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
modelObject.valueObservable.map { possibleValue -> String in
if let value = possibleValue {
return "Selected value is: \(value)"
} else {
return "No value selected"
}
}.bind(to: self.textLabel.rx.text).disposed(by: disposeBag)
}
1. Why is binding important?
As in the above code, compared to textLabel.text
the value set in many places, after the binding is established, this textLabel
will only be referenced once at the end. Responsive programming allows us to start from the destination - that is, the subscriber of the data, and trace back through the data deformation until we reach the original data dependency - observable ( ) observable
. By doing this, the data pipeline 可观察量
, 数据变形
and 订阅者
the three are separated. The data transformation part is the biggest advantage that reactive programming can bring, but it is also the part with the steepest learning curve.
2. Some basic types in RxSwift
Observable
is an observable that we can transform, subscribe to, or bind to UI elements.PublishSubject
It isObservable
a kind of , we can send values through it, these values will be sent to the observer, and finally the subscriber will receive the callback.BehaviorSubject
,ReplaySubject
isPublishSubject
similar to , but we can send values when no observers are connected to it. When there are new observers, they will receive the previously sent values temporarily stored in the "replay" buffer.Disposable
andDisposeBag
are used to control the life cycle of one or more subscriptions respectively. When aDisposable
is destroyed or manually discarded, the subscription behavior will end, and all observable components of the subscription will also be released.
3. Partial transformation functions in RxSwift
Map
:mapFilter
:filterconcat
: Two Observables A and B are "serialized", andonComplete
only A's message will be accepted before A sends it, and B's message will be received after A sends onComplete.Merge
: Merge multiple observable sequences, when one of them emits a message, it will receive a subscription callback.take
Andtake(while
,take(until
etc.: control the number of subscriptions or subscribe according to trigger conditions.flatMapLatest
: Keep onlyflatMapLatest
the latest subscription returnedObservable
(flatMapLatest
one returned by the functionObservable
).
More specific content can be viewed on the RxSwift website :
3. An example of two-way binding
In most cases, we only need one-way binding; but sometimes two-way binding may be required. The following is an example of two-way binding: The input box on the login page needs to be two-way bound to the data of the VM.
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var label: UILabel!
var userVM = UserViewModel()
let disposeBag = DisposeBag()
override func viewDidLoad() {
//将用户名与textField做双向绑定
userVM.username.asObservable().bind(to: textField.rx.text).disposed(by: disposeBag)
textField.rx.text.orEmpty.bind(to: userVM.username).disposed(by: disposeBag)
//将用户信息绑定到label上
userVM.userinfo.bind(to: label.rx.text).disposed(by: disposeBag)
}
}
// 我们可以将双向绑定定义一个为一个操作符(官方demo中有这个文件,可拷贝)
// 上述中双向绑定的代码可以简化为:
//将用户名与textField做双向绑定
_ = self.textField.rx.textInput <-> self.userVM.username
If you're interested, there's a boilerplate project RxSwift
at github
: github.com/ReactiveX/R…