当前位置: 首页 > news >正文

iOS ReactiveCocoa MVVM

学习了在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;@endNS_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;@endNS_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 frameworksuse_frameworks!pod 'ReactiveObjC'pod 'ReactiveCocoa'endpost_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"endend
end

相关文章:

iOS ReactiveCocoa MVVM

学习了在MVVM中如何使用RactiveCocoa&#xff0c;简单的写上一个demo。重点在于如何在MVVM各层之间使用RAC的信号来更方便的在各个层之间进行响应式数据交互。 demo需求&#xff1a;一个登录界面(登录界面只有账号和密码都有输入&#xff0c;登录按钮才可以点击操作)&#xff0…...

图文解析ASN.1中BER编码:结构类型、编码方法、编码实例

本文将详细介绍ASN.1中的BER编码规则&#xff0c;包括其编码机制、数据类型表示、以及如何将复杂的数据结构转换为二进制数据。通过本文的阅读&#xff0c;读者将对ASN.1中的BER编码有一个全面的理解。 目录 一.引言 二.BER编码基本结构 ▐ 1. 类型域&#xff08;Type&#…...

jQuery如何停止动画队列

在jQuery中&#xff0c;你可以使用.stop()方法来停止动画队列。.stop()方法有几个可选的参数&#xff0c;可以用来控制停止动画的方式。 以下是.stop()方法的基本用法和一些参数选项&#xff1a; 无参数&#xff1a;立即停止当前动画&#xff0c;并跳到最后的状态。后续的动画…...

vue3+electron搭建桌面软件

vue3electron开发桌面软件 最近有个小项目, 客户希望像打开 网易云音乐 那么简单的运行起来系统. 前端用 Vue 会比较快一些, 因此决定使用 electron 结合 Vue3 的方式来完成该项目. 然而, 在实施过程中发现没有完整的博客能够记录从创建到打包的流程, 摸索一番之后, 随即梳理…...

oracle常用经典SQL查询

oracle常用经典SQL查询(转贴) oracle常用经典SQL查询 常用SQL查询&#xff1a; 1、查看表空间的名称及大小 select t.tablespace_name, round(sum(bytes/(1024*1024)),0) ts_size from dba_tablespaces t, dba_data_files d where t.tablespace_name d.tablespace_name grou…...

Android shell 常用 debug 命令

目录 1、查看版本2、am 命令3、pm 命令4、dumpsys 命令5、sed命令6、log定位查看APK进程号7、log定位使用场景 1、查看版本 1.1、Android串口终端执行 getprop ro.build.version.release #获取Android版本 uname -a #查看linux内核版本信息 uname -r #单独查看内核版本 1.2、…...

Unity3D Shader数据传递语法详解

在Unity3D中&#xff0c;Shader是用于渲染图形的一种程序&#xff0c;它定义了物体在屏幕上的外观。Shader通过接收输入数据&#xff08;如顶点位置、纹理坐标、光照信息等&#xff09;并计算像素颜色来工作。为了使得Shader能够正确运行并产生期望的视觉效果&#xff0c;我们需…...

计算机组成原理(五)

一、链式查询方式 接口的优先级固定不变 在链式查询的情况下&#xff0c;设备的优先级通常与其在链中的位置有关。具体来说&#xff0c;越靠近查询链的起始位置的设备通常具有较高的优先级&#xff0c;而越靠近链的末尾位置的设备优先级较低。 优点&#xff1a; 简单实现&am…...

后端项目实战--瑞吉外卖项目软件说明书

瑞吉外卖项目软件说明书 一、项目概述 瑞吉外卖项目是一个外卖服务平台&#xff0c;用户可以通过该平台浏览餐厅菜单、下单、支付以及追踪订单状态。产品原型就是一款产品成型之前的一个简单的框架&#xff0c;就是将页面的排版布局展现出来&#xff0c;使产品得初步构思有一…...

LeetCode | 27.移除元素

这道题的思路和26题一模一样&#xff0c;由于要在元素组中修改&#xff0c;我们可以设置一个index表示目前要修改原数组的第几位&#xff0c;由于遍历&#xff0c;访问原数组永远会在我们修改数组之前&#xff0c;所以不用担心数据丢失的问题&#xff0c;一次遍历数组&#xff…...

为什么要选择AWS?AWS的优势有哪些?

亚马逊云服务器&#xff08;Amazon Web Services&#xff0c;AWS&#xff09;是全球领先的云计算服务提供商之一&#xff0c;其提供的云服务器是在全球范围内可用的弹性计算服务。对于很多用户来说&#xff0c;他们可能会担心亚马逊云服务器是否会对服务器的使用进行限制。以下…...

