iOS开发项目篇—38深层重构
一、简单说明
说明:可以发现每个工具类,内部方法的代码长相都差不多,可以考虑再抽取出一个处理业务的公共的工具类,让其他的业务类继承自这个工具类,降低代码的冗余度。
1.新建一个工具类
该基础业务处理工具类中的代码设计:
YYBaseTool.h文件
1 // 2 // YYBaseTool.h 3 4 //最基本的业务工具类 5 6 #import <Foundation/Foundation.h> 7 8 @interface YYBaseTool : NSObject 9 + (void)getWithUrl:(NSString *)url param:(id)param resultClass:(Class)resultClass success:(void (^)(id))success failure:(void (^)(NSError *))failure; 10 + (void)postWithUrl:(NSString *)url param:(id)param resultClass:(Class)resultClass success:(void (^)(id))success failure:(void (^)(NSError *))failure; 11 @end
YYBaseTool.m文件
1 // 2 // YYBaseTool.m 3 // 4 5 #import "YYBaseTool.h" 6 #import "MJExtension.h" 7 #import "YYHttpTool.h" 8 9 @implementation YYBaseTool 10 //get请求 11 + (void)getWithUrl:(NSString *)url param:(id)param resultClass:(Class)resultClass success:(void (^)(id))success failure:(void (^)(NSError *))failure 12 { 13 NSDictionary *params = [param keyValues]; 14 15 [YYHttpTool get:url params:params success:^(id responseObj) { 16 if (success) { 17 id result = [resultClass objectWithKeyValues:responseObj]; 18 success(result); 19 } 20 } failure:^(NSError *error) { 21 if (failure) { 22 failure(error); 23 } 24 }]; 25 } 26 27 //post请求 28 +(void)postWithUrl:(NSString *)url param:(id)param resultClass:(Class)resultClass success:(void (^)(id))success failure:(void (^)(NSError *))failure 29 { 30 NSDictionary *params = [param keyValues]; 31 32 [YYHttpTool post:url params:params success:^(id responseObj) { 33 if (success) { 34 id result = [resultClass objectWithKeyValues:responseObj]; 35 success(result); 36 } 37 } failure:^(NSError *error) { 38 if (failure) { 39 failure(error); 40 } 41 }]; 42 } 43 @end
2.其他的业务处理类,继承自该基础类
(1)用户信息业务处理类
YYUserTool.h文件
1 // 2 // YYUserTool.h 3 // 4 5 #import <Foundation/Foundation.h> 6 #import "YYUserInfoParam.h" 7 #import "YYUserInfoResult.h" 8 #import "YYBaseTool.h" 9 10 @interface YYUserTool : YYBaseTool 11 /** 12 * 加载用户的个人信息 13 * 14 * @param param 请求参数 15 * @param success 请求成功后的回调(请将请求成功后想做的事情写到这个block中) 16 * @param failure 请求失败后的回调(请将请求失败后想做的事情写到这个block中) 17 */ 18 +(void)userInfoWithParam:(YYUserInfoParam *)param success:(void (^)(YYUserInfoResult *result))success failure:(void (^)(NSError *error))failure; 19 @end
YYUserTool.m文件代码
1 // 2 // YYUserTool.m 3 // 4 5 #import "YYUserTool.h" 6 #import "YYHttpTool.h" 7 #import "MJExtension.h" 8 9 @implementation YYUserTool 10 +(void)userInfoWithParam:(YYUserInfoParam *)param success:(void (^)(YYUserInfoResult *result))success failure:(void (^)(NSError *error))failure 11 { 12 13 [self getWithUrl:@"https://api.weibo.com/2/users/show.json" param:param resultClass:[YYUserInfoResult class] success:success failure:failure]; 14 15 // [self getWithUrl:@"https://api.weibo.com/2/users/show.json" param:param resultClass:[HMUserInfoResult class] success:success failure:failure]; 16 // //把请求参数模型转换为字典 17 // NSDictionary *params=param.keyValues; 18 // 19 // [YYHttpTool get:@"https://api.weibo.com/2/users/show.json" params:params success:^(id responseObj) { 20 // if (success) { 21 // YYUserInfoResult *result=[YYUserInfoResult objectWithKeyValues:responseObj]; 22 // success(result); 23 // } 24 // } failure:^(NSError *error) { 25 // if (failure) { 26 // failure(error); 27 // } 28 // }]; 29 } 30 @end
(2)微博业务处理类
YYStatusTool.h文件
1 // 2 // YYStatusTool.h 3 // 微博业务类:处理跟微博相关的一切业务,比如加载微博数据、发微博、删微博 4 5 #import <Foundation/Foundation.h> 6 #import "YYHomeStatusesParam.h" 7 #import "YYHomeStatusesResult.h" 8 #import "YYSendStatusesParam.h" 9 #import "YYSendStatusesResult.h" 10 #import "YYBaseTool.h" 11 12 @interface YYStatusTool : YYBaseTool 13 /** 14 * 加载首页的微博数据 15 * 16 * @param param 请求参数 17 * @param success 请求成功后的回调(请将请求成功后想做的事情写到这个block中) 18 * @param failure 请求失败后的回调(请将请求失败后想做的事情写到这个block中) 19 */ 20 21 +(void)homeStatusesWithParam:(YYHomeStatusesParam *)param success:(void (^)(YYHomeStatusesResult *result))success failure:(void(^)(NSError *error))failure; 22 23 /** 24 * 发微博 25 * 26 * @param param 请求参数 27 * @param success 请求成功后的回调(请将请求成功后想做的事情写到这个block中) 28 * @param failure 请求失败后的回调(请将请求失败后想做的事情写到这个block中) 29 */ 30 31 +(void)sendStatusesWithParam:(YYSendStatusesParam *)param success:(void (^)(YYSendStatusesResult *result))success failure:(void(^)(NSError *error))failure; 32 33 @end
YYStatusTool.m文件
1 // 2 // YYStatusTool.m 3 // 4 5 #import "YYStatusTool.h" 6 #import "YYHttpTool.h" 7 #import "MJExtension.h" 8 @implementation YYStatusTool 9 10 +(void)homeStatusesWithParam:(YYHomeStatusesParam *)param success:(void (^)(YYHomeStatusesResult *))success failure:(void (^)(NSError *))failure 11 { 12 13 [self getWithUrl:@"https://api.weibo.com/2/statuses/home_timeline.json" param:param resultClass:[YYHomeStatusesResult class] success:success failure:failure]; 14 15 // //把请求参数模型转换为字典 16 // NSDictionary *params=param.keyValues; 17 // 18 // [YYHttpTool get:@"https://api.weibo.com/2/statuses/home_timeline.json" params:params success:^(id responseObj) { 19 // if (success) { 20 // //把获取的数据(返回结果)由字典转换为模型 21 // YYHomeStatusesResult *result=[YYHomeStatusesResult objectWithKeyValues:responseObj]; 22 // success(result); 23 // } 24 // } failure:^(NSError *error) { 25 // if (failure) { 26 // failure(error); 27 // } 28 // }]; 29 } 30 31 +(void)sendStatusesWithParam:(YYSendStatusesParam *)param success:(void (^)(YYSendStatusesResult *))success failure:(void (^)(NSError *))failure 32 { 33 [self postWithUrl:@"https://api.weibo.com/2/statuses/update.json" param:param resultClass:[YYSendStatusesResult class] success:success failure:failure]; 34 35 // //把请求参数模型转换为字典 36 // NSDictionary *params=param.keyValues; 37 // 38 // [YYHttpTool post:@"https://api.weibo.com/2/statuses/update.json" params:params success:^(id responseObj) { 39 // if (success) { 40 // //把获取的数据(返回结果)由字典转换为模型 41 // YYSendStatusesResult *result=[YYSendStatusesResult objectWithKeyValues:responseObj]; 42 // success(result); 43 // } 44 // } failure:^(NSError *error) { 45 // if (failure) { 46 // failure(error); 47 // } 48 // }]; 49 } 50 @end
说明:在控制器中的代码不需要做额外的更改
二、授权控制器中业务处理(获取access_token)的类似处理
1.新建一个封装请求参数的类,继承自YYBaseTool这个类
查看获取授权,新浪官方需要的请求参数
封装请求参数类的代码设计:
YYAccessTokenParam.h文件
1 // 2 // YYAccessTokenParam.h 3 // 封装获取授权(access_token)的请求参数 4 5 #import "YYBaseTool.h" 6 7 @interface YYAccessTokenParam : YYBaseTool 8 9 /** true string 申请应用时分配的AppKey。*/ 10 @property (nonatomic, copy) NSString *client_id; 11 12 /** true string 申请应用时分配的AppSecret。*/ 13 @property (nonatomic, copy) NSString *client_secret; 14 15 /** true string 请求的类型,填写authorization_code*/ 16 @property (nonatomic, copy) NSString *grant_type; 17 18 /** true string 调用authorize获得的code值。*/ 19 @property (nonatomic, copy) NSString *code; 20 21 /** true string 回调地址,需需与注册应用里的回调地址一致。*/ 22 @property (nonatomic, copy) NSString *redirect_uri; 23 24 @end
YYAccessTokenParam.m文件不做额外的处理。
2.封装返回结果的类
请求成功后,新浪官方接口返回的数据为
项目中的YYAccountModel类能够处理该部分的功能,可以使用这个类来作为封装返回结果的类。
该类的实现代码:
YYAccountModel.h文件
1 // 2 // YYAccountModel.h 3 // 4 5 #import <Foundation/Foundation.h> 6 7 @interface YYAccountModel : NSObject <NSCoding> 8 /**access_token string 用于调用access_token,接口获取授权后的access token。*/ 9 @property(nonatomic,copy)NSString *access_token; 10 11 /**expires_in string access_token的生命周期,单位是秒数。*/ 12 @property(nonatomic,copy)NSString *expires_in; 13 14 /**uid string 当前授权用户的UID。*/ 15 @property(nonatomic,copy)NSString *uid; 16 17 /** 18 *expires_time nsdate access_token的过期时间。 19 */ 20 @property(nonatomic,copy)NSDate *expires_time; 21 22 /** string 用户昵称*/ 23 @property(nonatomic,strong)NSString *name; 24 25 +(instancetype)accountModelWithDcit:(NSDictionary *)dict; 26 @end
YYAccountModel.m文件
1 // 2 // YYAccountModel.m 3 // 4 5 #import "YYAccountModel.h" 6 7 @implementation YYAccountModel 8 +(instancetype)accountModelWithDcit:(NSDictionary *)dict 9 { 10 //注意,json中返回的数据和model中的成员属性不对应,因此不能直接使用KVC 11 YYAccountModel *model=[[self alloc]init]; 12 model.access_token=dict[@"access_token"]; 13 model.expires_in=dict[@"expires_in"]; 14 model.uid=dict[@"uid"]; 15 16 17 //确定账号的过期时间= 账号创建的时间 + 有效期 18 NSDate *now=[NSDate date]; 19 model.expires_time=[now dateByAddingTimeInterval:model.expires_in.doubleValue]; 20 return model; 21 } 22 23 /** 24 *从文件中解析出一个对象的时候调用 25 *在这个方法中写清楚:怎么解析文件中的数据 26 */ 27 -(id)initWithCoder:(NSCoder *)aDecoder 28 { 29 if (self=[super init]) { 30 self.access_token=[aDecoder decodeObjectForKey:@"access_token"]; 31 self.expires_in=[aDecoder decodeObjectForKey:@"expires_in"]; 32 self.uid=[aDecoder decodeObjectForKey:@"uid"]; 33 self.expires_time=[aDecoder decodeObjectForKey:@"expires_time"]; 34 self.name=[aDecoder decodeObjectForKey:@"name"]; 35 36 } 37 return self; 38 } 39 40 /** 41 *将对象写入到文件中的时候调用 42 *在这个方法中写清楚:要存储哪些对象的哪些属性,以及怎样存储属性 43 */ 44 -(void)encodeWithCoder:(NSCoder *)aCoder 45 { 46 [aCoder encodeObject:self.access_token forKey:@"access_token"]; 47 [aCoder encodeObject:self.expires_in forKey:@"expires_in"]; 48 [aCoder encodeObject:self.uid forKey:@"uid"]; 49 [aCoder encodeObject:self.expires_time forKey:@"expires_time"]; 50 [aCoder encodeObject:self.name forKey:@"name"]; 51 52 } 53 @end
3.获取授权的业务处理类
注意:该类继承自YYBaseTool类
该类的代码设计:
YYAccountTool.h文件
1 // 2 // YYAccountTool.h 3 // 4 5 #import <Foundation/Foundation.h> 6 #import "YYAccessTokenParam.h" 7 #import "YYAccountModel.h" 8 9 @class YYAccountModel; 10 @interface YYAccountTool : YYBaseTool 11 /** 12 *存储数据 13 */ 14 +(void)save:(YYAccountModel *)accountModel; 15 /** 16 *读取数据 17 */ 18 +(YYAccountModel *)accountModel; 19 20 /** 21 * 获得accesToken 22 * 23 * @param param 请求参数 24 * @param success 请求成功后的回调(请将请求成功后想做的事情写到这个block中) 25 * @param failure 请求失败后的回调(请将请求失败后想做的事情写到这个block中) 26 */ 27 +(void)accessTokenWithParam:(YYAccessTokenParam *)param success:(void (^)(YYAccountModel *result))success failure:(void (^)(NSError *error))failure; 28 29 @end
YYAccountTool.m文件
1 // 2 // YYAccountTool.m 3 // 4 5 #define YYAccountfilePath [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"account.data"] 6 7 #import "YYAccountTool.h" 8 #import "YYAccountModel.h" 9 10 @implementation YYAccountTool 11 12 /** 13 *存储数据 14 */ 15 +(void)save:(YYAccountModel *)accountModel 16 { 17 //使用归档 18 [NSKeyedArchiver archiveRootObject:accountModel toFile:YYAccountfilePath]; 19 } 20 /** 21 *读取数据 22 */ 23 +(YYAccountModel *)accountModel 24 { 25 //解析归档数据 26 //读取账号 27 YYAccountModel *account = [NSKeyedUnarchiver unarchiveObjectWithFile:YYAccountfilePath]; 28 NSDate *now=[NSDate date]; 29 if ([now compare:account.expires_time]==NSOrderedDescending) {//过期 30 account=nil; 31 } 32 return account; 33 } 34 35 /** 36 NSOrderedAscending = -1L, 升序,越往右边越大 37 NSOrderedSame, 相等,一样 38 NSOrderedDescending 降序,越往右边越小 39 */ 40 41 +(void)accessTokenWithParam:(YYAccessTokenParam *)param success:(void (^)(YYAccountModel *))success failure:(void (^)(NSError *))failure 42 { 43 [self postWithUrl:@"https://api.weibo.com/oauth2/access_token" param:param resultClass:[YYAccountModel class] success:success failure:failure]; 44 } 45 @end
4.在授权控制器中获取授权
1 // 2 // YYOAuthViewController.m 3 // 4 5 #import "YYOAuthViewController.h" 6 #import "MBProgressHUD+MJ.h" 7 //#import "AFNetworking.h" 8 #import "YYTabBarViewController.h" 9 #import "YYNewfeatureViewController.h" 10 #import "YYControllerTool.h" 11 #import "YYAccountModel.h" 12 #import "YYAccountTool.h" 13 //#import "YYHttpTool.h" 14 15 16 17 @interface YYOAuthViewController ()<UIWebViewDelegate> 18 19 @end 20 21 @implementation YYOAuthViewController 22 23 - (void)viewDidLoad 24 { 25 [super viewDidLoad]; 26 27 //1.创建UIWebView 28 UIWebView *webView=[[UIWebView alloc]init]; 29 webView.frame=self.view.bounds; 30 [self.view addSubview:webView]; 31 32 33 //2.加载登陆界面 34 NSString *urlStr=[NSString stringWithFormat:@"https://api.weibo.com/oauth2/authorize?client_id=%@&redirect_uri=%@",YYAppKey,YYRedirectURI]; 35 NSURL *url=[NSURL URLWithString:urlStr]; 36 NSURLRequest *request=[[NSURLRequest alloc]initWithURL:url]; 37 [webView loadRequest:request]; 38 39 //3.设置代理 40 webView.delegate=self; 41 } 42 43 #pragma mark-UIWebViewDelegate 44 /** 45 * UIWebView开始加载资源的时候调用(开始发送请求) 46 */ 47 -(void)webViewDidStartLoad:(UIWebView *)webView 48 { 49 [MBProgressHUD showMessage:@"正在努力加载中···"]; 50 } 51 52 /** 53 * UIWebView加载完毕的时候调用(请求结束) 54 */ 55 -(void)webViewDidFinishLoad:(UIWebView *)webView 56 { 57 [MBProgressHUD hideHUD]; 58 } 59 60 /** 61 * UIWebView加载失败的时候调用(请求失败) 62 */ 63 -(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error 64 { 65 [MBProgressHUD hideHUD]; 66 } 67 68 /** 69 * UIWebView每当发送一个请求之前,都会先调用这个代理方法(询问代理允不允许加载这个请求) 70 * @param request 即将发送的请求 71 * @return YES允许加载,NO不允许加载 72 */ 73 -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType 74 { 75 //1.获得请求地址 76 NSString *urlStr=request.URL.absoluteString; 77 // NSLog(@"%@",urlStr); 78 79 //2.判断url是否为回调地址 80 //urlStr在字符串中的范围 81 //设置从等号位置开始,不用再额外的找位置 82 NSString *redirectURI=[NSString stringWithFormat:@"%@?code=",YYRedirectURI]; 83 NSRange range=[urlStr rangeOfString:redirectURI]; 84 //判断是否为回调地址 85 if (range.location!=NSNotFound) {//是回调地址 86 //截取授权成功后的请求标记 87 int from=range.location+range.length; 88 NSString *code=[urlStr substringFromIndex:from]; 89 // YYLog(@"%@--%@--",urlStr,code); 90 91 //根据code获得一个accessToken 92 [self accessTokenWithCode:code]; 93 94 //禁止加载回调页面,拿到想要的东西就好了。 95 return NO; 96 } 97 return YES; 98 } 99 /** 100 * 根据code获得一个accessToken(发送一个Post请求) 101 * @param code 授权成功后的请求标记 102 */ 103 -(void)accessTokenWithCode:(NSString *)code 104 { 105 // //1.封装请求参数 106 // NSMutableDictionary *params=[NSMutableDictionary dictionary]; 107 // params[@"client_id"] =YYAppKey; 108 // params[@"client_secret"] =YYAppSecret; 109 // params[@"grant_type"] [email protected]"authorization_code"; 110 // params[@"code"] =code; 111 // params[@"redirect_uri"] =YYRedirectURI; 112 // 113 // //2.发送网络请求 114 // [YYHttpTool post:@"https://api.weibo.com/oauth2/access_token" params:params success:^(id responseObj) { 115 // //隐藏遮罩 116 // [MBProgressHUD hideHUD]; 117 // 118 // //字典转模型 119 // YYAccountModel *accountmodel=[YYAccountModel accountModelWithDcit:responseObj]; 120 // //存储账号模型 121 // [YYAccountTool save:accountmodel]; 122 // 123 // //3.2切换控制器 124 // [YYControllerTool chooseRootViewController]; 125 // 126 // YYLog(@"请求成功"); 127 // 128 // } failure:^(NSError *error) { 129 // //隐藏遮罩 130 // [MBProgressHUD hideHUD]; 131 // YYLog(@"请求失败"); 132 // }]; 133 134 //1.封装请求参数 135 YYAccessTokenParam *param=[[YYAccessTokenParam alloc]init]; 136 param.client_id=YYAppKey; 137 param.client_secret=YYAppSecret; 138 param.grant_type=@"authorization_code"; 139 param.code=code; 140 param.redirect_uri=YYRedirectURI; 141 142 //2.获取access_token 143 [YYAccountTool accessTokenWithParam:param success:^(YYAccountModel *accountmodel) { 144 //隐藏遮罩 145 [MBProgressHUD hideHUD]; 146 147 //存储账号模型 148 [YYAccountTool save:accountmodel]; 149 150 //切换控制器 151 [YYControllerTool chooseRootViewController]; 152 153 YYLog(@"请求成功"); 154 } failure:^(NSError *error) { 155 //隐藏遮罩 156 [MBProgressHUD hideHUD]; 157 YYLog(@"请求失败"); 158 }]; 159 } 160 @end
三、补充
1.结构示意图
新建一个基本请求参数封装类,把access_token属性抽取出来,其他的请求参数封装类继承该类。
调整项目的文件结构如下:
在请求参数中,每次请求都需要获取相同的成功授权的标记,可以考虑在基础类中进行抽取。
YYBaseParam.h文件
1 // 2 // YYBaseParam.h 3 // 4 5 #import <Foundation/Foundation.h> 6 7 @interface YYBaseParam : NSObject 8 9 /**access_token false string 采用OAuth授权方式为必填参数,其他授权方式不需要此参数,OAuth授权后获得。*/ 10 @property(nonatomic,copy)NSString *access_token; 11 12 +(instancetype)param; 13 @end
YYBaseParam.m文件
1 // 2 // YYBaseParam.m 3 // 4 5 #import "YYBaseParam.h" 6 #import "YYAccountTool.h" 7 8 @implementation YYBaseParam 9 -(id)init 10 { 11 if (self=[super init]) { 12 self.access_token=[YYAccountTool accountModel].access_token; 13 } 14 return self; 15 } 16 17 +(instancetype)param 18 { 19 return [[self alloc]init]; 20 } 21 @end
修改控制器中相关实现的示例:
iOS开发项目篇—38深层重构