本地通知与远程通知详解
Local Notification(本地通知)
:是根据本机状态做出的通知行为,因此,凡是仅需依赖本机状态即可判断需要发出通知的行为都可以或者说应该使用Local
Notification来处理。比方说:iBeacon中进入了某个Beacon region,或者说自定义的一些定时提醒等。
构建Local Notification
在iOS中,构建LocalNotification非常简单,只需要掌握好NSLocalNotification这个类就够用了,基本步骤如下:
1. 创建并初始化 UILocalNotification对象
2. 配置该对象的属性:
- * 触发时间(fireDate,timeZone,repeatInterval,repeatCalendar),如果你想根据时间来触发。
- * 通知行为(alertAction,hasAction),定义查看通知的操作名。
- * 触发通知时的启动画面(alertLaunchImage)
- * 通知的正文内容(alertBody),
- * 通知的背景声(soundName)
- * 通知消息数的展示(applicationIconBadgeNumber),就是强迫症们最讨厌的App图标上左上角的那个小数字
- * 其它(userInfo),可以给通知绑定一些处理通知时需要的额外信息。
3.展示通知,展示通知有两个方式:
- * - (void)scheduleLocalNotification:(UILocalNotification *)notification:根据触发时间的配置展示通知消息,
- * - (void)presentLocalNotificationNow:(UILocalNotification *)notification:立即发送通知到本机
栗子:
- (void)showNotificationWithAction:(NSString *)action andContent:(NSString *)content { UILocalNotification *notification = [[UILocalNotification alloc] init]; notification.alertBody = content; notification.alertAction = action; notification.soundName = UILocalNotificationDefaultSoundName; [[UIApplication sharedApplication] presentLocalNotificationNow:notification]; } - (void)scheduleNotificationWithItem:(ToDoItem *)item interval:(int)minutesBefore { NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar]; NSDateComponents *dateComps = [[NSDateComponents alloc] init]; [dateComps setDay:item.day]; [dateComps setMonth:item.month]; [dateComps setYear:item.year]; [dateComps setHour:item.hour]; [dateComps setMinute:item.minute]; NSDate *itemDate = [calendar dateFromComponents:dateComps]; UILocalNotification *localNotif = [[UILocalNotification alloc] init]; if (localNotif == nil) return; localNotif.fireDate = [itemDate dateByAddingTimeIntervalInterval:-(minutesBefore*)]; localNotif.timeZone = [NSTimeZone defaultTimeZone]; localNotif.alertBody = [NSString stringWithFormat:NSLocalizedString(@"%@ in %i minutes.", nil), item.eventName, minutesBefore]; localNotif.alertAction = NSLocalizedString(@"View Details", nil); localNotif.soundName = UILocalNotificationDefaultSoundName; localNotif.applicationIconBadgeNumber = ; NSDictionary *infoDict = [NSDictionary dictionaryWithObject:item.eventName forKey:ToDoItemKey]; localNotif.userInfo = infoDict; [[UIApplication sharedApplication] scheduleLocalNotification:localNotif]; }
处理Local Notification
在处理本地通知时,我们需要考虑三种情况:
1. App没有启动,
这种情况下,当点击通知时,会启动App,而在App中,开发人员可以通过实现*AppDelegate中的方法:-
(BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary
*)launchOptions,然后从lauchOptions中获取App启动的原因,若是因为本地通知,则可以App启动时对App做对应的操作,
比方说跳转到某个画面等等。栗子:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSLog(@"Start App...."); .... UILocalNotification *localNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey]; if ([self isWelcomeNotification:localNotification]) { NSLog(@"start with welcome notification"); [self.mainViewController showOfficeIntroduction]; } return YES; }
2. App运行在后台
上面的2种情况的处理基本一致,
不同点只有当运行再后台的时候,会有弹窗提示用户另外一个App有通知,对于本地通知单的处理都是通过*AppDelegate的方法:-
(void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification来处理的。 栗子:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { if ([self isWelcomeNotification:notification]) { [self.mainViewController showOfficeIntroduction]; }if ([self isCustomerDataNotification:notification]) { [self.mainViewController showCustomerDataIntroduction]; } }
if ( UIApplication . instancesRespondToSelector ( Selector ( "registerUserNotificationSettings:" ))) { application. registerUserNotificationSettings ( UIUserNotificationSettings (forTypes: UIUserNotificationType .Sound | UIUserNotificationType .Alert | UIUserNotificationType .Badge, categories: nil )) } else { application. registerForRemoteNotificationTypes (.Alert | .Sound | .Badge) } 然后,在Appdelegate.swift 写相关远程推送、本地通知等代码 // 收到本地通知 func application(application: UIApplication , didReceiveLocalNotification notification: UILocalNotification ) { var alertView = UIAlertView (title: " 系统本地通知 " , message: notification. alertBody , delegate: nil , cancelButtonTitle: " 返回 " ) alertView. show () }
本地通知方法:
****************************** 本地通知方法 ********** ***** *************** // // TimeViewController.swift // UIControlDemo // // Created by on 14/12/10. // Copyright (c) 2014 年 马大哈 . All rights reserved. // import UIKit class TimeViewController: BaseViewController { var wordTextField: UITextField ? // 文字 var dateTextField: UITextField ? // 时间 var datePicker: UIDatePicker ? override func viewDidLoad() { super . viewDidLoad () self . title = " 时间 / 日期 / 本地通知 " let leftBarButton: UIBarButtonItem = UIBarButtonItem (barButtonSystemItem: . Trash , target: self , action: "locaNotifcationDeleteAll" ) self . navigationItem . rightBarButtonItem = leftBarButton; wordTextField = UITextField (frame: CGRectMake ( , , , )) wordTextField ?. backgroundColor = . clearColor () wordTextField !. tag = wordTextField ?. borderStyle = UITextBorderStyle . RoundedRect wordTextField ?. keyboardType = UIKeyboardType . Default wordTextField ?. returnKeyType = UIReturnKeyType . Done wordTextField ?. contentVerticalAlignment = UIControlContentVerticalAlignment . Center wordTextField ?. clearButtonMode = UITextFieldViewMode . WhileEditing wordTextField ?. secureTextEntry = false wordTextField ?. textColor = . blackColor () wordTextField ?. textAlignment = . Left wordTextField ?. placeholder = " 键盘 " wordTextField ?. font = UIFont . systemFontOfSize ( ) self . view . addSubview ( wordTextField !) dateTextField = UITextField (frame: CGRectMake ( , , , )) dateTextField ?. backgroundColor = . clearColor () dateTextField !. tag = dateTextField ?. borderStyle = UITextBorderStyle . RoundedRect dateTextField ?. keyboardType = UIKeyboardType . Default dateTextField ?. returnKeyType = UIReturnKeyType . Done dateTextField ?. contentVerticalAlignment = UIControlContentVerticalAlignment . Center dateTextField ?. textColor = . blackColor () dateTextField ?. textAlignment = . Left dateTextField ?. placeholder = " 日期 picker" dateTextField ?. font = UIFont . systemFontOfSize ( ) self . view . addSubview ( dateTextField !) datePicker = UIDatePicker () datePicker ?. datePickerMode = . DateAndTime datePicker ?. timeZone = NSTimeZone . systemTimeZone () datePicker ?. addTarget ( self , action: "datePickerSelected:" , forControlEvents: UIControlEvents .ValueChanged) dateTextField !. inputView = datePicker var button = UIButton . buttonWithType ( UIButtonType . Custom ) as UIButton button. frame = CGRectMake ( , , , ) button. backgroundColor = UIColor . redColor () button. setTitleColor ( UIColor . blackColor (), forState:.Normal) button. setTitle ( " 保存 " , forState: UIControlState .Normal) button. titleLabel !. font = UIFont . boldSystemFontOfSize ( CGFloat ( )) button. showsTouchWhenHighlighted = true button. addTarget ( self , action: "saveLocalNotificationButton" , forControlEvents: UIControlEvents .TouchUpInside) self . view . addSubview (button) } // 保存按钮方法 func saveLocalNotificationButton(){ var contentDic = [ "KEY" : "VALUE" ] locaNotifcationSchedule (chedulDate: datePicker !. date , alertBody: " 通知看到 : /( wordTextField !. text ) " , content: contentDic) var alertView = UIAlertView (title: " 保存成功 " , message: nil , delegate: nil , cancelButtonTitle: " 返回 " ) alertView. show () wordTextField ?. resignFirstResponder () dateTextField ?. resignFirstResponder () } // 注册本地通知 func locaNotifcationSchedule(#chedulDate: NSDate ,alertBody: String ,content: NSDictionary ) { var localNotif = UILocalNotification () localNotif. fireDate = chedulDate localNotif. timeZone = NSTimeZone . defaultTimeZone () // localNotif.repeatInterval = repeatInterval 0 代表不重复 localNotif. soundName = "iPhone.caf" // 此属性可以不写(默认系统声音 UILocalNotificationDefaultSoundName ) // localNotif.hasAction = true; // localNotif.alertAction = " 查看 "; localNotif. alertBody = alertBody localNotif. userInfo = content UIApplication . sharedApplication (). scheduleLocalNotification (localNotif) } // 删除所有注册的本地通知 func locaNotifcationDeleteAll() { let application = UIApplication . sharedApplication () application. cancelAllLocalNotifications () var alertView = UIAlertView (title: " 所有本地通知都已移除 " , message: nil , delegate: nil , cancelButtonTitle: " 返回 " ) alertView. show () } // 动态改变 textfield 内容 func datePickerSelected(datePicker: UIDatePicker ){ let dateString = timeDateFormatter (). stringFromDate (datePicker. date ) dateTextField !. text = dateString } override func touchesEnded(touches: NSSet , withEvent event: UIEvent ) { wordTextField ?. resignFirstResponder () dateTextField ?. resignFirstResponder () } override func didReceiveMemoryWarning() { super . didReceiveMemoryWarning () } }
首先是申请证书的网址
https://developer.apple.com/ios/manage/overview/index.action
登录成功以后,进入iOS 配置管理的 主页面.
第一步操作是去创建一个新的App
IDs创建成功后,会需要提供安全证书来激动推送服务,如下图:
选择存储到磁盘以后,生成一个文件名称为(简称CSR): CertificateSigningRequest.certSigningRequest
回到Apple页面 将这个提交并提示激动成功.
激活成功后的App
IDs 提供下载开发版或是发布版的主动推送证书(aps_development.cer),如果需要做服务器方面的主动推送的话,就必须要下载这个文件来使用推送服务了.
第二步要为App提供接受推送许可的证书,点击Provisioning进行设置,添加一个新的许可,选择刚刚新创建的App
IDs. 再选择可以调试的iPhone 设备.
最后,同样是下载下来: YsyPushMessageDemo.mobileprovision双击该证书,让其加载一次.
接下来,进入iOS工程,选择使用该证书来调试.
红圈中,全部设置刚刚加载的许可证书.
那么到这里,关于证书类的准备工作,已经全部准备就绪.
在这里再此强调一次,每个文件的具体作用
1: CertificateSigningRequest.certSigningRequest :
为生成App IDs 而用
2: aps_development.cer 为开发主动推送服务而用到的证书
3: YsyPushMessageDemo.mobileprovision 为App
接受推送通知的许可服务
主动推送的Push 代码及使用,请参考一开始介绍的第一篇博客
这里只附上下载地址: https://github.com/stefanhafeneger/PushMeBaby
接下来,说说收到推送通知代码方面.
1:申请本App需要接受来自服务商提供推送消息,
2:申请发出后,如果成功,委托会自动返回一个设备令牌(toKen),如果失败,将会进入另外一个失败的委托
//远程通知注册成功委托 - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSLog(@"%@",deviceToken); self.viewController.toKenValueTextView.text = [NSString stringWithFormat:@"%@",deviceToken]; self.viewController.pushStatusLabel.text = @"已经注册."; } //远程通知注册失败委托 -(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { self.viewController.toKenValueTextView.text = [error description]; }
3:将设备令牌码提供给服务商,以供推送到具体的手机上面.
如果远程推送消息来了,用户点击了推送消息,或者应用已经处于打开状态,系统都会自动调用以下委托:
//点击某条远程通知时调用的委托 如果界面处于打开状态,那么此委托会直接响应 -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { NSLog(@"远程通知"); [self PMD_uesPushMessage:userInfo]; }
4:
第三点里面的介绍的情况是应用程序已经处于运行状态,上面的委托才会被执行,如果应用程序处于未启用状态,此时又需要响应消息,那么需要以下委托处理.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //这里处理应用程序如果没有启动,但是是通过通知消息打开的,此时可以获取到消息. if (launchOptions != nil) { NSDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; [self PMD_uesPushMessage:userInfo]; } return YES; }
5:清空通知中心已有的推送消息,只需要将指定App 的 Badge 设置为 0即可
6:主动推送的字符串必须符合如下Json数组的格式,才能正确推送到手机当中.
@"{ //自定义参数 "userinfo": { "name":"remote notice" }, //标准写法 "aps": { "alert": { "action-loc-key":"Open",//支持多语言 "body":"messgae content"//消息正文 }, ,//为App 的icon 标记 具体数值 "sound":"default" //播放的音频文件,default 表示系统默认的选择列铃声 } }";
到这里就差不多结束了.
php服务器推送消息
<?php $deviceToken = '38c866dd bb323b39 ffa73487 5e157ee5 a85e0b7ce90d56e9 fe145bcc 6c2c594b'; // masked for security reason // Passphrase for the private key (ck.pem file) // $pass = ''; // Get the parameters from http get or from command line $message = $_GET[] or $message = 'Message received from javacom'; $badge = (]; $sound = $_GET[]; // Construct the notification payload $body = array(); $body['aps'] = array('alert' => $message); if ($badge) $body['aps']['badge'] = $badge; if ($sound) $body['aps']['sound'] = $sound; $ctx = stream_context_create(); stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem'); // assume the private key passphase was removed. // stream_context_set_option($ctx, 'ssl', 'passphrase', $pass); $fp = stream_socket_client(, STREAM_CLIENT_CONNECT, $ctx); if (!$fp) { print "Failed to connect $err $errstrn"; return; } else { print "Connection OKn"; } $payload = json_encode($body); $msg = chr() . pack() . pack('H*', str_replace(' ', '', $deviceToken)) . pack("n",strlen($payload)) . $payload; print "sending message :" . $payload . "n"; fwrite($fp, $msg); fclose($fp); ?>
// 远程推送通知 注册成功 func application(application: UIApplication , didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData ) { println (deviceToken. description ) } // 远程推送通知 注册失败 func application(application: UIApplication , didFailToRegisterForRemoteNotificationsWithError error: NSError ) { { println ( "Push notifications are not supported in the iOS Simulator." ) } else { println ( "application:didFailToRegisterForRemoteNotificationsWithError: /(error) " ) } } // 8.0 之前 收到远程推送通知 func application(application: UIApplication , didReceiveRemoteNotification userInfo: [ NSObject : AnyObject ]) { let notif = userInfo as NSDictionary let apsDic = notif. objectForKey ( "aps" ) as NSDictionary let alertDic = apsDic. objectForKey ( "alert" ) as String var alertView = UIAlertView (title: " 系统本地通知 " , message: alertDic, delegate: nil , cancelButtonTitle: " 返回 " ) alertView. show () } // 8.0 之后 收到远程推送通知 func application(application: UIApplication , didReceiveRemoteNotification userInfo: [ NSObject : AnyObject ], fetchCompletionHandler completionHandler: ( UIBackgroundFetchResult ) -> Void ) { let notif = userInfo as NSDictionary let apsDic = notif. objectForKey ( "aps" ) as NSDictionary let alertDic = apsDic. objectForKey ( "alert" ) as String var alertView = UIAlertView (title: " 远程推送通知 " , message: alertDic, delegate: nil , cancelButtonTitle: " 返回 " ) alertView. show () } // 注册通知 alert 、 sound 、 badge ( 8.0 之后,必须要添加下面这段代码,否则注册失败) func application(application: UIApplication , didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings ) { application. registerForRemoteNotifications () }