您的位置:首页 > 其它

17.0~17.9 通知,系统通知,推送消息

2014-07-21 11:13 211 查看
17.0. IntroductionNotifications

通知可以携带数据被广播到多个接收对象上。利用它可以很好的分解工作(code),但如果使用不当,也是很容易失控的。

3种类型的通知

一般通知(NSNotification):app可以发送这种通知,iOS也可以发送这种通知,比如键盘弹出,隐藏。利用这些通知可以更好的解耦代码,可以把复杂的iOS应用清楚的分成几个部分、

本地通知(UILocalNotification):你安排这种通知,使其在特定的时间通知你的应用。

你的应用将收到通知,即使是进入后台,或根本就没运行,如果没运行,你的应用将会启动起来。

如果你希望在某个特定时间,确保你的程序可以响应时,那么你可能就会设定这种通知

推送通知(Push notifications):APNS服务器发过来的推送通知

本地通知比较特别,他们可以是可视的,用户可以对他们进行操作。对于用户的操作,你的应用将得到iOS的通知。

而一般通知是不可视的项,只能在应用内部广播。用户不参与的。当然了,接收通知后,可以弹出UI让用户参与。

17.1. Sending Notifications

NSString *const kNotificationName =
@"NotificationNameGoesHere";

NSNotification *notification = [NSNotification

notificationWithName:kNotificationName

object:self

userInfo:@{@"Key 1" :
@"Value 1",

@"Key 2" : @2}];

[[NSNotificationCenter
defaultCenter] postNotification:notification];

+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary
*)aUserInfo;

Name:消息的名称
object:一般设置为self,表示谁发送的消息,当然要设置成nil也可以。当有多个对象发送同一个消息时,接收者通过这个判断是谁发送的消息,如何处理

userInfo:是你能够在消息通多带一些数据过去。

17.2. Listening for and Reacting to Notifications
监听和处理消息

#import "AppDelegate.h"

//#import "Person.h"

@interface Person :
NSObject

@property (strong,nonatomic)
NSString * firstName;

@property (strong,nonatomic)
NSString * lastName;

@end

@implementation Person
- (void) handleSetPersonInfoNotification:(NSNotification *)paramNotification{

NSLog(@"%s",__FUNCTION__);

NSLog(@"userInfo=%@",paramNotification.userInfo);

self.firstName = paramNotification.userInfo[kSetPersonInfoKeyFirstName];

self.lastName = paramNotification.userInfo[kSetPersonInfoKeyLastName];

}

- (instancetype) init{

self = [super
init];

if (self !=
nil){

NSNotificationCenter *center = [NSNotificationCenter
defaultCenter];
[center
addObserver:self
selector:@selector(handleSetPersonInfoNotification:)

name:kSetPersonInfoNotification

object:[[UIApplication
sharedApplication] delegate]];
}

return
self;
}

- (void) dealloc{

NSLog(@"%s",__FUNCTION__);

[[NSNotificationCenter
defaultCenter] removeObserver:self];
}

@end

NSString *const kSetPersonInfoNotification =
@"SetPersonInfoNotification";
NSString *const kSetPersonInfoKeyFirstName =
@"firstName";
NSString *const kSetPersonInfoKeyLastName =
@"lastName";

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary
*)launchOptions
{

// Override point for customization after application launch.

Person *steveJobs = [[Person
alloc] init];

NSNotification *notification =[NSNotification

notificationWithName:kSetPersonInfoNotification

object:self

userInfo:@{kSetPersonInfoKeyFirstName :
@"Steve",

kSetPersonInfoKeyLastName :
@"Jobs"}];

/* The person class is currently listening for this notification. That class

will extract the first name and last name from it and set its own first

name and last name based on the userInfo dictionary of the notification. */

[[NSNotificationCenter
defaultCenter] postNotification:notification];

/* Here is proof */

NSLog(@"Person's first name = %@", steveJobs.firstName);

NSLog(@"Person's last name = %@", steveJobs.lastName);

return
YES;
}

