使用Rxswift+Realm实现Livedata+Room的效果(MVVM)

使用Rxswift+Realm实现Livedata+Room的效果(MVVM)

笔者最近做了一个关于app开发现状的分享,分享中提到了Google推荐的Android官方开发架构,充分肯定了其开发效率的提升,分享结束后有同事问到iOS是否有类似架构,于是便有了此篇博文。

首先来看一下Android官方推荐的开发架构,架构图如下:

该架构遵循MVVM开发模式,利用Jetpack中的Room数据库及LiveData能够有效提升开发效率。使用过的同学自然深有体会,这里举个小例子,以群组列表页面为例。首先看一下GroupFragment中部分代码:

public class GroupFrament extends BaseFragment {
    @BindView(R2.id.rv_contact_group)
    RecyclerView mGroupRv;
    private GroupRvAdapter mGroupRvAdapter;

    @Override
    protected void initVariables() {
        mViewModel = ViewModelProviders.of(this).get(GroupViewModel.class);
        mViewModel.loadGroup().observe(this, new Observer<List<GroupEntity>>() {
            @Override
            public void onChanged(@Nullable List<GroupEntity> groupEntities) {
                mGroupRvAdapter.setItems(groupEntities);
            }
        });
    }

  ...
}

通过调用viewmodel中的loadgroup方法,返回livedata数据,对这些数据进行observe,并实现更新代码。这样一来整个界面的加载逻辑就实现了,如果数据库有更新,比如新建群或者删除群,群列表界面就会自动更新,无需额外代码。

再来看下viewmodel中的实现:

    public LiveData<List<GroupEntity>> loadGroup() {
        return GroupRepository.getInstance().loadGroup();
    }

viewmodel中并没有真正的数据库相关操作,而是交给了一个Repository单例来完成。GroupRepository中的相关代码如下:

    /**
     * 到库中查询群组
     */
    public LiveData<List<GroupEntity>> loadGroup() {
        return getGroupDao().findByOwner(CoreModule.getInstance().getCurrentIdentity());
    }

Dao中的实现代码如下:

    @Query("SELECT * FROM groups WHERE owner = :owner")
    LiveData<List<GroupEntity>> findByOwner(String owner);

上面的核心代码严格遵循了谷歌官方推荐的Android开发架构,那么iOS开发中能否实现类似架构呢。答案是肯定的,利用rxswift以及realm数据库可以实现类似效果。

注:以下代码参考笔者2019年初完成的项目,部分api可能已经过时。

同样以群组列表为例,首先看一下GroupController的部分代码:

import UIKit
import RxSwift

class FEGroupController: UITableViewController {
    var viewModel = FEGroupViewModel()
    var dataSource = [GroupEntity]()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.title = "我的群组"
        self.tableView.register(UINib.init(nibName: String(describing: FEContactCell.self), bundle: nil), forCellReuseIdentifier:String(describing: FEContactCell.self))
        viewModel.loadGroup().asDriver().drive(onNext: { [weak self](list) in
            guard let strongSelf = self else {return}
            strongSelf.dataSource.removeAll()
            strongSelf.dataSource.append(contentsOf: list)
            strongSelf.tableView.reloadData()

        }, onCompleted: nil, onDisposed: nil).disposed(by: rx.disposeBag)
    }

  ...
}

通过调用viewmodel中的loadgroup方法,返回Variable数据,作为驱动来驱动界面更新。这样一来整个界面的加载逻辑就实现了,如果数据库有更新,比如新建群或者删除群,群列表界面也会自动更新,同样无需额外代码。由于iOS没有Android中提供的Jetpack相关组件(例如LiveData),这里viewmodel中的数据是怎么检测到更新的呢,下面是viewmodel中的部分代码实现:

import UIKit
import RxSwift
import RealmSwift

class FEGroupViewModel: NSObject {
    var mGroupId = ""
    var groupLiveData = Variable<[GroupEntity]>([GroupEntity]())
    var token: NotificationToken? = nil

    func loadGroup() ->Variable<[GroupEntity]>{
        let realm = try! Realm()
        let groups = GroupDao.findByOwner(FECoreModule.shared.getCurrentIdentity() ?? "", realm: realm)
        token = groups.observe({ [weak self](change) in
            var list = [GroupEntity]()
            list.append(contentsOf: groups)
            self?.groupLiveData.value = list
        })
        return self.groupLiveData
    }

   ...
}

上述代码,在获取到groups之后的observe操作类似livedata的使用,这里的groups到底是什么类型呢?继续往下看,下面是Dao中的相关实现代码:

import UIKit
import RealmSwift

class GroupDao: NSObject {
    static func findByOwner(_ owner: String, realm: Realm) -> Results<GroupEntity>{
        return realm.objects(GroupEntity.self).filter("owner == \'\(owner)\'")
    }

  ...
}

原来realm数据库提供了Results类型的返回值,该类型数据类似LiveData,可以对其进行observe,监听数据集的变化。

通过上面的一系列代码,同样实现了界面逻辑书写一次,自动更新的效果。不同的是Android中的viewmodel继承自系统级API(Jetpack中的ViewModel),其生命周期由系统负责管理,iOS中的viewmodel基于NSObject自定义实现。

上述iOS代码,viewmodel中并未通过封装Repository来实现,针对这点,笔者认为可以视情况而定,可以根据代码量来评估,视情况封装相应的Repository来达到分层的目的,如果viewmodel比较轻量,直接进行数据库操作也未尝不可。

内容完。欢迎留言交流学习。

本文由Openwit 创作,采用知识共享署名4.0国际许可协议进行许可。
本站文章除注明转载外,均为本站原创或翻译,转载请注明出处。
最后更新于: 2020-11-24

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×