? ? ? ?iOS中通知機(jī)制又叫消息機(jī)制,其包括兩類:一類是本地通知;另一類是推送通知,也叫遠(yuǎn)程通知。兩種通知在iOS中的表現(xiàn)一致,可以通過橫幅或者彈出提醒兩種形式告訴用戶,并且點(diǎn)擊通知可以會(huì)打開應(yīng)用程序,但是實(shí)現(xiàn)原理卻完全不同。
本地通知:
? ? ? ?本地通知是由本地應(yīng)用觸發(fā)的,它是基于時(shí)間行為的一種通知形式,例如鬧鐘定時(shí)、待辦事項(xiàng)提醒,又或者一個(gè)應(yīng)用在一段時(shí)候后不使用通常會(huì)提示用戶使用此應(yīng)用等都是本地通知。創(chuàng)建一個(gè)本地通知通常分為以下幾個(gè)步驟:
創(chuàng)建UILocalNotification。設(shè)置處理通知的時(shí)間fireDate。
配置通知的內(nèi)容:通知主體、通知聲音、圖標(biāo)數(shù)字等。
配置通知傳遞的自定義數(shù)據(jù)參數(shù)userInfo(這一步可選)。
調(diào)用通知,可以使用scheduleLocalNotification:按計(jì)劃調(diào)度一個(gè)通知,也可以使用presentLocalNotificationNow立即調(diào)用通知。
在AppDelegate中:
首先注冊(cè)通知:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIUserNotificationSettings *userNotification = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil];
[application registerUserNotificationSettings:userNotification];//注冊(cè)本地推送
return YES;
}
//通知回調(diào)
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
NSLog(@"%@",notification.alertBody);
UILabel*label = [[UILabel alloc]init];
label.frame = CGRectMake(0, -60, [UIScreen mainScreen].bounds.size.width, 60);
label.layer.cornerRadius = 10;
label.backgroundColor = [UIColor blackColor];
label.text = notification.alertBody;
label.numberOfLines = 0;
label.textColor = [UIColor whiteColor];
label.font = [UIFont systemFontOfSize:14];
label.textAlignment = NSTextAlignmentCenter;
self.label = label;
[self appearView];
[self.window addSubview:self.label];
}
//頂部彈窗動(dòng)畫
-(void)appearView{
[UIView animateWithDuration:1 animations:^{
self.label.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 60);
} completion:^(BOOL finished) {}];
[self returnNotificationLabel];
}
//頂部彈窗消失
-(void)returnNotificationLabel{
[UIView animateWithDuration:1 delay:2 options:0 animations:^{
self.label.frame = CGRectMake(0, -60, [UIScreen mainScreen].bounds.size.width, 60);
} completion:^(BOOL finished) {
}];
}
在ViewController.h中
設(shè)置本地通知
- (IBAction)localPushNow:(id)sender {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//本地推送
UILocalNotification*notification = [[UILocalNotification alloc]init];
// 設(shè)置觸發(fā)通知的時(shí)間
NSDate * pushDate = [NSDate dateWithTimeIntervalSinceNow:10];
if (notification != nil) {
notification.fireDate = pushDate;
notification.timeZone = [NSTimeZone defaultTimeZone]; ? // 時(shí)區(qū)
notification.repeatInterval = kCFCalendarUnitDay;? ? // 通知重復(fù)提示的單位,可以是天、周、月
notification.soundName = UILocalNotificationDefaultSoundName; ? ?// 通知被觸發(fā)時(shí)播放的聲音
notification.alertBody = @"hello world ?。?!"; ? ? // 通知內(nèi)容
notification.applicationIconBadgeNumber = 0;
NSDictionary*info = [NSDictionary dictionaryWithObject:@"test" forKey:@"name"]; ?// 通知參數(shù)
notification.userInfo = info;
[[UIApplication sharedApplication] scheduleLocalNotification:notification];? // 執(zhí)行通知注冊(cè)
notification.timeZone = [NSTimeZone defaultTimeZone];
// 設(shè)置重復(fù)的間隔
notification.repeatInterval = kCFCalendarUnitSecond; ? ?// 設(shè)置重復(fù)的間隔
});
}
遠(yuǎn)程推送:
1.應(yīng)用程序注冊(cè)APNs推送消息。
說明:
a.只有注冊(cè)過的應(yīng)用才有可能接收到消息,程序中通常通過UIApplication的registerUserNotificationSettings:方法注冊(cè),iOS8中通知注冊(cè)的方法發(fā)生了改變,如果是iOS7及之前版本的iOS請(qǐng)參考其他代碼。
b.注冊(cè)之前有兩個(gè)前提條件必須準(zhǔn)備好:開發(fā)配置文件(provisioning profile,也就是.mobileprovision后綴的文件)的App ID不能使用通配ID必須使用指定APP ID并且生成配置文件中選擇Push Notifications服務(wù),一般的開發(fā)配置文件無法完成注冊(cè);應(yīng)用程序的Bundle Identifier必須和生成配置文件使用的APP ID完全一致。
2.iOS從APNs接收device token,在應(yīng)用程序獲取device token。
說明:
a.在UIApplication的-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken代理方法中獲取令牌,此方法發(fā)生在注冊(cè)之后。
b.如果無法正確獲得device token可以在UIApplication的-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error代理方法中查看詳細(xì)錯(cuò)誤信息,此方法發(fā)生在獲取device token失敗之后。
c.必須真機(jī)調(diào)試,模擬器無法獲取device token。
3.iOS應(yīng)用將device token發(fā)送給應(yīng)用程序提供商,告訴服務(wù)器端當(dāng)前設(shè)備允許接收消息。
說明:
a.device token的生成算法只有Apple掌握,為了確保算法發(fā)生變化后仍然能夠正常接收服務(wù)器端發(fā)送的通知,每次應(yīng)用程序啟動(dòng)都重新獲得device token(注意:device token的獲取不會(huì)造成性能問題,蘋果官方已經(jīng)做過優(yōu)化)。
b.通常可以創(chuàng)建一個(gè)網(wǎng)絡(luò)連接發(fā)送給應(yīng)用程序提供商的服務(wù)器端, 在這個(gè)過程中最好將上一次獲得的device token存儲(chǔ)起來,避免重復(fù)發(fā)送,一旦發(fā)現(xiàn)device token發(fā)生了變化最好將原有的device token一塊發(fā)送給服務(wù)器端,服務(wù)器端刪除原有令牌存儲(chǔ)新令牌避免服務(wù)器端發(fā)送無效消息。
4.應(yīng)用程序提供商在服務(wù)器端根據(jù)前面發(fā)送過來的device token組織信息發(fā)送給APNs。
說明:
a.發(fā)送時(shí)指定device token和消息內(nèi)容,并且完全按照蘋果官方的消息格式組織消息內(nèi)容,通常情況下可以借助其他第三方消息推送框架來完成。
5.APNs根據(jù)消息中的device token查找已注冊(cè)的設(shè)備推送消息。
說明:
a.正常情況下可以根據(jù)device token將消息成功推送到客戶端設(shè)備中,但是也不排除用戶卸載程序的情況,此時(shí)推送消息失敗,APNs會(huì)將這個(gè)錯(cuò)誤消息通知服務(wù)器端以避免資源浪費(fèi)(服務(wù)器端此時(shí)可以根據(jù)錯(cuò)誤刪除已經(jīng)存儲(chǔ)的device token,下次不再發(fā)送)。
下面將簡(jiǎn)單演示一下推送通知的簡(jiǎn)單流程:
@implementation AppDelegate
#pragma mark - 應(yīng)用程序代理方法
#pragma mark 應(yīng)用程序啟動(dòng)之后
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
_window=[[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
_window.backgroundColor =[UIColor colorWithRed:249/255.0 green:249/255.0 blue:249/255.0 alpha:1];
//設(shè)置全局導(dǎo)航條風(fēng)格和顏色
[[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:23/255.0 green:180/255.0 blue:237/255.0 alpha:1]];
[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];
KCMainViewController *mainController=[[KCMainViewController alloc]init];
_window.rootViewController=mainController;
[_window makeKeyAndVisible];
//注冊(cè)推送通知(注意iOS8注冊(cè)方法發(fā)生了變化)
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
[application registerForRemoteNotifications];
return YES;
}
#pragma mark 注冊(cè)推送通知之后
//在此接收設(shè)備令牌
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
[self addDeviceToken:deviceToken];
NSLog(@"device token:%@",deviceToken);
}
#pragma mark 獲取device token失敗后
-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
NSLog(@"didFailToRegisterForRemoteNotificationsWithError:%@",error.localizedDescription);
[self addDeviceToken:nil];
}
#pragma mark 接收到推送通知之后
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
NSLog(@"receiveRemoteNotification,userInfo is %@",userInfo);
}
#pragma mark - 私有方法
/**
*? 添加設(shè)備令牌到服務(wù)器端
*
*? @param deviceToken 設(shè)備令牌
*/
-(void)addDeviceToken:(NSData *)deviceToken{
NSString *key=@"DeviceToken";
NSData *oldToken= [[NSUserDefaults standardUserDefaults]objectForKey:key];
//如果偏好設(shè)置中的已存儲(chǔ)設(shè)備令牌和新獲取的令牌不同則存儲(chǔ)新令牌并且發(fā)送給服務(wù)器端
if (![oldToken isEqualToData:deviceToken]) {
[[NSUserDefaults standardUserDefaults] setObject:deviceToken forKey:key];
[self sendDeviceTokenWidthOldDeviceToken:oldToken newDeviceToken:deviceToken];
}
}
-(void)sendDeviceTokenWidthOldDeviceToken:(NSData *)oldToken newDeviceToken:(NSData *)newToken{
//注意一定確保真機(jī)可以正常訪問下面的地址
NSString *urlStr=@"http://192.168.1.101/RegisterDeviceToken.aspx";
urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url=[NSURL URLWithString:urlStr];
NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:10.0];
[requestM setHTTPMethod:@"POST"];
NSString *bodyStr=[NSString stringWithFormat:@"oldToken=%@&newToken=%@",oldToken,newToken];
NSData *body=[bodyStr dataUsingEncoding:NSUTF8StringEncoding];
[requestM setHTTPBody:body];
NSURLSession *session=[NSURLSession sharedSession];
NSURLSessionDataTask *dataTask= [session dataTaskWithRequest:requestM completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"Send failure,error is :%@",error.localizedDescription);
}else{
NSLog(@"Send Success!");
}
}];
[dataTask resume];
}
@end
? ? ? ? ?iOS開發(fā)過程中如果需要進(jìn)行真機(jī)調(diào)試、發(fā)布需要注冊(cè)申請(qǐng)很多證書,會(huì)牽扯到一些特殊配置,這里就簡(jiǎn)單的對(duì)iOS開發(fā)的常用證書和秘鑰等做一說明。
證書
iOS常用的證書包括開發(fā)證書和發(fā)布證書,無論是真機(jī)調(diào)試還是最終發(fā)布應(yīng)用到App Store這兩個(gè)證書都是必須的,它是iOS開發(fā)的基本證書。
a.開發(fā)證書:開發(fā)證書又分為普通開發(fā)證書和推送證書,如果僅僅是一般的應(yīng)用則前者即可滿足,但是如果開發(fā)推送應(yīng)用則必須使用推送證書。
b.發(fā)布證書:發(fā)布證書又可以分為普通發(fā)布證書、推送證書、Pass Type ID證書、站點(diǎn)發(fā)布證書、VoIP服務(wù)證書、蘋果支付證書。同樣的,對(duì)于需要使用特殊服務(wù)的應(yīng)用則必須選擇對(duì)應(yīng)的證書。
應(yīng)用標(biāo)識(shí)
App ID,應(yīng)用程序的唯一標(biāo)識(shí),對(duì)應(yīng)iOS應(yīng)用的Bundle Identifier,App ID在蘋果開發(fā)者中心中分為通配應(yīng)用ID和明確的應(yīng)用ID,前者一般用于普通應(yīng)用開發(fā),一個(gè)ID可以適用于多個(gè)不同標(biāo)識(shí)的應(yīng)用;但是對(duì)于使用消息推送、Passbook、站點(diǎn)發(fā)布、iCloud等服務(wù)的應(yīng)用必須配置明確的應(yīng)用ID。
設(shè)備標(biāo)識(shí)
UDID,用于標(biāo)識(shí)每一臺(tái)硬件設(shè)備的標(biāo)示符。注意它不是device token,device token是根據(jù)UDID使用一個(gè)只有Apple自己才知道的算法生成的一組標(biāo)示符。
本文內(nèi)容自參考:http://www.cnblogs.com/kenshincui/p/4168532.html
http://www.cnblogs.com/kenshincui/p/4168532.html