等待异步代码完成Swift

时间:2016-10-13 10:41:55

标签: ios swift asynchronous notifications unusernotificationcenter

我一直致力于我的一个项目,我允许用户在他们想要的时间安排多个通知。我在iOS 10中使用了新的UserNotifications

为了正确安排所有通知,每个通知都需要有自己的唯一标识符。我根据我的数据模型编写了我的数据:

  1. 我的模特的身份
  2. 每次创建新通知时递增的数字
  3. 上面用下划线
  4. 分隔

    例如,如果我必须为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使用前一个函数返回的字典来生成一个正确的标识符。

    例如,如果磁盘上有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就不会返回正确的字典,因为通知没有完成创建。

    考虑到上面的例子,如果我必须安排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
    

    那么,我怎样才能在返回循环之前等待这个异步调用完成?

    提前感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

如果您不需要能够从标识符“读取”它,那么您可以使用随机字符串作为标识符。

即使可以像现在这样正确生成唯一ID,也不应该依赖控制流来生成正确的ID。这通常被认为是错误的编码实践,特别是在依赖(第3)方库或API时。一个改变可能会打破它。

您可以按照here所述生成随机字符串。使用24个字符的字母数字字符串可以得到(36 + 36 + 10)^ 24个组合,这使得碰撞的可能性变得可以忽略不计。

您可以使用userinfo字典或其他一些持久性方法将标识符与特定通知相关联。如果您使用的是CoreData,则可以将通知对象与唯一标识符关联到medicineRequests。

相关问题