iOS開(kāi)發(fā)-集成個(gè)推遠(yuǎn)程推送

證書配置+個(gè)推應(yīng)用配置略過(guò)

Xcode集成

在項(xiàng)目中添加 Notification Service Extension

  • 打開(kāi) Xcode 9烫映,菜單中選擇 File -> New -> Target -> Notification Service Extension
Notification Service Extension

填寫Target信息時(shí)需要注意以下兩點(diǎn):

Extension 的 Bundle Identifier 不能和 Main Target(也就是你自己的 App Target)的 Bundle Identifier 相同,否則會(huì)報(bào) BundeID 重復(fù)的錯(cuò)誤噩峦。

Extension 的 Bundle Identifier 需要在 Main Target 的命名空間下锭沟,比如說(shuō) Main Target 的 BundleID 為 ent.getui.xxx,那么Extension的BundleID應(yīng)該類似與ent.getui.xxx.yyy這樣的格式识补。如果不這么做族淮,會(huì)引起命名錯(cuò)誤。

因此我們建議使用<Main Target Bundle ID>.NotificationService 格式作為Extension的BundleID.

  • 添加 Notification Service Extension 后會(huì)生成相應(yīng)的 Target凭涂。點(diǎn)Finish按鈕后會(huì)彈出是否激活該 Target 對(duì)應(yīng) scheme 的選項(xiàng)框祝辣,選擇 Activate,如果沒(méi)有彈出該選項(xiàng)框切油,需要自行添加相應(yīng)的 scheme蝙斜。如下圖:
image
  • 開(kāi)啟多媒體地址 Http 訪問(wèn)支持:

[圖片上傳失敗...(image-e4dca5-1526711478100)]

CocoaPods集成

  #個(gè)推
  pod 'GTSDK', '2.0.0.0-noidfa'
  
  target 'NotificationService' do
      platform :ios, "10.0"
      pod 'GTExtensionSDK'
  end

開(kāi)啟推送功能

Xcode 8.x 以上,必須開(kāi)啟Push Notification能力澎胡。找到應(yīng)用Target設(shè)置中的Capabilities -> Push Notifications孕荠,確認(rèn)開(kāi)關(guān)已經(jīng)設(shè)為ON狀態(tài)。如果沒(méi)有開(kāi)啟該開(kāi)關(guān)攻谁,在 Xcode 8.x 上編譯后的應(yīng)用將獲取不到DeviceToken

image

后臺(tái)運(yùn)行權(quán)限設(shè)置

為了更好支持消息推送稚伍,提供更多的推送樣式,提高消息到達(dá)率戚宦,需要配置后臺(tái)運(yùn)行權(quán)限:

image

在項(xiàng)目設(shè)置中添加以下系統(tǒng)庫(kù)支持:

libc++.tbd
libz.tbd
libsqlite3.tbd
Security.framework
MobileCoreServices.framework
SystemConfiguration.framework
CoreTelephony.framework
AVFoundation.framework
CoreLocation.framework
UserNotifications.framework (iOS 10 及以上需添加个曙,使用 Optional 方式接入)
AdSupport.framework   (如果使用無(wú)IDFA版本SDK,則需刪除該 AdSupport 庫(kù))

添加 GtExtensionSdk 依賴庫(kù)

libz.tbd
libsqlite3.tbd
GTExtensionSDK.framework
UserNotifications.framework

NotificationService.m

#import <GTExtensionSDK/GeTuiExtSdk.h>


- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    [GeTuiExtSdk handelNotificationServiceRequest:request
                          withAttachmentsComplete:^(NSArray *attachments, NSArray *errors) {
                              //TODO:用戶可以在這里處理通知樣式的修改阁苞,eg:修改標(biāo)題
                              //self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [Success]", self.bestAttemptContent.title];
                              
                              self.bestAttemptContent.attachments = attachments; //設(shè)置通知中的多媒體附件
                              NSLog(@"處理個(gè)推APNs展示遇到錯(cuò)誤:%@", errors);    //如果APNs處理有錯(cuò)誤困檩,可以在這里查看相關(guān)錯(cuò)誤詳情
                              
                              self.contentHandler(self.bestAttemptContent); //展示推送的回調(diào)處理需要放到個(gè)推回執(zhí)完成的回調(diào)中
                              
                              
                          }];
}

- (void)serviceExtensionTimeWillExpire {
    // Called just before the extension will be terminated by the system.
    // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
    //銷毀SDK,釋放資源
    [GeTuiExtSdk destory];
    self.contentHandler(self.bestAttemptContent);
    
}

