iOS-APNs推送從證書(shū)配置到測(cè)試通知


一癌蚁、證書(shū)相關(guān)

? ? 1幻梯、首先我們要配置兩個(gè)證書(shū):推送測(cè)試證書(shū)、推送正式證書(shū)努释。 ? 先打開(kāi)AppID的推送功能


? ? 在配置證書(shū)界面礼旅,Certificates-Development(這是開(kāi)發(fā)證書(shū)即測(cè)試用,Production這是生產(chǎn)證書(shū)即正式)


? ? ? ? <1.測(cè)試證書(shū):選擇 Apple Push Notification service SSL (Sandbox) ? 選擇 App ID ? 選擇 創(chuàng)建mac的csr文件(csr創(chuàng)建步? ? ? ? 驟:選擇鑰匙串訪(fǎng)問(wèn)工具-- 證書(shū)助理--從證書(shū)頒發(fā)機(jī)構(gòu)請(qǐng)求證書(shū)--存到磁盤(pán))


? ? 下載并安裝測(cè)試證書(shū)

? ? ? ? <2.正式證書(shū):選擇 App Store and Ad Hoc ? 其他與測(cè)試證書(shū)步驟一致

? ? ? 2.為后臺(tái)服務(wù)方提供p12文件

? ? ? ? 不論是使用第三方(極光友盟等)還是后端人員自己寫(xiě)的推送洽洁,都需要將安裝的證書(shū)導(dǎo)出為P12文件:在mac的鑰匙串中,選擇安裝的推送證書(shū)菲嘴,右鍵選擇導(dǎo)出饿自,選擇.p12

(一定要是創(chuàng)建證書(shū)的電腦才可以導(dǎo)出為p12)。 有的推送第三方需要.pem文件龄坪,則需要對(duì)p12文件進(jìn)行轉(zhuǎn)換昭雌,具體:http://www.reibang.com/p/cc952ea07a08?mType=Group

二、代碼

? ? 1.打開(kāi)項(xiàng)目中的推送開(kāi)關(guān)

? ? ? ? Capabilities--Push Notifications 若AppID的推送打開(kāi)健田,且證書(shū)安裝好烛卧,則Push Notifications下的兩步都會(huì)打鉤,證明準(zhǔn)備步驟都已就緒


? ? ? 2.各種方法的作用,Appdelegate要遵守UNUserNotificationCenterDelegate協(xié)議

? ? ? ? ? ? ? ? 首先是啟動(dòng)方法didFinishLaunchingWithOptions妓局,在這里注冊(cè)推送服務(wù)

// 程序第一次啟動(dòng)的時(shí)候調(diào)用(點(diǎn)擊通知啟動(dòng) 和 點(diǎn)擊app圖標(biāo)啟動(dòng) 都會(huì)走這個(gè)方法总放,所以這種情況下 要在這里獲取推送的內(nèi)容)

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

{

? ? self.isFirstLaunch = YES;

//這里是獲取推送內(nèi)容

? ? NSDictionary* remoteNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];

? ? if (remoteNotification) {

? ? ? ? //點(diǎn)擊通知框進(jìn)入app可以在此接收通知(只在殺死狀態(tài)下可以接收到),之后會(huì)調(diào)用didReceiveRemoteNotification,因?yàn)榇朔椒ㄕ{(diào)用時(shí),控制器已經(jīng)創(chuàng)建完畢,可以跳轉(zhuǎn)

? ? }else{

? ? ? ? //點(diǎn)擊圖標(biāo)進(jìn)入app

? }

//這里要注冊(cè)推送服務(wù)

? if (kOS >= 10.0) { // 設(shè)置能夠接收通知 的類(lèi)型

? ? ? UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];? ? ? ? ? ? ? ?

? ? ? ? [center setDelegate:self];

? ? ? UNAuthorizationOptions type = UNAuthorizationOptionBadge|UNAuthorizationOptionSound|UNAuthorizationOptionAlert;

? ? ? [center requestAuthorizationWithOptions:type completionHandler:^(BOOL granted, NSError * _Nullable error) {

? ? ? ? ? if (granted) {

? ? ? ? ? ? ? Log(@"注冊(cè)成功");

? ? ? ? ? ? ? [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {

? ? ? ? ? ? ? ? ? ? Log(@"%@",settings);

? ? ? ? ? ? ? ? }];

? ? ? ? ? }else{

? ? ? ? ? ? ? Log(@"注冊(cè)失敗");

? ? ? ? ? }

? ? ? }];

? } else if (kOS >= 8.0){

? ? ? UIUserNotificationType notificationTypes = UIUserNotificationTypeBadge |

? ? ? UIUserNotificationTypeSound |

? ? ? UIUserNotificationTypeAlert;

? ? ? UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:notificationTypes categories:nil];

? ? ? [application registerUserNotificationSettings:settings];

? }

? // 注冊(cè)通知

? ? [application registerForRemoteNotifications];

? return YES;

}

? ? ? ? 接下來(lái)這是獲得deviceToken的方法(deviceToken好爬,是apns服務(wù)給每個(gè)設(shè)備的標(biāo)示局雄,通過(guò)這個(gè)標(biāo)示才能推送到指定設(shè)備,且deviceToken是變化的存炮,一般在應(yīng)用卸載重裝炬搭,或者系統(tǒng)升級(jí)才會(huì)改變)

// 1 每次程序啟動(dòng)都會(huì)調(diào)用

- (void)application:(UIApplication *)application

didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken

{

? ? //在這里把token傳給后臺(tái)

? ? NSString *deviceTokenStr = [[[[deviceToken description]

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? stringByReplacingOccurrencesOfString:@"<" withString:@""]

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? stringByReplacingOccurrencesOfString:@">" withString:@""]

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? stringByReplacingOccurrencesOfString:@" " withString:@""];

? Log(@"deviceTokenStr:\n%@",deviceTokenStr);

? ? NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

? ? [defaults setObject:deviceTokenStr forKey:@"deviceToken"];

? ? [defaults synchronize];

//這個(gè)方法 是我向后端數(shù)據(jù)庫(kù)更新token的請(qǐng)求蜈漓,如果用第三方推送可忽略

? ? [PushService updateDeviceToken];

}

? ? ? ? didFailToRegisterForRemoteNotificationsWithError這個(gè)方法就不多說(shuō)了,是token獲取失敗調(diào)用宫盔,我們可以在這里向后臺(tái)注銷(xiāo)這個(gè)用戶(hù)的token

? ? ? ? willPresentNotification這個(gè)方法在iOS10才有的融虽,用來(lái)設(shè)置應(yīng)用在前臺(tái)運(yùn)行時(shí),收到通知的提醒模型

#pragma mark- UNUserNotificationCenterDelegate(iOS10以上)

//在前臺(tái)收到通知(程序運(yùn)行時(shí)走此方法)

- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{

? // 需要執(zhí)行這個(gè)方法灼芭,選擇是否提醒用戶(hù)有额,有Badge、Sound姿鸿、Alert三種類(lèi)型可以設(shè)置

? ? //如果加上這三個(gè)枚舉谆吴,則用戶(hù)即使在前臺(tái)收到通知也會(huì)出現(xiàn),有聲音和角標(biāo)苛预。如果去掉在前臺(tái)則不會(huì)收到

? ? completionHandler(UNNotificationPresentationOptionBadge|

? ? ? ? ? ? ? ? ? ? UNNotificationPresentationOptionSound|

? ? ? ? ? ? ? ? ? ? UNNotificationPresentationOptionAlert);

}

? ? ? ? didReceiveNotificationResponse是應(yīng)用處于任何狀態(tài)下句狼,用戶(hù)點(diǎn)擊了推送通知的彈框,就會(huì)調(diào)用

// iOS 10之后 任何狀態(tài)用戶(hù)點(diǎn)擊了通知 (用戶(hù)點(diǎn)擊了通知時(shí)走此方法)

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler{

? ? NSDictionary *userInfo = response.notification.request.content.userInfo;

//這里可以處理收到的推送消息

? ? ? ? if (userInfo[@"aps"][@"url"]) {

? ? ? ? ? ? [ToolsHelper receiveMessageAndPushcontroller:userInfo[@"aps"][@"url"]];

? ? ? ? }

? ? }

? completionHandler();

? ? return;

}

? ? didReceiveRemoteNotification? 這個(gè)方法在iOS10以下热某,應(yīng)用在前臺(tái)的時(shí)候腻菇,有推送來(lái),會(huì)直接來(lái)到這個(gè)方法昔馋,但通知欄不會(huì)有提示筹吐,角標(biāo)也不會(huì)有,應(yīng)用如果在后臺(tái)后者關(guān)閉狀態(tài)秘遏,點(diǎn)擊推送來(lái)的消息也會(huì)來(lái)到這個(gè)方法丘薛,我們可以在這里處理業(yè)務(wù)邏輯(由于要處理iOS10以前設(shè)備在前臺(tái)收到通知的提醒,所以要將通知轉(zhuǎn)為本地通知進(jìn)行彈窗提示)

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

? Log(@"userInfo: %@", userInfo.description);

