【问题标题】:Swift URLSession completion HandlerSwift URLSession 完成处理程序
【发布时间】:2016-11-11 10:23:16
【问题描述】:

我正在尝试在我的 Swift 项目中进行 API 调用。我刚开始实现它,我正试图从调用中返回一个 Swift Dictionary

但我认为我在完成处理程序上做错了! 我无法从我的 API 调用中获取返回值。

import UIKit
import WebKit
import SafariServices
import Foundation

var backendURLs = [String : String]()

class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {



    @IBOutlet var containerView : UIView! = nil
    var webView: WKWebView!


    override func viewDidLoad() {
        super.viewDidLoad()

        self.getBackendURLs { json in
            backendURLs = self.extractJSON(JSON: json)
            print(backendURLs)


        }
        print(backendURLs)

    }


    func getBackendURLs(completion: @escaping (NSArray) -> ()) {

        let backend = URL(string: "http://example.com")

        var json: NSArray!

        let task = URLSession.shared.dataTask(with: backend! as URL) { data, response, error in

            guard let data = data, error == nil else { return }

            do {

                json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? NSArray
                completion(json)

            } catch {

                #if DEBUG

                    print("Backend API call failed")

                #endif
            }


        }

        task.resume()


    }


    func extractJSON(JSON : NSArray) -> [String : String] {

        var URLs = [String : String]()

        for i in (0...JSON.count-1) {

            if let item = JSON[i] as? [String: String] {
                URLs[item["Name"]! ] = item["URL"]!

            }
        }

        return URLs
    }
}

第一个 print() 语句给了我正确的值,但第二个是“nil”。

有人对我做错了什么有建议吗?

【问题讨论】:

  • 像这样声明你的字典,var backendURLs: [String : String] = [:]
  • 调用是异步的,需要等待响应才能得到结果。为此,您的打印方法之一返回 nil。
  • @lubilis:感谢您的回答。我知道它是异步的,但我认为 completionHandler 会处理它
  • 你说“第一个 print() 语句给了我正确的值,但第二个是“nil”。”,你的意思是viewDidLoad()?第一个是回调内部的,第二个是回调外部的吗?
  • @pbodsk 是的,第一个在回调内部,第二个在外部。

标签: ios swift urlsession


【解决方案1】:

从技术上讲,@lubilis 已经回答了,但我无法将其放入评论中,所以请多多包涵。

这是你的viewDidLoad

override func viewDidLoad() {
   super.viewDidLoad()

   self.getBackendURLs { json in
       backendURLs = self.extractJSON(JSON: json)
       print(backendURLs)
   }
   print(backendURLs)
}

接下来会发生什么:

  1. viewDidLoad 被调用,backendURLs 为零
  2. 您调用getBackendURLs,它在后台某处的另一个线程上启动。
  3. 立即之后,您的代码继续到外部 print(backendURLs),它打印 nil,因为 backendURLs 仍然是 nil,因为您的回调尚未被调用,因为 getBackendURLs 仍在另一个线程上工作。
  4. 稍后您的getBackendURLs 完成检索数据和解析并执行此行completion(json)
  5. 现在您的回调使用数组执行,并且您的内部 print(backendURLs) 被调用...并且 backendURLs 现在有了一个值。

要解决您的问题,您需要在回调方法中刷新数据。

如果是UITableView,您可以进行reloadData() 调用,或者编写一个为您更新UI 的方法。重要的部分是您更新了回调中的 UI ,因为在此之前您没有有效的值。

更新

在你对这个答案的 cmets 中你说:

我需要在 completionHandler 之后访问变量 backendURLs

为此,您可以创建一个新方法:

func performWhateverYouNeedToDoAfterCallbackHasCompleted() {
    //Now you know that backendURLs has been updated and can work with them
    print(backendURLs)
    //do what you must
}

在您发送到self.getBackendURLs 的回调中,您调用该方法,如果您想确保它发生在主线程上,您可以按照您已经发现的那样执行:

self.getBackendURLs { json in
   backendURLs = self.extractJSON(JSON: json)
   print(backendURLs)
   DispatchQueue.main.async { 
       self.performWhateverYouNeedToDoAfterCallbackHasCompleted()
   } 
}

现在你的方法在回调完成后被调用

由于您的getBackendURLs 是一个异步方法,您无法知道它何时完成,因此您不能期望从getBackedURLs 获得的值在调用getBackendURLs 后立即准备好,直到getBackendURLs 才准备好实际上已经完成并准备调用它的回调方法。

希望这是有道理的。

【讨论】:

  • 非常感谢您的回答!但我不确定如何更新字典中的数据。
  • 你有理由声明backendURLs 你的ViewController吗?
  • 您的backendURLs 在您执行此操作时没有更新吗? backendURLs = self.extractJSON(JSON: json) print(backendURLs)。我的意思是,您没有看到那里打印的正确值吗?
  • 我需要在 completionHandler 之后访问变量 backendURLs(我的第二个打印语句放置的地方)。之后的一段时间,我在另一个班级需要它。我尝试通过 DispatchQueue.main.async { backendURLs = URLs } 更新字典,但这没有用。我在 completionHandler 中看到了正确的值。但我无法在它之外更新字典
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-17
  • 1970-01-01
  • 1970-01-01
  • 2018-02-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多