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.p12PushCertificate.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.pemPushKey.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的事情。

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

  1. DWR实现服务器端向客户端推送消息

    2019独角兽企业重金招聘Python工程师标准>>> 1.简介 DWR(Direct Web Remoting)是一个用于改善web页面与Java类交互的远程服务器端Ajax开源框 ...

  2. php 通知客户端,PHP+SSE服务器向客户端推送消息

    SSE与WebSocket作用相似,都是建立浏览器与服务器之间的通信渠道,然后服务器向浏览器推送信息. 但是WebSocket比SSE强大很多,SSE只能作为一个轻量级的消息推送方案,解决了从服务端向 ...

  3. Android中集成Jpush实现推送消息通知与根据别名指定推送附示例代码下载

    场景 经常会有后台服务向Android推送消息通知的情况. 实现 首先在Android Studio中新建一个Android应用 在Project根目录的build.gradle中配置了jcenter ...

  4. android点击通知跳转到服务,Android 接收推送消息跳转到指定页面的方法

    问题的提出 本次接入的是个推,其他家的推送没有研究过,思路应该是类似的 App在前台,这个时候需要弹出一个对话框,提醒用户有新的消息,是否要查看,查看的话跳转到指定页面 App在后台,或是App进程已 ...

  5. iOS 推送通知及推送扩展

    概述 iOS中的通知包括本地推送通知和远程推送通知,两者在iOS系统中都可以通过弹出横幅的形式来提醒用户,点击横幅会打开应用.在iOS 10及之后版本的系统中,还支持通知扩展功能(UNNotifica ...

  6. Android,ios,安卓app推送消息通知,java后台向手机推送app的通知教程

    文章目录 一.业务介绍 1.1 产品简介 1.2 名词解释 1.3 消息推送流程 二.应用创建 三.客户端 SDK 集成 3.1 Android 3.2 iOS 四.服务端推送 4.1 服务端消息下发 ...

  7. python爬虫实现实时爬取学校最新通知并推送

    1.背景 由于考研复试需要实时获取报考学校的最新通知,以免错过重要的消息,而手动刷新的方式费时费力,因此想到通过爬虫实现实时获取最新通知的功能.但还需解决几个问题: 爬虫爬取的最新通告,采用什么方式推 ...

  8. ios中的通知和推送

    推送通知 1.作用:让不在前台(后台或者关闭)的APP知道APP内部发生的事情 2.在"设置"-->"通知中心"-----> 可以关闭推送通知 3. ...

  9. Java给特定用户发通知_微信公众平台向特定用户推送消息

    最近研究微信公众平台,这里整理了一下向特定用户推送消息的思路 一.首先需要将微信的openid与系统用户绑定. 在用户关注公众平台的时候,回复一个链接,要求用户绑定,可以设计如下消息进行回复,(ope ...

最新文章

  1. kuayu react_react跨域解决方案
  2. matplotlib 中将图直接从buffer中变为PIL 再到numpy
  3. python脚本自动消除安卓版_Android:检测内存泄漏的自动化测试Python脚本
  4. document.getElementByName()的用法
  5. [翻译]ElasticSearch官方文档-安装
  6. docker运行随机分配端口
  7. Ubuntu14.04中安装ROS Indigo(亲测)
  8. bzoj2299 [HAOI2011]向量 结论 裴蜀定理
  9. 蔡崇信完成对布鲁克林篮网和巴克莱中心的全资收购
  10. 为什么wait和notify必须在同步方法或同步块中调用?
  11. 智能优化算法:未来搜索算法-附代码
  12. win10定时关机c语言,win10 定时关机命令怎么设置_win10怎么设置定时关机指令-win7之家...
  13. Python批量下载抖音大V主页视频
  14. 判断一元二次方程完整版(有无虚根)
  15. 【vue-router源码】五、router.addRoute、router.removeRoute、router.hasRoute、router.getRoutes源码分析
  16. 面向对象编程思想 以及类与对象
  17. matlab ps液化,photoshop液化工具崩溃怎么办 ps液化工具崩溃解决方法
  18. DetectGPT VS ChatGPT:AI反击战?
  19. Linux进程间通信源码剖析,共享内存(shmget函数详解)
  20. 在大厂入职三年已是老员工?大学教授:年轻人压力很大。。。

热门文章

  1. 古月居ros第十讲遇到的问题
  2. 90后都会选择的购车模式“网上购车平台一成首付”
  3. 逻辑函数常用的描述方法及相互间的转化
  4. PHP:ThinkPHP
  5. 关于ThinkPHP
  6. 标准分幅地图-选择比例尺
  7. 阿德莱德大学计算机科学学士学分,留学360:阿德莱德大学软件工程计算机科学学士专业简析...
  8. Vista不是黄金甲
  9. 如何判断Android app退后台、进前台
  10. PPT基础(四十五)调整图片透明度