【Intel CVPR 2024】通过图像扩散模型生成高质量360度场景,只需要一个语言模型

在当前人工智能取得突破性进展的时代&#xff0c;从单一输入图像生成全景场景仍是一项关键挑战。大多数现有方法都使用基于扩散的迭代或同步多视角内绘。然而&#xff0c;由于缺乏全局场景布局先验&#xff0c;导致输出结果存在重复对象&#xff08;如卧室中的多张床&#xff0…...

postman教程-21-Newman运行集合生成测试报告

上一小节我们Postman Newman的安装方法&#xff0c;本小节我们讲解一下Postman Newman的具体使用方法。 使用Newman运行集合 1、导出Postman集合&#xff1a; 在Postman中&#xff0c;选择你想要运行的集合&#xff0c;然后点击“导出”按钮&#xff0c;选择导出为“Collect…...

基于条件谱矩的时间序列分析(以轴承故障诊断为例,MATLAB)

谱矩方法可以对数据的表面形貌做较为细致的描述&#xff0e;它以随机过程为理论基础&#xff0c;用各阶谱矩及统计不变量等具体的参数表征表面的几何形态&#xff0c;算术平均顶点曲率是一种基于四阶谱矩的统计不变量。 鉴于此&#xff0c;采用条件谱矩方法对滚动轴承进行故障诊…...

ArcGIS Pro 3.0加载在线高德地图

1、打开ArcGIS Online官网&#xff0c;登录自己的账号&#xff0c;登录后效果如下图所示 官网地址&#xff1a;https://www.arcgis.com/home/webmap/viewer.html 2、点击Add&#xff0c;选择Add Layer from Web&#xff0c;如下图所示 3、在显示的Add Layer from Web页面内&am…...

服务器防漏扫,主机加固方案来解决

什么是漏扫&#xff1f; 漏扫是漏洞扫描的简称。漏洞扫描是一种安全测试方法&#xff0c;用于发现计算机系统、网络或应用程序中的潜在漏洞和安全弱点。通过使用自动化工具或软件&#xff0c;漏洞扫描可以检测系统中存在的已知漏洞&#xff0c;并提供相关的报告和建议&#xf…...

Linux2(基本命令2)

目录 一、文件类型分类 二、基本命令 1. find 帮助查询 2. stat 查看文件的信息 3. wc 统计文本 4. 查看文本内容 4.1 cat 4.2 more 4.3 less 4.4 head 4.5 tail 5. cal 显示日历 6. date 显示时间 7. du 文件大小 8. ln 链接 软链接 硬链接 区别 9. history…...

拼团+秒杀+优惠折扣+个人免签双端商城源码

源码说明 可用拼团秒杀优惠折扣个人免签双端商城源码&#xff0c;全功能完美双端&#xff0c;对接个人免签支付。 这款商城源码非常完整&#xff0c;整体也非常简洁&#xff0c;功能全面&#xff0c;没有那么多冗杂的多余页面和无用代码&#xff0c;拿到后优化了下整体代码&a…...

【数据结构】双向链表(C语言)

哈喽铁子们&#xff0c;这里是博主鳄鱼皮坡。这篇文章将分享交流双向链表的相关知识&#xff0c;下面正式开始。 1. 双向链表的结构 注意&#xff1a;这里的“带头”跟前面我们说的“头节点”是两个概念&#xff0c;实际前面的在单链表阶段称呼不严 谨&#xff0c;但是为了老…...

【TensorFlow深度学习】WGAN与DCGAN在图像生成中的应用实例

WGAN与DCGAN在图像生成中的应用实例 WGAN与DCGAN在图像生成中的应用实例&#xff1a;一场深度学习的视觉盛宴DCGAN简介WGAN简介应用实例&#xff1a;基于DCGAN的图像生成应用实例&#xff1a;WGAN的图像生成实践结语 WGAN与DCGAN在图像生成中的应用实例&#xff1a;一场深度学习…...

垫付商贩任务补单平台补单系统网站源码提供

垫付商贩任务补单平台补单系统网站源码提供...

vue富文本wangeditor加@人功能(vue2 vue3都可以)

依赖 "wangeditor/editor": "^5.1.23", "wangeditor/editor-for-vue": "^5.1.12", "wangeditor/plugin-mention": "^1.0.0",RichEditor.vue <template><div style"border: 1px solid #ccc; posit…...

######## redis各章节终篇索引(更新中) ############