AppDelegate

  • AppDelegate增加回調(diào)接口類那槽。在iOS 10以前的設(shè)備,回調(diào)事件通過(guò)GeTuiSdkDelegate來(lái)進(jìn)行等舔,在 iOS 10以后骚灸,可以使用UserNotifications 框架來(lái)實(shí)現(xiàn)。示例代碼如下:
#import <UIKit/UIKit.h>
#import <GTSDK/GeTuiSdk.h>     // GetuiSdk頭文件應(yīng)用

// iOS10 及以上需導(dǎo)入 UserNotifications.framework
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
#import <UserNotifications/UserNotifications.h>
#endif

/// 使用個(gè)推回調(diào)時(shí)慌植,需要添加"GeTuiSdkDelegate"
/// iOS 10 及以上環(huán)境甚牲,需要添加 UNUserNotificationCenterDelegate 協(xié)議义郑,才能使用 UserNotifications.framework 的回調(diào)
@interface AppDelegate : UIResponder <UIApplicationDelegate, GeTuiSdkDelegate, UNUserNotificationCenterDelegate>
// 用來(lái)判斷是否是通過(guò)點(diǎn)擊通知欄開(kāi)啟(喚醒)APP
@property (nonatomic, assign) BOOL isLaunchedByNotification;
  • 初始化SDK并注冊(cè)APNs
/// 個(gè)推開(kāi)發(fā)者網(wǎng)站中申請(qǐng)App時(shí),注冊(cè)的AppId丈钙、AppKey非驮、AppSecret
#define kGtAppId           @"iMahVVxurw6BNr7XSn9EF2"
#define kGtAppKey          @"yIPfqwq6OMAPp6dkqgLpG5"
#define kGtAppSecret       @"G0aBqAD6t79JfzTB6Z5lo5"

    // 通過(guò)個(gè)推平臺(tái)分配的appId、 appKey 雏赦、appSecret 啟動(dòng)SDK劫笙,注:該方法需要在主線程中調(diào)用
    dispatch_async(dispatch_get_main_queue(), ^{
        [GeTuiSdk startSdkWithAppId:kGtAppId appKey:kGtAppKey appSecret:kGtAppSecret delegate:self];
    });
    
    // 注冊(cè) APNs
    [self registerRemoteNotification];
    
  • 注冊(cè)APNs獲取DeviceToken的流程,根據(jù)項(xiàng)目設(shè)置的不同以及手機(jī)系統(tǒng)版本的不同星岗,注冊(cè)代碼會(huì)有所區(qū)別填大,可以參考如下方式進(jìn)行適配:
// 注冊(cè) APNs
- (void)registerRemoteNotification {
    /*
     警告:Xcode8 需要手動(dòng)開(kāi)啟"TARGETS -> Capabilities -> Push Notifications"
     */
    
    /*
     警告:該方法需要開(kāi)發(fā)者自定義,以下代碼根據(jù) APP 支持的 iOS 系統(tǒng)不同俏橘,代碼可以對(duì)應(yīng)修改允华。
     以下為演示代碼,注意根據(jù)實(shí)際需要修改寥掐,注意測(cè)試支持的 iOS 系統(tǒng)都能獲取到 DeviceToken
     */
    if ([[UIDevice currentDevice].systemVersion floatValue] >= 10.0) {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 // Xcode 8編譯會(huì)調(diào)用
        if (@available(iOS 10.0, *)) {
            UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
            center.delegate = self;
            [center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionCarPlay) completionHandler:^(BOOL granted, NSError *_Nullable error) {
                if (!error) {
                    DLog(@"request authorization succeeded!");
                }
            }];
        } else {
            // Fallback on earlier versions
        }
        
        [[UIApplication sharedApplication] registerForRemoteNotifications];
#else // Xcode 7編譯會(huì)調(diào)用
        UIUserNotificationType types = (UIUserNotificationTypeAlert | UIUserNotificationTypeSound | UIUserNotificationTypeBadge);
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
        [[UIApplication sharedApplication] registerForRemoteNotifications];
        [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
#endif
    } else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
        UIUserNotificationType types = (UIUserNotificationTypeAlert | UIUserNotificationTypeSound | UIUserNotificationTypeBadge);
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
        [[UIApplication sharedApplication] registerForRemoteNotifications];
        [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
    } else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
        //寫在這個(gè)中間的代碼,都不會(huì)被編譯器提示-Wdeprecated-declarations類型的警告
        UIRemoteNotificationType apn_type = (UIRemoteNotificationType)(UIRemoteNotificationTypeAlert |UIRemoteNotificationTypeSound |UIRemoteNotificationTypeBadge);
        [[UIApplication sharedApplication] registerForRemoteNotificationTypes:apn_type];
#pragma clang diagnostic pop
        
    }
}
  • 向個(gè)推服務(wù)器注冊(cè)DeviceToken
