MVP、MVVM响应式基础架构送给你,要不要?

11,383 阅读3分钟

基于 MVVM + RxSwift 搭建响应式数据绑定基础架构

这是一款响应式项目基础架构库,采用协议模式设计,目的在于如何快速的搭建项目基础架构,其中包含常用的响应式刷新,空数据展示等等。

同时这边也采用Mediator方式快速实现组件化操作,也包含组件化时刻各种资源读取处理;

这边基础框架也采用分模块导入处理,并且内部也采用协议模式注入,简单方便使用;

For Example:

  • 导入响应式基础模块:pod 'Rickenbacker/Adapter'
  • 导入自动刷新模块:pod 'Rickenbacker/MJRefresh'
  • 导入空数据自动展示模块:pod 'Rickenbacker/DZNEmptyDataSet'
  • 导入组件化模块:pod 'Rickenbacker/Mediatror'

CatHome

  • 资源模块,主要处理图片资源和文本资源读取

    • 读取图片资源:R.image("base_black_back")
    • 读取文本资源:R.text("base_empty_title")
    • 读取颜色资源:R.color("background")

    WX20220323-120306@2x.png

BeeBox

  • 主要收集RxSwift相关好用方法类等

Adapter

Mediatror

  • 该模块主要就是提供设计组件化中间层

  • 设计组件化中间层有两种比较有代表性的方案:

    • 基于URL注册跳转的方式,参考蘑菇街开源 MGJRouter
    • 基于Objective-C运行时的Mediator方式,参考 CTMediator
  • 简单谈谈二者优势区别:

    • URL注册的方式在使用上非常繁琐而且很多时候其实没有必要。首先每一个页面跳转都需要事先注册好URL,这里会牵涉到非常多字符串硬编码。
    • 基于runtime的Mediator方式,首先它不需要注册,省去了很多比对字符串的过程,其次它可以非常容易的传递各种参数来进行组建间通信。
  • 因此这边最终选择提供方案也是Mediator方式;

使用示例:

extension Mediator {
    static func Second_viewController(title: String) -> UIViewController? {
        self.perform(target: SecondTarget.self,
                     action: "setupSecondViewController",
                     module: "Rickenbacker_Example",
                     params: ["title": title])
    }
}

class SecondTarget: NSObject {
    /// 备注提示,这里必须加上`@objc`
    /// 否则会出现找不到该方法从而导致控制器为`nil`问题
    @objc func setupSecondViewController(_ params: NSDictionary) -> UIViewController? {
        guard let title = params["title"] as? String else { return nil }
        let vm = SecondViewModel.init(title: title)
        let vc = SecondViewController.init(viewModel: vm)
        return vc
    }
}

MJRefresh

该模块是基于MJRefresh封装列表刷新功能

  • 注入下拉刷新功能,只需要简单的实现ViewModelHeaderable协议即可
  • 注入上拉加载更多功能,只需要简单的实现ViewModelFooterable协议即可

使用示例:

extension MJRefreshViewModel: ViewModelHeaderable, ViewModelFooterable {
    
    var enterBeginRefresh: Bool {
        return false
    }
    // 自动无感上拉刷新功能
    var footer: MJRefreshFooter {
        let footer = MJRefreshAutoFooter()
        footer.triggerAutomaticallyRefreshPercent = -5
        return footer
    }
}

备注提示:当然这边你也可以根据你的需求来自定义headerfooter

DZNEmptyDataSet

该模块是基于DZNEmptyDataSet封装列表空数据展示功能

  • 注入空数据展示功能,只需要简单的实现ViewModelEmptiable协议

使用示例:

class EmptyViewModel: ViewModel, ViewModelEmptiable, ViewModelHeaderable {
    
    let dataSource: BehaviorRelay<[String]> = BehaviorRelay(value: [])
    
    func loadData() {
        let driver = NetworkService().randomResult().asObservable()
        driver.bind(to: dataSource).disposed(by: disposeBag)
        driver.map { $0.isEmpty }.bind(to: isEmptyData).disposed(by: disposeBag)
        driver.subscribe { _ in } onCompleted: {
            self.refreshSubject.onNext(.endHeaderRefresh)
        }.disposed(by: disposeBag)
    }
}
  • 这边也支持自定义样式设计,只需要实现DZNEmptyDataSetSourceableDZNEmptyDataSetDelegateable协议,接口都是直接对DZNEmptyDataSet提供的做转行处理。

使用示例:

// 配置空数据展示信息
extension DZNEmptyDataSetViewController: DZNEmptyDataSetable {
    
    func DZNEmptyDataSetImage(scrollView: UIScrollView) -> UIImage {
        return R.image("base_network_error_black")
    }
    
    func DZNEmptyDataSetImageTintColor(scrollView: UIScrollView) -> UIColor? {
        return UIColor.red
    }
    
    func DZNEmptyDataSetTitle(scrollView: UIScrollView) -> NSAttributedString? {
        NSAttributedString(string: R.text("TEXT"))
    }
    
    func DZNEmptyDataSetDescription(scrollView: UIScrollView) -> NSAttributedString? {
        NSAttributedString(string: R.text("测试网络异常展示"))
    }
    
    func DZNEmptyDataSetVerticalOffset(scrollView: UIScrollView) -> CGFloat {
        return -77
    }
}

觉得有帮助的老哥们,请帮忙点个星 ⭐.. 救救孩子吧,谢谢各位老板。

🥺

最后

  • 附上演示Demo地址,RickenbackerDemo 直接搬去使用;
  • 再附上一个开发加速库KJCategoriesDemo地址 喜欢的老板们可以点个星;
  • 最后再附上一个滤镜框架,Harbeth有需要的朋友也可以去玩玩;
  • GIF图像滤镜框架,ImageX 分享到你;

✌️.