首先,按照個推SDK集成指南配置好一個完整的工程。或者直接下載現有工程(須要修改bundle identifier、kGtAppId、kGtAppKey、kGtAppSecret)。
若有錯誤和待完善的地方,還請指正。 ios
新建推送:git
本文將介紹對推送消息的兩種處理方式。
在接收到推送消息時,分爲3種狀況,本文還將後二者細分爲兩種狀況:github
首先爲AppDelegate添加一個屬性,json
// 用來判斷是不是經過點擊通知欄開啓(喚醒)APP @property (nonatomic) BOOL isLaunchedByNotification;
當經過點擊通知欄來啓動或喚醒APP時,會調用didReceiveRemoteNotification:
方法,在該方法裏將isLaunchedByNotification
的值置爲YES:api
/** APP已經接收到「遠程」通知(推送) - 透傳推送消息 */ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { // 當APP處於後臺或者關閉狀態,點擊通知欄就會先走這個方法,再使得個推SDK收到透傳消息回調 // 處理APNs代碼,經過userInfo能夠取到推送的信息(包括內容,角標,自定義參數等)。若是須要彈窗等其餘操做,則須要自行編碼。 NSLog(@"\n>>>APP已經接收到「遠程」通知(推送)[Receive RemoteNotification - Background Fetch]:%@\n\n",userInfo); completionHandler(UIBackgroundFetchResultNewData); self.isLaunchedByNotification = YES; }
而後會調用如下方法(當APP處於前臺時會直接調用此方法),其中payloadData的值轉爲NSString對象即爲圖2中的消息內容裏的JSON數據:xcode
/** SDK收到透傳消息回調 */ - (void)GeTuiSdkDidReceivePayloadData:(NSData *)payloadData andTaskId:(NSString *)taskId andMsgId:(NSString *)msgId andOffLine:(BOOL)offLine fromGtAppId:(NSString *)appId { // 收到個推消息 NSString *payloadMsg = nil; if (payloadData) { payloadMsg = [[NSString alloc] initWithBytes:payloadData.bytes length:payloadData.length encoding:NSUTF8StringEncoding]; } // 當app不在前臺時,接收到的推送消息offLine值均爲YES // 判斷app是不是點擊通知欄消息進行喚醒或開啓 // 若是是點擊icon圖標使得app進入前臺,則不作操做,而且同一條推送通知,此方法只執行一次 if (offLine) { // 離線消息,說明app接收推送時不在前臺 if (self.isLaunchedByNotification) { // app是經過點擊通知欄進入前臺 [[NSNotificationCenter defaultCenter] postNotificationName:kNOTIFICATION_PUSH object:nil userInfo:@{kNOTIFICATION_PUSH : payloadMsg}]; self.isLaunchedByNotification = NO; } else { // app是經過點擊icon進入前臺,在這裏不作操做 } } else if(!self.isLaunchedByNotification) { // app已經處於前臺,提示框提示 [[NSNotificationCenter defaultCenter] postNotificationName:kNOTIFICATION_ALERT object:nil userInfo:@{kNOTIFICATION_ALERT : payloadMsg}]; }elf.isLaunchedByNotification = NO; } }
2.2和3.2狀況下的問題服務器
這兩種狀況下,若是用戶不點擊通知欄而是點擊桌面icon圖標啓動或喚起APP,會直接調用GeTuiSdkDidReceivePayloadData:
方法,根據本文的方式處理的話確實不會有任何操做,可是通知欄的消息仍然存在,若是再次點擊通知欄消息,仍會調動didReceiveRemoteNotification:
方法,可是不會再調用GeTuiSdkDidReceivePayloadData:
方法,這樣的話isLaunchedByNotification
的值會被置爲YES,並且沒法再被置爲NO。
解決辦法:在app進入前臺後經過將Badge角標置爲0來移除通知欄信息,代碼以下:app
- (void)applicationDidBecomeActive:(UIApplication *)application { // 這裏的寫法是爲了在app進入前臺後,清除通知欄消息 NSInteger badge = [UIApplication sharedApplication].applicationIconBadgeNumber; badge = badge == 1 ? 2 : 1; // 這裏通過兩次賦值才能夠移除通知欄消息 [UIApplication sharedApplication].applicationIconBadgeNumber = badge; [UIApplication sharedApplication].applicationIconBadgeNumber = 0; // // 下面這個方法只有Badge角標不爲0時才執行,若是個推推送時Badge爲0,那麼不會走下面的方法 // if (badge) { // // badge = badge == 1 ? 2 : 1; // [UIApplication sharedApplication].applicationIconBadgeNumber = badge; // [UIApplication sharedApplication].applicationIconBadgeNumber = 0; // } }
這種方式默認在經過點擊icon使app進入前臺時不作操做。
當經過點擊通知欄來啓動或喚醒APP時,會調用didReceiveRemoteNotification:
方法,接收到的推送內容包含在userInfo
參數裏,能夠在此方法裏對推送消息進行操做:less
/** APP已經接收到「遠程」通知(推送) - 透傳推送消息 */ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { // 當APP處於後臺或者關閉狀態,點擊通知欄就會先走這個方法,再使得個推SDK收到透傳消息回調 // 處理APNs代碼,經過userInfo能夠取到推送的信息(包括內容,角標,自定義參數等)。若是須要彈窗等其餘操做,則須要自行編碼。 NSLog(@"\n>>>APP已經接收到「遠程」通知(推送)[Receive RemoteNotification - Background Fetch]:%@\n\n",userInfo); completionHandler(UIBackgroundFetchResultNewData); // app是經過點擊通知欄進入前臺 [[NSNotificationCenter defaultCenter] postNotificationName:kNOTIFICATION_PUSH object:nil userInfo:@{kNOTIFICATION_PUSH : payloadMsg}]; }
同時,因爲系統方法調用完成後,個推仍會調用一次GeTuiSdkDidReceivePayloadData:
方法,須要在GeTuiSdkDidReceivePayloadData:
方法裏判斷當前消息是否爲offLine離線消息,若是是離線消息則不作任何處理:ide
/** SDK收到透傳消息回調 */ - (void)GeTuiSdkDidReceivePayloadData:(NSData *)payloadData andTaskId:(NSString *)taskId andMsgId:(NSString *)msgId andOffLine:(BOOL)offLine fromGtAppId:(NSString *)appId { // 收到個推消息 NSString *payloadMsg = nil; if (payloadData) { payloadMsg = [[NSString alloc] initWithBytes:payloadData.bytes length:payloadData.length encoding:NSUTF8StringEncoding]; } // 當app在前臺時,接收到的推送消息offLine值均爲NO // 對於離線消息,這裏不作操做 if (!offLine) { // app已經處於前臺,提示框提示 [[NSNotificationCenter defaultCenter] postNotificationName:kNOTIFICATION_ALERT object:nil userInfo:@{kNOTIFICATION_ALERT : payloadMsg}]; } }
對於角標的處理能夠參考方式一中的處理方法。
前言 在去年的蘋果大會上,蘋果帶來的iOS 10 系統中將以前繁雜的推送通知統一成UserNotifications.framework 來集中管理和使用通知功能,還增長一些實用的功能——撤回單條通知、更新已展現通知、中途修改通知內容、在通知中顯示多媒體資源、自定義UI等功能。
在去年的蘋果大會上,蘋果帶來的iOS 10 系統中將以前繁雜的推送通知統一成UserNotifications.framework 來集中管理和使用通知功能,還增長一些實用的功能——撤回單條通知、更新已展現通知、中途修改通知內容、在通知中顯示多媒體資源、自定義UI等功能。
那麼在ios10以前,ios的消息推送是怎麼分類的呢?
在ios以前,iOS推送分爲Local Notifications(本地推送) 和 Remote Notifications(遠程推送)。
不須要服務器支持(無需聯網)就能發出的推送通知,app本地建立通知,加入到系統的Schedule裏,若是觸發器條件達成時會推送相應的消息內容,如常見的定時任務鬧鐘等。
使用上也是很是簡單。
/*
@property(nonatomic,copy) NSDate *fireDate; @property(nonatomic,copy) NSTimeZone *timeZone; 時區 @property(nonatomic) NSCalendarUnit repeatInterval; 重複間隔(枚舉) @property(nonatomic,copy) NSCalendar *repeatCalendar; 重複日期(NSCalendar) @property(nonatomic,copy) CLRegion *region 設置區域(設置當進入某一個區域時,發出一個通知) @property(nonatomic,assign) BOOL regionTriggersOnce YES,只會在第一次進入某一個區域時發出通知.NO,每次進入該區域都會發通知 @property(nonatomic,copy) NSString *alertBody; @property(nonatomic) BOOL hasAction; 是否隱藏鎖屏界面設置的alertAction @property(nonatomic,copy) NSString *alertAction; 設置鎖屏界面一個文字 @property(nonatomic,copy) NSString *alertLaunchImage; 啓動圖片 @property(nonatomic,copy) NSString *alertTitle @property(nonatomic,copy) NSString *soundName; @property(nonatomic) NSInteger applicationIconBadgeNumber; @property(nonatomic,copy) NSDictionary *userInfo; // 設置通知的額外的數據 */ - (IBAction)addLocalNote:(id)sender { // 建立一個本地通知 UILocalNotification *localNote = [[UILocalNotification alloc] init]; // 設置本地通知的一些屬性(通知發出的時間/通知的內容) // 設置通知發出的時間 localNote.fireDate = [NSDate dateWithTimeIntervalSinceNow:5.0]; //設置通知的內容 localNote.alertBody = @"吃飯了嗎?"; //設置鎖屏界面的文字 localNote.alertAction = @"查看具體的消息"; //設置鎖屏界面alertAction是否有效 localNote.hasAction = YES; //設置經過點擊通知打開APP的時候的啓動圖片(不管字符串設置成什麼內容,都是顯示應用程序的啓動圖片) localNote.alertLaunchImage = @"111"; //設置通知中心通知的標題 localNote.alertTitle = @"222222222222"; //設置音效 localNote.soundName = @"buyao.wav"; //設置應用程序圖標右上角的數字 localNote.applicationIconBadgeNumber = 1; //設置通知以後的屬性 localNote.userInfo = @{@"name" : @"張三", @"toName" : @"李四"}; //調度通知 [[UIApplication sharedApplication] scheduleLocalNotification:localNote]; }
當用戶點擊本地推送通知的時候,會自動打開app,這裏有2種狀況:app在後臺運行,或者被系統進程殺死,對於這兩種狀況,咱們怎麼處理呢?
這時候咱們只須要調用下AppDelegate方法便可。代碼實現
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { // 跳轉邏輯 if (application.applicationState == UIApplicationStateActive) return; if (application.applicationState == UIApplicationStateInactive) { // 當應用在後臺收到本地通知時執行的跳轉代碼 [self jumpToSession]; } NSLog(@"local notifacation %@", notification); } - (void)jumpToSession { UILabel *redView = [[UILabel alloc] init]; redView.backgroundColor = [UIColor redColor]; redView.frame = CGRectMake(0, 100, 300, 400); redView.numberOfLines = 0; // redView.text = [NSString stringWithFormat:@"%@", launchOptions]; [self.window.rootViewController.view addSubview:redView]; }
對於app被殺死的狀況,要先啓動app,啓動完畢會調用AppDelegate方法。
須要特別注意的是:在iOS8.0之後本地通知有了一些變化,若是要使用本地通知,須要獲得用戶的許可。
部分代碼實現:
#define IS_iOS8 ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { /* UIUserNotificationTypeNone = 0, 不發出通知 UIUserNotificationTypeBadge = 1 << 0, 改變應用程序圖標右上角的數字 UIUserNotificationTypeSound = 1 << 1, 播放音效 UIUserNotificationTypeAlert = 1 << 2, 是否運行顯示橫幅 */ [application setApplicationIconBadgeNumber:0]; if (IS_iOS8) { UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil]; [application registerUserNotificationSettings:settings]; } // 若是是正常啓動應用程序,那麼launchOptions參數是null,其餘方式須要對launchOptions設置 if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) { // 當被殺死狀態收到本地通知時執行的跳轉代碼 // [self jumpToSession]; UILabel *redView = [[UILabel alloc] init]; redView.backgroundColor = [UIColor redColor]; redView.frame = CGRectMake(0, 100, 300, 400); redView.numberOfLines = 0; redView.text = [NSString stringWithFormat:@"%@", launchOptions]; [self.window.rootViewController.view addSubview:redView]; } return YES; }
遠程推送指從遠程服務器推送給客戶端的通知(須要聯網),遠程推送服務通常採用蘋果的APNS (Apple Push Notification Service)。
要實現遠程推送,通常會涉及到三個階段:
條件:新建一個對應你bundle的push 證書,打開Push Notifications 開關(XCode7不打開也能夠正常使用,XCode8之後必須打開)。
代碼實現:
註冊接受APNs通知。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { if ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0) { // 1.註冊UserNotification,以獲取推送通知的權限 UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge categories:nil]; [application registerUserNotificationSettings:settings]; // 2.註冊遠程推送 [application registerForRemoteNotifications]; } else { [application registerForRemoteNotificationTypes:UIRemoteNotificationTypeNewsstandContentAvailability | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound]; } return YES; }
調用AppDelegate方法,獲取到用戶的deviceToken。
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { // <32e7cf5f 8af9a8d4 2a3aaa76 7f3e9f8e 1f7ea8ff 39f50a2a e383528d 7ee9a4ea> // <32e7cf5f 8af9a8d4 2a3aaa76 7f3e9f8e 1f7ea8ff 39f50a2a e383528d 7ee9a4ea> NSLog(@"%@", deviceToken.description); }
推送通知,和本地通知同樣有兩種情況。
// 當接受到遠程退職時會執行該方法(當進入前臺或者應用程序在前臺) - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { NSLog(@"%@", userInfo); UIView *redView = [[UIView alloc] init]; redView.backgroundColor = [UIColor redColor]; redView.frame = CGRectMake(100, 100, 100, 100); [self.window.rootViewController.view addSubview:redView]; }
蘋果建議使用方法
/* 1.開啓後臺模式 2.調用completionHandler,告訴系統你如今是否有新的數據更新 3.userInfo添加一個字段:"content-available" : "1" : 只要添加了該字段,接受到通知都會在後臺運行 */ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { NSLog(@"%@", userInfo); UIView *redView = [[UIView alloc] init]; redView.backgroundColor = [UIColor redColor]; redView.frame = CGRectMake(100, 100, 100, 100); [self.window.rootViewController.view addSubview:redView]; completionHandler(UIBackgroundFetchResultNewData); }
iOS10 中統一了本地推送和遠程推送的 API,在 UserNotifications.framework 來統一處理與推送相關任務,並增長了圖片、音頻、視頻,自定義通知 UI 等新特性。
在這次版本中,iOS10 不只新增消息的3dtouch等,還對圖片、音頻、視頻等多媒體作了改進和優化。
類型 | 限制大小 |
---|---|
圖片 | 10M |
音頻 | 5M |
視頻 | 50M |
多媒體推送代碼:
if #available(iOS 10.0, *) { let content = UNMutableNotificationContent() content.title = "iOS10 推送測試" content.body = "附件" content.userInfo = ["icon":"1","mutable-content":1] content.categoryIdentifier = "InputSomething" let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false) let requestIdentifier = "imageLocal" if let imageURL = Bundle.main.url(forResource: "avatar@2x", withExtension: "png"), let attachment = try? UNNotificationAttachment(identifier: "imageAttachment", url: imageURL, options: nil) { content.attachments = [attachment] } let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger) UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in if (error != nil) { print("error: \(error.debugDescription)") } }) }
一般在作多媒體自定義推送的時候,通常會用到UNNotificationServiceExtension應用擴展,經過在 payload 中增長 mutable-content 字段來觸發擴展。
{
"aps":{ "alert":"IOS10 推送測試", "sound":"default", "badge":1, "mutable-content":1, "category":"InputSomething" }, "image":"https://ws1.sinaimg.cn/mw690/934b5ef8gw1fapg2ssteej20oz0oz420.jpg" }
當推送達到 app 時,會啓動擴展並回調 didReceive 方法。在該方法裏面能夠對推送的 UNMutableNotificationContent 作出相應的修改。在 didReceive 回調方法中的 request 包含了推送的具體信息,能夠經過其 userInfo 屬性來解析出多媒體的 url。
let imageURL = Bundle.main.url(forResource: "lufei", withExtension: "jpg")
值得注意的是這裏 Bundle 指的是擴展的沙盒,不是 app 的沙盒,因此資源的路徑要正確。
而讀取遠程資源比讀取本地資源通常要多一步保存操做。
private func downloadAndSave(url: URL, handler: @escaping (_ localURL: URL?) -> Void) { let task = URLSession.shared.dataTask(with: url, completionHandler: { data, res, error in var localURL: URL? = 下載完以後保存到本地並返回本地的 url handler(localURL) }) task.resume() }
獲得本地的 url 以後操做就同樣了,都是經過 url 來生成一個 UNNotificationAttachment 對象。一切都操做完以後將這個 UNMutableNotificationContent 對象返還 contentHandler(bestAttemptContent)。
其中上面的黃色區域能夠理解成一個 ViewController 操做,下面綠色部分就是 Title 之類的顯示內容。這部分是能夠隱藏的。在擴展的目錄下的 info.plist 編輯一些界面相關的東西。
說明: