iOS ReactiveCocoa MVVM

news2024/11/24 12:27:00

学习了在MVVM中如何使用RactiveCocoa,简单的写上一个demo。重点在于如何在MVVM各层之间使用RAC的信号来更方便的在各个层之间进行响应式数据交互。
demo需求:一个登录界面(登录界面只有账号和密码都有输入,登录按钮才可以点击操作),一个登录成功后跳转展示界面。
在这里插入图片描述
ViewModel中是负责登录的业务逻辑层,该层中负责数据验证,网络请求,数据存储等一些和ui无关的业务逻辑。
在这里插入图片描述
因为ViewModel层是独立于UI层而存在的,所以可以在没有UI的情况下我们就可以去实现相应模块的ViewModel层。这正好减少了个个层次间的耦合性,同时也提高了可测试性,总体上改善了可维护性。好废话少说,接下来要实现登录的Model层。

//
//  User.h
//  RACDemo
//
//  Created by johnny on 2024/6/5.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface User : NSObject

@property (nonatomic, copy) NSString *name;

@property (nonatomic, copy) NSString *pwd;

@end

NS_ASSUME_NONNULL_END

接下来要实现登录的ViewModel层。

//
//  UserViewModel.h
//  RACDemo
//
//  Created by johnny on 2024/6/5.
//

#import <Foundation/Foundation.h>
#import "ReactiveObjC.h"
#import "User.h"

NS_ASSUME_NONNULL_BEGIN

@interface UserViewModel : NSObject

@property (nonatomic, strong) User *user;

// 登录成功
@property (nonatomic, strong) RACSubject *successObject;

// 认证失败
@property (nonatomic, strong) RACSubject *failObject;

// 登录失败
@property (nonatomic, strong) RACSubject *errorObject;

// 登录按钮是否可用
- (id)loginBtnIsValid;

// 登录
-(void)login;

@end

NS_ASSUME_NONNULL_END

登录ViewModel层对应的类的头文件中内容如上所示。其实上面一些常用的信号可以抽象出来放到ViewModel的父类中。属性user是用来绑定用户输入的用户名和密码。上面三个定义信号successObject,failObject,errorObject用来发送网络请求的数据。

successObject 负责处理网络请求成功且符合正常业务逻辑事件。
failObject 负责处理网络请求成功不符合正常业务逻辑的处理
errorObject 负责处理网络异常处理。
结合项目中的实例来解释一下什么时候发送successObject信号,如何发送failureObject信号,何时使用errorObject信号。
以某些理财App中购买理财产品的业务流程为例。在用户下单之前先去判断用户是否实名认证以及绑定银行卡,如果用户已经实名和绑定银行卡就走正常支付流程(用户就是想去下单购买),VM就往VC发送successObject信号,当前VC就会根据信号的指示跳转到下单支付页面。 但是如果用户没有实名或者绑卡,那么VM就给VC发送failureObject信号,根据信号中的参数来判断是走实名认证流程还是走绑定银行卡流程。 errorObject就比较简单了,网络异常,后台服务器抛出的异常等不需要iOS这边做业务逻辑处理的,就放在errorObject中负责错误信息的展示。

文字说完了,如果有些小伙伴还不太明白,那看下面这张原理图吧。把三种信号我们可以类比成十字路口的红绿灯。successObject就是绿灯,可以走正常流程。failureObject是黄灯,先等一下,完成该做的就可以走绿灯了。而errorObject就是一红灯,报错异常,终止业务流程并提升错误信息。有图有真相,到这儿如果还不理解我就没招了。
在这里插入图片描述
备注:借用的图片
在Public方法中- (id) buttonIsValid; 负责返回登录按钮是否可用的信号。- (void)login;发起网络请求,调用登录网络接口。

//
//  UserViewModel.m
//  RACDemo
//
//  Created by johnny on 2024/6/5.
//

#import "UserViewModel.h"


@interface UserViewModel ()

@property (nonatomic, strong)RACSignal *userNameSignal;
@property (nonatomic, strong)RACSignal *pswSignal;

@property (nonatomic, strong) NSArray *requestData;

@end

@implementation UserViewModel