@end

打印:
2014-07-15 11:11:21.443 cookbook7_17_2[487:a0b] -[Person handleSetPersonInfoNotification:]
2014-07-15 11:11:21.444 cookbook7_17_2[487:a0b] userInfo={
firstName = Steve;
lastName = Jobs;
}
2014-07-15 11:11:21.444 cookbook7_17_2[487:a0b] Person's first name = Steve
2014-07-15 11:11:21.445 cookbook7_17_2[487:a0b] Person's last name = Jobs
2014-07-15 11:11:21.445 cookbook7_17_2[487:a0b] -[Person dealloc]

有没有发现,消息发出后,会先处理消息,处理完再回来继续往下走,单线程这样处理比较方便,也比较好处理不是吗。
千万不要以为消息发出后,就继续往下走,消息在未来的某个时间才被处理

17.3. Listening and Reacting to Keyboard Notifications
监听处理键盘消息

有时候键盘一弹出来就会覆盖掉你的UI,而你又希望你的UI在键盘弹出后能够被看见,比如说编辑框。要是被遮住了,那体验可真不好。

#import <UIKit/UIKit.h>

@interface ViewController :
UIViewController

@property (strong,
nonatomic) IBOutlet
UIScrollView *scrollView;

@property (strong,
nonatomic) IBOutlet
UITextField *textField;

@property (strong,
nonatomic) IBOutlet
UIImageView *imageView;

@end

#import "ViewController.h"

//#import <UIKit/UIKit.h>

@interface
ViewController () <UITextFieldDelegate>

@end

@implementation ViewController

- (void)viewDidLoad
{

[super
viewDidLoad];

// Do any additional setup after loading the view, typically from a nib.

self.scrollView.contentSize =
self.imageView.frame.size;
}

- (void) viewWillAppear:(BOOL)paramAnimated{
[super
viewWillAppear:paramAnimated];

NSLog(@"contentsize=%@",NSStringFromCGSize(self.scrollView.contentSize));

NSLog(@"self.scrollView.frame=%@",NSStringFromCGRect(self.scrollView.frame));

NSNotificationCenter *center = [NSNotificationCenter
defaultCenter];
[center
addObserver:self
selector:@selector(handleKeyboardWillShow:)

name:UIKeyboardWillShowNotification
object:nil];
[center
addObserver:self
selector:@selector(handleKeyboardWillHide:)

name:UIKeyboardWillHideNotification
object:nil];
}
- (void)viewWillDisappear:(BOOL)paramAnimated{
[super
viewWillDisappear:paramAnimated];

[[NSNotificationCenter
defaultCenter] removeObserver:self];
}

- (void)didReceiveMemoryWarning
{

[super
didReceiveMemoryWarning];

// Dispose of any resources that can be recreated.
}

- (void) handleKeyboardWillShow:(NSNotification *)paramNotification{

NSLog(@"%s",__FUNCTION__);

NSDictionary *userInfo = paramNotification.userInfo;

/* Get the duration of the animation of the keyboard for when it

gets displayed on the screen. We will animate our contents using

the same animation duration */

NSValue *animationDurationObject = userInfo[UIKeyboardAnimationDurationUserInfoKey];

NSValue *keyboardEndRectObject = userInfo[UIKeyboardFrameEndUserInfoKey];

double animationDuration = 0.0;

CGRect keyboardEndRect = CGRectMake(0.0f,
0.0f, 0.0f,
0.0f);
[animationDurationObject
getValue:&animationDuration];
[keyboardEndRectObject
getValue:&keyboardEndRect];

UIWindow *window = [UIApplication
sharedApplication].keyWindow;

/* Convert the frame from window's coordinate system to

our view's coordinate system */
keyboardEndRect = [self.view
convertRect:keyboardEndRect
fromView:window];

NSLog(@"keyboardEndRect=%@",NSStringFromCGRect(keyboardEndRect));

// [NSString stringwith]

/* Find out how much of our view is being covered by the keyboard */

CGRect intersectionOfKeyboardRectAndWindowRect =
CGRectIntersection(self.view.frame, keyboardEndRect);

NSLog(@"intersectionOfKeyboardRectAndWindowRect=%@",NSStringFromCGRect(intersectionOfKeyboardRectAndWindowRect));

/* Scroll the scroll view up to show the full contents of our view */

[UIView
animateWithDuration:animationDuration animations:^{

self.scrollView.contentInset =

UIEdgeInsetsMake(0.0f,

0.0f,
intersectionOfKeyboardRectAndWindowRect.size.height,

0.0f);

NSLog(@"contentInset=%@",NSStringFromUIEdgeInsets(self.scrollView.contentInset));

[self.scrollView
scrollRectToVisible:self.textField.frame
animated:NO];
}];
}

