Practice of MVVM architecture design in iOS

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-Ca structural diagram of the design mode, which Cdoes not refer to the controller, but as a display or shutdown controller Coordinate(coordinator). In actual development, we generally Controllercomplete 展示或者关闭控制器tasks in , so we don't pay attention to the coordinator here.MVVM-C.png

1. Division of responsibilities

In comparison MVC, a new one has been added VM, and the following are the responsibilities of each module:

VM : VMIt is a bridge between Vand M, providing a series of properties for Viewthe display, and the properties contain the values ​​that should be Modelconverted to Viewthe display when transforming the deformation. In iOS, it is usually also responsible for network requests and Modelupdates.

VC : Responsible for establishing the binding relationship VMbetween 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 Veither VMor .控制器

2. Reactive programming RxSwiftfor binding

  • The above mentioned VCis responsible for establishing the binding relationship, we can use it KVOto achieve it, but it is more troublesome when there is a need for data transformation and transformation, it is not recommended; RxSwiftit 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 in RxSwiftfunctions, 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 textLabelwill 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

  • Observableis an observable that we can transform, subscribe to, or bind to UI elements.
  • PublishSubject It is Observablea 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 is PublishSubjectsimilar 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 and DisposeBagare used to control the life cycle of one or more subscriptions respectively. When a Disposableis 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:map
  • Filter:filter
  • concat: Two Observables A and B are "serialized", and onCompleteonly 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.
  • takeAnd take(while, take(untiletc.: control the number of subscriptions or subscribe according to trigger conditions.
  • flatMapLatest: Keep only flatMapLatestthe latest subscription returned Observable( flatMapLatestone returned by the function Observable).

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 RxSwiftat github: github.com/ReactiveX/R…

Guess you like

Origin juejin.im/post/7247566462088101948