Swift URLSession完成处理程序

时间:2016-11-11 10:23:17

标签: 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”。

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

1 个答案:

答案 0 :(得分:1)

从技术上来说,@ ibilis已经回答了,但我不能在评论中说明这一点,所以请耐心等待。

这是您的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),由于您的回调尚未被调用为backendURLs,因此getBackendURLs仍为零。仍然在另一个线程上工作。
  4. 稍后您的getBackendURLs完成检索数据并解析并执行此行completion(json)
  5. 现在您的回调是使用数组执行的,您的内部print(backendURLs)被调用...而backendURLs现在有一个值。
  6. 要解决您的问题,您需要在回调方法中刷新数据。

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

    更新

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

      

    我需要在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实际完成并准备调用其回调方法之前尚未就绪。

    希望这是有道理的。