ios - Swift URLSession 完成处理程序

标签 ios swift urlsession

我正在尝试在我的 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)
}

接下来会发生什么:

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

相关文章:

swift - 从 XCUITest 中的 URLSession 获取输出

json - URLSession 错误

java - 通过套接字或回调的连续服务器客户端通信

ios - 关闭我使用 root 呈现的 ViewC Controller

ios - UIImage 在滚动之前不会显示在 TableView 单元格中

ios - 使用自动调整大小的单元格和不可滚动的表格时调整 tableView 高度

Swift - 合并订阅未被调用

ios - 基于 UITableView Edit 重新排序 NSArray

ios - 如何以编程方式创建自定义导航栏?

swift - Swift 2.0 do-try-catch 和常规 Java/C#/C++ 异常有什么区别