【问题标题】:How do I ensure that all network calls have been made before accessing my core data model?在访问我的核心数据模型之前,如何确保所有网络调用都已进行?
【发布时间】:2019-04-19 17:51:22
【问题描述】:

我连续进行多个 api 调用,当我最终推送到下一个视图控制器时,我的核心数据模型中的数据完全空白。在ViewController A,我按此顺序提出了以下请求:

Api.verifyOtp(email, otp).continueWith { (task) -> Any? in
            if task.succeed {

                self.apiCallOne()
                self.apiCallTwo()
                self.apiCallThree()
                self.apiCallFour()
                self.apiCallFive()

            } else {
                Hud.hide()
                task.showError()
            }
            return nil
        }

现在所有这些调用都是异步进行的。但是最后一个方法是self.apiCallFive() 是推送到ViewController B 的方法。这是电话:

Api.apiCallFive().continueOnSuccessWith { (task) -> Any? in
        Hud.hide()
        if task.succeed {
            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            let viewB storyboard.instantiateViewController(withIdentifier: "ViewControllerB"                        self.navigationController?.pushViewController(viewB, animated: true)
        }

我的猜测是,由于所有这些请求都是异步发生的,因此无法保证哪个调用将首先完成。所以apiCallFive() 在其他人能够完成之前推送和加载 ViewController B。我怎样才能做到这一点,以便在所有任务完成之前不会加载或推送下一个视图?

谢谢!

【问题讨论】:

  • 我猜那些 api 调用也会做一些事情,比如将 api 调用结果写入核心数据?
  • 是的,这些都包含在 api 请求中。一旦调用完成检索数据,就会创建一个对象并将其存储到核心数据中。
  • 其他 api 调用完成时你会收到通知吗?
  • 无论解决方案如何,确保在任何调用失败时都不会永远冻结。

标签: ios swift


【解决方案1】:

我也遇到过同样的问题。使用DispatchGroup 修复它。

代码:

定义为property

let APIGroup = DispatchGroup()

在任何 API 调用开始时执行以下代码。

APIGroup.enter()

当任何 API 调用完成时执行以下代码。

downloadGroup.leave()

通知块:

APIGroup.notify(queue: DispatchQueue.main) {
    print("All APIs called successfully: Perform required operation")
}

无需通过任何计数器或其他变量进行管理。 notify 在所有任务成功完成后自动阻止调用。

这里真正重要的是进出对。你必须的 非常小心,并确保您离开小组。这很容易 在上面的代码中引入一个错误。假设我们没有离开 上面那个守卫声明中的组,就在返回之前。如果 调用的 API 失败,或者 JSON 格式错误,组数>条目数与叶子数不匹配。所以小组完成 处理程序永远不会被调用。如果您从 UI 并在您的网络请求时显示活动指示器 正在运行,您将永远不会从该方法中获得回调,并且您 会继续旋转?

Apple documents

【讨论】:

  • 上帝之母,这是一个绝妙的解决方案。所以我认为如果操作失败最好我可以显示一条消息通知用户他们应该检查他们的网络或任何其他错误消息,然后再试一次?另外,请停止旋转器;)
  • @butter_baby 很高兴听到。快乐编码:)
【解决方案2】:

要解决此问题,您需要一种在每次通话结束时收到通知的方法。 最简单的方法是在每次调用时使用完成块。

func apiCall(completion: @escaping () -> Void) {
    ....
}

将完成块添加到 api 调用后,您的块可能如下所示:

let dispatchGroup = DispatchGroup()

dispatchGroup.enter()
apiCallOne {
    dispatchGroup.leave()
}

dispatchGroup.enter()
apiCallTwo {
    dispatchGroup.leave()
}

...


dispatchGroup.enter()
apiCallN {
    dispatchGroup.leave()
}
dispatchGroup.wait(timeout: Constants.timeout)

请记住,wait 语句将阻塞您调用它的线程,直到所有 leave() 语句都执行完毕,因此请注意不要导致死锁。

【讨论】:

  • 啊,太好了。我实际上是从一个 Api 类中调用其中的大部分,在这些方法中输入调度组并不是一个好主意,因为它们也在我的项目的其他地方使用。所以在这样的方法之外调用dispatchGroup.enter() 然后在完成处理程序内部调用dispatchGroup.leave() 是安全的吗?
  • 是的,像我上面显示的那样称呼他们应该没问题。不知道我是否完全理解你的问题。
猜你喜欢
  • 1970-01-01
  • 2020-01-27
  • 2019-08-17
  • 1970-01-01
  • 1970-01-01
  • 2013-04-14
  • 1970-01-01
  • 2011-10-23
相关资源
最近更新 更多