原文链接:https://www.openwit.net/archives/iosmvvm
笔者最近做了一个关于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>() {
@Override
public void onChanged(@Nullable List groupEntities) {
mGroupRvAdapter.setItems(groupEntities);
}
});
}
...
}
通过调用viewmodel中的loadgroup方法,返回livedata数据,对这些数据进行observe,并实现更新代码。这样一来整个界面的加载逻辑就实现了,如果数据库有更新,比如新建群或者删除群,群列表界面就会自动更新,无需额外代码。
再来看下viewmodel中的实现:
public LiveData> loadGroup() {
return GroupRepository.getInstance().loadGroup();
}
这里遵循了该架构的设计,viewmodel中并没有真正的数据库相关操作,而是交给了一个Repository单例来完成。GroupRepository中的相关代码如下:
/**
* 到库中查询群组
*/
public LiveData> loadGroup() {
return getGroupDao().findByOwner(CoreModule.getInstance().getCurrentIdentity());
}
Dao中的实现代码如下:
@Query("SELECT * FROM groups WHERE owner = :owner")
LiveData> 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{
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比较轻量,直接进行数据库操作也未尝不可。
内容完。欢迎留言交流学习。