从 Web API 检索数据时不显示进度条

Progress bar not showing whilst retrieving data from web APIs

Swift3/Xcode8/ios10

我有一个方法可以从几个 api 端点获取大量数据。我挑选出我想要保留的位,然后将该数据格式化并打印到一个文本文件中。

当应用程序启动时,它会检查文本文件是否为空。如果它为空,则 API 请求为 运行 并相应地填充文本文件。到目前为止,一切都很好。这一切都按预期工作。

我现在想通过进度条向用户显示数据检索的进度。这就是我卡住的地方。我似乎无法在数据检索之前和期间显示进度条。它仅在数据检索完成后显示 - 并且也表示同样多。通过协议方法中的一些打印语句,我可以看到进度条似乎正在更新 - 只是当我想要它时,不会发生进度条的转场。

以上描述涉及3个class:

  1. MasterVC - 检查文本文件并在必要时转到 ProgressBar。在将 performSegue 调用到 ProgressBar 后立即通过 GetData.start() 启动 API 调用。
  2. ProgressBar - 显示进度条并从 GetData 接收更新。
  3. GetData - 包含从 APIs 获取数据的方法。

在MasterVC的viewDidAppear中,第一段代码如下:

    if fileSize == 0{

        //SHOW PROGRESS OVERLAY AND RETRIEVE API DATA
        performSegue(withIdentifier: "toProgressBarOverlay", sender: self)

        GetData.start() //IF THIS IS ACTIVE, DATA IS RETRIEVED BUT PROGRESS BAR APPEARS AT THE END.

    }

此 运行 之后的任何代码仅在通过 API 调用填充文本文件后。我通过使用 API 调用方法中包含的信号量来完成此操作。相反,如果我将 GetData.start() 放在 ProgressBar 的 viewDidAppear 中,我会在本节中收到有关 variables/arrays 等为空的错误。

通过谷歌搜索和搜索,我得到的印象是进度条的延迟显示与仅在每个周期结束时完成的视图更新有关,虽然我首先调用了 performSegue,但它只发生在最后,在 class 中的其余代码被迭代之后。

如何让这个进度条按预期显示?

编辑 1:

好的,所以我设法让进度条现在出现在 API 调用的开始处。然而遗憾的是,进度条没有正确更新。但是,更新仍在控制台中正确报告。进度条显示默认起始值​​,然后一旦 API 调用完成,进度条突然冲到 100%,没有中间值。使用 dispatchQueue.main.async/sync 仍然无效。以下是我所做的更改:

上面的代码部分(在 MasterVC 中)现在如下所示:

//FETCH MP INFO AND POPULATE MPDATA.TXT.
    if fileSize == 0{

        //SHOW PROGRESS OVERLAY AND RETRIEVE API DATA
        performSegue(withIdentifier: "toProgressBarOverlay", sender: self)

    }

此后的所有代码(处理填充的文本文件)都已移至其自己的方法(称为 processTextFile)。

因此,当应用程序启动时,它会检查一个空文件,如果找到空文件,则转到 ProgressBar。 ProgressBar 现在还包含对 MasterVC 的新引用(称为 masterVcReference),这是在 prepareForSegue 期间设置的。

ProgressBar 现在启动 API 调用:

GetData.start(masterVc: masterVcReference!)

一旦所有数据都被检索并写入文件,GetData 使用 masterVcReference 调用 processTextFile。

实际协议方法如下:

func updateProgressBar(message: String, percentComplete: Float) {

    print("Progress: \(percentComplete)%") << this is being reported correctly.

    ProgressBar?.setProgress(percentComplete, animated: true)
    ProgressBarMessageField.text = message

    if percentComplete == 100{
        //dismiss(animated: true, completion: nil)
    }
}

如果我放置 "ProgressBar?.setProgress(percentComplete, animated: true)" 并且 "ProgressBarMessageField.text = message" 在 dispatchQueue.main.async/sync 块中,没有区别。

所以目前,除了进度条没有按预期更新外,该应用程序正常运行。

编辑 2:

好的,所以使用 DispatchQueue.global(qos: .userInteractive).async 而不是 dispatchQueue.main.async 来更新进度条有一点不同。进度条更新,有点。在进度条应该经历的 649 次更新(来自已处理的 649 条数据)中,如果幸运的话,它只会更新 1、2 或 3 次。此外,每个 运行 应用程序的更新频率也不相同。

编辑 3:

OK - 结果 dispatchQueue.main.async 一直在工作,但它似乎在排队所有更新并在最后执行所有操作,而不是在每个协议方法调用时执行。混乱盛行。

终于解决了!

我使用信号量来确保来自 API 调用的数据以正确的顺序处理。解决方案是在调用委托方法之前放置 "semaphore.signal()" 语句。最初,委托调用位于 "let semaphore = DispatchSemaphore(value: 0)" 和 "semaphore.signal()" 之间。不知何故(我还不明白),这导致 UI 更新排队,直到所有 API 数据都完成处理后才执行。

瞧!

我使用信号量来确保来自 API 调用的数据以正确的顺序处理。解决方案是在调用委托方法之前放置 "semaphore.signal()" 语句。最初,委托调用位于 "let semaphore = DispatchSemaphore(value: 0)" 和 "semaphore.signal()" 之间。不知何故(我还不明白),这导致 UI 更新排队并且在所有 API 数据完成处理后才执行。