-(User *)user {
    if (_user == nil) {
        _user = [[User alloc]init];
    }
    return _user;
}
// VCViewModel的初始化方法如下,负责初始化属性
-(instancetype)init {
    if (self= [super init]) {
        [self initialize];
    }
    return self;
}


- (void)initialize {
    _userNameSignal = RACObserve(self, self.user.name);
    _pswSignal = RACObserve(self, self.user.pwd);
    
    // 初始化 success
    _successObject = [RACSubject subject];
    _failObject = [RACSubject subject];
    _errorObject = [RACSubject subject];
}

// 合并两个输入框信号,并返回按钮bool类型的值
-(id)loginBtnIsValid {
   RACSignal *isValid = [RACSignal combineLatest:@[_userNameSignal, _pswSignal] reduce:^id (NSString *user, NSString *psw){
        return @(user.length > 3 && psw.length > 3);
    }];
    return isValid;
}

// 模拟网络请求的发送,并发出网络请求成功的信号
-(void)login {
    // 网络请求
    _requestData = @[_user.name, _user.pwd];
    
    NSLog(@"网络请求");
    
    // 成功发送信号
    [_successObject sendNext:_requestData];
    
    [_failObject sendNext:@"认证失败"];
    
    [_errorObject sendNext:@"登录失败"];
}


@end

上面是VM的实现,如果要进行单元测试的话,就对相应的VM类进行初始化,调用相应的函数进行单元测试即可。接着就是看如何在相应的VC模块中使用VM。

剩下的就是在VC中实例化响应的VM类。

//
//  LoginViewController.m
//  RACDemo
//
//  Created by johnny on 2024/6/5.
//

#import "LoginViewController.h"
#import "ReactiveObjC.h"
#import "UserViewModel.h"


@interface LoginViewController ()

@property (nonatomic, strong) UITextField *accountField;
@property (nonatomic, strong) UITextField *pwdField;
@property (nonatomic, strong) UIButton *loginBtn;

@property (nonatomic, strong) UserViewModel *viewModel;

@end

@implementation LoginViewController

-(UserViewModel *)viewModel {
    if (_viewModel == nil) {
        _viewModel = [[UserViewModel alloc]init];
    }
    return _viewModel;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor yellowColor];
    
    [self setUP];
    
    [self bindViewModel];
    
    //按钮点击事件
    @weakify(self);
    [[self.loginBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        NSLog(@"点击登录按钮");
        @strongify(self);
        [self.viewModel login];
    }];
    
    //登录成功要处理的方法
    [_viewModel.successObject subscribeNext:^(id  _Nullable x) {
        NSLog(@"successObject %@", x);
    }];
    
    //fail
    [_viewModel.failObject subscribeNext:^(id  _Nullable x) {
        NSLog(@"failObject %@", x);
    }];
    //error
    [_viewModel.errorObject subscribeNext:^(id  _Nullable x) {
        NSLog(@"errorObject %@", x);
    }];
}

//关联ViewModel
- (void)bindViewModel {
    RAC(self.viewModel.user, name) = _accountField.rac_textSignal;
    RAC(self.viewModel.user, pwd) = _pwdField.rac_textSignal;
    RAC(self.loginBtn, enabled) = _viewModel.loginBtnIsValid;
    
    [_viewModel.loginBtnIsValid subscribeNext:^(id  _Nullable x) {
        NSLog(@"loginBtnIsValid %@", x);
        self.loginBtn.backgroundColor = [UIColor systemPinkColor];
    }];
}

- (void)setUP {
    self.accountField = [[UITextField alloc]initWithFrame:CGRectMake(100, 100, 200, 40)];
    self.accountField.backgroundColor = [UIColor redColor];
    [self.view addSubview:self.accountField];
    
    self.pwdField = [[UITextField alloc]initWithFrame:CGRectMake(100, 160, 200, 40)];
    self.pwdField.backgroundColor = [UIColor greenColor];
    [self.view addSubview:self.pwdField];
    
    self.loginBtn = [[UIButton alloc]initWithFrame:CGRectMake(100, 400, 50, 50)];
    self.loginBtn.backgroundColor = [UIColor blackColor];
    [self.view addSubview:self.loginBtn];
}


@end

