我正在尝试在我的 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”。
有没有人对我做错了什么提出建议?
最佳答案
技术上 @lubilis 已经回答了,但我无法将其放入评论中,所以请多多包涵。
这是你的viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
self.getBackendURLs { json in
backendURLs = self.extractJSON(JSON: json)
print(backendURLs)
}
print(backendURLs)
}
接下来会发生什么:
viewDidLoad
被调用,backendURLs
为 nil- 您调用
getBackendURLs
,它在后台某处的另一个线程上启动。 - 立即 之后你的代码继续到外部
print(backendURLs)
,它打印 nil 因为backendURLs
仍然是 nil 因为你的回调有尚未被调用,因为getBackendURLs
仍在另一个线程上工作。 - 稍后您的
getBackendURLs
完成检索数据和解析并执行此行completion(json)
- 现在你的回调是用数组执行的,你的内部
print(backendURLs)
被调用...并且backendURLs
现在有一个值.
要解决您的问题,您需要在回调方法中刷新数据。
如果它是一个 UITableView
,您可以执行一个 reloadData()
调用,或者编写一个方法来为您更新 UI。重要的部分是您更新回调内部的 UI,因为在那之前您没有有效值。
更新
在您对此答案的评论中,您说:
i need to access the variable backendURLs right after the completionHandler
为此,您可以创建一个新方法:
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
实际完成并准备好调用其回调方法后,它们才准备就绪。
希望这是有道理的。
关于ios - Swift URLSession 完成处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40545904/