0

사용자가 원하는 시간에 여러 알림을 예약 할 수있게 해주는 프로젝트 중 하나에서 작업하고 있습니다. iOS 10에서 새로운 UserNotifications을 사용하고 있습니다.비동기 코드 대기 완료 Swift

모든 알림을 올바르게 예약하려면 각 알림에 고유 한 고유 식별자가 있어야합니다. 내 데이터 모델에있어서 내 구성 :

  1. 내 모델의 ID
  2. 새로운 통지
  3. 위 밑줄로 구분되어 생성 될 때마다 증가하는 숫자

예를 들어 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을 호출합니다.

["3" : 15] 

createNotification 단순히 사전에 값을 걸릴 것입니다 : 예를 들어

는 같을 것이다 아이디 3 모델을 기다리고 디스크에 전달 5 알림 및 10 다른 사람이 있다면 식별자를 생성하기 위해 1 씩 증가시킵니다. 그것은 비동기 작업입니다

UNUserNotificationCenter.current().add(takeMedecineRequest, withCompletionHandler: { (error) in 
      if let _ = error { 
       print("There was an error : \(error)") 
      } 
     }) 

:와

진짜 문제는 온다. 그것이 지연되지 않는다는 것을 고려하면, generateNotifications의 루프로 돌아 가면 checkDeliveredAndPendingNotifications은 알림 작성을 완료하지 않았기 때문에 올바른 사전을 반환하지 않습니다.

print("identifierDictionary -> \(identifierDictionary)") // ["3":15], ["3":16], ["3":17] 
print("unique identifier created -> \(takeMedecineIdentifier") // 3_16, 3_17, 3_18 

하지만 지금은 내가 갖는 : 나는 3 알림을 예약해야한다면 나는 이런 식으로 뭔가 좀하고 싶습니다 위의 예를 고려

그래서

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 예, 불행히도. 식별자를 사용하여 보류 중 및 전달 된 알림 만 제거 할 수 있습니다. 나는이 id를 통보 요청의 userInfo 딕셔너리에 전달하고 식별자에 대한 무작위 화 된 문자열을 생성 할 수 있지만, 두 개의 통보에 당신의 링크를 따라 가면서 동일한 식별자가 없다는 것을 확신 할 수 있습니까? 식별자가 고유하지 않으면 모든 알림을 전달하지 않아 내 앱을 위반하게됩니다. – Croisciento

+1

다른 시드를 사용하는 [arc4random] (https://developer.apple.com/legacy/library/documentation/Darwin/Reference/Man4agesom_uniform.3.html)을 사용하므로 동일한 시퀀스가 ​​생성되지 않습니다. 24 문자의 영숫자 문자열을 사용하면 (36 + 36 + 10)^24 개의 조합이 생겨 충돌을 무시할 수 있습니다. – Thomm

답변

1

알림에서 식별자를 '읽을'필요가없는 경우 식별자 대신 임의의 문자열을 사용할 수 있습니다.

올바른 ID 생성을 위해 제어 흐름에 의존해서는 안되는 것처럼 이제는 고유 한 ID를 올바르게 생성 할 수있는 경우에도 마찬가지입니다. 이것은 일반적으로 (3 번째) 파티 라이브러리 나 API에 의존 할 때 잘못된 코딩 방법으로 간주됩니다. 하나의 변화가 그것을 깨뜨릴 수 있습니다.

here과 같이 임의의 문자열을 생성 할 수 있습니다. 24 문자의 영숫자 문자열을 사용하면 (36 + 36 + 10)^24 개의 조합이 생겨 충돌을 무시할 수 있습니다.

userinfo 사전이나 다른 지속성 방법을 사용하여 식별자를 특정 알림과 연결할 수 있습니다. CoreData을 사용하는 경우 고유 식별자가있는 알림 객체를 medicineRequests에 연결할 수 있습니다.

관련 문제