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

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

MVVM + RxSwift


👌. MVVM + RxSwift + CTMediatror + MJRefresh + DZNEmptyDataSet


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


CatHome


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

读取图片资源:R.image("base_black_back")

读取文本资源:R.text("base_empty_title")

读取颜色资源:R.color("background")

1.png


BeeBox


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


Adapter


该模块主要就是封装基类

BaseViewController: 支持oc基类,公共部分

VMTableViewController: 列表基类, 内部采用响应式处理

VMViewController: 该基类继承时需指定ViewModel或其子类作为泛型,该类会自动懒加载指定类型的VM对象。

ViewModel: 基础模型,子类必须继承使用

关于子类,这边目前都采用协议的方式去设计修改,需要什么就导入什么协议即可。


例如:

引入上拉和下拉刷新功能

只需要遵循这两个协议即可 - ViewModelHeaderable, ViewModelFooterable

引入空数据展示功能

只需要遵循空数据协议 - ViewModelEmptiable


CTMediatror


该模块主要解决Swift采用该组件化方案CocoaPods问题

extension CTMediator {
    /// Swift调度者
    func Second_viewController(title: String) -> (UIViewController?) {
        let params = [
            "title": title,
            kCTMediatorParamsKeySwiftTargetModuleName: "Rickenbacker_Example"
        ] as [AnyHashable : Any]
        let viewController = self.performTarget(String(describing: SecondViewController.self),
                                                action: "Extension_ViewController",
                                                params: params,
                                                shouldCacheTarget: false) as? UIViewController
        return (viewController)
    }
}
class Target_SecondViewController: NSObject {
    /// 备注提示,这里必须加上`@objc`
    /// 否则会出现找不到该方法从而导致控制器为`nil`问题
    @objc func Action_Extension_ViewController(_ 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
    }
}


HBDNavigationBar


该模块是基于HBDNavigationBar二次封装底层基础导航栏


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

final class MJRefreshViewModel: ViewModel {
    struct Input {
        let headerRefresh: Observable<Void>
        let footerRefresh: Observable<Void>
    }
    struct Output {
        let items: Driver<[String]>
    }
}
extension MJRefreshViewModel: ViewModelType {
    func transform(input: Input) -> Output {
        var objects: [String] = []
        var page: Int = 1
        /// 下拉刷新
        let refresh = input.headerRefresh.then(page = 1)
            .flatMapLatest {
                MJRefreshAPI.refresh(page).request()
            }.map { [unowned self] (items) -> [String] in
                refreshSubject.onNext(.endHeaderRefresh)
                objects = items
                return objects
            }.asDriver(onErrorJustReturn: [])
        /// 上拉加载更多
        let more = input.footerRefresh.then(page += 1)
            .flatMapLatest {
                MJRefreshAPI.refresh(page).request()
            }.map { [unowned self] (items) -> [String] in
                if items.isEmpty {
                   refreshSubject.onNext(.endFooterRefreshWithNoData)
                } else {
                    refreshSubject.onNext(.endFooterRefresh)
                }
                objects += items
                return objects
            }.asDriver(onErrorJustReturn: [])
        /// 数据响应    
        let items = Driver.of(refresh, more).merge()
        /// 空数据绑定
        items.map { $0.isEmpty }.asObservable().bind(to: isEmptyData).disposed(by: disposeBag)
        return Output(items: items)
    }
}


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)
    }
}
let driver = NetworkService().randomResult().asObservable()
driver.map { $0.isEmpty }.bind(to: isEmptyData).disposed(by: disposeBag)

这边也支持自定义样式设计,只需要实现DZNEmptyDataSetSourceableDZNEmptyDataSetDelegateable协议,接口都是直接对DZNEmptyDataSet提供的做转行处理。

