Building ViewModels with Combine framework

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

内容简介: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!


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

查看所有标签

猜你喜欢:

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

P2P网贷投资手册

P2P网贷投资手册

徐红伟 / 同济大学出版社 / 2015-4 / CNY 28.00

《P2P网贷投资手册》由“P2P网络借贷知多少”、“新手如何开始P2P网贷投资”和“如何确定适合自己的网贷投资策略”三部分组成。将网贷之家平台上众多投资人和从业者的智慧集结成册,分享给网贷投资上的同路人。一起来看看 《P2P网贷投资手册》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器