首页 > 代码库 > iOS开发项目篇—38深层重构
iOS开发项目篇—38深层重构
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 YYBaseTool10 //get请求11 + (void)getWithUrl:(NSString *)url param:(id)param resultClass:(Class)resultClass success:(void (^)(id))success failure:(void (^)(NSError *))failure12 {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 *))failure29 {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 : YYBaseTool11 /**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 YYUserTool10 +(void)userInfoWithParam:(YYUserInfoParam *)param success:(void (^)(YYUserInfoResult *result))success failure:(void (^)(NSError *error))failure11 {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 : YYBaseTool13 /**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 *))failure11 {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 *))failure32 {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中的成员属性不对应,因此不能直接使用KVC11 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 *)aDecoder28 {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 *)aCoder45 {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 : YYBaseTool11 /**12 *存储数据13 */14 +(void)save:(YYAccountModel *)accountModel;15 /**16 *读取数据17 */18 +(YYAccountModel *)accountModel;19 20 /**21 * 获得accesToken22 *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 YYAccountTool11 12 /**13 *存储数据14 */15 +(void)save:(YYAccountModel *)accountModel16 {17 //使用归档18 [NSKeyedArchiver archiveRootObject:accountModel toFile:YYAccountfilePath];19 }20 /**21 *读取数据22 */23 +(YYAccountModel *)accountModel24 {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 *))failure42 {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 *)code104 {105 // //1.封装请求参数106 // NSMutableDictionary *params=[NSMutableDictionary dictionary];107 // params[@"client_id"] =YYAppKey;108 // params[@"client_secret"] =YYAppSecret;109 // params[@"grant_type"] =@"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_token143 [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)init10 {11 if (self=[super init]) {12 self.access_token=[YYAccountTool accountModel].access_token;13 }14 return self;15 }16 17 +(instancetype)param18 {19 return [[self alloc]init];20 }21 @end
修改控制器中相关实现的示例: