iOS APNs原生推送實現零基礎教程

app實現遠程推送是每一個App的必需要求,本公司開發(fā)的XX app是混合開發(fā)第练,采用的Ionic3框架摇予,之前集成過第三方推送解決方案擦囊,效果不太理想,現在直接對接Ios官方接口實現推送刨啸,速度也快些。本人毫無Ios基礎,對ios原生代碼這塊甚至搞不清是Swift語音還是Object-C羽圃,特此記錄此次實現步驟。

環(huán)境:Xcode11, 測試機Ios13

基礎知識

工程的AppDelegate.m文件里提供了如下方法:

//當應用程序啟動后抖剿,可能需要進行其他邏輯處理時調用的方法
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

//當程序被喚醒時調用的方法
- (void)applicationDidBecomeActive:(UIApplication *)application;

/// 用戶同意接收通知后朽寞,會調用此程序
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken;

/// 注冊失敗調用
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;

//收到推送后調用的方法(iOS 10 及以上)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler;

//收到推送后調用的方法(iOS 10 以下)
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler;

遠程推送的實現原理:

1.打開App時: 發(fā)送UDID和BundleID給APNs加密后返回deviceToken

2.獲取Token后,App調用接口,將用戶身份信息和deviceToken發(fā)給服務器牙躺,服務器記錄

3.當推送消息時, 服務器按照用戶身份信息找到存儲的deviceToken愁憔,將消息和deviToken發(fā)送給APNs

4.蘋果的APNs通過deviceToken, 找到指定設備的指定程序, 并將消息推送給用戶

第一步,配置應用

1打開推送開關

image

第二步孽拷,注冊推送吨掌,授權

用戶同意后,會調用此程序脓恕,獲取系統的deviceToken膜宋,應把deviceToken傳給服務器保存,此函數會在程序每次啟動時調用(前提是用戶允許通知):

在工程的AppDelegate.m文件中進行代碼編寫炼幔,在App啟動鉤子中注冊推送服務秋茫,代碼如下

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    self.viewController = [[MainViewController alloc] init];
    NSLog(@"app 啟動了....");
    //消息推送注冊
    UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
    center.delegate = self;
    UNAuthorizationOptions type = UNAuthorizationOptionBadge|UNAuthorizationOptionSound|UNAuthorizationOptionAlert;
    [center requestAuthorizationWithOptions:type completionHandler:^(BOOL granted, NSError * _Nullable error) {
       NSLog(@"is register");
       if (granted) {
            NSLog(@"注冊成功");
       }else{
        NSLog(@"注冊失敗");
        }
    }];
    //注冊遠程通知
    [application registerForRemoteNotifications];
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

重寫 didFailToRegisterForRemoteNotificationsWithError 方法,打印出錯誤日志;

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    NSLog(@"注冊推送失斈诵恪:%@",error);
}

重寫 didRegisterForRemoteNotificationsWithDeviceToken 方法肛著,獲取推送token;

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    NSLog(@"weboey......");
    NSLog(@"token:%@", deviceToken);
    if (![deviceToken isKindOfClass:[NSData class]]) return;
    const unsigned *tokenBytes = [deviceToken bytes];
    NSString *hexToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",
                          ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
                          ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
                          ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];
    NSLog(@"deviceToken:%@",hexToken);
    AppDelegate *myDelegate = [[UIApplication sharedApplication] delegate];
    myDelegate.token = hexToken;
}

收到消息后處理通知

-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{
    NSLog(@"收到推送內容");
    NSDictionary * userInfo = notification.request.content.userInfo;
    if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        //TODO:處理遠程推送內容
        NSLog(@"%@", userInfo);
    }
    completionHandler(UNNotificationPresentationOptionSound|UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionAlert);
}

App被喚醒后清空通知欄消息

- (void)applicationDidBecomeActive:(UIApplication *)application{
    NSLog(@"app 被喚醒....");
    [[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications];
}

角標計數等要求可以后端接口實現,消息通知合并可以按App分類合并跺讯,也可以在接口推送請求頭中設置apns-collapse-id

image

編碼結束枢贿,上面代碼已能獲取到推送Token,下一步就可以把日志中打印的Token復制下來刀脏,可以進行測試了局荚。

第三步,配置證書

App調用接口,需要進行用戶身份信息驗證:有2種方式創(chuàng)建身份驗證密鑰(p8證書和p12證書)

創(chuàng)建你的APNs keys 或者 創(chuàng)建推送證書愈污,這兩個創(chuàng)建一個即可實現推送耀态。這兩個創(chuàng)建一個即可實現推送。這兩個創(chuàng)建一個即可實現推送暂雹。重要的事情說三遍首装。并且創(chuàng)建證書和你的app注冊推送無關系,也能獲取到推送token杭跪,只進行身份驗證的簿盅,使服務器具有權限給你的app推送消息挥下。(很多人對此有誤區(qū),認為證書不正確無法獲取推送token)

  • 證書驗證 (P12格式身份證書)
  1. 此方式比較麻煩桨醋,分為生產證書和測試證書棚瘟。
  2. 還需要生成.pem文件才能測試。
  3. 有效期一年要換一次喜最。

不建議使用證書驗證偎蘸,推薦使用 token驗證方式(即P8格式證書,永久有效瞬内,只可下載一次記得保存好C匝)

  • token驗證
image
image

證書配置就不寫了,請參考友盟官方文檔: 證書配置指南

https://developer.umeng.com/docs/67966/detail/66748

常見錯誤及解決辦法:

錯誤一:報錯Code=3000 "未找到應用程序的“aps-environment”的授權字符串"

1:如果你的項目中已經添加了 KeychainAccessGroups.plist 文件虫蝶,在此文件中新增一對鍵值
aps-environment:development
2: 如果沒有此文件則章咧,點擊 capalities 中找到 push notifications
此時應該能看到錯誤信息,還有一個 fixissue 的按鈕能真,點擊即可
3:aps-environment:development 這個鍵值對明顯是針對開發(fā)環(huán)境的赁严。如果到線上環(huán)境的時候還用更改為 aps-environment:production

錯誤二:didRegisterForRemoteNotificationsWithDeviceToken不調用 (獲取不到 devicetoken)

注冊推送后會彈出是否同意,用戶點擊同意后此鉤子方法會自動調用粉铐,如果不被調用可參試如下解決:

  1. 設置中沒有打開通知開關疼约; (在配置常看蝙泼,是否允許通知)

  2. 程序問題:

  • 看didRegisterForRemoteNotificationsWithDeviceToken這個方法程剥,有沒有被重寫。

  • 通知的配置是否正確汤踏。

  • 看 (void)application didFailToRegisterForRemoteNotificationsWithError 的錯誤信息
    重寫didFailToRegisterForRemoteNotificationsWithError這個方法织鲸,查看錯誤信息是什么。

  1. 卸載重裝溪胶、重啟 App昙沦、關機重啟后測試,反正我是這么解決的T乩蟆!2商摇@廖酢!F瞻臁工扎!
  2. 換別的IOS 13 系統手機測試 (少部分手機會存在取不到 token 的問題,該問題 第三方推送SDK 無法解決衔蹲,大家可以用原生推送集成后測試是否可以取到 token肢娘,取不到就是系統的問題)
  3. 切換網絡呈础!不要用內網,VPN 之類的可能會導致不允許連接 Apple 服務器

錯誤三: 推送的 deviceToken 獲取到的格式發(fā)生變化

原本可以直接將 NSData 類型的 deviceToken 轉換成 NSString 字符串橱健,然后替換掉多余的符號即可:

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    NSString *token = [deviceToken description];
    for (NSString *symbol in @[@" ", @"<", @">", @"-"]) {
        token = [token stringByReplacingOccurrencesOfString:symbol withString:@""];
    }
    NSLog(@"deviceToken:%@", token);
}

在 iOS 13 中而钞,這種方法已經失效,NSData類型的 deviceToken 轉換成的字符串變成了:
解決方案:需要進行一次數據格式處理拘荡,參考友盟的做法臼节,可以適配新舊系統,代碼如下

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    if (![deviceToken isKindOfClass:[NSData class]]) return;
    const unsigned *tokenBytes = [deviceToken bytes];
    NSString *hexToken = [NSString stringWithFormat:@"xxxxxxxx",
                          ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
                          ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
                          ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];
    NSLog(@"deviceToken:%@", hexToken);
}

測試工具

https://github.com/onmyway133/PushNotifications#how-to-install
https://github.com/node-apn/node-apn

測試代碼:

參考文章

https://www.raywenderlich.com/8164-push-notifications-tutorial-getting-started

http://docs-im.easemob.com/im/ios/apns/deploy

大佬鏈接:http://www.reibang.com/p/1419b7d4f5d4

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末珊皿,一起剝皮案震驚了整個濱河市网缝,隨后出現的幾起案子,更是在濱河造成了極大的恐慌蟋定,老刑警劉巖粉臊,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異驶兜,居然都是意外死亡扼仲,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門促王,熙熙樓的掌柜王于貴愁眉苦臉地迎上來犀盟,“玉大人,你說我怎么就攤上這事蝇狼≡某耄” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵迅耘,是天一觀的道長贱枣。 經常有香客問我,道長颤专,這世上最難降的妖魔是什么纽哥? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮栖秕,結果婚禮上春塌,老公的妹妹穿的比我還像新娘。我一直安慰自己簇捍,他們只是感情好只壳,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著暑塑,像睡著了一般吼句。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上事格,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天惕艳,我揣著相機與錄音搞隐,去河邊找鬼。 笑死远搪,一個胖子當著我的面吹牛劣纲,可吹牛的內容都是我干的。 我是一名探鬼主播终娃,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼味廊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了棠耕?” 一聲冷哼從身側響起余佛,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎窍荧,沒想到半個月后辉巡,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡蕊退,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年郊楣,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓤荔。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡净蚤,死狀恐怖,靈堂內的尸體忽然破棺而出输硝,到底是詐尸還是另有隱情今瀑,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布点把,位于F島的核電站橘荠,受9級特大地震影響,放射性物質發(fā)生泄漏郎逃。R本人自食惡果不足惜哥童,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望褒翰。 院中可真熱鬧贮懈,春花似錦、人聲如沸优训。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽型宙。三九已至,卻和暖如春伦吠,著一層夾襖步出監(jiān)牢的瞬間妆兑,已是汗流浹背魂拦。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留搁嗓,地道東北人芯勘。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像腺逛,于是被迫代替她去往敵國和親荷愕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內容