1.前言
-
最近整理以前项目的资料,处理推送消息的时候显示只能在主页面,安卓那边就很好处理,可以根据不同的推送消息指定跳转到任意控制器,那么iOS 能不能实现呢?答案是肯定的!
-
逛gitHub,真有人实现了,能跳转,不过需要后台配合,传一个要跳转的控制器的名字以及指定控制器所需参数 原文 地址 ,原文代码就不拷贝过来了,简单说说出现的问题吧
-
问题一
没有封装工具类使用,delegate里面实现并处理逻辑,这显然不应该是delegate去做,造成delegate 冗余
-
问题二
面向字典开发,可复用性差,处理情况单一(json格式定死),逻辑处理不完善,数据获取麻烦且容易出错(需要写key,字符串容易出错,如果需要多次使用就更麻烦)
-
2.完善
-
封装工具类,继承NSObject,解耦合,移植性、复用性、拓展性强,对外提供一个方法,专门处理推送消息,自动跳转控制器,交给专业的人去做,更加面向对象
-
面向模型,后台返回json数据,转模型存储数据,存储方便,不容易出错,而且如果后台返回的json格式不一样了,我依然可以创建子类,重写构造方法进行特殊处理,而且面向模型开发,不也更面向对象吗?哈哈,反正我喜欢模型
3.API 设计
-
工具类一般弄成单例,需要一个单例创建方法,还需要对外提供一个方法,接收到外界数据后,处理逻辑并跳转到指定控制器
-
数据当然从模型里面那,因此还需要一个模型类,专门存储消息数据,在工具类中创建,统一管理,最关键、最重要的是claseName属性,后台必须返回一个需要跳转的控制器的名字给我,其他不同控制器对应的属性,就交给子类去添加,有属性当然就需要自定义构造方法,字典转模型嘛
/*
* @author Clarence-lie 孔凡列
*
* QQ:279761135
*
* gitHub:https://github.com/gitkong
* cocoaChina:http://code.cocoachina.com/user/index/upload/
* 简书 : http://www.jianshu.com/users/fe5700cfb223/latest_articles
* 喜欢给个like & star呗~
*/
#import <Foundation/Foundation.h>
/**
* @author Clarence-lie, 16-09-08 21:09:07
*
* 子类继承,对应不同的控制器所需的参数,添加对应的属性到子类中,默认层级都是同一级,子类可重写fl_remotePushModel方法,对应不同的字典转模型
*
* 注意一点:子类中所添加的属性,应该都为对象,基本数据类型不行喔,而且后台返回的都是对象,基本数据用NSNumber包装
*/
@interface FLRemotePushModel : NSObject
/**
* @author Clarence-lie, 16-09-08 22:09:01
*
* 需要跳转的类名,后台返回
*/
@property (nonatomic,copy)NSString *className;
/**
* @author Clarence-lie, 16-09-08 22:09:38
*
* 字典转模型,可重写,根据不同格式转模型(如果重写,需要调用父类)
*
* @param dict 后台返回的字典(json)
*
* @return 返回一个模型
*/
+ (instancetype)fl_remotePushModel:(NSDictionary *)dict;
@end
@interface FLRemotePushManager : NSObject
+ (instancetype)fl_shareInstance;
/**
* @author Clarence-lie, 16-09-08 22:09:59
*
* 根据模型内容跳转到指定界面
*
* @param remotePushModel FLRemotePushModel子类
*/
- (void)fl_pushWithRemotePushModel:(FLRemotePushModel *)remotePushModel;
@end
4.实现
大概思路:根据json数据创建模型 -> 工具类获取模型数据 -> 根据类名创建控制器实例对象 -> 获取模型对象属性名并存储到数组中 -> 遍历模型属性名数组并判断控制器实例对象是否有对应属性 -> 有对应属性则通过KVC赋值给控制器 -> 获取导航控制器进行跳转
-
技术点
-
1.获取对象的属性列表: class_copyPropertyList
unsigned int outCount, i;
// 获取对象里的属性列表
objc_property_t * properties = class_copyPropertyList([instance
class], &outCount);
- 2.通过属性名创建对应属性的getter方法:NSSelectorFromString OC的getter方法就是属性的本身
/**
* @author Clarence-lie, 16-09-08 22:09:33
*
* 通过字符串来创建该字符串的getter方法
*
* @param propertyName 属性名
*
* @return 对应的getter方法
*/
- (SEL)fl_creatGetterWithPropertyName: (NSString *) propertyName{
//1.返回get方法: oc中的get方法就是属性的本身
return NSSelectorFromString(propertyName);
}
- 3.获取属性名对应的属性值:获得方法签名(getter方法)-> 签名中获取调用对象 -> 设置对象的target -> 设置对象的方法 -> 发送消息 -> 接收返回值(就是属性值)
/**
* @author Clarence-lie, 16-09-08 22:09:49
*
* 获取指定属性名对应的值
*
* @param propertyName 属性名
*
* @return 属性值
*/
- (id)fl_getValueByPropertyName:(NSString *)propertyName{
//获取get方法
SEL getSel = [self fl_creatGetterWithPropertyName:propertyName];
if ([self respondsToSelector:getSel]) {
//获得方法的签名
NSMethodSignature *signature = [self methodSignatureForSelector:getSel];
//从签名获得调用对象
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
//设置target
[invocation setTarget:self];
//设置selector
[invocation setSelector:getSel];
//接收返回的值
NSObject *__unsafe_unretained returnValue = nil;
//发消息
[invocation invoke];
//接收返回值
[invocation getReturnValue:&returnValue];
return returnValue;
}
else{
return nil;
}
}
5.调用
- (void)pushSecond
{
// 跟服务端沟通好跳转对应的界面需要对应的参数以及格式
NSDictionary *userInfo = @{
@"className": @"SecondViewController",
@"id": @"12",
@"content": @"测试推送内容消息"
};
[[FLRemotePushManager fl_shareInstance] fl_pushWithRemotePushModel:[FLSecondVcRemoteModel fl_remotePushModel:userInfo]];
}
- (void)pushThird{
// 非默认同一级的json格式,子类重写构造方法即可
NSDictionary *userInfo = @{
@"className": @"ThirdViewController",
@"msg" : @{
@"name": @"clarence",
@"age": @12
}
};
[[FLRemotePushManager fl_shareInstance] fl_pushWithRemotePushModel:[FLThirdVcRemoteModel fl_remotePushModel:userInfo]];
}
6.总结
-
先放好个传送门 gitHub 地址 喜欢给个like 和 star 呗~
-
注意点:必须跟后台配合,定好规则,哪条推送消息需要跳转到哪个控制器、以及该控制器中请求后台所需的参数都要返回给客户端,归根到底还是利用了OC的运行时特点,可以获取对象以及对象属性、方法,最后还是给个效果图吧~