- (void) handleKeyboardWillHide:(NSNotification *)paramSender{

NSLog(@"%s",__FUNCTION__);

NSDictionary *userInfo = [paramSender userInfo];

NSValue *animationDurationObject =

[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey];

double animationDuration = 0.0;
[animationDurationObject
getValue:&animationDuration];

[UIView
animateWithDuration:animationDuration animations:^{

self.scrollView.contentInset =
UIEdgeInsetsZero;
}];
}

- (BOOL) textFieldShouldReturn:(UITextField *)paramTextField{

NSLog(@"%s",__FUNCTION__);
[paramTextField
resignFirstResponder];

return
YES;
}

@end

打印:
2014-07-15 16:10:48.413 cookbook7_17_2[1209:c07] contentsize={320, 950}
2014-07-15 16:10:48.415 cookbook7_17_2[1209:c07] self.scrollView.frame={{0, 0}, {320, 460}}
2014-07-15 16:10:50.152 cookbook7_17_2[1209:c07] -[ViewController handleKeyboardWillShow:]
2014-07-15 16:10:50.152 cookbook7_17_2[1209:c07] keyboardEndRect={{0, 244}, {320, 216}}
2014-07-15 16:10:50.153 cookbook7_17_2[1209:c07] intersectionOfKeyboardRectAndWindowRect={{0, 244}, {320, 216}}
2014-07-15 16:10:50.153 cookbook7_17_2[1209:c07] contentInset={0, 0, 216, 0}
2014-07-15 16:10:52.386 cookbook7_17_2[1209:c07] -[ViewController textFieldShouldReturn:]
2014-07-15 16:10:52.388 cookbook7_17_2[1209:c07] -[ViewController handleKeyboardWillHide:]

悲催的是,当键盘弹出时,self.scrollView并没有滚动,self.textField也被键盘遮住了,不知道我哪里没写对

17.4. Scheduling Local Notifications
如果开发一个闹钟或日历应用,那么在程序没运行时,或者在后台时,通知用户(响闹钟等)

UILocalNotification,
[UIApplication sharedAp plication] scheduleLocalNotification:
当应用没在运行,或是在后台运行时,本地通知local notification
自己会弹消息通知给用户
当应用在前台运行时,将不会弹出消息,而是调用app 代理。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary
*)launchOptions
{

// Override point for customization after application launch.

UILocalNotification *notification = [[UILocalNotification
alloc]
init];

/* Time and timezone settings */

notification.fireDate = [NSDate
dateWithTimeIntervalSinceNow:8.0];
notification.timeZone = [[NSCalendar
currentCalendar] timeZone];

notification.alertBody =
NSLocalizedString(@"A new item is downloaded.",
nil);

/* Action settings */
notification.hasAction =
YES;
notification.alertAction =
NSLocalizedString(@"View",
nil);

/* Badge settings */

notification.applicationIconBadgeNumber = [UIApplication
sharedApplication].applicationIconBadgeNumber +
1;

/* Additional information, user info */
notification.userInfo =
@{@"Key 1" :
@"Value 1",

@"Key 2" : @"Value 2"};

/* Schedule the notification */

[[UIApplication
sharedApplication] scheduleLocalNotification:notification];

return
YES;
}