// 遠(yuǎn)程通知注冊(cè)成功委托
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    NSString *token = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    token = [token stringByReplacingOccurrencesOfString:@" " withString:@""];
    DLog(@"\n>>>[DeviceToken Success]:%@\n\n", token);
    // 向個(gè)推服務(wù)器注冊(cè)deviceToken
    [GeTuiSdk registerDeviceToken:token];
}
  • 統(tǒng)計(jì)APNs通知的點(diǎn)擊數(shù)

iOS 10 以前靴寂,為處理 APNs 通知點(diǎn)擊事件,統(tǒng)計(jì)有效用戶點(diǎn)擊數(shù)召耘,需在AppDelegate.m里的didReceiveRemoteNotification回調(diào)方法中調(diào)用個(gè)推SDK統(tǒng)計(jì)接口:

/** APP已經(jīng)接收到“遠(yuǎn)程”通知(推送) - 透?jìng)魍扑拖?{app 在后臺(tái)沒(méi)殺死或者殺死 點(diǎn)擊通知 首先進(jìn)入的方法} */
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
    // 處理APNs代碼榨汤,通過(guò)userInfo可以取到推送的信息(包括內(nèi)容,角標(biāo)怎茫,自定義參數(shù)等)收壕。如果需要彈窗等其他操作,則需要自行編碼轨蛤。
    DLog(@"\n>>>[Receive RemoteNotification - Background Fetch]:%@\n\n",userInfo);
    
    self.isLaunchedByNotification = YES;
    //靜默推送收到消息后也需要將APNs信息傳給個(gè)推統(tǒng)計(jì)
    [GeTuiSdk handleRemoteNotification:userInfo];
    completionHandler(UIBackgroundFetchResultNewData);
    
    
}
  • 對(duì)于iOS 10 及以后版本蜜宪,為處理 APNs 通知點(diǎn)擊,統(tǒng)計(jì)有效用戶點(diǎn)擊數(shù)祥山,需先添加 UNUserNotificationCenterDelegate圃验,然后在AppDelegate.mdidReceiveNotificationResponse回調(diào)方法中調(diào)用個(gè)推SDK統(tǒng)計(jì)接口:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
//  iOS 10: App在前臺(tái)獲取到通知
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler  API_AVAILABLE(ios(10.0)){
    
    DLog(@"willPresentNotification:%@", notification.request.content.userInfo);
    
    // 根據(jù)APP需要,判斷是否要提示用戶Badge缝呕、Sound澳窑、Alert
    completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
}

//  iOS 10: 點(diǎn)擊通知進(jìn)入App時(shí)觸發(fā),在該方法內(nèi)統(tǒng)計(jì)有效用戶點(diǎn)擊數(shù)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler  API_AVAILABLE(ios(10.0)){
    DLog(@"didReceiveNotification:%@", response.notification.request.content.userInfo);
    
    self.isLaunchedByNotification = YES;
    // [ GTSdk ]:將收到的APNs信息傳給個(gè)推統(tǒng)計(jì)
    [GeTuiSdk handleRemoteNotification:response.notification.request.content.userInfo];
    completionHandler();
}

#endif
  • 獲取CID信息
/** SDK啟動(dòng)成功返回cid */
- (void)GeTuiSdkDidRegisterClient:(NSString *)clientId {
    //個(gè)推SDK已注冊(cè)供常,返回clientId
    
    DLog(@"-=-=-=----\n>>>[GeTuiSdk RegisterClient]:%@\n\n", clientId);
    
    // 通過(guò)接口上傳的APP服務(wù)器
}
  • 接收個(gè)推通道下發(fā)的透?jìng)飨?/li>

當(dāng) SDK 在線時(shí)(即 App 在前臺(tái)運(yùn)行時(shí))進(jìn)行消息推送摊聋,該消息將直接通過(guò)個(gè)推通道發(fā)送給 App ,通常這種方式比通過(guò)APNs發(fā)送來(lái)得更及時(shí)更穩(wěn)定栈暇;當(dāng) SDK 離線時(shí)(即停止 SDKApp 后臺(tái)運(yùn)行 或 App 停止?fàn)顟B(tài)時(shí))進(jìn)行消息推送麻裁,個(gè)推平臺(tái)會(huì)給蘋果 APNs 推送消息,同時(shí)保存?zhèn)€推通道的離線消息,當(dāng) SDK 重新上線后煎源,個(gè)推平臺(tái)會(huì)重新推送所有離線的消息

