首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在后台触发多步骤api事务?

如何在后台触发多步骤api事务?
EN

Stack Overflow用户
提问于 2019-04-17 21:55:04
回答 1查看 89关注 0票数 0

如果重要的话,这个应用程序是一个4.2应用程序,但正在升级到5.0与新的功能,包括这个。

作为对可用内容APN的响应,在触发消息给第三方之前,我需要将本地设备数据与远程数据结合起来。在前台,这个过程工作,但在后台,它似乎冻结,直到应用程序在前景。

我想用一个DispatchQueue来解决这个问题--这让我走得更远了,但它仍然没有完全解决。

当我收到我的APN时,我确保它看起来是正确的(这是一个内容-avaialbe通知,并且有一个类别),然后启动loadPrediction。

代码语言:javascript
复制
    // Tells the app that a remote notification arrived that indicates there is data to be fetched.
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler:
        @escaping (UIBackgroundFetchResult) -> Void) {
        guard let aps = userInfo["aps"] as? [String: AnyObject] else {
            completionHandler(.failed)
            return
        }

        if aps["content-available"] as? Int == 1 {
            guard let category = aps["category"] as? String else {
                print("on didReceiveRemoteNotification - did not receive payload with category")
                print(String(describing: userInfo))
                return
            }

            switch category {
            case APNCATEGORIES.PREDICTION.rawValue:
                DataModel.shared.loadPredictions() {
                    completionHandler(.newData)
                }
                break
            default:
                print("on didReceiveRemoteNotification - received unknown category '\(category)'")
                completionHandler(.failed)
            }
        } else  {
            print("on didReceiveRemoteNotification - did not receive content-available in APN")
            print(String(describing: aps))

            completionHandler(.noData)
        }
    }

在loadPredictions中,我从后端请求两段数据。编辑: I've read that you might want to start a different queue for each POST request,所以我已经将下一个代码块修改为它的当前形式,而不仅仅是一个队列:

代码语言:javascript
复制
    /** load prediction data for notification scheduling */
    func loadPredictions(_ callback: @escaping () -> Void) {
        print("loading predictions")
        let queue = DispatchQueue(label: "loadingPredictions", qos: .utility, attributes: .concurrent)

        queue.sync { [weak self] in
            print("loading predictions - in async task, about to getPredictionsDataFromFirestore")

            self?.getPredictionsDataFromFirestore() { [weak self] in
                print("getting Predictions Data from Firestore")

                if let error = $2 {
                    NotificationCenter.default.post(Notification(name: DataModel.constants.dataFailedToLoad, object: error))
                } else {
                    let apps = $0
                    apps.forEach { app in
                        print("for each app - about to getNotificationSpecificationFromFireStore")
                        let queue = DispatchQueue(label: "getNotificationSpecificationFromFireStore_\(app.name)", qos: .utility, attributes: .concurrent)

                        queue.async { [weak self] in
                            print("getting Notification Specification from FireStore")

                            self?.getNotificationSpecificationFromFireStore(app: app) { [weak self] spec, error in
                                print("got Notification Specification from FireStore, about to post notification")

                                if(error != nil) {
                                    return
                                }
                                guard let spec = spec else {
                                    return
                                }
                                self?.postNotification(app: app, spec: spec)
                            }
                        }
                    }
                    // loadMergedForecasts($1)
                    NotificationCenter.default.post(Notification(name: DataModel.constants.predictionsDataLoaded))
                }

                callback()
            }
        }
    }

他们真的不需要像那样互相依赖,但是如果第一个失败了,做第二个没有意义。如果它们都成功了,我应该在postNotification中向收件人发送一个通知:

代码语言:javascript
复制
    /** notify third party app of available notificatiions to schedule */
    func postNotification (app: App, spec: NotificationSpecification) {
        print("posting notification")
        do {
            let notify = Data(app.notify.utf8)
            let localNotificationDetails = try JSONDecoder().decode(NotificationDetails.self, from: notify)

            if spec.p8 != "custom" {
                let token = localNotificationDetails.token


            } else {
                guard let bodyJSON = localNotificationDetails.body else {
                    return
                }

                guard let url = spec.custom_endpoint else { return }

                guard let methodString = spec.custom_method?.uppercased() else { return }
                guard let method = HTTPMethod(rawValue:methodString) else { return }
                if ![.post, .put, .patch].contains(method) {
                    print("app has unsupported method '\(method)' -- \(String(describing: app))")
                    return
                }
                guard var headers = spec.custom_headers else { return }
                if !headers.keys.map({ entry_key in entry_key.uppercased() }).contains("CONTENT-TYPE") {
                    headers["Content-Type"] = "application/json"
                }

                print("manually posting the notification with \(String(describing: bodyJSON))")

                let queue = DispatchQueue(label: "manuallyPostNotifications", qos: .utility, attributes: .concurrent)

                AF.request(url, method:method, parameters: bodyJSON).responseJSON(queue: queue) { response in
                    switch response.result {
                    case .success:
                        print("Validation Successful")
                    case .failure(let error):
                        print(error)
                    }
                }
            }
        } catch let e {
            print("error posting notification to app \(app.id)\n\(e)")
        }
    }

这些方法都不在视图中。

一开始,没有任何提示,我不知道我是否通过了第一个loadPrediction。在当前状态下,当应用程序处于后台时,日志如下所示:

代码语言:javascript
复制
loading predictions
loading predictions - in async task, about to getPredictionsDataFromFirestore
getting Predictions Data from Firestore
for each app - about to getNotificationSpecificationFromFireStore
getting Notification Specification from FireStore

编辑:这是额外的一行,但它并不代表额外队列的任何改进。

