Building ViewModels with Combine framework

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

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


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

查看所有标签

猜你喜欢:

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

黑客秘笈

黑客秘笈

[美]彼得·基姆 / 徐文博、成明遥 / 人民邮电出版社 / 2015-7-1 / 45.00

所谓的渗透测试,就是借助各种漏洞扫描工具,通过模拟黑客的攻击方法,来对网络安全进行评估。 本书采用大量真实案例和集邮帮助的建议讲解了在渗透测试期间会面临的一些障碍,以及相应的解决方法。本书共分为10章,其内容涵盖了本书所涉的攻击机器/工具的安装配置,网络扫描,漏洞利用,人工地查找和搜索Web应用程序的漏洞,攻陷系统后如何获取更重要的信息,社工方面的技巧,物理访问攻击,规避杀毒软件的方法,破解......一起来看看 《黑客秘笈》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换