0

我一直对我的项目,我允许用户在他们希望的时间安排的多项通知之一。我使用的是iOS中10等待异步代码完成斯威夫特

UserNotifications为了要安排所有通知正确每个通知都需要有自己独特的标识。我根据我的数据模型由矿:

  1. 我的模型的ID
  2. 一个数字,每次创建一个新的通知
  3. 上述由下划线分隔时间递增

因此,例如,如果我不得不安排15个对象的id为3的通知它看起来像这样: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)") 
      } 
     }) 

这是一个异步任务。考虑到它不会等待,只要我回到我的循环在generateNotificationscheckDeliveredAndPendingNotifications不返回正确的字典,因为通知中未完成创建。

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

您是否需要能够从标识符中“读取”它是哪种通知?您可以使用[randomized string](http://stackoverflow.com/a/33860834/4019540)作为标识符。即使有可能,您也不应该依赖控制流来生成正确的ID,但是一个代码或API更改可能会破坏所有内容。 – Thomm

+0

@Thomm不幸的是。您只能通过使用标识符来删除挂起和已发送的通知。我可以在通知请求的userInfo字典中传递id,并为标识符创建一个随机化的字符串,但是我可以绝对确定两个通知不会通过跟随链接具有相同的标识符吗?如果标识符不是唯一的,它将不会提供所有通知,从而破坏我的应用程序。 – Croisciento

+1

它使用[arc4random](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/arc4random_uniform.3.html),它使用不同的种子,因此它不会生成相同的序列。使用24个字符的字母数字字符串给出(36 + 36 + 10)^ 24个组合,使得碰撞的机会可以忽略不计。 – Thomm

回答

1

如果您不需要从标识符中“读取”它的通知,则可以使用随机字符串作为标识符。

即使可以像现在一样正确生成唯一的ID,也不应该依赖控制流来生成正确的ID。这通常被认为是不好的编码习惯,尤其是在依赖第三方库或API时。一个改变可能会打破它。

您可以按here所述生成随机化字符串。使用24个字符的字母数字字符串给出(36 + 36 + 10)^ 24个组合,使得碰撞的机会可以忽略不计。

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