Создать последовательную очередь для сетевых вызовов, сгенерированных push-уведомлениями

Я получаю до четырех push-уведомлений для каждого события, на которое я подписан. Я просмотрел все, что связано с моими подписками CloudKit и реестром уведомлений, и я убежден, что это проблема Apple. Вместо этого я обратил свое внимание на правильную обработку уведомлений, независимо от того, сколько я получаю. Вот упрощенная версия того, что я делаю:

func recievePrivatePush(_ pushInfo: [String:NSObject], completion: @escaping ()->Void) {

    let notification = CKNotification(fromRemoteNotificationDictionary: pushInfo)
    let alertBody = notification.alertBody

    if let queryNotification = notification as? CKQueryNotification {
        let recordID = queryNotification.recordID
        guard let body = queryNotification.alertBody else {
            return
        }

        if recordID != nil {
            switch body {
            case "Notification Type":
                let id = queryNotification.recordID
                switch queryNotification.queryNotificationReason {
                case .recordCreated:
                    DataCoordinatorInterface.sharedInstance.fetchDataItem(id!.recordName, completion: {
                        //
                    })
                    break

                default:
                    break
                }
           }
       }
    }
}

Код получения выглядит примерно так:

func fetchDataItem(_ id: String, completion: @escaping ()-> Void) {

    if entityExistsInCoreData(id) {return}


    let db = CKContainer.default().privateCloudDatabase
    let recordID = CKRecordID(recordName: id)
    db.fetch(withRecordID: recordID) { (record, error) in
        if let topic = record {
            //Here I create and save the object to core data.
        }
        completion()
    }
}

Весь мой код работает, проблема заключается в том, что когда я получаю несколько уведомлений, несколько запросов на выборку запускаются до того, как будет создан первый основной объект данных, что приводит к избыточным объектам основных данных.

Что я хотел бы сделать, так это найти способ добавить запросы на выборку в последовательную очередь, чтобы они обрабатывались по одному. Я могу поместить свои вызовы запросов в последовательную очередь, но обратные вызовы всегда выполняются асинхронно, поэтому несколько запросов на выборку по-прежнему выполняются до того, как будет сохранен первый объект данных.

Я пробовал использовать семафоры и группы отправки с шаблоном, который выглядит следующим образом:

let semaphore = DispatchSemaphore(value: 1)    

func recievePrivatePush(_ pushInfo: [String:NSObject], completion: @escaping ()->Void) {

    _ = semaphore.wait(timeout: .distantFuture)

    let notification = CKNotification(fromRemoteNotificationDictionary: pushInfo)
    let alertBody = notification.alertBody

    if let queryNotification = notification as? CKQueryNotification {
        let recordID = queryNotification.recordID
        guard let body = queryNotification.alertBody else {
            return
        }

        if recordID != nil {
            switch body {
            case "Notification Type":
                let id = queryNotification.recordID
                switch queryNotification.queryNotificationReason {
                case .recordCreated:
                    DataCoordinatorInterface.sharedInstance.fetchDataItem(id!.recordName, completion: {
                        semaphore.signal()
                    })
                    break

                default:
                    break
                }
           }
       }
    }
}

Как только вышеуказанная функция вызывается во второй раз и вызывается semaphore.wait, выполнение первого сетевого запроса приостанавливается, что приводит к зависанию приложения.

Опять же, что я хотел бы сделать, добавляя асинхронные сетевые запросы в очередь, чтобы они выполнялись только по одному за раз, то есть первый сетевой вызов завершался до начала второго запроса.


person CarlNathan    schedule 29.12.2016    source источник
comment
Карл, не уверен, правильно ли я понял проблему, но здесь альтернативный подход к работе с несколькими [дублирующими] уведомлениями. Это очень просто, просто используйте словарь, чтобы сохранять уведомления по мере их поступления, чтобы вы могли легко распознавать дубликаты. Таким образом, даже если вы получите второе, третье или более уведомлений об одном и том же событии, это не будет иметь значения. Вы просто проверяете свой словарь и выбрасываете дубликат.   -  person user3069232    schedule 29.12.2016
comment
Проблема по-прежнему в том, что уведомления приходят почти одновременно и обрабатываются делегатом приложения параллельно. Я получу несколько ложных срабатываний, прежде чем у меня будет время добавить пару ключ-значение в словарь и проверить ее.   -  person CarlNathan    schedule 30.12.2016


Ответы (2)


Карл,

Возможно, вы найдете свои решения с группами диспетчеризации, несколькими ключевыми выражениями, на которые стоит обратить внимание.

let group = DispatchGroup()
group.enter()

... код ...

group.leave
group.wait()

Я использую их, чтобы ограничить количество HTTP-запросов, которые я отправляю в пакете, чтобы дождаться ответа. Возможно, вы могли бы использовать их вместе с предложением в моем комментарии. Посмотрите это видео тоже, здесь диспетчерские группы, я думаю больше.

https://developer.apple.com/videos/play/wwdc2016/720/
person user3069232    schedule 30.12.2016
comment
Спасибо за ответ. Я обнаружил, что группы диспетчеризации и семафоры плохо подходят для решения этой конкретной проблемы. Я воспользовался советом из вашего предыдущего комментария. То, что я сделал для создания последовательной очереди для управления доступом к массиву вместо того, чтобы делать запросы, оказалось, работает хорошо. Спасибо за помощь! Я обновлю свой вопрос рабочим кодом. - person CarlNathan; 30.12.2016

Эти простые занятия помогли мне решить проблему.

class PushQueue {

internal var pushArray: Array<String> = [String]()
internal let pushQueue = DispatchQueue(label: "com.example.pushNotifications")

public func addPush(_ push: Push) {
    pushQueue.sync {
        if pushArray.contains(push.id) {
            return
        } else {
            pushArray.append(push.id)
            processNotification(push: push)
        }
    }
}

internal func processNotification(push: Push) {
    PushInterface.sharedInstance.recievePrivatePush(push.userInfo as! [String: NSObject])
}

}


class CKPush: Equatable {

init(userInfo: [AnyHashable: Any]) {

    let ck = userInfo["ck"] as? NSDictionary
    let id = ck?["nid"] as? String
    self.id = id!
    self.userInfo = userInfo
}

var id: String
var userInfo: [AnyHashable:Any]

public static func ==(lhs: CKPush, rhs: CKPush) -> Bool {
    return lhs.id == rhs.id ? true : false
}

}

Пожалуйста, не обращайте внимания на небрежное силовое развертывание. Их нужно убирать.

person CarlNathan    schedule 30.12.2016