public typealias DZNEmptyDataSetable = DZNEmptyDataSetSourceable & DZNEmptyDataSetDelegateable
public protocol DZNEmptyDataSetSourceable {
    /// Asks the data source for the image of the dataset.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    /// - Returns: An image for the dataset.
    func DZNEmptyDataSetImage(scrollView: UIScrollView) -> UIImage
    /// Asks the data source for the title of the dataset.
    /// The dataset uses a fixed font style by default, if no attributes are set. If you want a different font style, return a attributed string.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    /// - Returns: An attributed string for the dataset title, combining font, text color, text pararaph style, etc.
    func DZNEmptyDataSetTitle(scrollView: UIScrollView) -> NSAttributedString?
    /// Asks the data source for the description of the dataset.
    /// The dataset uses a fixed font style by default, if no attributes are set. If you want a different font style, return a attributed string.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    /// - Returns: An attributed string for the dataset description text, combining font, text color, text pararaph style, etc.
    func DZNEmptyDataSetDescription(scrollView: UIScrollView) -> NSAttributedString?
    /// Asks the data source for a tint color of the image dataset. Default is nil.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    /// - Returns: A color to tint the image of the dataset.
    func DZNEmptyDataSetImageTintColor(scrollView: UIScrollView) -> UIColor?
    /// Asks the data source for the image animation of the dataset.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    /// - Returns: image animation
    func DZNEmptyDataSetImageAnimation(scrollView: UIScrollView) -> CAAnimation?
    /// Asks the data source for the title to be used for the specified button state.
    /// The dataset uses a fixed font style by default, if no attributes are set. If you want a different font style, return a attributed string.
    /// - Parameters:
    ///   - scrollView: A scrollView subclass informing the data source.
    ///   - state: The state that uses the specified title. The possible values are described in UIControlState.
    /// - Returns: An attributed string for the dataset button title, combining font, text color, text pararaph style, etc.
    func DZNEmptyDataSetButtonTitle(scrollView: UIScrollView, for state: UIControl.State) -> NSAttributedString?
    /// Asks the data source for the image to be used for the specified button state.
    /// This method will override buttonTitleForEmptyDataSet:forState: and present the image only without any text.
    /// - Parameters:
    ///   - scrollView: A scrollView subclass informing the data source.
    ///   - state: The state that uses the specified title. The possible values are described in UIControlState.
    /// - Returns: An image for the dataset button imageview.
    func DZNEmptyDataSetButtonImage(scrollView: UIScrollView, for state: UIControl.State) -> UIImage?
    /// Asks the data source for a background image to be used for the specified button state.
    /// There is no default style for this call.
    /// - Parameters:
    ///   - scrollView: A scrollView subclass informing the data source.
    ///   - state: The state that uses the specified image. The values are described in UIControlState.
    /// - Returns: An attributed string for the dataset button title, combining font, text color, text pararaph style, etc.
    func DZNEmptyDataSetButtonBackgroundImage(scrollView: UIScrollView, for state: UIControl.State) -> UIImage?
    /// Asks the data source for the background color of the dataset. Default is clear color.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    /// - Returns: A color to be applied to the dataset background view.
    func DZNEmptyDataSetBackgroundColor(scrollView: UIScrollView) -> UIColor
    /// Asks the data source for a custom view to be displayed instead of the default views such as labels, imageview and button. Default is nil.
    /// Use this method to show an activity view indicator for loading feedback, or for complete custom empty data set.
    /// Returning a custom view will ignore -offsetForEmptyDataSet and -spaceHeightForEmptyDataSet configurations.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    /// - Returns: The custom view.
    func DZNEmptyDataSetCustomView(scrollView: UIScrollView) -> UIView?
    /// Asks the data source for a offset for vertical and horizontal alignment of the content. Default is CGPointZero.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    /// - Returns: The offset for vertical and horizontal alignment.
    func DZNEmptyDataSetVerticalOffset(scrollView: UIScrollView) -> CGFloat
    /// Asks the data source for a vertical space between elements. Default is 11 pts.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    /// - Returns: The space height between elements.
    func DZNEmptyDataSetSpaceHeight(scrollView: UIScrollView) -> CGFloat
}
public protocol DZNEmptyDataSetDelegateable {
    /// Asks the delegate to know if the empty dataset should fade in when displayed. Default is YES.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    /// - Returns: YES if the empty dataset should fade in.
    func DZNEmptyDataSetShouldFadeIn(_ scrollView: UIScrollView) -> Bool
    /// Asks the delegate to know if the empty dataset should still be displayed when the amount of items is more than 0. Default is NO
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    /// - Returns: YES if empty dataset should be forced to display
    func DZNEmptyDataSetShouldBeForcedToDisplay(_ scrollView: UIScrollView) -> Bool
    /// Asks the delegate for touch permission. Default is YES.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    /// - Returns: YES if the empty dataset receives touch gestures.
    func DZNEmptyDataSetShouldAllowTouch(_ scrollView: UIScrollView) -> Bool
    /// Asks the delegate for scroll permission. Default is NO.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    /// - Returns: YES if the empty dataset is allowed to be scrollable.
    func DZNEmptyDataSetShouldAllowScroll(_ scrollView: UIScrollView) -> Bool
    /// Asks the delegate for image view animation permission. Default is NO.
    /// Make sure to return a valid CAAnimation object from imageAnimationForEmptyDataSet:
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    /// - Returns: YES if the empty dataset is allowed to animate
    func DZNEmptyDataSetShouldAnimateImageView(_ scrollView: UIScrollView) -> Bool
    /// Tells the delegate that the empty data set will appear.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    func DZNEmptyDataSetWillAppear(_ scrollView: UIScrollView)
    /// Tells the delegate that the empty data set did appear.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    func DZNEmptyDataSetDidAppear(_ scrollView: UIScrollView)
    /// Tells the delegate that the empty data set will disappear.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    func DZNEmptyDataSetWillDisappear(_ scrollView: UIScrollView)
    /// Tells the delegate that the empty data set did disappear.
    /// - Parameter scrollView: A scrollView subclass informing the data source.
    func DZNEmptyDataSetDidDisappear(_ scrollView: UIScrollView)
}