到此为止,一个完整模拟登录模块的RAC下的MVVM就实现完毕。

 platform :ios, '12.0'
 

target 'RACDemo' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

 pod 'ReactiveObjC'
 pod 'ReactiveCocoa'


end

post_install do |installer|
  
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['ENABLE_BITCODE'] = 'NO'
      # Xcode 14 以后 bundle 需要需要签名
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '10.0'
#      config.build_settings['EXPANDED_CODE_SIGN_IDENTITY'] = ""
      config.build_settings['CODE_SIGNING_REQUIRED'] = "NO"
      config.build_settings['CODE_SIGNING_ALLOWED'] = "NO"

    end
  end
end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1817510.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

主流3D视频编码技术

3D视频通过模拟人眼的立体视觉&#xff0c;使我们能够感受到深度和距离&#xff0c;提供了一种更加真实而富有沉浸感的视觉体验。长期以来&#xff0c;大量3D视频内容并没有使用专用的视频编码标准&#xff0c;而是使用通用的视频编码标准进行编码。主要的做法是将3D视频以SBS&…

安卓动画特效(帧动画、补间动画、属性动画、遮罩动画及滚动器)

本章介绍App开发中常见的动画特效技术&#xff0c;主要包括&#xff1a;如何使用帧动画实现电影播放效果&#xff0c;如何使用补间动画实现视图的4种基本状态变化&#xff0c;如何使用属性动画实现视图各种状态的动态变换效果&#xff0c;以及如何借助绘图层次与滚动器实现动画…

Javaweb04-Servlet技术2(HttpServletResponse, HttpServletRequest)

Servlet技术基础 HttpServletResponse对象 HttpServletResponce对象是继承ServletResponse接口&#xff0c;专门用于封装Http请求 HttpServletResponce有关响应行的方法 方法说明功能描述void setStatus(int stauts)用于设置HTTP响应消息的状态码&#xff0c;并生成响应状态…

Flutter- AutomaticKeepAliveClientMixin 实现Widget保持活跃状态

前言 在 Flutter 中&#xff0c;AutomaticKeepAliveClientMixin 是一个 mixin&#xff0c;用于给 State 类添加能力&#xff0c;使得当它的内容滚动出屏幕时仍能保持其状态&#xff0c;这对于 TabBarView 或者滚动列表中使用 PageView 时非常有用&#xff0c;因为这些情况下你…

【blender特效】卡通火焰

核心思想就是通过多个不同缩放尺寸的沃罗诺伊叠加&#xff0c;分别构成火焰的大型&#xff0c;中型和小型&#xff08;形状&#xff09;&#xff0c;最后通过自发光纹理实现火焰加亮。 用的是ev渲染&#xff0c;完全可以把噪音贴图都烘焙出来&#xff0c;自己改改shader就可以扔…

Python 越来越火爆

Python 越来越火爆 Python 在诞生之初&#xff0c;因为其功能不好&#xff0c;运转功率低&#xff0c;不支持多核&#xff0c;根本没有并发性可言&#xff0c;在计算功能不那么好的年代&#xff0c;一直没有火爆起来&#xff0c;甚至很多人根本不知道有这门语言。 随着时代的…

从深度嵌套的JSON结构中提取值

问题背景 在某些情况下&#xff0c;我们可能需要从深度嵌套的JSON结构中提取值。例如&#xff0c;给定以下JSON结构&#xff1a; {"foo_code": 404,"foo_rbody": {"query": {"info": {"acme_no": "444444","…

html是什么?http是什么?

html Html是什么&#xff1f;http是什么&#xff1f; Html 超文本标记语言&#xff1b;负责网页的架构&#xff1b; http(&#xff08;HyperText Transfer Protocol&#xff09;超文本传输协议&#xff1b; https&#xff08;全称&#xff1a;Hypertext Transfer Protocol …

数组还可以这样用!常用但不为人知的应用场景

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

NLP入门——数据预处理:子词切分及应用

BPE(Byte-Pair Encoding)算法 【西湖大学 张岳老师&#xff5c;自然语言处理在线课程 第十六章 - 4节】BPE&#xff08;Byte-Pair Encoding&#xff09;编码 如果有一个字符串aabaadaab&#xff0c;对其执行BPE算法 因为字符对aa出现频率最高&#xff0c;因此将其替换为码Z&…

