0

我正在尝试在我的 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”。

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

4

1 回答 1

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),它打印 nilbackendURLs仍然是 nil 因为你的回调还没有被调用,因为getBackendURLs它仍在另一个线程上工作。
  4. 稍后,您getBackendURLs完成检索数据和解析并执行此行completion(json)
  5. 现在你的回调是用数组执行的,你的内部print(backendURLs)被调用了……backendURLs现在有了一个值。

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

如果是这样,UITableView您可以reloadData()拨打电话,或者编写一个为您处理更新 UI 的方法。重要的部分是您回调中更新 UI,因为在此之前您没有有效值。

更新

在您对此答案的评论中,您说:

我需要在完成处理程序之后立即访问变量 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实际完成并准备好调用其回调方法时才准备好。

希望这是有道理的。

于 2016-11-11T10:49:14.943 回答