运行之后8秒,状态栏会弹出消息,app引用图片右上角增加消息条数标号

17.5. Listening for and Reacting to Local Notifications

监听和处理本地消息UILocalNotification
应用的状态不一样,消息的处理也不一样
应用在前台运行时:调用application:didReceiveLocalNotification:
应用在后台运行时:一旦用户点击通知,iOS将激活应用,然后调用application:didReceiveLocalNotification:
应用没运行:调用application:didFinishLaunchingWithOptions:
Option参数的中UIApplicationLaunchOptionsLocalNotificationKey键值就是对应的消息。

#import "AppDelegate.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary
*)launchOptions
{

NSLog(@"%s",__FUNCTION__);

//如果是消息启动了程序,则处理消息,否则重建一个消息

if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey] !=
nil){
UILocalNotification *notification = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
[self application:application didReceiveLocalNotification:notification];
}
else {
[self scheduleLocalNotification];
}

return
YES;
}
- (void) scheduleLocalNotification{

NSLog(@"%s",__FUNCTION__);

UILocalNotification *notification = [[UILocalNotification alloc] init];

/* Time and timezone settings */

notification.fireDate = [NSDate
dateWithTimeIntervalSinceNow:8.0];
notification.timeZone = [[NSCalendar currentCalendar] timeZone];
notification.alertBody = NSLocalizedString(@"A new item is downloaded.",
nil);

/* Action settings */
notification.hasAction =
YES;
notification.alertAction = NSLocalizedString(@"View",
nil);

/* Badge settings */
notification.applicationIconBadgeNumber = [UIApplication sharedApplication].applicationIconBadgeNumber +
1;

/* Additional information, user info */
notification.userInfo =
@{@"Key 1" :
@"Value 1",

@"Key 2" : @"Value 2"};

/* Schedule the notification */
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}

