[iOS] 通知詳解: UIUserNotification

通知相關(guān)系列文章
iOS10 之前通知使用介紹
[iOS] 通知詳解: UIUserNotification
iOS10 相關(guān)API
[iOS] 通知詳解:iOS 10 UserNotifications API
iOS10 本地/遠(yuǎn)程通知
[iOS] 通知詳解: iOS 10 UserNotifications
iOS10 通知附加包
[iOS] 通知詳解: iOS 10 UserNotifications -- 附加包Media Attachments
iOS10 自定義UI
[iOS] 通知詳解: iOS 10 UserNotifications -- 自定義通知UI

本文主要是介紹iOS10之前的推送處理相關(guān)API及示例,很多文章都是將iOS10之前的API和iOS10新出的一起介紹,個人感覺比較混亂,而且不夠清晰,所以才重新總結(jié)了一些文章并結(jié)合自己的使用經(jīng)驗(yàn),將 iOS10 之前的使用和iOS10之后的進(jìn)行歸納。

通知權(quán)限請求

不管是本地通知,還是遠(yuǎn)程通知,都需要向用戶申請通知的權(quán)限

UIUserNotificationSettings (iOS 8 ---- iOS 10)

UIUserNotificationSettings 主要由一個初始化方法來完成配置:

// types:通知的類型 見 UIUserNotificationType
// categories:自定義行為按鈕,見 UIUserNotificationCategory,可傳nil
public convenience init(types: UIUserNotificationType, categories: Set<UIUserNotificationCategory>?)

UIUserNotificationType 通知類型
public struct UIUserNotificationType : OptionSet {

    public init(rawValue: UInt)

    // 角標(biāo)
    public static var badge: UIUserNotificationType { get } 
// 聲音
    public static var sound: UIUserNotificationType { get } // the application may play a sound upon a notification being received
// 彈框
    public static var alert: UIUserNotificationType { get } // the application may display an alert upon a notification being received
}

一般使用(不帶有動作按鈕)
  • 注冊通知設(shè)置
// categories 傳nil,則通知沒有額外的動作按鈕
let setting = UIUserNotificationSettings(types: [.sound, .alert, .badge], categories: nil)
        
UIApplication.shared.registerUserNotificationSettings(setting)

registerUserNotificationSettings 方法調(diào)用后,成功后會以代理的方式進(jìn)行反饋

// registerUserNotificationSettings 回調(diào)代理
    func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
        // 注冊 deviceToken
        application.registerForRemoteNotifications()
    }

這里我們需要調(diào)用registerForRemoteNotifications,來注冊通知,獲取deviceToken,也會以代理的方式進(jìn)行反饋

// registerForRemoteNotifications 成功的回調(diào),獲取到token
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

        let token = deviceToken.map { String(format: "%02hhx", $0) }.joined()
      
// Next do ...
// 一般是發(fā)送給自己的服務(wù)后臺,或者第三方的推送服務(wù)器后臺
    }

//    registerForRemoteNotifications 失敗的回調(diào)
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print(error)
    }

到此,通知的權(quán)限申請及遠(yuǎn)程注冊就完成了,接下來就是發(fā)送通知,本地或者遠(yuǎn)程。
此時,通知的彈框是沒有其他動作按鈕的,彈框下拉后,只有消息標(biāo)題,內(nèi)容等信息,沒有額外的動作按鈕


帶有動作選擇的通知

帶有動作的通知,需要設(shè)置其 categories ,涉及到以下幾個類的配置

UIUserNotificationCategory

攜帶動作的Category,一般是使用其可變實(shí)例 UIMutableUserNotificationCategory來添加多個行為:

// 唯一標(biāo)識符
open var identifier: String?

    // Sets the UIUserNotificationActions in the order to be displayed for the specified context
    open func setActions(_ actions: [UIUserNotificationAction]?, for context: UIUserNotificationActionContext)

UIUserNotificationActions

動作實(shí)例,一般是使用其可變實(shí)例 UIMutableUserNotificationAction 來配置更多的行為

// 唯一標(biāo)識符
    open var identifier: String?
    // 按鈕 title
    open var title: String?
   // 用戶點(diǎn)擊該按鈕時的行為,有兩種