开源-Docker部署Cook菜谱工具

开源-Docker部署Cook菜谱工具 文章目录 开源-Docker部署Cook菜谱工具介绍资源列表基础环境一、安装Docker二、配置加速器三、查看Docker版本四、拉取cook镜像五、部署cook菜谱工具5.1、创建cook容器5.2、查看容器运行状态5.3、查看cook容器日志 六、访问cook菜谱服务6.1、访问c…

Web端在线Stomp服务测试与WebSocket服务测试

Stomp服务测试 支持连接、发送、订阅、接收&#xff0c;可设置请求头、自动重连 低配置云服务器&#xff0c;首次加载速度较慢&#xff0c;请耐心等候 预览页面&#xff1a;http://www.daelui.com/#/tigerlair/saas/preview/lxbho9lkzvgc 演练页面&#xff1a;http://www.da…

设计模式-创建型-04-建造者模式

1、盖房项目需求 1&#xff09;需要建房子&#xff1a;这一过程为打桩、砌墙、封顶2&#xff09;房子有各种各样的&#xff0c;比如普通房&#xff0c;高楼&#xff0c;别墅&#xff0c;各种房子的过程虽然一样&#xff0c;但是要求不要相同的3&#xff09;请编写程序&#xf…

解决linux jenkins要求JDK版本与项目版本JDK不一致问题

背景–问题描述&#xff1a; 新入职公司&#xff0c;交接人说jenkins运行有问题&#xff0c;现在都是手动发布&#xff0c;具体原因让我自己看&#xff08;笑哭&#xff09;。我人都蒙了&#xff0c;测试环境都手动发布&#xff0c;那不是麻烦的要死&#xff01; 接手后&am…

bfs+枚举,CF666B - World Tour

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 666B - Codeforces 二、解题报告 1、思路分析 数据量允许跑N次bfs预处理所有点的最短路&#xff0c;以及预处理到达每个点距离最远的3个点&#xff0c;以及每个点能够到达的最远的3个点 我们枚举…

计算机网络(5) ARP协议

什么是ARP 地址解析协议&#xff0c;即ARP&#xff08;Address Resolution Protocol&#xff09;&#xff0c;是根据IP地址获取物理地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请求广播到局域网络上的所有主机&#xff0c;并接收返回消息&#xff0c;以此确定…

边缘网关在智能制造工厂中的创新应用及效果-天拓四方

在数字化浪潮席卷之下&#xff0c;智能制造工厂正面临着前所未有的数据挑战与机遇。边缘网关&#xff0c;作为数据处理与传输的关键节点&#xff0c;在提升工厂运营效率、确保数据安全方面发挥着日益重要的作用。本文将通过一个具体案例&#xff0c;详细阐述边缘网关在智能制造…

华为云DDoS攻击下的应对策略

当华为云上的服务遭遇大规模DDoS攻击导致网络流量异常&#xff0c;触发了华为云的自动防护机制&#xff0c;即所谓的“黑洞”状态时&#xff0c;服务将暂时无法访问&#xff0c;直至攻击停止或流量恢复正常。本文将探讨如何在这一情况下&#xff0c;通过引入第三方安全产品来快…

如何利用智能家居打造一个“会呼吸的家”?一体化电动窗帘

如何利用智能家居打造一个“会呼吸的家”&#xff1f;一体化电动窗帘 史新华 隐藏式一体化智能电动窗帘与市面上其他窗帘不同的是&#xff0c;电机内置于轨道之中&#xff0c;一体化&#xff0c;美观、安静、滑动顺畅。 每次都会自动打开和关闭&#xff0c;相当漂亮。 众多家庭…

【精品方案】大型企业数字化转型管控平台解决方案(55页PPT),干货满满!

引言&#xff1a;随着信息技术的飞速发展&#xff0c;数字化转型已成为大型企业提升竞争力、实现持续创新的必经之路。本解决方案旨在为企业提供一套全面、高效、灵活的数字化转型管控平台&#xff0c;助力企业实现业务流程的数字化重构、数据资产的智能管理以及决策过程的精准…