使用示例:

// 配置空数据展示信息
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
    }
}


CocoaPods Install


目前暂时包含如下七大模块:

导入项目响应式基类模块:pod 'Rickenbacker/Adapter'

导入资源模块:pod 'Rickenbacker/CatHome'

导入收集模块:pod 'Rickenbacker/BeeBox'

导入组件化模块:pod 'Rickenbacker/CTMediatror'

导入导航栏基础模块:pod 'Rickenbacker/HBDNavigationBar'

导入自动刷新模块:pod 'Rickenbacker/MJRefresh'

导入空数据自动展示模块:pod 'Rickenbacker/DZNEmptyDataSet'


觉得有帮助的老哥们,请帮忙点个星 ⭐..


救救孩子吧,谢谢各位老板。

🥺


最后


附上传送RickenbackerDemo 地址.

再附上一个开发加速库KJCategoriesDemo地址 🎷喜欢的老板们可以点个星🌟

最后再附上一个好玩的滤镜框架,有需要的朋友也可以去玩玩,Harbeth✌️.

相关文章
|
20天前
|
前端开发 测试技术 数据处理
Kotlin教程笔记 - MVP与MVVM架构设计的对比
Kotlin教程笔记 - MVP与MVVM架构设计的对比
42 4
|
20天前
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
24 2
|
1月前
|
XML 前端开发 Android开发
Kotlin教程笔记(80) - MVVM架构设计
Kotlin教程笔记(80) - MVVM架构设计
|
1月前
|
前端开发 JavaScript 测试技术
android做中大型项目完美的架构模式是什么?是MVVM吗?如果不是,是什么?
在 Android 开发中,选择合适的架构模式对于构建中大型项目至关重要。常见的架构模式有 MVVM、MVP、MVI、Clean Architecture 和 Flux/Redux。每种模式都有其优缺点和适用场景,例如 MVVM 适用于复杂 UI 状态和频繁更新,而 Clean Architecture 适合大型项目和多平台开发。选择合适的架构应考虑项目需求、团队熟悉度和可维护性。
50 6
|
14天前
|
XML 前端开发 Android开发
Kotlin教程笔记(80) - MVVM架构设计
Kotlin教程笔记(80) - MVVM架构设计
|
1月前
|
前端开发 Java 测试技术
android MVP契约类架构模式与MVVM架构模式,哪种架构模式更好?
android MVP契约类架构模式与MVVM架构模式,哪种架构模式更好?
44 0
|
26天前
|
缓存 负载均衡 JavaScript
探索微服务架构下的API网关模式
【10月更文挑战第37天】在微服务架构的海洋中,API网关犹如一座灯塔,指引着服务的航向。它不仅是客户端请求的集散地,更是后端微服务的守门人。本文将深入探讨API网关的设计哲学、核心功能以及它在微服务生态中扮演的角色,同时通过实际代码示例,揭示如何实现一个高效、可靠的API网关。
|
7天前
|
弹性计算 API 持续交付
后端服务架构的微服务化转型
本文旨在探讨后端服务从单体架构向微服务架构转型的过程,分析微服务架构的优势和面临的挑战。文章首先介绍单体架构的局限性,然后详细阐述微服务架构的核心概念及其在现代软件开发中的应用。通过对比两种架构,指出微服务化转型的必要性和实施策略。最后,讨论了微服务架构实施过程中可能遇到的问题及解决方案。
|
24天前
|
Cloud Native 安全 数据安全/隐私保护
云原生架构下的微服务治理与挑战####
随着云计算技术的飞速发展,云原生架构以其高效、灵活、可扩展的特性成为现代企业IT架构的首选。本文聚焦于云原生环境下的微服务治理问题,探讨其在促进业务敏捷性的同时所面临的挑战及应对策略。通过分析微服务拆分、服务间通信、故障隔离与恢复等关键环节,本文旨在为读者提供一个关于如何在云原生环境中有效实施微服务治理的全面视角,助力企业在数字化转型的道路上稳健前行。 ####
|
6天前
|
Java 开发者 微服务
从单体到微服务:如何借助 Spring Cloud 实现架构转型
**Spring Cloud** 是一套基于 Spring 框架的**微服务架构解决方案**,它提供了一系列的工具和组件,帮助开发者快速构建分布式系统,尤其是微服务架构。
104 68
从单体到微服务:如何借助 Spring Cloud 实现架构转型