/*public enum UIUserNotificationActionBehavior : UInt { 
// 一般的點(diǎn)擊事件
    case `default` // the default action behavior
// 點(diǎn)擊按鈕后會彈出輸入框,可以輸入一段文字快捷回復(fù)
    case textInput // system provided action behavior, allows text input from the user
}
*/
    // The behavior of this action when the user activates it.
    @available(iOS 9.0, *)
    open var behavior: UIUserNotificationActionBehavior

    
    // Parameters that can be used by some types of actions.
    @available(iOS 9.0, *)
    open var parameters: [AnyHashable : Any]

    // 按鈕響應(yīng)事件的模式,有兩種
/*public enum UIUserNotificationActivationMode : UInt {
// 在該模式下,點(diǎn)擊按鈕后會打開app,可以在代理方法中做一些跳轉(zhuǎn)操作
    case foreground // activates the application in the foreground
// 在該模式下,點(diǎn)擊后不會打開app,可以后臺做一些操作
    case background // activates the application in the background, unless it's already in the foreground
}
*/
    // How the application should be activated in response to the action.
    open var activationMode: UIUserNotificationActivationMode

    // 是否需要授權(quán)
    // Whether this action is secure and should require unlocking before being performed. If the activation mode is UIUserNotificationActivationModeForeground, then the action is considered secure and this property is ignored.
    open var isAuthenticationRequired: Bool

    // 當(dāng)需要執(zhí)行某些破壞性的操作時,需要醒目提醒,紅色的按鈕
    // Whether this action should be indicated as destructive when displayed.
    open var isDestructive: Bool

示例:

let ok = UIMutableUserNotificationAction()
        ok.identifier = "okidentifier"
        ok.activationMode = . foreground // 這里需要打開app,所以設(shè)置為foreground模式
        ok.title = "查看"
        ok.isDestructive = false
        ok.isAuthenticationRequired = false
        
        
        let cancel = UIMutableUserNotificationAction()
        cancel.identifier = "cancelidentifier"
        cancel.activationMode = .background // 這里不需要打開app,所以設(shè)置為background模式
        cancel.title = "關(guān)閉"
        cancel.isDestructive = true
        cancel.isAuthenticationRequired = false
        
        let category = UIMutableUserNotificationCategory()
        category.identifier = "categoryidentifier"
        category.setActions([ok, cancel], for: .default)
        
        
        let set = Set([category])
        
        let setting = UIUserNotificationSettings(types: [.sound, .alert, .badge], categories: set)
        
        UIApplication.shared.registerUserNotificationSettings(setting)

效果,彈框下拉后,除了消息標(biāo)題,內(nèi)容等信息,還有添加的動作按鈕

添加的按鈕點(diǎn)擊后,會調(diào)用下面的代理方法,本地通知和遠(yuǎn)程通知調(diào)用的方法不一樣

// ios 8 --- ios 10
    func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, withResponseInfo responseInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
        
        print("handleActionWithIdentifier1 \(identifier)")
        
        completionHandler()
    }
    // ios 9 --- ios 10
    // 本地推送的UIMutableUserNotificationAction回調(diào)
    func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, completionHandler: @escaping () -> Void) {
        
        print("handleActionWithIdentifier2 \(identifier)")
        completionHandler()
    }

如果將創(chuàng)建 UIMutableUserNotificationAction 實(shí)例對象時的屬性 behavior 設(shè)置為 .textInput,則點(diǎn)擊按鈕時,會彈出輸入框:

輸入的值,可以在代理方法的 responseInfo 獲取:

// ios 8 --- ios 10
    func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, withResponseInfo responseInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
               
        let input = responseInfo[UIUserNotificationActionResponseTypedTextKey]
        print(input)

        completionHandler()
    }

本地通知

UILocalNotification(iOS 4.0 ---- iOS 10.0)

UILocalNotification 基本屬性

UILocalNotification 常用的基本屬性有以下幾個:

// 本地通知指定處罰時間
    open var fireDate: Date?

// 通知彈框的內(nèi)容
    open var alertBody: String?
// 通知彈框的標(biāo)題
@available(iOS 8.2, *)
    open var alertTitle: String?
