寫此文的原因可見此文章的姊妹篇iOS推送之遠端推送(iOS Notification Of Remote Notification),如果你看過了它的姊妹篇,瞭解過了遠端推送,那麼再來看此本地推送,真真是易如反掌啊!
此篇文章的邏輯如下圖所示:
本地推送介紹
本地推送和遠端推送的功能是一樣的,都是要提醒使用者去做某些事情。但是和遠端推送不同的就是本地推送是不需要裝置聯網的,而遠端推送是必需要裝置聯網的,因為只有聯網狀態下,才能和蘋果的APNs伺服器建立長連線,從而推送訊息。本地推送是由App自己設定的,並且傳送給安裝此App的這臺裝置,屬於一對一的對應關係。
本地推送適合
日曆
to-do list
等型別的App,注意:一個App最多隻能設定64個本地推送,當超過此限制的時候,系統會自動忽略多餘的本地推送,而保留能最快觸發的64個。迴圈的本地推送會被系統認為是同一個本地推送。
本地推送應用
iOS8本地推送註冊
iOS8之後推送要求必須註冊App支援的使用者互動型別,註冊程式碼和遠端推送註冊程式碼相同如下
1 2 3 4 5 6 |
// iOS8註冊本地通知型別 UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert; UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil]; [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; |
基本應用
UILocalNotification的基本屬性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
fireDate:啟動時間 timeZone:啟動時間參考的時區 repeatInterval:重複推送時間(NSCalendarUnit型別),0代表不重複 repeatCalendar:重複推送時間(NSCalendar型別) alertBody:通知內容 alertAction:解鎖滑動時的事件 alertLaunchImage:啟動圖片,設定此欄位點選通知時會顯示該圖片 alertTitle:通知標題,適用iOS8.2之後 applicationIconBadgeNumber:收到通知時App icon的角標 soundName:推送是帶的聲音提醒,設定預設的欄位為UILocalNotificationDefaultSoundName userInfo:傳送通知時附加的內容 category:此屬性和註冊通知型別時有關聯,(有興趣的同學自己瞭解,不詳細敘述)適用iOS8.0之後 region:帶有定位的推送相關屬性,具體使用見下面【帶有定位的本地推送】適用iOS8.0之後 regionTriggersOnce:帶有定位的推送相關屬性,具體使用見下面【帶有定位的本地推送】適用iOS8.0之後 |
Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
- (void)scheduleNotificationWithItem:(ToDoItem *)item interval:(int)minutesBefore { NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar]; NSDateComponents *dateComps = [[NSDateComponents alloc] init]; [dateComps setDay:item.day]; [dateComps setMonth:item.month]; [dateComps setYear:item.year]; [dateComps setHour:item.hour]; [dateComps setMinute:item.minute]; NSDate *itemDate = [calendar dateFromComponents:dateComps]; UILocalNotification *localNotif = [[UILocalNotification alloc] init]; if (localNotif == nil) return; localNotif.fireDate = [itemDate dateByAddingTimeIntervalInterval:-(minutesBefore*60)]; localNotif.timeZone = [NSTimeZone defaultTimeZone]; localNotif.alertBody = [NSString stringWithFormat:NSLocalizedString(@"%@ in %i minutes.", nil), item.eventName, minutesBefore]; localNotif.alertAction = NSLocalizedString(@"View Details", nil); localNotif.alertTitle = NSLocalizedString(@"Item Due", nil); localNotif.soundName = UILocalNotificationDefaultSoundName; localNotif.applicationIconBadgeNumber = 1; NSDictionary *infoDict = [NSDictionary dictionaryWithObject:item.eventName forKey:ToDoItemKey]; localNotif.userInfo = infoDict; // 設定好本地推送後必須呼叫此方法啟動此推送 [[UIApplication sharedApplication] scheduleLocalNotification:localNotif]; } |
取消本地推送的方法
1 2 3 4 |
// 取消某一個本地推送 [[UIApplication sharedApplication] cancelLocalNotification:notification]; // 取消所有的本地推送 [[UIApplication sharedApplication] cancelAllLocalNotifications]; |
收到本地通知時的回撥方法
application: didFinishLaunchingWithOptions:
此方法在程式第一次啟動是呼叫,也就是說App從Terminate狀態進入Foreground狀態的時候,根據方法內程式碼判斷是否有推送訊息。
1 2 3 4 5 6 7 8 |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // userInfo為收到遠端通知的內容 NSDictionary *userInfo = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]; if (userInfo) { // 有推送的訊息,處理推送的訊息 } return YES; } |
application: didReceiveLocalNotification:
如果App處於Background狀態時,只用使用者點選了通知訊息時才會呼叫該方法;如果App處於Foreground狀態,會直接呼叫該方法。
1 2 3 |
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { } |
帶有定位的本地推送
iOS8之後,本地推送可以建立帶有定位的本地推送,當使用者進入這一指定的區域的時候就會傳送此推送。並且可以指定此訊息是隻傳送一次,還是每次使用者進入此區域的時候都傳送此推送。設定此屬性對應的屬性欄位為:regionTriggersOnce。
開啟定位服務
註冊帶有定位的推送必要要求使用者開啟定位功能,授權使用定位服務
1 2 3 4 |
// 獲得授權去追蹤使用者的位置 CLLocationManager *locManager = [[CLLocationManager alloc] init]; locManager.delegate = self; [locManager requestWhenInUseAuthorization]; |
當你第一次請求使用授權定位服務的時候,系統會詢問使用者是同意還是拒絕。為了提醒使用者,系統會顯示一些在Info.plist中NSLocationWhenInUseUsageDescription
此key值對應的額外資訊。使用定位服務此key值一定要配置。只有使用者同意了此App訪問他的位置資訊,此App才會接收到回到方法。
處理定位回撥
首先要檢查使用者對定位服務的授權狀態,只有使用者授權了可以訪問使用者的位置資訊,才會回撥以下方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { //檢查狀態判斷App是否授權 if (status == kCLAuthorizationStatusAuthorizedWhenInUse) { // 設定通知 [self startShowingLocationNotifications]; } } // 設定通知 - (void)startShowingLocationNotifications { UILocalNotification *locNotification = [[UILocalNotification alloc] init]; locNotification.alertBody = @"You have arrived!"; locNotification.regionTriggersOnce = YES; locNotification.region = [[CLCircularRegion alloc] initWithCenter:LOC_COORDINATE radius:LOC_RADIUS identifier:LOC_IDENTIFIER]; [[UIApplication sharedApplication] scheduleLocalNotification:locNotification]; } // 收到本地通知的時候,回撥此方法,處理通知 - (void)application:(UIApplication *)application didReceiveLocalNotification: (UILocalNotification *)notification { if (notification.region) { [self tellUserArrivedAtRegion:region]; } } |
本地推送使用注意(本地推送的坑)
本地推送的準確率和到達率都是秒殺遠端推送的,但是本地推送也有一些問題,處理不善的話很容易讓使用者誤解的。本地推送無論是在App處於什麼狀態,當有訊息的時候,在手機下拉的Notifications中都是會有顯示的。當App不是處於Foreground狀態的時候,都是會有banner通知的,但是當App處於Foreground狀態時,通知還是會一樣發出,而且手機下拉Notifications中會有磁條通知,但是banner並不會出現。這樣就會引起一個時間錯覺問題。而且本地推送手機下拉Notifications中的訊息顯示,當使用者開啟App的時候不會消失的啊。只有使用者點選此條訊息或者直接給清除掉才會訊息的啊。做此功能的時候,一直被說本地推送不準啊,我都同步資料了,為什麼還讓同步,經過幾番測試,最終發現原因出於此,不知道算不算系統的一個bug。
Example
我設定一條推送,內容是提醒使用者去同步資料,此推送觸發條件是(每天10:00,並且使用者沒有同步資料),當使用者同步完資料,我就取消這個提醒。假設一使用者在9:55的時候開啟App,一直使用到10:05,這時提醒使用者去同步資料的推送訊息已經發出,由於App處於Foreground狀態下並沒有出現banner提醒,使用者並不知道有訊息提醒,這時他去同步資料,然後退出App,手機下拉Notifications中會有提示去同步資料的這條推送訊息啊,這時使用者就會很鬱悶。。。他的表情一定是這樣的:
總結
本地推送相比遠端推送是簡單的多了。但是在專案實際運用中會有各種複雜的邏輯判讀,達到什麼條件,什麼時間在去推送;這些業務相關邏輯就都需要客戶端去寫了。不比遠端推送,這些業務邏輯是在服務端處理。我們要做的就是收到推送訊息,然後再處理就可以了。另外iOS8出的帶有定位的本地推送這個功能自我感覺還是比較好玩的。其中可能還有本篇沒有提到的一些本地推送相關知識,有什麼好的想法,也歡迎你私密我一起討論,共同進步。關於遠端推送參看此文姊妹篇iOS推送之遠端推送(iOS Notification Of Remote Notification)