其他 父子关系&#xff08;ctx、协程&#xff09;#### golang存在的父子关系 ####_子goroutine panic会导致父goroutine挂掉吗-CSDN博客 参数传递&#xff08;slice、map&#xff09;#### go中参数传递&#xff08;涉及&#xff1a;切片slice、map、channel等&#xff09; ###…...

一个基于MySQL的数据库课程设计的基本框架

数据库课程设计&#xff08;MySQL&#xff09;通常涉及多个步骤&#xff0c;以确保数据库的有效设计、实现和维护。以下是一个基于MySQL的数据库课程设计的基本框架&#xff0c;结合参考文章中的相关信息进行整理&#xff1a; ### 一、引言 * **背景**&#xff1a;简要介绍为…...

架构设计基本原则

开闭原则 开闭原则&#xff08;Open Closed Principle&#xff0c;OCP&#xff09;是面向对象编程&#xff08;OOP&#xff09;中的一个核心原则&#xff0c;主要强调的是软件实体&#xff08;类、模块、函数等&#xff09;应该对扩展开放&#xff0c;对修改封闭。 解释&…...

云原生应用开发培训,开启云计算时代的新征程

在云计算时代&#xff0c;云原生应用开发技术已经成为IT领域的热门话题。如果您想要转型至云原生领域&#xff0c;我们的云原生应用开发培训将帮助您开启新征程。 我们的课程内容涵盖了云原生技术的基础概念、容器技术、微服务架构、持续集成与持续发布&#xff08;CI/CD&#…...

【数据库设计】宠物商店管理系统

目录 &#x1f30a;1 问题的提出 &#x1f30a;2 需求分析 &#x1f30d;2.1 系统目的 &#x1f30d;2.2 用户需求 &#x1f33b;2.2.1 我国宠物行业作为新兴市场&#xff0c;潜力巨大 &#x1f33b;2.2.2 我国宠物产品消费规模逐年增大 &#x1f33b;2.2.3 我国宠物主选…...

前端 JS 经典:node 的模块查找策略

前言&#xff1a;我们引入模块后&#xff0c;node 大概的查找步骤分为 文件查找、文件夹查找、内置模块查找、第三方模块查找&#xff0c;在 node 中使用 ESM 模块语法&#xff0c;需要创建 package.json 文件&#xff0c;并将 type 设置为 module。简单起见&#xff0c;我们用…...

C++中的23种设计模式

目录 摘要 创建型模式 1. 工厂方法模式&#xff08;Factory Method Pattern&#xff09; 2. 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09; 3. 单例模式&#xff08;Singleton Pattern&#xff09; 4. 生成器模式&#xff08;Builder Pattern&#xff0…...

vue.js+node.js+mysql在线聊天室源码

vue.jsnode.jsmysql在线聊天室源码 技术栈&#xff1a;vue.jsElement UInode.jssocket.iomysql vue.jsnode.jsmysql在线聊天室源码...

西安做网站魔盒/万网注册域名查询

1&#xff0c;HQL查询 Hibernate 支持两种主要的查询方式&#xff1a; Criteria查询&#xff0c;又称为“对象查询”&#xff0c;它用面向对象的方式将构造查询的过程做了封装映射文件。HQL&#xff08;Hibernate Query Languge&#xff0c;Hibernate 查询语言&#xff09;查询…...

vue.js 可以做网站吗/如何自己开发软件app

...

泊头西环网站建设/菏泽地网站seo

名称&#xff1a;text-decoration分类&#xff1a;字体样式简述&#xff1a;设置字体闪烁、上划线、下划线、贯穿线概述&#xff1a;text-decoration是控制文字闪烁、上划线、下划线、贯穿线等文字装饰效果的样式(Style)。一般来说&#xff0c;text-decoration使用最多的地方是…...

聊城网站制作/百度云网盘

可以使用Python中的字典来实现对每行日志模板频率的统计。具体的代码如下&#xff1a; withopen(HDFS.TXT, r) as file:freq_dict {}for line in file:template line.strip() # 去除行末的换行符if template in freq_dict:freq_dict[template] 1else:freq_dict[template] …...

iis wordpress index.php/iis7站长工具

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

桥头网站建设/大数据精准获客软件

使用方法&#xff1a; 需要右滑返回的activity继承baseActivity就可以&#xff0c; 如&#xff1a; public class SettingActivity extends BaseActivity {} 为防止滑动出现黑屏效果&#xff0c;记得需要定义 <!-- 滑动activity必须使用该主题&#xff0c;防侧滑时黑屏 --&g…...