// 通知來時的提示音文件名稱(需要加擴(kuò)展名),UILocalNotificationDefaultSoundName為系統(tǒng)設(shè)置的默認(rèn)提示音
    open var soundName: String? 
// 角標(biāo)消息數(shù)量
    // badge
    open var applicationIconBadgeNumber: Int // 0 means no change. defaults to 0
// 通知代理方法調(diào)用后攜帶的參數(shù)內(nèi)容
    // user info
    open var userInfo: [AnyHashable : Any]? 
// 動作按鈕,此處的值要和設(shè)置的 UIUserNotificationCategory 實(shí)例的identifier屬性值一致
    // category identifer of the local notification, as set on a UIUserNotificationCategory and passed to +[UIUserNotificationSettings settingsForTypes:categories:]
    @available(iOS 8.0, *)
    open var category: String?

//  觸發(fā)時間所屬的時區(qū),默認(rèn)是本機(jī)設(shè)定時區(qū)
    open var timeZone: TimeZone?
// 重復(fù)觸發(fā)的頻率,詳見NSCalendar.Unit,例如 day:按天;month:按月;0 為不重復(fù)
    open var repeatInterval: NSCalendar.Unit 
// 指定日歷,默認(rèn)即可
    open var repeatCalendar: Calendar?

// 當(dāng)進(jìn)入/離開某個地理范圍時觸發(fā)本地通知;例如進(jìn)入某地/某商場范圍觸發(fā)本地通知
// 需要有 "when-in-use" 的定位權(quán)限
    @available(iOS 8.0, *)
    @NSCopying open var region: CLRegion?
// 若為YES,每次超出/進(jìn)入該范圍都會觸發(fā);若為NO,則只觸發(fā)一次;默認(rèn)為YES
    @available(iOS 8.0, *)
    open var regionTriggersOnce: Bool

// 鎖屏狀態(tài)下,是否顯示滑動的提示語,默認(rèn)YES,iOS11好像沒有這個提示語了
    open var hasAction: Bool
// 鎖屏狀態(tài)下,顯示滑動的提示語,默認(rèn)為“滑動解鎖xxx”,如果設(shè)置此值為“打開xxx”,則是“滑動打開xxx”
    open var alertAction: String?

// 打開通知進(jìn)入應(yīng)用時的啟動圖片
    open var alertLaunchImage: String? 

添加/執(zhí)行通知的方法
在通知設(shè)置完成后,調(diào)用下面的方法來執(zhí)行/添加到執(zhí)行隊列

// 將本地通知添加到調(diào)度池,定時發(fā)送 fireDate 
    [[UIApplication sharedApplication] scheduleLocalNotification:notification];  

// 立即發(fā)送
   [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];

本地通知的取消

// 取消某個本地通知,參數(shù)為通知的實(shí)例對象
open func cancelLocalNotification(_ notification: UILocalNotification)

// 取消所有待你執(zhí)行的本地通知
    open func cancelAllLocalNotifications()

本地通知的處理

收到本地通知時,分兩種情況來處理獲取到的通知:

  • APP在前臺運(yùn)行,或者單機(jī)Home鍵切換到后臺運(yùn)行(未雙擊Home鍵完全退出)
    會調(diào)用下面的代理方法
    當(dāng)前APP在前臺運(yùn)行時,會立即調(diào)用該方法;
    當(dāng)APP從后臺被喚起時,點(diǎn)擊推送消息打開時調(diào)用
func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
        // 獲取當(dāng)前APP的狀態(tài)
        let state = application.applicationState
        
        var msg = ""
        // 處在活躍狀態(tài),前臺運(yùn)行
        if state == .active {
            // qiantai
            
            msg += "qian tai"
        } else if state == .inactive {
// 處于不活躍狀態(tài),后臺或后臺被系統(tǒng)掛起
            // houtai
            msg += "hou tai"
        }
        // 注冊通知時添加的 userInfo 信息
        msg += "\n\(notification.userInfo)"
        
        self.alert(msg)
        application.cancelLocalNotification(notification)
    }
  • APP 被殺死(雙擊Home鍵,從后臺關(guān)閉)
    此時,應(yīng)該從 didFinishLaunchingWithOptions 方法獲取通知內(nèi)容
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        // 應(yīng)用被殺死后,如果來了通知,會在此處獲取推送內(nèi)容
       if let localNoti = launchOptions?[UIApplication.LaunchOptionsKey.localNotification] as? UILocalNotification {
            // NEXT TO DO ...
            print(localNoti.userInfo)
        }
        
        
        return true
    }

