證書配置+個(gè)推應(yīng)用配置略過(guò)
Xcode集成
在項(xiàng)目中添加 Notification Service Extension
- 打開(kāi)
Xcode 9
烫映,菜單中選擇File
->New
->Target
->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蝙斜。如下圖:
- 開(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
:
后臺(tái)運(yùn)行權(quán)限設(shè)置
為了更好支持消息推送稚伍,提供更多的推送樣式,提高消息到達(dá)率戚宦,需要配置后臺(tái)運(yùn)行權(quán)限:
在項(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.m
的didReceiveNotificationResponse
回調(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í)(即停止 SDK
或 App
后臺(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
}
}