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!


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

查看所有标签

猜你喜欢:

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

轻资产创业

轻资产创业

蔡余杰 / 广东人民出版社 / 2017-11 / 45.00元

在互联网时代,资金和资源已经不是制约创业的关键因素。如今即便没有充足的资金和资产做后盾,创业梦依旧可以成为现实。相信轻资产创业模式能够帮助众多经营管理者和创业者实现管理与创业的梦想。 轻资产创业存在误区,如何跨过? 如何巧用四大模式让自媒体创业落地? 如何用一个点子引发创意型创业? 如何利用电商平台实现流量为王的营销型创业? 如何巧用知识节点做好知识产型创业? ......一起来看看 《轻资产创业》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

在线进制转换器
在线进制转换器

各进制数互转换器

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具