需要注意的是,此時雖然獲取到了通知內(nèi)容,但是,跟視圖控制器可能還沒有加載,業(yè)務(wù)的處理邏輯應(yīng)該放到跟視圖加載完成后再去處理。

如果某個推送已讀,需要重置角標(biāo)數(shù)量,可參考下面的方法

// 獲取角標(biāo)數(shù)量
        var badegNumber = UIApplication.shared.applicationIconBadgeNumber
        // 修改角標(biāo)數(shù)量
        badegNumber -= 1
        // 重新賦值角標(biāo)數(shù)量
        UIApplication.shared.applicationIconBadgeNumber = badegNumber >= 0 ? badegNumber: 0

遠(yuǎn)程通知 APNs(Apple Push Notification Services)

遠(yuǎn)程推送獲取權(quán)限、自定義快捷操作以及獲取 deviceToken 和本地通知相同,不同的只是收到通知后,回調(diào)的代理方法不一樣。

  • 遠(yuǎn)程通知必須使用真機(jī)
  • 遠(yuǎn)程通知必須使用真機(jī)
  • 遠(yuǎn)程通知必須使用真機(jī)

測試推送服務(wù)

在開始之前,先介紹一個發(fā)送遠(yuǎn)程通知的軟件:NWPusher,可以在GitHub下載后自己編譯運(yùn)行,也可以直接下載其二進(jìn)制文件使用NWPusher app,打開后如下圖:

NWPusher

如果不是真機(jī),是獲取不到deviceToken的
payload的內(nèi)容模版為:

{"aps":{"alert":"Testing.. (0)","badge":1,"sound":"default"}}

key值aps是蘋果定義的,推送的消息Json格式一定要是這樣,除了此key,還有以下key值是蘋果定義的:

  • alert :彈框內(nèi)容,可以是 Dictionary
  • badge:角標(biāo)的數(shù)量
  • sound:收到通知時播放的聲音文件名,需要提前添加到Bundle中
  • content-availabel:如果其值為1,則是靜默通知,且不能設(shè)置alert,sound,badge,否則無效
  • category:一組額外的快捷按鈕標(biāo)識符,即UIMutableUserNotificationCategory的identifier屬性

注意: payload的長度是有限制的,
iOS 8以下是 256字節(jié),
iOS 8 --- iOS 9是2k,
iOS 9+ 是4k,
超過限制的消息,蘋果后臺是拒絕推送的。

如果需要添加自己的內(nèi)容,直接在里面的字典添加自定義的key即可,例如:

{"aps":{"alert":"Testing.. (0)","badge":1,"sound":"default","extinfo": {}}}

然后,點(diǎn)擊右下角的 push按鈕即可發(fā)送一個遠(yuǎn)程通知。

PS:如果軟件長時間不操作,點(diǎn)擊push的報錯,可點(diǎn)擊右上角的Reconnect 重新連接服務(wù)即可?。?!

一般遠(yuǎn)程通知

一般的遠(yuǎn)程通知注冊和本地通知的注冊一樣,遠(yuǎn)程通知不需要本地創(chuàng)建通知對象,而是發(fā)送 payload 由系統(tǒng)來創(chuàng)建對應(yīng)的對象,payload 就相當(dāng)于給通知賦值:

let setting = UIUserNotificationSettings(types: [.sound, .alert, .badge], categories: nil)
    
    UIApplication.shared.registerUserNotificationSettings(setting)

測試發(fā)送一個 Payload

{"aps":{"alert":"Remote Notification Test Info","badge":1,"sound":"default","extinfo": {"info": "Test remote info"}}}

使用上面介紹的軟件發(fā)送:


Remote Notification

帶快捷交互的通知

如果想要帶有快捷交互的通知,需要在Payload中添加 category 字段,且其值要與注冊通知時創(chuàng)建的 UIMutableUserNotificationCategory 實(shí)例的屬性identifier值一致:
例如,注冊的通知:

    func registerAPN() {
        
        let ok = UIMutableUserNotificationAction()
        ok.identifier = "okidentifier"
        ok.activationMode = . foreground // 這里需要打開app,所以設(shè)置為foreground模式
        ok.title = "查看"
        ok.isDestructive = false
        ok.isAuthenticationRequired = false
        ok.behavior = .default
        
        
        let cancel = UIMutableUserNotificationAction()
        cancel.identifier = "cancelidentifier"
        cancel.activationMode = .background // 這里不需要打開app,所以設(shè)置為background模式
        cancel.title = "關(guān)閉"
        cancel.isDestructive = true
        cancel.isAuthenticationRequired = false
        
        let category = UIMutableUserNotificationCategory()
        category.identifier = "categoryidentifier"
        category.setActions([ok, cancel], for: .default)
        
        
        let set = Set([category])
        
        let setting = UIUserNotificationSettings(types: [.sound, .alert, .badge], categories: set)
        
        UIApplication.shared.registerUserNotificationSettings(setting) 
    }

將上面的Payload修改為:

{"aps":{"alert":"Remote Notification Test Info","badge":1,"sound":"default","category": "categoryidentifier","extinfo": {"info": "Test remote info"}}}

再次發(fā)送通知:


這時點(diǎn)擊對應(yīng)的快捷按鈕,會調(diào)用系統(tǒng)代理方法:

// 遠(yuǎn)程推送的UIMutableUserNotificationAction回調(diào)
    func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
        
        if identifier == "okidentifier" {
            print("ok")
        } else {
            print("cancel")
        }
    }

其中的 userInfo 即是我們發(fā)送的Payload中的內(nèi)容,在這里處理相應(yīng)的業(yè)務(wù)邏輯即可;

如果,快捷操作的類型是輸入框(iOS 9 --- iOS 10),只需要將UIMutableUserNotificationAction示例對象的屬性behavior修改為.textInput 即可,這時輸入完成會調(diào)用方法:

func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], withResponseInfo responseInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
        
        let input = responseInfo[UIUserNotificationActionResponseTypedTextKey]
        print(input)
    }

遠(yuǎn)程通知的處理

同本地通知一樣,遠(yuǎn)程通知的處理也分兩種情況:

  • app 處于前臺或后臺(未雙擊Home鍵,將app殺死)
  • app 被殺死,雙擊Home鍵,將app從后臺殺死

以上兩種情況,如果來了遠(yuǎn)程通知,都會調(diào)用下面的代理方法:

// iOS 7 +
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        
        let state = UIApplication.shared.applicationState
        
        var msg = ""
        
        if state == .active {
            // qiantai
            
            msg += "active"
        } else {
            // houtai
            msg += "inactive"
        }
        
        print("Receive remote notification at \(msg)")
        print(userInfo)
        
        completionHandler(.noData)
    }

    // iOS 3 -- iOS 10
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
      
        print("didReceiveRemoteNotification old")
    }

iOS 7以后主要是使用第一個方法,只有在iOS 7以下的版本才會走第二個代理方法,現(xiàn)在幾乎是使用不到了。

如果app正在前臺運(yùn)行,此時來了遠(yuǎn)程消息,會直接調(diào)用該代理方法,不會有彈框,通過 userInfo 可以獲取推送的Playload信息;
如果app在后臺運(yùn)行或被系統(tǒng)掛起或被殺死,如果來了遠(yuǎn)程消息,就有彈框提醒,此時不會調(diào)用該方法,當(dāng)點(diǎn)擊消息打開app的時候,才會調(diào)用。

當(dāng)app被殺死后,也可能會走didFinishLaunchingWithOptions方法:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        
        self.registerAPN()
        
        // 應(yīng)用被殺死后,如果來了通知,會在此處獲取推送內(nèi)容
        if let pushNiti = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] as? [String: Any] {
            
            print(pushNiti)
        }

        return true
    }

也可以添加如上的處理方法,現(xiàn)在一般都會走didReceiveRemoteNotification代理方法。

靜默push iOS 7 +