? if ( application.applicationState == UIApplicationStateActive) {

? ? ? ? // 程序在運(yùn)行過(guò)程中收到推送通知,轉(zhuǎn)換成一個(gè)本地通知邦危,顯示到通知欄

? ? ? ? UILocalNotification *localNotification = [[UILocalNotification alloc] init];

? ? ? ? localNotification.userInfo = userInfo;

? ? ? ? localNotification.soundName = UILocalNotificationDefaultSoundName;

? ? ? ? localNotification.alertBody = [[userInfo objectForKey:@"aps"]objectForKey:@"alert"];

? ? ? ? localNotification.fireDate = [NSDate date];

? ? ? ? [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];

? } else {

? ? ? //在后臺(tái)或者關(guān)閉狀態(tài)收到推送通知

? ? }

? completionHandler(UIBackgroundFetchResultNewData);

}

以上就是常規(guī)推送需要處理的方法


在iOS10洋侨,推送已經(jīng)可以推圖片,git圖倦蚪,短視頻了希坚,也可以自定義彈窗的操作按鈕。接下來(lái)就看一下陵且,怎么推送簡(jiǎn)單的圖片裁僧,這種推送有自己的UI和方法,也相當(dāng)于一個(gè)target了慕购。所以創(chuàng)建方法也跟普通推送不太一樣聊疲。

? ? 選擇File-New-Target


創(chuàng)建后,系統(tǒng)會(huì)生成一個(gè)PushServiceEx的文件夾沪悲,里面有一個(gè)NotificationService的類(lèi)售睹,同時(shí)也會(huì)有一個(gè)PushServiceEx的target生成,如果未找到這個(gè)target? 就在manger Schemes里面勾上可训。


下面是NotificationService類(lèi)中的代碼昌妹,這個(gè)類(lèi)的方法會(huì)在手機(jī)接收到帶圖片的推送時(shí)調(diào)用捶枢,且負(fù)責(zé)下載這個(gè)圖片,然后保存展示飞崖。(imageUrl是推送時(shí)烂叔,定義圖片url的字段)

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {

? ? self.contentHandler = contentHandler;

? ? self.bestAttemptContent = [request.content mutableCopy];

? ? NSDictionary *dict =? self.bestAttemptContent.userInfo;

? ? NSDictionary *notiDict = dict[@"aps"];

? ? NSString *imgUrl = [NSString stringWithFormat:@"%@",notiDict[@"imageUrl"]];

? if (!imgUrl.length) {

? ? ? self.contentHandler(self.bestAttemptContent);

? }else{

//這是下載推片的方法

? ? ? ? [self loadAttachmentForUrlString:imgUrl withType:@"image" completionHandle:^(UNNotificationAttachment *attach) {

? ? ? ? ? if (attach) {

? ? ? ? ? ? ? ? self.bestAttemptContent.attachments = [NSArray arrayWithObject:attach];

? ? ? ? ? ? }

? ? ? ? ? ? self.contentHandler(self.bestAttemptContent);

? ? ? ? }];

? ? }

}

- (void)loadAttachmentForUrlString:(NSString *)urlStr

? ? ? ? ? ? ? ? ? ? ? ? ? withType:(NSString *)type

? ? ? ? ? ? ? ? ? completionHandle:(void(^)(UNNotificationAttachment *attach))completionHandler{

? __block UNNotificationAttachment *attachment = nil;

? ? NSURL *attachmentURL = [NSURL URLWithString:urlStr];

? ? NSString *fileExt = [self fileExtensionForMediaType:type];

? NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];

? ? [[session downloadTaskWithURL:attachmentURL

? ? ? ? ? ? ? ? completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) {

? ? ? ? ? ? ? ? ? ? if (error != nil) {

? ? ? ? ? ? ? ? ? ? ? ? NSLog(@"%@", error.localizedDescription);

? ? ? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? ? ? NSFileManager *fileManager = [NSFileManager defaultManager];

? ? ? ? ? ? ? ? ? ? ? ? NSURL *localURL = [NSURL fileURLWithPath:[temporaryFileLocation.path stringByAppendingString:fileExt]];

? ? ? ? ? ? ? ? ? ? ? ? [fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error];

? ? ? ? ? ? ? ? ? ? ? NSError *attachmentError = nil;

? ? ? ? ? ? ? ? ? ? ? ? attachment = [UNNotificationAttachment attachmentWithIdentifier:@"" URL:localURL options:nil error:&attachmentError];

? ? ? ? ? ? ? ? ? ? ? ? if (attachmentError) {

//? ? ? ? ? ? ? ? ? ? ? ? ? ? log(@"%@", attachmentError.localizedDescription);

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? completionHandler(attachment);

? ? ? ? ? ? ? ? }] resume];

}

- (NSString *)fileExtensionForMediaType:(NSString *)type {

? ? NSString *ext = type;

? if ([type isEqualToString:@"image"]) {

? ? ? ? ext = @"jpg";

? ? }

? if ([type isEqualToString:@"video"]) {

? ? ? ? ext = @"mp4";

? ? }

? if ([type isEqualToString:@"audio"]) {

? ? ? ? ext = @"mp3";

? ? }

? return [@"." stringByAppendingString:ext];

}


要完成這個(gè)圖片推送? PushServiceEx? target有幾個(gè)要注意的點(diǎn):

? ? 第一個(gè):測(cè)試一定要選擇這個(gè)target進(jìn)行安裝。

? ? 第二個(gè):這個(gè)target支持的development target 版本一定要填對(duì)固歪,我之前就因?yàn)樘盍薸OS11蒜鸡,但是用10的機(jī)子測(cè)了半天都只是普通推送沒(méi)有圖片。

? ? 第三個(gè):target的版本不能小于項(xiàng)目的版本牢裳,否則會(huì)在提交應(yīng)用到appstore的時(shí)候逢防,有警告

? ? 第四個(gè): "mutable-content" : 1? 推送時(shí)一定要有這個(gè)參數(shù),不然就會(huì)被手機(jī)認(rèn)為是普通推送蒲讯。推送的json應(yīng)該像這樣 忘朝。? ?

{

? ? "aps" : {

? ? ? ? "alert" : "Your message here.",

? ? ? ? "badge" : 9,

? ? ? ? "sound" : "default",

? ? ? ? "mutable-content" : 1,

? ? ? ? "imageUrl" : "https://www.xxxxxxxx.png"

? ? },

}


三、測(cè)試

? ? 1.推送工具

? ? ? ? 如果后臺(tái)的小伙伴沒(méi)時(shí)間跟我們測(cè)試推送判帮,就可以自己用推送小工具局嘁,只需要我們下載的推送證書(shū)(后綴為cer的文件),自己進(jìn)行推送測(cè)試晦墙,這里是工具地址:https://itunes.apple.com/cn/app/easy-apns-provider/id989622350?mt=12

注意悦昵,推送只能進(jìn)行真機(jī)測(cè)試,模擬器是沒(méi)有token的晌畅。 我們需要添加要推送設(shè)備的token但指,然后選擇證書(shū),如果是正式就選連接至sandbox那個(gè)選項(xiàng)抗楔。然后就可以開(kāi)始推送了

? ? 2.推送的環(huán)境說(shuō)明

? ? 不論是這個(gè)小工具還是服務(wù)器推送棋凳,如果APP是生產(chǎn)環(huán)境(Appstore下載的或Adhoc環(huán)境),則選擇正式證書(shū)谓谦。我們自己開(kāi)發(fā)安裝的則選擇測(cè)試證書(shū)。所以我們是沒(méi)辦法用生產(chǎn)證書(shū)進(jìn)行上線(xiàn)前測(cè)試的(Adhoc除外)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末贪婉,一起剝皮案震驚了整個(gè)濱河市反粥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌疲迂,老刑警劉巖才顿,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異尤蒿,居然都是意外死亡郑气,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)腰池,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)尾组,“玉大人忙芒,你說(shuō)我怎么就攤上這事』淝龋” “怎么了呵萨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)跨跨。 經(jīng)常有香客問(wèn)我潮峦,道長(zhǎng),這世上最難降的妖魔是什么勇婴? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任忱嘹,我火速辦了婚禮,結(jié)果婚禮上耕渴,老公的妹妹穿的比我還像新娘拘悦。我一直安慰自己,他們只是感情好萨螺,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布窄做。 她就那樣靜靜地躺著,像睡著了一般慰技。 火紅的嫁衣襯著肌膚如雪椭盏。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天吻商,我揣著相機(jī)與錄音掏颊,去河邊找鬼。 笑死艾帐,一個(gè)胖子當(dāng)著我的面吹牛乌叶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播柒爸,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼准浴,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了捎稚?” 一聲冷哼從身側(cè)響起乐横,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎今野,沒(méi)想到半個(gè)月后葡公,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡条霜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年催什,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宰睡。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蒲凶,死狀恐怖气筋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情豹爹,我是刑警寧澤裆悄,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站臂聋,受9級(jí)特大地震影響光稼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜孩等,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一艾君、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧肄方,春花似錦冰垄、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至隅要,卻和暖如春蝴罪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背步清。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工要门, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人廓啊。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓欢搜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親谴轮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子炒瘟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345