Building ViewModels with Combine framework

栏目: IT技术 · 发布时间: 6年前

内容简介:One of my first posts on this blog was about using theYou can check

One of my first posts on this blog was about using the ViewModel pattern in iOS apps. I’m still using this concept in some old school UIKit projects. But I think it’s time to remaster that post. This week we will talk about building reactive ViewModels using Combine framework .

What is ViewModel?

ViewModel is a layer between your view and data. ViewModels usually fetch the data using service objects, format it, and provide a formatted version to your view.

You can check my old post about MVVM if you need more information about the pattern itself.

Apple started promoting MVVM

I noticed an interesting thing when Apple moved the ObservableObject protocol to the Combine framework . It looks like Apple began promoting the MVVM pattern . Let’s take a look at the ObservableObject protocol to understand what’s going on there.

/// A type of object with a publisher that emits before the object has changed.
public protocol ObservableObject : AnyObject {

    /// The type of publisher that emits before the object has changed.
    associatedtype ObjectWillChangePublisher : Publisher = ObservableObjectPublisher where Self.ObjectWillChangePublisher.Failure == Never

    /// A publisher that emits before the object has changed.
    var objectWillChange: Self.ObjectWillChangePublisher { get }
}

ObservableObject protocol has the only one requirement, a publisher that emits before the object changes. Let’s write our first ViewModel that conforms to ObservableObject protocol.

final class PostsViewModel: ObservableObject {
    let objectWillChange = PassthroughSubject<Void, Never>()

    private (set) var posts: [Post] = []

    func fetch() {
        // fetch posts
        objectWillChange.send()
        // assign new data to the posts variable
    }
}

Here we have the ViewModel that fetches posts, stores them in the variable, and emits a notification via objectWillChange publisher. Let’s take a look at a sample ViewController that uses this ViewModel .

final class PostsViewController: UIViewController {
    let viewModel: PostsViewModel

    override func viewDidLoad() {
        super.viewDidLoad()
        bindViewModel()
        viewModel.fetch()
    }

    private func bindViewModel() {
        viewModel.objectWillChange.sink { [weak self] in
            guard let self = self else {
                return
            }
            self.renderPosts(self.viewModel.posts)
        }
    }
}

As you can see in the example above, we have PostsViewController that starts observing changes in ViewModel and then ask ViewModel to fetch data. As soon as ViewModel fetches data, it emits, and ViewController calls renderPosts function that displays downloaded posts.

Published property wrapper

We can go further by using @ Published property wrapper. @ Published property wrapper allows us to wrap any property with the publisher that emits the current value whenever the property changes.

Moreover, you even don’t need to define objectWillChange publisher when you use @ Published property wrapper. Swift compiler will automatically synthesize the objectWillChange , and it will emit whenever any @ Published property changes. Let’s take a look at the refactored version of our ViewModel that uses @ Published property wrapper.

final class PostsViewModel: ObservableObject {
    @Published private(set) var posts: [Post] = []

    func fetch() {
        // fetch posts and assign them to `posts` variable
    }
}

As you can see in the example above, we don’t need to send a value manually to objectWillChange publisher — all the work synthesized by the Swift compiler. And we can keep the same implementation of PostsViewController .

As I said before, @ Published property wrapper wraps our property with a publisher. Let’s take a look at how we can use it in our PostsViewController .

final class PostsViewController: UIViewController {
    let viewModel: PostsViewModel

    override func viewDidLoad() {
        super.viewDidLoad()
        bindViewModel()
        viewModel.fetch()
    }

    private func bindViewModel() {
        viewModel.$posts.sink { [weak self] posts in
            self?.renderPosts(posts)
        }
    }
}

Here we have a refactored version of our PostsViewController . Please take a look at how we changed bindViewModel function. It subscribes to $posts now, and it allows us to update our view only when specific properties change. You will see the benefits as soon as your ViewModel has more and more fields which can affect the view.

Conclusion

We can easily implement the very same logic using RxSwift , ReactiveSwift , or any other reactive framework like Bond . But I feel like MVVM is going to be a default choice in architecting iOS apps. At least now, when Apple provides us all the needed tools to build it out of the box. I hope you enjoy the post. Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading, and see you next week!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

C#本质论

C#本质论

米凯利斯 / 周靖 / 人民邮电出版社 / 2010-9 / 99.00元

《C#本质论(第3版)》是一部好评如潮的语言参考书,作者用一种非常合理的方式来组织《C#本质论(第3版)》的内容,由浅人深地介绍了C#语言的各个方面。全书共包括21章及6个附录,每章开头的“思维导图”指明了本章要讨论的主题,以及各个主题之间的层次关系。书中所包含的丰富的示例代码和精要的语言比较,都有助于读者理解C#语言。《C#本质论(第3版)》首先介绍了C#语言的基础知识,随后深人讲解了泛型、迭代......一起来看看 《C#本质论》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具