靜默push是沒有彈框,沒有聲音,沒有任何提醒的push,但是他能喚起你的app維持3分鐘的運(yùn)行,前提是你的app被切入后臺運(yùn)行或被系統(tǒng)掛起,而不是雙擊Home鍵殺死app。完全退出后,也是接收不到靜默push的。他是iOS 7之后才出現(xiàn)的一種推送方式。
靜默push的前提是:
payload中,沒有alert及sound字段,而且添加content-available字段, 并設(shè)置其值為1:

{"aps":{"content-available":"1","extinfo": {"info": "Test remote info"}}}

同樣,我們可以在接收一般通知的代理方法中來處理靜默push的信息:

// iOS 7 +
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        
        let state = UIApplication.shared.applicationState
        
        var msg = ""
        
        if state == .active {
            // qiantai
            
            msg += "active"
        } else {
            // houtai
            msg += "inactive"
        }
        
        print("Receive remote notification at \(msg)")
        print(userInfo)
        completionHandler(.noData)
    }

一般我們需要在不打擾用戶的情況下更新一些數(shù)據(jù)時,可以選擇使用靜默push,來后臺下載更新相關(guān)的數(shù)據(jù)。

這里completionHandler的參數(shù)是UIBackgroundFetchResult類型,他有三個值:

@available(iOS 7.0, *)
public enum UIBackgroundFetchResult : UInt {

    // 新數(shù)據(jù)下載成功
    case newData
// 沒有新數(shù)據(jù)需要下載
    case noData
// 新數(shù)據(jù)下載失敗
    case failed
}

通知中心快捷回復(fù)

同本地通知一樣,在通知中心快捷回復(fù),只需要在創(chuàng)建UIMutableUserNotificationAction 實(shí)例對象時的屬性 behavior 設(shè)置為 .textInput,則點(diǎn)擊按鈕時,會彈出輸入框,同樣是在下面的代理方法中可以獲取到輸入的內(nèi)容:

func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], withResponseInfo responseInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
        
        let input = responseInfo[UIUserNotificationActionResponseTypedTextKey]
        print(input)
        
        completionHandler()
    }

到此,iOS10以下的通知處理基本就結(jié)束了,但是在iOS10中,這些處理方式都廢棄了,使用新的API來處理通知:UserNotifications

后臺任務(wù)示例代碼

最后給一個后臺任務(wù)的示例代碼

func backgroundTask() {
        
        let app = UIApplication.shared
        
        var taskid = UIBackgroundTaskIdentifier.invalid
        
        taskid = app.beginBackgroundTask {
            
            app.endBackgroundTask(taskid)
            taskid = .invalid
        }
        
        let queue = DispatchQueue(label: "backgroundTaskQueue")
        queue.async {
            while true {
                let time = app.backgroundTimeRemaining
                if time < 5 {
                    break
                }
                
                Thread.sleep(forTimeInterval: 1)
            }
        }
        
        app.endBackgroundTask(taskid)
        taskid = .invalid
    }

參考文章

iOS下的UILocalNotification的使用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文作者:陳裕發(fā), 騰訊系統(tǒng)測試工程師,由騰訊WeTest整理發(fā)表。 1、引言 開發(fā)iOS系統(tǒng)中的Push推送,通...
    SuGrand閱讀 5,239評論 5 22
  • 1、通過CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,203評論 3 119
  • 一人一青裳,一硯一墨香 一徑一斜陽,一鐘一回蕩 一雁一寒江,一葦一青霜 一望一蒼茫,一思一遠(yuǎn)方 一袖一輕揚(yáng),一舞...
    林阿阿閱讀 533評論 0 0
  • 我最近開始重新把一些事搬回到 Safari 上去做了,電腦馬上涼快了許多,然后續(xù)航水平也恢復(fù)了正常。這也是科技還沒...
    Oriharas閱讀 280評論 0 0
  • 創(chuàng)業(yè)公司只有兩個兩個關(guān)鍵點(diǎn):一是被市場證明,另一個是井噴。 其它只要不犯特別大的錯誤,都沒有什么關(guān)系。不要過于焦慮...
    言晴1988閱讀 235評論 0 1

友情鏈接更多精彩內(nèi)容