我试图在我的Swift项目中调用API。我刚开始实现它,我正试图从调用返回一个快速字典。
但是我想我在完成处理程序上做了一些错误的事情!
我无法从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为零
您可以调用getBackendURLs,它从后台的另一个线程开始。
紧接着,您的代码继续到外部print(backendURLs),它将nil打印为backendURLs仍然为nil,因为您的回调尚未被调用,因为getBackendURLs仍在另一个线程上工作。
稍后,您的getBackendURLs将完成数据检索和解析,并执行此行completion(json)
现在用数组执行回调,并调用内部的print(backendURLs),现在backendURLs有一个值。
要解决问题,需要在回调方法中刷新数据。
如果是UITableView调用,则可以执行reloadData()调用,或者编写一个方法来为您更新UI。重要的部分是更新回调中的UI,因为在此之前没有有效的值。
更新
在你对这个答案的评论中,你说:
我需要在completionHandler之后访问变量backendurl
为此,您可以创建一个新方法:
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实际完成并准备好调用其回调方法之前,它们不会准备就绪。
希望这是有道理的。

09-25 22:22