- (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{

NSLog(@"%s",__FUNCTION__);

NSString *key1Value = notification.userInfo[@"Key 1"];
NSString *key2Value = notification.userInfo[@"Key 2"];

if ([key1Value length] > 0 && [key2Value length] >
0){

NSLog(@"Handling the local notification");

UIAlertView *alert =
[[UIAlertView alloc]
initWithTitle:nil
message:@"Handling the local notification"
delegate:nil

cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];

}
}

@end

运行程序后等待8秒,打印:
2014-07-15 17:45:06.543 cookbook7_17_4[1526:a0b] -[AppDelegate application:didFinishLaunchingWithOptions:]
2014-07-15 17:45:06.544 cookbook7_17_4[1526:a0b] -[AppDelegate scheduleLocalNotification]
2014-07-15 17:45:06.551 cookbook7_17_4[1526:a0b] -[AppDelegate applicationDidBecomeActive:]
2014-07-15 17:45:14.546 cookbook7_17_4[1526:a0b] -[AppDelegate application:didReceiveLocalNotification:]
2014-07-15 17:45:14.547 cookbook7_17_4[1526:a0b] Handling the local notification

运行程序后,home键进入后台等待,弹出通知后点击通知
2014-07-15 17:45:48.846 cookbook7_17_4[1536:a0b] -[AppDelegate application:didFinishLaunchingWithOptions:]
2014-07-15 17:45:48.846 cookbook7_17_4[1536:a0b] -[AppDelegate scheduleLocalNotification]
2014-07-15 17:45:48.853 cookbook7_17_4[1536:a0b] -[AppDelegate applicationDidBecomeActive:]
2014-07-15 17:45:52.860 cookbook7_17_4[1536:a0b] -[AppDelegate applicationWillResignActive:]
2014-07-15 17:45:52.862 cookbook7_17_4[1536:a0b] -[AppDelegate applicationDidEnterBackground:]
2014-07-15 17:45:59.613 cookbook7_17_4[1536:a0b] -[AppDelegate applicationWillEnterForeground:]
2014-07-15 17:45:59.614 cookbook7_17_4[1536:a0b] -[AppDelegate application:didReceiveLocalNotification:]
2014-07-15 17:45:59.615 cookbook7_17_4[1536:a0b] Handling the local notification
2014-07-15 17:45:59.672 cookbook7_17_4[1536:a0b] -[AppDelegate applicationDidBecomeActive:]

17.6. Handling Local System Notifications
监听系统通知
NSCurrentLocaleDidChangeNotification:修改应用场景(the user changes her locale),比如修改语言设置
NSUserDefaultsDidChangeNotification:
iOS设置页面修改应用设置(如果有的话)
UIDeviceBatteryStateDidChangeNotification:
电池状态改变,比如充电
UIDeviceProximityStateDidChangeNotification:应该是距离感应吧(
the state of the proximity sensor changes)

在程序进入后台后会发生很多事情,比如左横屏、右横屏、竖屏等。当程序再次进入前台时,iOS只会通知最后一次屏幕方向消息。

#import "ViewController.h"

@interface
ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{

[super
viewDidLoad];

// Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{

[super
didReceiveMemoryWarning];

// Dispose of any resources that can be recreated.
}

- (void) orientationChanged:(NSNotification *)paramNotification{

NSLog(@"Orientation Changed ");
}

- (void)viewDidAppear:(BOOL)paramAnimated{
[super
viewDidAppear:paramAnimated];

/* Listen for the notification */

[[NSNotificationCenter
defaultCenter]

addObserver:self

selector:@selector(orientationChanged:)

name:UIDeviceOrientationDidChangeNotification

object:nil];
}
- (void) viewDidDisappear:(BOOL)paramAnimated{
[super
viewDidDisappear:paramAnimated];

/* Stop listening for the notification */

[[NSNotificationCenter
defaultCenter]

removeObserver:self

name:UIDeviceOrientationDidChangeNotification

object:nil];
}

@end

模拟器启动应用后,左旋,然后右旋打印:
2014-07-16 11:45:54.945 cookbook7_17_4[496:a0b] -[AppDelegate applicationDidBecomeActive:]
2014-07-16 11:46:02.222 cookbook7_17_4[496:a0b] Orientation Changed
2014-07-16 11:46:04.943 cookbook7_17_4[496:a0b] Orientation Changed

模拟器启动应用后,首页,左旋,右旋,点击应用回到前台,打印:
2014-07-16 11:47:09.302 cookbook7_17_4[508:a0b] -[AppDelegate applicationDidBecomeActive:]
2014-07-16 11:47:14.267 cookbook7_17_4[508:a0b] -[AppDelegate applicationWillResignActive:]
2014-07-16 11:47:14.269 cookbook7_17_4[508:a0b] -[AppDelegate applicationDidEnterBackground:]
2014-07-16 11:47:25.610 cookbook7_17_4[508:a0b] -[AppDelegate applicationWillEnterForeground:]
2014-07-16 11:47:25.612 cookbook7_17_4[508:a0b] -[AppDelegate applicationDidBecomeActive:]

没有打印Orientation Changed,噢,又是一次失败的尝试,是模拟器的原因吗?

给应用增加一些设置,
Xcode
-> new file -> iOS category -> resources subcategory -> setting bundle -> save

运行后会在系统的设置页增加应用的设置

#import "AppDelegate.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary
*)launchOptions
{

// Override point for customization after application launch.

[[NSNotificationCenter
defaultCenter]

addObserver:self

selector:@selector(handleSettingsChanged:)

name:NSUserDefaultsDidChangeNotification

object:nil];

return
YES;
}