APP 可以通過(guò)[GeTuiSdkDelegate GeTuiSdkDidReceivePayloadData]回調(diào)方法獲取透?jìng)飨⑸兀渲?code>payloadData參數(shù)為透?jìng)飨?shù)據(jù),offLine參數(shù)則表明該條消息是否為離線消息手销。示例代碼如下:

/** SDK收到透?jìng)飨⒒卣{(diào) {app在前臺(tái) 只走這一個(gè)方法}{app在后臺(tái)歇僧,進(jìn)來(lái)前臺(tái)第二個(gè)走的方法} */
- (void)GeTuiSdkDidReceivePayloadData:(NSData *)payloadData andTaskId:(NSString *)taskId andMsgId:(NSString *)msgId andOffLine:(BOOL)offLine fromGtAppId:(NSString *)appId {
    
    [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
    //收到個(gè)推消息
    NSString *payloadMsg = nil;
    if (payloadData) {
        payloadMsg = [[NSString alloc] initWithBytes:payloadData.bytes length:payloadData.length encoding:NSUTF8StringEncoding];
    }
    
    NSDictionary *payLoadMegDic = [NSJSONSerialization JSONObjectWithData:payloadData options:NSJSONReadingMutableLeaves error:nil];
    DLog(@"%@",payLoadMegDic);
    if (offLine) {
            if (payLoadMegDic) {
            if (self.isLaunchedByNotification) { // app是通過(guò)點(diǎn)擊通知欄進(jìn)入前臺(tái)
            UITabBarController *_tab = (UITabBarController *)(self.window.rootViewController);
            UINavigationController *nva = (UINavigationController *)_tab.viewControllers[_tab.selectedIndex];
            // 根據(jù)payLoadMegDic判斷跳轉(zhuǎn)類型
                self.isLaunchedByNotification = NO;
            }else{
                // app是通過(guò)點(diǎn)擊icon進(jìn)入前臺(tái),在這里不做操作
            }
        }

        NSString *msg = [NSString stringWithFormat:@"taskId=%@,messageId:%@,payloadMsg:%@%@",taskId,msgId, payloadMsg,offLine ? @"<離線消息>" : @""];
        DLog(@"\n>>>[GexinSdk ReceivePayload]:%@\n\n", msg);
        
    }else if(!self.isLaunchedByNotification){
        // app已經(jīng)處于前臺(tái)锋拖,提示框提示
        // 在這里拿不到apns 里的title body! 如果想要處理的話可以讓服務(wù)端把a(bǔ)ps 的內(nèi)容傳到transmissionContent 透?jìng)鲀?nèi)容里 诈悍,應(yīng)用在線的時(shí)候可以拿到這個(gè)透?jìng)?        self.isLaunchedByNotification = NO;
        // 轉(zhuǎn)換成一個(gè)本地通知,顯示到通知欄姑隅,你也可以直接顯示出一個(gè)alertView写隶,只是那樣稍顯aggressive
    }
    
    
    
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市讲仰,隨后出現(xiàn)的幾起案子慕趴,更是在濱河造成了極大的恐慌,老刑警劉巖鄙陡,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冕房,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡趁矾,警方通過(guò)查閱死者的電腦和手機(jī)耙册,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)毫捣,“玉大人详拙,你說(shuō)我怎么就攤上這事÷” “怎么了饶辙?”我有些...
    開(kāi)封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)斑粱。 經(jīng)常有香客問(wèn)我弃揽,道長(zhǎng),這世上最難降的妖魔是什么则北? 我笑而不...
    開(kāi)封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任矿微,我火速辦了婚禮,結(jié)果婚禮上尚揣,老公的妹妹穿的比我還像新娘涌矢。我一直安慰自己,他們只是感情好惑艇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布蒿辙。 她就那樣靜靜地躺著拇泛,像睡著了一般滨巴。 火紅的嫁衣襯著肌膚如雪思灌。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天恭取,我揣著相機(jī)與錄音泰偿,去河邊找鬼。 笑死蜈垮,一個(gè)胖子當(dāng)著我的面吹牛耗跛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播攒发,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼调塌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了惠猿?” 一聲冷哼從身側(cè)響起羔砾,我...
    開(kāi)封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎偶妖,沒(méi)想到半個(gè)月后姜凄,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡趾访,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年态秧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扼鞋。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡申鱼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出云头,到底是詐尸還是另有隱情捐友,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布盘寡,位于F島的核電站楚殿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏竿痰。R本人自食惡果不足惜脆粥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望影涉。 院中可真熱鬧变隔,春花似錦、人聲如沸蟹倾。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至肌厨,卻和暖如春培慌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背柑爸。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工吵护, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人表鳍。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓馅而,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親譬圣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瓮恭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容