等待异步代码完成 Swift
Wait for async code to finish Swift
我一直在做我的一个项目,我允许用户在他们想要的时间安排多个通知。我在 iOS 10.
中使用新的 UserNotifications
为了正确安排所有通知,每个通知都需要有自己的唯一标识符。我根据我的数据模型组成了我的:
- 我的模型id
- 每次创建新通知时递增的数字
- 以上用下划线分隔
因此,例如,如果我必须为 ID 为 3 的对象安排 15 个通知,它将如下所示:3_1, 3_2, 3_3...3_15
我是这样做的:
@available(iOS 10.0, *)
func checkDeliveredAndPendingNotifications(completionHandler: @escaping (_ identifierDictionary: Dictionary<String, Int>) -> ()) {
var identifierDictionary:[String: Int] = [:]
UNUserNotificationCenter.current().getDeliveredNotifications { (notifications) in
for notification in notifications {
let identifierArraySplit = notification.request.identifier.components(separatedBy: "_")
if identifierDictionary[identifierArraySplit[0]] == nil || identifierDictionary[identifierArraySplit[0]]! < Int(identifierArraySplit[1])! {
identifierDictionary[identifierArraySplit[0]] = Int(identifierArraySplit[1])
}
}
UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: { (requests) in
for request in requests {
let identifierArraySplit = request.identifier.components(separatedBy: "_")
if identifierDictionary[identifierArraySplit[0]] == nil || Int(identifierArraySplit[1])! > identifierDictionary[identifierArraySplit[0]]! {
identifierDictionary[identifierArraySplit[0]] = Int(identifierArraySplit[1])
}
}
completionHandler(identifierDictionary)
})
}
}
@available(iOS 10.0, *)
func generateNotifications() {
for medecine in medecines {
self.checkDeliveredAndPendingNotifications(completionHandler: { (identifierDictionary) in
DispatchQueue.main.async {
self.createNotification(medecineName: medecine.name, medecineId: medecine.id, identifierDictionary: identifierDictionary)
}
})
}
}
@available(iOS 10.0, *)
func createNotification(medecineName: String, medecineId: Int identifierDictionary: Dictionary<String, Int>) {
let takeMedecineAction = UNNotificationAction(identifier: "TAKE", title: "Take your medecine", options: [.destructive])
let category = UNNotificationCategory(identifier: "message", actions: [takeMedecineAction], intentIdentifiers:[], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
let takeMedecineContent = UNMutableNotificationContent()
takeMedecineContent.userInfo = ["id": medecineId]
takeMedecineContent.categoryIdentifier = "message"
takeMedecineContent.title = medecineName
takeMedecineContent.body = "It's time for your medecine"
takeMedecineContent.badge = 1
takeMedecineContent.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: false)
var takeMedecineIdentifier = ""
for identifier in identifierDictionary {
if Int(identifier.key) == medecineId {
let nextIdentifierValue = identifier.value + 1
takeMedecineIdentifier = String(medecineId) + "_" + String(nextIdentifierValue)
}
}
let takeMedecineRequest = UNNotificationRequest(identifier: takeMedecineIdentifier, content: takeMedecineContent, trigger: trigger)
UNUserNotificationCenter.current().add(takeMedecineRequest, withCompletionHandler: { (error) in
if let _ = error {
print("There was an error : \(error)")
}
})
}
checkDeliveredAndPendingNotifications
确保稍后我将创建尚不存在的标识符。
当它完成它的工作时,我调用 createNotification
,它使用由前一个函数 return 编辑的字典来生成一个正确的标识符。
例如,如果磁盘上传送了 5 个通知,还有 10 个通知在等待 ID 为 3 的模型,它看起来像这样:
["3" : 15]
createNotification
只是简单地获取字典中的值并将其递增 1 以创建标识符。
真正的问题在于:
UNUserNotificationCenter.current().add(takeMedecineRequest, withCompletionHandler: { (error) in
if let _ = error {
print("There was an error : \(error)")
}
})
这是一个异步任务。考虑到它不会等待,一旦我回到 generateNotifications
中的循环,checkDeliveredAndPendingNotifications
就不会 return 一个正确的字典,因为通知没有完成创建。
考虑到上面的示例,如果我必须安排 3 个通知,我希望得到这样的信息:
print("identifierDictionary -> \(identifierDictionary)") // ["3":15], ["3":16], ["3":17]
print("unique identifier created -> \(takeMedecineIdentifier") // 3_16, 3_17, 3_18
但现在我得到:
print("identifierDictionary -> \(identifierDictionary)") // ["3":15], ["3":15], ["3":15]
print("unique identifier created -> \(takeMedecineIdentifier") // 3_16, 3_16, 3_16
那么,在返回我的循环之前,我如何才能等待这个异步调用完成?
在此先感谢您的帮助。
如果您不需要从标识符中 'read' 知道它是哪个通知,您可以使用随机字符串作为标识符。
即使可以像您现在这样正确生成唯一 ID,您也不应依赖控制流来正确生成 ID。这通常被认为是糟糕的编码习惯,尤其是在依赖(第 3 方)库或 API 时。一项更改可能会破坏它。
您可以按照 here 所述生成随机字符串。使用 24 个字符的字母数字字符串给出 (36+36+10)^24 组合,使得碰撞的可能性可以忽略不计。
您可以使用 userinfo 字典或其他一些持久性方法将标识符与特定通知相关联。如果您使用 CoreData
,您可以将具有唯一标识符的通知对象关联到 medicineRequests。
我一直在做我的一个项目,我允许用户在他们想要的时间安排多个通知。我在 iOS 10.
中使用新的UserNotifications
为了正确安排所有通知,每个通知都需要有自己的唯一标识符。我根据我的数据模型组成了我的:
- 我的模型id
- 每次创建新通知时递增的数字
- 以上用下划线分隔
因此,例如,如果我必须为 ID 为 3 的对象安排 15 个通知,它将如下所示:3_1, 3_2, 3_3...3_15
我是这样做的:
@available(iOS 10.0, *)
func checkDeliveredAndPendingNotifications(completionHandler: @escaping (_ identifierDictionary: Dictionary<String, Int>) -> ()) {
var identifierDictionary:[String: Int] = [:]
UNUserNotificationCenter.current().getDeliveredNotifications { (notifications) in
for notification in notifications {
let identifierArraySplit = notification.request.identifier.components(separatedBy: "_")
if identifierDictionary[identifierArraySplit[0]] == nil || identifierDictionary[identifierArraySplit[0]]! < Int(identifierArraySplit[1])! {
identifierDictionary[identifierArraySplit[0]] = Int(identifierArraySplit[1])
}
}
UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: { (requests) in
for request in requests {
let identifierArraySplit = request.identifier.components(separatedBy: "_")
if identifierDictionary[identifierArraySplit[0]] == nil || Int(identifierArraySplit[1])! > identifierDictionary[identifierArraySplit[0]]! {
identifierDictionary[identifierArraySplit[0]] = Int(identifierArraySplit[1])
}
}
completionHandler(identifierDictionary)
})
}
}
@available(iOS 10.0, *)
func generateNotifications() {
for medecine in medecines {
self.checkDeliveredAndPendingNotifications(completionHandler: { (identifierDictionary) in
DispatchQueue.main.async {
self.createNotification(medecineName: medecine.name, medecineId: medecine.id, identifierDictionary: identifierDictionary)
}
})
}
}
@available(iOS 10.0, *)
func createNotification(medecineName: String, medecineId: Int identifierDictionary: Dictionary<String, Int>) {
let takeMedecineAction = UNNotificationAction(identifier: "TAKE", title: "Take your medecine", options: [.destructive])
let category = UNNotificationCategory(identifier: "message", actions: [takeMedecineAction], intentIdentifiers:[], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
let takeMedecineContent = UNMutableNotificationContent()
takeMedecineContent.userInfo = ["id": medecineId]
takeMedecineContent.categoryIdentifier = "message"
takeMedecineContent.title = medecineName
takeMedecineContent.body = "It's time for your medecine"
takeMedecineContent.badge = 1
takeMedecineContent.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: false)
var takeMedecineIdentifier = ""
for identifier in identifierDictionary {
if Int(identifier.key) == medecineId {
let nextIdentifierValue = identifier.value + 1
takeMedecineIdentifier = String(medecineId) + "_" + String(nextIdentifierValue)
}
}
let takeMedecineRequest = UNNotificationRequest(identifier: takeMedecineIdentifier, content: takeMedecineContent, trigger: trigger)
UNUserNotificationCenter.current().add(takeMedecineRequest, withCompletionHandler: { (error) in
if let _ = error {
print("There was an error : \(error)")
}
})
}
checkDeliveredAndPendingNotifications
确保稍后我将创建尚不存在的标识符。
当它完成它的工作时,我调用 createNotification
,它使用由前一个函数 return 编辑的字典来生成一个正确的标识符。
例如,如果磁盘上传送了 5 个通知,还有 10 个通知在等待 ID 为 3 的模型,它看起来像这样:
["3" : 15]
createNotification
只是简单地获取字典中的值并将其递增 1 以创建标识符。
真正的问题在于:
UNUserNotificationCenter.current().add(takeMedecineRequest, withCompletionHandler: { (error) in
if let _ = error {
print("There was an error : \(error)")
}
})
这是一个异步任务。考虑到它不会等待,一旦我回到 generateNotifications
中的循环,checkDeliveredAndPendingNotifications
就不会 return 一个正确的字典,因为通知没有完成创建。
考虑到上面的示例,如果我必须安排 3 个通知,我希望得到这样的信息:
print("identifierDictionary -> \(identifierDictionary)") // ["3":15], ["3":16], ["3":17]
print("unique identifier created -> \(takeMedecineIdentifier") // 3_16, 3_17, 3_18
但现在我得到:
print("identifierDictionary -> \(identifierDictionary)") // ["3":15], ["3":15], ["3":15]
print("unique identifier created -> \(takeMedecineIdentifier") // 3_16, 3_16, 3_16
那么,在返回我的循环之前,我如何才能等待这个异步调用完成?
在此先感谢您的帮助。
如果您不需要从标识符中 'read' 知道它是哪个通知,您可以使用随机字符串作为标识符。
即使可以像您现在这样正确生成唯一 ID,您也不应依赖控制流来正确生成 ID。这通常被认为是糟糕的编码习惯,尤其是在依赖(第 3 方)库或 API 时。一项更改可能会破坏它。
您可以按照 here 所述生成随机字符串。使用 24 个字符的字母数字字符串给出 (36+36+10)^24 组合,使得碰撞的可能性可以忽略不计。
您可以使用 userinfo 字典或其他一些持久性方法将标识符与特定通知相关联。如果您使用 CoreData
,您可以将具有唯一标识符的通知对象关联到 medicineRequests。