它将完成并成功,如果我前景(和他整个事情花了1-2秒,当充分在前景)。但我现在想做我所有的工作。

问题:

我排队不对。我怎么才能不排完我的队呢?

谁能确认或否认这将在应用程序关闭时工作吗?我可以看到,工作是在应用程序关闭时完成的,但之后我没有回去测试api调用是否工作,因为我不能让它在后台工作。

增编

修正后的当前答案代码

代码语言:javascript
复制
    /** load prediction data for notification scheduling */
    func loadPredictions(_ callback: @escaping () -> Void) {
        print("loading predictions")
        let queue = DispatchQueue(label: "loadingPredictions", qos: .default)

        queue.sync { [weak self] in
            let group = DispatchGroup()
            group.enter()
            print("loading predictions - in async task, about to getPredictionsDataFromFirestore")

            self?.getPredictionsDataFromFirestore() { [weak self] in
                print("getting Predictions Data from Firestore")

                if let error = $2 {
                    NotificationCenter.default.post(Notification(name: DataModel.constants.dataFailedToLoad, object: error))
                } else {
                    let apps = $0
                    apps.forEach { app in
                        print("for each app - about to getNotificationSpecificationFromFireStore")

                        group.enter()
                        print("getting Notification Specification from FireStore")

                        self?.getNotificationSpecificationFromFireStore(app: app) { [weak self] spec, error in
                            print("got Notification Specification from FireStore, about to post notification")

                            if(error != nil) {
                                group.leave()
                                return
                            }
                            guard let spec = spec else {
                                group.leave()
                                return
                            }
                            self?.postNotification(app: app, spec: spec) {
                                group.leave()
                            }
                        }
                        group.leave()
                    }
                    // loadMergedForecasts($1)
                    NotificationCenter.default.post(Notification(name: DataModel.constants.predictionsDataLoaded))
                    group.leave()
                }
                group.notify(queue: .main) {
                    callback()
                    print("I am being called too early?")
                }
            }
        }
    }

和(在最后的方法调用中添加了一个回调):

代码语言:javascript
复制
    /** notify third party app of available notificatiions to schedule */
    func postNotification (app: App, spec: NotificationSpecification, _ callback: @escaping () -> Void ) {
        print("posting notification")
        do {
            let notify = Data(app.notify.utf8)
            let localNotificationDetails = try JSONDecoder().decode(NotificationDetails.self, from: notify)

            if spec.p8 != "custom" {
                let token = localNotificationDetails.token

                callback()
            } else {
                guard let bodyJSON = localNotificationDetails.body else {
                    callback()
                    return
                }

                guard let url = spec.custom_endpoint else {
                    callback()
                    return
                }

                guard let methodString = spec.custom_method?.uppercased() else {
                    callback()
                    return
                }
                guard let method = HTTPMethod(rawValue:methodString) else {
                    callback()
                    return
                }
                if ![.post, .put, .patch].contains(method) {
                    print("app has unsupported method '\(method)' -- \(String(describing: app))")
                    callback()
                    return
                }
                guard var headers = spec.custom_headers else { return }
                if !headers.keys.map({ entry_key in entry_key.uppercased() }).contains("CONTENT-TYPE") {
                    headers["Content-Type"] = "application/json"
                }

                print("manually posting the notification with \(String(describing: bodyJSON))")

                let queue = DispatchQueue(label: "manuallyPostNotifications", qos: .utility, attributes: .concurrent)

                AF.request(url, method:method, parameters: bodyJSON).responseJSON(queue: queue) { response in
                    switch response.result {
                    case .success:
                        print("Validation Successful")
                    case .failure(let error):
                        print(error)
                    }

                    callback()
                }
            }
        } catch let e {
            print("error posting notification to app \(app.id)\n\(e)")
            callback()
        }
    }

意识到我的打印声明不在通知回调中,我修改了它--仍然没有进入第二个firebase调用。

代码语言:javascript
复制
loading predictions
loading predictions - in async task, about to getPredictionsDataFromFirestore
getting Predictions Data from Firestore
for each app - about to getNotificationSpecificationFromFireStore
getting Notification Specification from FireStore
I am being called too early?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-04-18 00:58:37

您正在触发异步任务,您的callback()将在这些任务完成之前执行。由于callback()最终调用了completionHandler,您的应用程序将在其所有工作完成之前被暂停。

您可以使用调度组来延迟callBack(),直到一切都完成。额外的调度队列是不必要的。

代码语言:javascript
复制
func loadPredictions(_ callback: @escaping () -> Void) {
    print("loading predictions")

    let dispatchGroup = DispatchGroup() 
    print("loading predictions - in async task, about to getPredictionsDataFromFirestore")

    dispatchGroup.enter() 

    self.getPredictionsDataFromFirestore() {
        print("getting Predictions Data from Firestore")
        if let error = $2 {
            NotificationCenter.default.post(Notification(name: DataModel.constants.dataFailedToLoad, object: error))
        } else {
            let apps = $0
            apps.forEach { app in
                print("for each app - about to getNotificationSpecificationFromFireStore")
                dispatchGroup.enter()
                self.getNotificationSpecificationFromFireStore(app: app) { spec, error in
                    print("got Notification Specification from FireStore, about to post notification")
                    if(error != nil) {
                        dispatchGroup.leave()
                        return
                    }
                    guard let spec = spec else {
                        dispatchGroup.leave()
                        return
                    }
                    self.postNotification(app: app, spec: spec)
                    dispatchGroup.leave()
                }
            }
        }
        NotificationCenter.default.post(Notification(name: DataModel.constants.predictionsDataLoaded))
        dispatchGroup.leave()
    }
    dispatchGroup.notify(queue: .main) {
        callback()
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55736506

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档