一,创建工程,并用cocoapods
安装了如下库
pod 'ReactiveCocoa', '~>2.5'
pod 'AFNetworking', '~> 3.1.0'
pod 'MJExtension', '~> 3.0.9'
pod 'MJRefresh', '~> 3.1.0'
二,编写代码
首先创建了如下的类
-
SJApiModel
将请求地址,参数,模型类,回调封装成一个Model
@interface SJApiModel : NSObject
@property (nonatomic, strong, readonly) SJApiHelper * _Nonnull manager;
/**
* 请求地址
*/
@property(nonatomic,strong) NSString * _Nonnull url ;
/**
* 请求参数
*/
@property(nonatomic,strong) NSMutableDictionary * _Nullable params ;
/**
* 成功回调
*/
@property(nonatomic,strong) void(^ _Nullable successfulBlock)(id _Nullable obj);
/**
* 错误回调
*/
@property(nonatomic,strong) void(^ _Nullable errBlock)(NSError * _Nullable error);
/**
* 模型的类
*/
@property(nonatomic,strong) Class _Nullable cls;
/**
* 请求方法
*/
@property(nonatomic,assign) RequestMethod method;
/**
* 请求
*/
- (NSURLSessionTask * _Nullable)request;
@end
-
SJApiModel
实现文件
@interface SJApiModel ()
@property (nonatomic, strong) SJApiHelper *manager;
@property (nonatomic, strong) AFNetworkReachabilityManager *statusManager;
@property (nonatomic, strong) NSURLSessionTask *currentOperation;
@end
@implementation SJApiModel
- (SJApiHelper *)manager {
if (!_manager) {
_manager = [[SJApiHelper alloc] init];
}
return _manager;
}
- (AFNetworkReachabilityManager *)statusManager {
if (!_statusManager) {
_statusManager = [AFNetworkReachabilityManager sharedManager];
}
return _statusManager;
}
- (instancetype)init {
if (self == [super init]) {
_currentOperation = nil;
self.params = [NSMutableDictionary dictionary];
self.method = RequestMethodGET;
}
return self;
}
- (NSURLSessionTask *)request {
if (self.statusManager.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) {
// 如果没有联网的情况我们直接 return
return nil;
}
@weakify(self);
// 通过 SJApiHelper 发起请求 并将返回的 Task 保存起来
self.currentOperation = [self.manager requestWirlApiURLString:self.url
params:self.params
method:self.method success:^(id _Nullable obj) {
@strongify(self);
// 请求成功 将请求的到数据转换成模型然后再传递出去 [self createModel:obj];
} failure:^(NSError * _Nullable error) {
@strongify(self);
!self.errBlock ?: self.errBlock(error);
}];
return nil;
}
- (void)createModel:(id)responseObject {
// 首先判断数据是否有值
if (!responseObject) {
!self.successfulBlock ?: self.successfulBlock(nil);
return;
}
// 在判断是否设置了模型类
if (self.cls) {
id obj;
// 如果有设置 模型类 则 利用字典转模型框架转换为对应的模型 并回调出去
if ([responseObject isKindOfClass:[NSDictionary class]]) {
obj = [self.cls mj_objectWithKeyValues:responseObject];
} else if ([responseObject isKindOfClass:[NSArray class]]) {
obj = [self.cls mj_objectArrayWithKeyValuesArray:responseObject];
}
// 回主线程回调
dispatch_async(dispatch_get_main_queue(), ^{
!self.successfulBlock ?: self.successfulBlock(obj);
});
} else {
// 如果没有设置 模型类 则传递原始数据
!self.successfulBlock ?: self.successfulBlock(responseObject);
}
}
-(void)dealloc{
[self.currentOperation cancel];
}
@end
-
SJApiHelper
通过这个类来发起请求,在头文件声明了一个请求的API
@interface SJApiHelper : NSObject
/**
* 网络请求
*
* @param apiURLString 请求的后缀地址
* @param params 请求参数
* @param method 请求类型
* @param success 请求成功的回调
* @param failure 请求失败的回调
*/
- (NSURLSessionTask*)requestWirlApiURLString:(NSString*)apiURLString
params:(NSDictionary*)params
method:(RequestMethod)method
success:(SuccessfulBlock)success
failure:(ErrBlock)failure;
@end
- 再来看看这个类的实现文件
// 这只是简单版
// 可以在这里可以配置网络请求
// 列如加载证书, 设置请求,响应的序列化格式,请求超时等等
// 等等.....
@interface SJApiHelper()
@property(nonatomic,strong)AFHTTPSessionManager *manager;
@end
@implementation SJApiHelper
- (instancetype)init {
if (self == [super init]) {
self.manager = [AFHTTPSessionManager manager];
}
return self;
}
- (NSURLSessionTask *)requestWirlApiURLString:(NSString *)apiURLString params:(NSDictionary *)params method:(RequestMethod)method success:(SuccessfulBlock)success failure:(ErrBlock)failure {
if (method == RequestMethodGET) {
return [self.manager GET:apiURLString
parameters:params
progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
success(responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
failure(error);
}];
} else if (method == RequestMethodPOST) {
return [self.manager POST:apiURLString
parameters:params
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
success(responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
failure(error);
}];
} else {
return nil;
}
}
@end
```
- `SJBaseViewModel` 声明文件.支持链式调用
```objective-c
/** 通知界面刷新 */
@interface SJVMNotificationObject : NSObject
/**
* 刷新的类型,类型的枚举自己定义,枚举类型是NSInteger
*/
@property(nonatomic,assign) NSInteger notificationType;
/**
* 刷新时带的数据
*/
@property(nonatomic,strong) id object;
/**
* 其它刷新时带的数据
*/
@property(nonatomic,strong) id otherObject;
//生成一个只有通知类型的简单信号通知
+ (SJVMNotificationObject*)createWithNotionType:(NSInteger)type;
@end
@class ApiModel;
@interface SJBaseViewModel : NSObject
// ViewModel里面刷新数据
@property (nonatomic, strong) UITableView *tableView ;
// 通知界面刷新的信号
@property(nonatomic,strong) RACSignal *signal;
/**
* 创建ViewModel,初始化网络请求对象
*/
+ (SJBaseViewModel *(^)())create;
/**
* 设置请求的地址
*/
- (SJBaseViewModel *(^)(NSString* url))setUrl;
/**
* 设置请求方法
*/
- (SJBaseViewModel *(^)(RequestMethod method))setMethod;
/**
* 增加请求的参数
*/
- (SJBaseViewModel *(^)(NSDictionary* params))addParams;
/**
* 设置返回对象类型
*/
- (SJBaseViewModel *(^)(Class cls))setResponseClass;
/**
* 设置网络请求回调函数
*/
- (SJBaseViewModel *(^)(void(^block)(id obj)))setRequestBlock;
/**
* 请求
*/
- (SJBaseViewModel *(^)())request;
/**
* 通知界面刷新数据
*/
- (void)refreshNotification:(SJVMNotificationObject *)obj;
/**
* 没有设置block的情况下提供一个默认的方法来处理网络请求的回调
*
* @param obj 网络请求后的数据
*/
- (void)dataResponse:(id)obj;
//销毁 可以做一些清理操作
- (void)destory;
@end
- 实现文件
@implementation SJVMNotificationObject
+ (SJVMNotificationObject*)createWithNotionType:(NSInteger)type
{
SJVMNotificationObject* obj = [[SJVMNotificationObject alloc] init] ;
obj.notificationType = type ;
return obj ;
}
@end
@interface SJBaseViewModel ()
@property(nonatomic,strong) SJApiModel *apiModel ;
@property(nonatomic,strong) id<RACSubscriber> subscriber;
@end
@implementation SJBaseViewModel
+ (SJBaseViewModel *(^)())create
{
return ^{
SJBaseViewModel* vm = [[SJBaseViewModel alloc] init] ;
vm.apiModel = [[SJApiModel alloc] init] ;
vm.apiModel.cls = nil;
return vm ;
};
}
- (void)dealloc
{
}
- (instancetype)init
{
self = [super init] ;
if(self)
{
[self setup];
}
return self ;
}
- (void)setup
{
self.apiModel = [[SJApiModel alloc] init] ;
@weakify(self);
// 创建信号 以及订阅者
self.signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self);
self.subscriber = subscriber ;
return [RACDisposable disposableWithBlock:^{
}];
}];
// 设置回调
self.apiModel.successfulBlock = ^(id obj){
@strongify(self);
[self dataResponse:obj] ;
};
}
- (SJBaseViewModel *(^)(NSString* url))setUrl
{
return ^(NSString* url){
self.apiModel.url = url ;
return self ;
};
}
- (SJBaseViewModel *(^)(RequestMethod method))setMethod
{
return ^(RequestMethod method){
self.apiModel.method = method ;
return self ;
};
}
- (SJBaseViewModel *(^)(NSDictionary* params))addParams
{
return ^(NSDictionary* params){
[self.apiModel.params addEntriesFromDictionary:params] ;
return self ;
};
}
- (SJBaseViewModel *(^)(Class cls))setResponseClass
{
return ^(Class cls){
self.apiModel.cls = cls;
return self ;
};
}
- (SJBaseViewModel *(^)(void(^block)(id obj)))setRequestBlock
{
return ^(void(^block)(id obj)){
self.apiModel.successfulBlock = block ;
return self ;
};
}
- (SJBaseViewModel *(^)())request
{
return ^{
[self.apiModel request];
return self;
};
}
- (void)refreshNotification:(SJVMNotificationObject *)obj
{
@weakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
@strongify(self);
// 通过订阅者发送一个消息 并携带一个数据
[self.subscriber sendNext:obj];
});
}
- (void)dataResponse:(id)obj
{
//子类来重载实现
}
- (void)destory
{
//子类重载
}
@end
三,实践
-
首先用SB 搭建一个界面,并连好引用
Markdown -
然后新建一个 继承自
SJBaseViewModel
的ViewModel
#import "SJBaseViewModel.h"
// 定义通知类型
typedef NS_OPTIONS(NSUInteger, SJSearchViewModelType) {
SJSearchViewModelTypeSucces = 0,
};
@interface SJSearchViewModel : SJBaseViewModel
// 加载数据
- (void)loadDataWithSearcText:(NSString *)text;
@end
// 实现文件
#import "SJSearchViewModel.h"
#import "GitHubModel.h"
@implementation SJSearchViewModel
- (void)loadDataWithSearcText:(NSString *)text {
self.setUrl(@"https://api.github.com/search/repositories")
.addParams(@{@"q" : text})
.setResponseClass([GitHubModel class]) // 设置模型类
.setRequestBlock(^(GitHubModel *obj){
// 请求成功的回调就是模型
if (obj) {
// 创建一个通知 并绑定类型以及需要携带的数据
SJVMNotificationObject *notObj = [[SJVMNotificationObject alloc] init];
notObj.notificationType = SJSearchViewModelTypeSucces;
notObj.object = obj;
// 更新通知
[self refreshNotification:notObj];
}
})
.request();
}
@end
- 新建一个
GitHubModel
模型类
#import <Foundation/Foundation.h>
@interface GitHUbInfoModel : NSObject
@property (nonatomic, strong) NSString *ID;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *full_name;
@end
@interface GitHubModel : NSObject
@property (nonatomic, assign) NSInteger total_count;
@property (nonatomic, strong) NSArray<GitHUbInfoModel *> *items;
@end
#import "GitHubModel.h"
#import <MJExtension/MJExtension.h>
@implementation GitHUbInfoModel
+ (void)load {
[GitHUbInfoModel mj_setupReplacedKeyFromPropertyName:^NSDictionary *{
return @{@"ID" : @"id"};
}];
}
@end
@implementation GitHubModel
+ (void)load {
[GitHubModel mj_setupObjectClassInArray:^NSDictionary *{
return @{@"items" : [GitHUbInfoModel class]};
}];
}
@end
- 控制器代码
#import "ViewController.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
#import "SJSearchViewModel.h"
#import "GitHubModel.h"
@interface ViewController ()<UITableViewDataSource>
@property (weak, nonatomic) IBOutlet UITextField *inpuView;
@property (weak, nonatomic) IBOutlet UIButton *searchBtn;
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic, strong) SJSearchViewModel *viewModel;
@property (nonatomic, strong) NSArray<GitHUbInfoModel *> *dataSouces;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.searchBtn.enabled = NO;
RAC(self.searchBtn, enabled) = [RACSignal combineLatest:@[self.inpuView.rac_textSignal] reduce:^id(NSString *text){
return text.length >= 5 ? @(YES) : @(NO);
}];
@weakify(self);
[[self.searchBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
@strongify(self);
[self loadData];
}];
self.viewModel = [[SJSearchViewModel alloc] init];
// 通过ViewModel 的 signal 监听 数据更新的通知
[self.viewModel.signal subscribeNext:^(SJVMNotificationObject *x) {
@strongify(self);
if (x.notificationType == SJSearchViewModelTypeSucces) {
[self dataChange:x.object];
}
}];
self.dataSouces = [NSArray array];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
}
- (void)loadData {
NSLog(@"请求数据.....");
[self.inpuView resignFirstResponder];
// 调用ViewModel 请求数据
[self.viewModel loadDataWithSearcText:self.inpuView.text];
}
- (void)dataChange:(GitHubModel *)obj {
if (obj) {
// 请求到数据 然后展示
self.dataSouces = obj.items;
[self.tableView reloadData];
}
}
#pragma mark ------------------------------------
#pragma mark UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.dataSouces.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
cell.textLabel.text = self.dataSouces[indexPath.row].name;
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [NSString stringWithFormat:@"搜索到 %ld 条数据", self.dataSouces.count];
}
@end
四,最终的效果
我的微博
我的小站
GitHub源码
网友评论