使用RxSwift+Realm实现LiveData+Room的效果(MVVM)

该文章介绍了如何利用RxSwift和Realm数据库在iOS开发中实现类似Android官方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 UIKitimport RxSwiftclass 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 UIKitimport RxSwiftimport RealmSwiftclass 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 UIKitimport RealmSwiftclass 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比较轻量,直接进行数据库操作也未尝不可。内容完。欢迎留言交流学习。