前段时间的WWDC推了 iOS 7 出来,作为一个穷苦的码农我还没有好的idea迫使我去申请一个开发者账号(因果关系搞反了=_=),所以就没有beta版可以玩了,对于它的风格改动,我还是持支持的,毕竟苹果把很多越狱插件的功能都做进系统中了,我也没什么越狱的欲望了。言归正传,后面的内容是只针对 iOS 6 的。
本文首先会介绍一下iOS推送通知的使用和推送流程,然而文中重点是我对iOS接收到通知后的处理方法。一般而言,推送通知可以只做文本提示,应用可以完全不处理推送通知的内容,但我们也可以利用推送通知去做更多的操作,如界面跳转。比如微信,我通过推送通知打开微信的时候,微信会直接帮我条到对话界面,而不需要我慢慢手动点进去。
推送流程
完成推送通知需要后端服务器的支持,主要过程是:
- 用户启动应用后,选一个时机获取苹果的token
- 收到这个token后,系统会application:didRegisterForRemoteNotificationsWithDeviceToken:这个方法,让我们处理这个方法
- 把token发给后端存好
- 后端使用各种连接apns的框架就可以实现消息推送了
这里只说明大概的过程,但后面的内容比较专注于处理推送消息的内容,后端的内容可以找其他文章。
推送注册
调用这个方法,就可以获取一个推送token,这个token,是与应用、手机相关联的。
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
在刚刚提到的方法中自由处理一下就可以了
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
//do something
}
接受推送通知方法
点击推送通知打开应用,会出现以下两个情况:
应用没有启动过,或呆在后台已经有一段时间(应用可能已经被挂起或内存被系统清空)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //userInfo就是推送通知内容 NSDictionary* userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; //do something return YES; }后台激活应用
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { //userInfo就是推送通知内容 //do something }
这两个不同入口的区别就是应用启动过和应用未启动过,这个值得注意的是,有的界面的loadView和viewDidLoad方法是没有执行过的,如果直接利用推送通知的信息进行界面跳转处理的话,应用并不会做出任何反应。
推送协议
推送通知的内容都由 NSDictionary 封装,官方使用的有badge(icon显示的数字), sound(推送消息声音), msg(推送内容)等字段。推送通知有256字节的限制,所以一次推送不能推送太多内容。另外,我们可以定义更多的kv对,作为参数带入应用,从而做相应的ui跳转。
推送处理
对于简单的推送通知,比如弹出一个UIAlertView显示消息并让用户确认,可以直接在以上提到的两个方法中直接处理。但如果需要对特定的界面做操作,我们需要判断界面是不是已经运行了viewDidLoad方法,这个界面是不是当前正在显示的界面,这可能会引起相当复杂的逻辑判断。
因此,我在这里考虑的是继承UIViewContrller写一个自己应用的ViewController(应用的基本风格也可以在这里处理好,好处相当多)。在这里我把推送通知的处理放到viewDidAppear方法中,这样我就不用考虑太多事情了,利用ViewController本身的生命周期就能完成这些事情。
这里的处理过程只能称之为方法,不能得上是框架。
Message:推送通知的封装
@interface Message : NSObject { NSDictionary *_pushDict; } //Message工厂 + (Message *)createMessage:(NSDictionary *)dict; //根据传入的推送通知内容构造Message对象 - (id)initWithPushDict:(NSDictionary *)dict; - (void)execInterface:(UIViewController*)vc; @endExecutor:执行推送通知的执行器
@interface Executor : NSObject - (void)execute:(Message *)msg viewController:(UIViewController*)vc @end @implementation Executor - (void)execute:(Message *)msg viewController:(UIViewController*)vc { [msg execInterface:vc]; } @endPCViewController:
继承自UIViewController,这个ViewController拥有Execotor的对象和Message的对象,我们准备用利用它的生命周期嵌入推送处理的过程。而后续开发的过程,所有用到的ViewController都继承这个PCViewController。这里我利用的是viewDidAppear方法
- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if(message!=nil) { [executor execute:message viewController:self]; message = nil; } }PCTabBarController
- PCNavigationController
以上两个通常作为rootViewController使用,可以根据自己业务逻辑摆放执行execotor的位置。
界面跳转的实践
接到推送通知后,提取通知内容,新建下面这个方法,在适当的位置调用
-(void)pushToJump:(NSDictionary*)userInfo withFirstStart:(BOOL)firstStart
{
PCTabBarController* rootVC = (PCTabBarController*)self.window.rootViewController;
Message *msg = [Message createMessage:userInfo];
rootVC.message = msg;
if(!firstStart){
//如果不是第一次启动,就没法利用生命周期来自动执行页面跳转
[rootVC executeJump];
}
}
createMessage是一个工厂方法,userInfo是推送通知,可以利用userInfo中比如type字段来定义推送通知的类型,并利用反射构造相应的message。
在Message的execInterface方法可以定义如下内容:
- (void)execInterface:(UIViewController*)vc
{
PCViewController *nextVC = [[[PCViewController alloc] init] autorelease];
nextVC.message = [Message createMessage:[NSDictionary dictionary…]];
[vc.navigationController pushViewController:nextVC animated:YES];
}
这种做法同样可以做成点击点击按钮,切换segment,但需要注意的是,这里不应该进行需要长时间等待的操作,除非用多线程。
小结
以上提到的方法,同样可以用于应用间跳转(handle open url),需要注意的是,iOS 应用的页面跳转和网页跳转还是有区别的,iOS应用更强调界面跳转逻辑,不能没头没尾的就跳到一个与当前操作无关的页面,就是说不能点着点着就突然回到首页,除非提供了一个回到首页的按钮。
评论
发表评论