- (void) handleSettingsChanged:(NSNotification *)paramNotification{

NSLog(@"Settings changed");

NSLog(@"Notification Object = %@", paramNotification.object);

NSLog(@"Notification userinfo = %@", paramNotification.userInfo);
}

- (void)applicationWillResignActive:(UIApplication *)application
{

NSLog(@"%s",__FUNCTION__);
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{

NSLog(@"%s",__FUNCTION__);
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{

NSLog(@"%s",__FUNCTION__);
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{

NSLog(@"%s",__FUNCTION__);
}

- (void)applicationWillTerminate:(UIApplication *)application
{

NSLog(@"%s",__FUNCTION__);

[[NSNotificationCenter
defaultCenter] removeObserver:self];
}

@end
2014-07-16 14:06:23.179 cookbook7_17_6[463:a0b] -[AppDelegate applicationDidBecomeActive:]
2014-07-16 14:06:25.119 cookbook7_17_6[463:a0b] Settings changed
2014-07-16 14:06:25.120 cookbook7_17_6[463:a0b] Notification Object = <NSUserDefaults: 0x8943b70>
2014-07-16 14:06:25.121 cookbook7_17_6[463:a0b] Notification userinfo = (null)
2014-07-16 14:06:30.503 cookbook7_17_6[463:a0b] -[AppDelegate applicationWillResignActive:]
2014-07-16 14:06:30.505 cookbook7_17_6[463:a0b] -[AppDelegate applicationDidEnterBackground:]
2014-07-16 14:06:58.983 cookbook7_17_6[463:a0b] -[AppDelegate applicationWillEnterForeground:]
2014-07-16 14:06:58.985 cookbook7_17_6[463:a0b] -[AppDelegate applicationDidBecomeActive:]
2014-07-16 14:06:58.986 cookbook7_17_6[463:a0b] Settings changed
2014-07-16 14:06:58.986 cookbook7_17_6[463:a0b] Notification Object = <NSUserDefaults: 0x8943b70>
2014-07-16 14:06:58.987 cookbook7_17_6[463:a0b] Notification userinfo = (null)

17.7. Setting Up Your App for Push Notifications
推送消息配置

1,添加配置文件开启推送消息
2,注册消息推送
3,收集设备推送id,发送给服务器

想让应用能够接收推送消息,必须要有配置文件,配置过程参照:
1,登陆开发者中心
2,Certificates, Identifiers & Profiles
3,在Identifiers 部分,创建你的应用ID。如:com.pixolity.ios.cookbook.PushNotificationApp
4,在appID的application serverces部分,确保
Push Notifications 为Enable。
5,提交操作
6,切到Provisioning Profiles
7,为你的应用创建开发者证书,你可以创建ad hoc和app store版本的证书,不过现在,你只需要创建Development版本的。当你要发布到应用商店的时候你再来创建app store版本的。
请确保你你创建的证书关联了你刚才创建app id
8,下载证书并把它拖到iTunes来安装,请不要用双击来安装,这会改变安装的证书的文件名,以至于。。。。(
Doing so will change the name of the installed profile’s filename to the MD5 hash name of the profile, which is very difficult to identify on disk.

9,在Xcode编译设置那边,选择刚安装的证书(发布的时候,选择发布版本的)
10,拖拽证书到文件编辑器,会发现证书内容大致如下:

<key>Entitlements</key>
<dict>

<key>application-identifier</key>
<string>F3FU372W5M.com.pixolity.ios.cookbook.PushNotificationApp</string>

<key>aps-environment</key>

<string>development</string>

<key>get-task-allow</key>

<true/>

<key>keychain-access-groups</key>

<array>

<string>F3FU372W5M.*</string>

</array>

</dict>
11,Xcode中创建新的plist文件,命名为Entitlements.plist.
open as -> Source Code
结果大致如下:

<plist version="1.0">

<dict/>

</plist>
12,把证书的内容节点copy过来,结果:

<plist version="1.0">
<dict>

<key>application-identifier</key>

<string>F3FU372W5M.com.pixolity.ios.cookbook.PushNotificationApp</string>

<key>aps-environment</key>

<string>development</string>

<key>get-task-allow</key>

<true/>

<key>keychain-access-groups</key>

<array>

<string>F3FU372W5M.*</string>

</array>

</dict>

</plist>

13,编译选项中,如果Entitlements.plist在target目录下则 Code Signing
输入$(SRCROOT)/$(TARGET_NAME)/Entitlements.plist
,如果在工程的目录下则输入$(SRCROOT)/Entitlements.plist
,路径要对,否则编译器会抱怨找不到文件的
14,编译程序,确保没报错,如果有,可能是你的证书有问题,或是证书路径问题
15,在应用代理里面注册远程消息通知
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary
*)launchOptions
{

// Override point for customization after application launch.

[[UIApplication
sharedApplication] registerForRemoteNotificationTypes:

UIRemoteNotificationTypeAlert |

UIRemoteNotificationTypeBadge |

UIRemoteNotificationTypeSound];

return
YES;
}
设备将发送一条推送消息的注册请求到APNS,别当心,他会先咨询用户的同意。
16,实现代理application:didRegisterForRemoteNotificationsWithDeviceToken:,当成功注册推送消息通知时,会获得一个token
17,实现代理application:didFailToRegisterForRemoteNotifications WithError:,注册失败,原因可能会是:证书不对,网络连接不成功等

ok that is all

17.8. Delivering Push Notifications to Your App
推送消息到用户的设备
为了与APNS通讯,你需要Apple-issued SSL certificate,生成过程如下:
1,登陆开发者中心
2,Certificates, Identifiers & Profiles
3,找到对应App ID 编辑
4,在Push Notification项部分,点击【Create Certificate】创建证书,现在我们只需创建development ssl certificate,当你要发布的时候再创建Distribution all certificate
5,下载证书到你的电脑上,双击以安装到钥匙串
6,钥匙串窗体中,钥匙串->登陆, 种类->我的证书,可以看到刚安装的证书
7,右键证书导出,选择.cer格式,命名
PushCertificate.cer.
8,右键专用密钥导出,选择.p12格式,命名PushKey.p12,会提示输入密码,要记住密码

下面将用php发送一个简单的推送到设备为例
创建PEM文件,
假设PushKey.p12
和 PushCertificate.cer 文件在桌面上
1,打开Terminal
2,openssl x509 -in PushCertificate.cer -inform der -out PushCertificate.pem
3,openssl pkcs12 -nocerts -in PushKey.p12 -out PushKey.pem
4,他会要求你输入密码,那个你从钥匙串那边导入时设定的密码。一旦密码校验通过,OpenSSL会要求你为PEN文件设定一个密码,而且至少4个字符,当然密码是要记住的
5,ok,现在我们有了两个pem文件了PushCertificate.pem
和 PushKey.pem ,现在你需要把它们合并成一个。
cat PushCertificate.pem PushKey.pem > PushCertificateAndKey.pem
6,测试一下能不能用生成的.pem文件连接到apns

openssl s_client -connect gateway.sandbox.push.apple.com:2195 \
-cert PushCertificate.pem -key PushKey.pem

如果一切顺利,他将要求你输入密码。如果连接成功,将可以看到OpenSSL要求你输入一些字符来关闭连接。随便输几个字符回车就可以关闭。这意味着你用那个证书和专用密钥连接服务器成功了。

好了,是时候利用php脚本来给设备发送一条推送了。首先需要获得设备的token。
- (void) application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData
*)deviceToken{

/* Each byte in the data will be translated to its hex value like 0x01 or

0xAB excluding the 0x part, so for 1 byte, we will need 2 characters to

represent that byte, hence the * 2 */

NSLog(@"%s",__FUNCTION__);

NSMutableString *tokenAsString = [[NSMutableString
alloc]

initWithCapacity:deviceToken.length *
2];

char *bytes = malloc(deviceToken.length); [deviceToken
getBytes:bytes];

for (NSUInteger byteCounter =
0; byteCounter < deviceToken.length; byteCounter++){

char byte = bytes[byteCounter];
[tokenAsString
appendFormat:@"%02hhX", byte];
}

free(bytes);

NSLog(@"Token = %@", tokenAsString);
}

打印的信息类似如下:
Token = 05924634A8EB6B84437A1E8CE02E6BE6683DEC83FB38680A7DFD6A04C6CC586E

php
脚本

<?php

/* We are using the sandbox version of the APNS for development. For production

environments, change this to ssl://gateway.push.apple.com:2195 */

$apnsServer =
'ssl://gateway.sandbox.push.apple.com:2195';

/* Make sure this is set to the password that you set for your private key

when you exported it to the .pem file using openssl on your OS X */

$privateKeyPassword =
'1234';

/* Put your own message here if you want to */

$message =
'Welcome to iOS 7 Push Notifications';

/* Pur your device token here */

$deviceToken =

'05924634A8EB6B84437A1E8CE02E6BE6683DEC83FB38680A7DFD6A04C6CC586E';

/* Replace this with the name of the file that you have placed by your PHP

script file, containing your private key and certificate that you generated

earlier */

$pushCertAndKeyPemFile = 'PushCertificateAndKey.pem';

$stream =
stream_context_create();

stream_context_set_option($stream,

'ssl',

'passphrase',

$privateKeyPassword);

stream_context_set_option($stream,

'ssl',

'local_cert',

$pushCertAndKeyPemFile);

$connectionTimeout = 20;

$connectionType =
STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT;

$connection = stream_socket_client($apnsServer,

$errorNumber,

$errorString,

$connectionTimeout,

$connectionType,

$stream);

if (!$connection){

echo "Failed to connect to the APNS server. Error no =
$errorNumber<br/>";
exit;

} else
{

echo "Successfully connected to the APNS. Processing...</br>";
}

$messageBody['aps']
= array('alert'
=> $message,
'sound' => 'default',

'badge' => 2,
);

$payload = json_encode($messageBody);

$notification = chr(0)
.

pack('n',
32)
.

pack('H*',
$deviceToken)
.

pack('n',
strlen($payload))
.

$payload;

$wroteSuccessfully = fwrite($connection,
$notification, strlen($notification));

if (!$wroteSuccessfully){

echo "Could not send the message<br/>";

}

else {

echo "Successfully sent the message<br/>"; }

fclose($connection);

替换你的token

如果万事大吉,那么用浏览器打开你的php脚本,就可以在手机上看到推送的消息。php脚本发送通知到apns服务器,apns再把通知传给手机。当推送通知传到app,而手机当时锁屏,则可在通知栏看到相应的通知。

17.9. Reacting to Push Notifications
如何处理推送消息

推送消息已经传到手机上了,如何处理呢?
实现
application:didReceiveRemoteNotification:
代理

当用户点击推送消息,而你的应用在前台或者后台,但不能是终止状态,这个代理将被调用。

如果你的应用处于终止状态,iOS将启动程序,并把消息封装到option参数中传给application:didFinishLaunchingWithOptions:
代理方法。通过UIApplicationLaunchOptionsRemoteNotificationKey可以获取到这个消息

didReceiveRemoteNotification参数是个NSDictionary.这个字典有个aps字典对象,这个字典对象可能包含以下值
badge: number类型,表示应用图标右上角的应设的徽章数值
alert: String类型,推送通知的消息。
sound: string ,标记着你该播放哪个音乐文件
content-available: number 类型,为1时,表示服务端有新内容,希望你应用能更新下。当然了,实际更不更新那是app的事情。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: