iOS推送之本地推送

pub10 8年前發布 | 10K 次閱讀 iOS開發 移動開發

來自: http://ios.jobbole.com/83949/

寫此文的原因可見此文章的姊妹篇 iOS推送之遠程推送(iOS Notification Of Remote Notification) ,如果你看過了它的姊妹篇,了解過了遠程推送,那么再來看此本地推送,真真是易如反掌啊!

此篇文章的邏輯如下圖所示:

圖0-0 此篇文章的邏輯圖

本地推送介紹

本地推送和遠程推送的功能是一樣的,都是要提醒用戶去做某些事情。但是和遠程推送不同的就是本地推送是不需要設備聯網的,而遠程推送是必需要設備聯網的,因為只有聯網狀態下,才能和蘋果的APNs服務器建立長連接,從而推送消息。本地推送是由App自己設定的,并且發送給安裝此App的這臺設備,屬于一對一的對應關系。

本地推送適合 日歷 to-do list 等類型的App,注意:一個App最多只能設置64個本地推送,當超過此限制的時候,系統會自動忽略多余的本地推送,而保留能最快觸發的64個。循環的本地推送會被系統認為是同一個本地推送。

本地推送應用

iOS8本地推送注冊

iOS8之后推送要求必須注冊App支持的用戶交互類型,注冊代碼和遠程推送注冊代碼相同如下

Objective-C

// iOS8注冊本地通知類型
UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;

UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];

[[UIApplication sharedApplication] registerUserNotificationSettings:settings];</pre>

// iOS8注冊本地通知類型
UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
 
UIUserNotificationSettings *settings = [UIUserNotificationSettingssettingsForTypes:typescategories:nil];
 
[[UIApplication sharedApplication]registerUserNotificationSettings:settings];
</div>

基本應用

UILocalNotification的基本屬性

Objective-C

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之后</pre>

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之后
</div>

Example

Objective-C

- (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];

}</pre>

- (void)scheduleNotificationWithItem:(ToDoItem *)iteminterval:(int)minutesBefore {
 
    NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
    NSDateComponents *dateComps = [[NSDateComponents alloc]init];
    [dateCompssetDay:item.day];
    [dateCompssetMonth:item.month];
    [dateCompssetYear:item.year];
    [dateCompssetHour:item.hour];
    [dateCompssetMinute:item.minute];
    NSDate *itemDate = [calendardateFromComponents:dateComps];
 
    UILocalNotification *localNotif = [[UILocalNotification alloc]init];
    if (localNotif == nil) return;
 
    localNotif.fireDate = [itemDatedateByAddingTimeIntervalInterval:-(minutesBefore*60)];
    localNotif.timeZone = [NSTimeZone defaultTimeZone];
    localNotif.alertBody = [NSStringstringWithFormat: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 = [NSDictionarydictionaryWithObject:item.eventNameforKey:ToDoItemKey];
    localNotif.userInfo = infoDict;
 
    //  設置好本地推送后必須調用此方法啟動此推送
    [[UIApplication sharedApplication]scheduleLocalNotification:localNotif];
}
</div>

取消本地推送的方法

Objective-C

//  取消某一個本地推送
[[UIApplication sharedApplication] cancelLocalNotification:notification];
//  取消所有的本地推送
[[UIApplication sharedApplication] cancelAllLocalNotifications];
//  取消某一個本地推送
[[UIApplication sharedApplication]cancelLocalNotification:notification];
//  取消所有的本地推送
[[UIApplication sharedApplication]cancelAllLocalNotifications];
</div>

收到本地通知時的回調方法

application: didFinishLaunchingWithOptions:此方法在程序第一次啟動是調用,也就是說App從Terminate狀態進入Foreground狀態的時候,根據方法內代碼判斷是否有推送消息。

Objective-C

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
   // userInfo為收到遠程通知的內容
   NSDictionary *userInfo = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
   if (userInfo) { 
      // 有推送的消息,處理推送的消息 
   }
   return YES;
}
- (BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // userInfo為收到遠程通知的內容
  NSDictionary *userInfo = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
  if (userInfo) { 
      // 有推送的消息,處理推送的消息
  }
  return YES;
}
</div>

application: didReceiveLocalNotification:如果App處于Background狀態時,只用用戶點擊了通知消息時才會調用該方法;如果App處于Foreground狀態,會直接調用該方法。

Objective-C

- (void)application:(UIApplication )application didReceiveLocalNotification:(UILocalNotification )notification {

}</pre>

- (void)application:(UIApplication *)applicationdidReceiveLocalNotification:(UILocalNotification *)notification {
 
}
</div>

帶有定位的本地推送

iOS8之后,本地推送可以創建帶有定位的本地推送,當用戶進入這一指定的區域的時候就會發送此推送。并且可以指定此消息是只發送一次,還是每次用戶進入此區域的時候都發送此推送。設置此屬性對應的屬性字段為:regionTriggersOnce。

開啟定位服務

注冊帶有定位的推送必要要求用戶開啟定位功能,授權使用定位服務

Objective-C

// 獲得授權去追蹤用戶的位置
CLLocationManager *locManager = [[CLLocationManager alloc] init];
locManager.delegate = self;
[locManager requestWhenInUseAuthorization];
// 獲得授權去追蹤用戶的位置
CLLocationManager *locManager = [[CLLocationManager alloc]init];
locManager.delegate = self;
[locManagerrequestWhenInUseAuthorization];
</div>

當你第一次請求使用授權定位服務的時候,系統會詢問用戶是同意還是拒絕。為了提醒用戶,系統會顯示一些在Info.plist中 NSLocationWhenInUseUsageDescription 此key值對應的額外信息。使用定位服務此key值一定要配置。只有用戶同意了此App訪問他的位置信息,此App才會接收到回到方法。

處理定位回調

首先要檢查用戶對定位服務的授權狀態,只有用戶授權了可以訪問用戶的位置信息,才會回調以下方法

Objective-C

- (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];
    

    } }</pre>

    - (void)locationManager:(CLLocationManager *)managerdidChangeAuthorizationStatus:(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_COORDINATEradius:LOC_RADIUSidentifier:LOC_IDENTIFIER];       [[UIApplication sharedApplication]scheduleLocalNotification:locNotification]; } // 收到本地通知的時候,回調此方法,處理通知
  • (void)application:(UIApplication )applicationdidReceiveLocalNotification: (UILocalNotification )notification {       if (notification.region) {           [selftellUserArrivedAtRegion:region];     } }</pre> </div>

    本地推送使用注意(本地推送的坑)

    本地推送的準確率和到達率都是秒殺遠程推送的,但是本地推送也有一些問題,處理不善的話很容易讓用戶誤解的。本地推送無論是在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)

    參考

 本文由用戶 pub10 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!