I am trying to make an API call in my Swift project. I just started implementing it and i am trying to return a Swift Dictionary from the call.
But I think i am doing something wrong with the completion handler! I am not able to get the returning values out of my API call.
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
}
}
The first print() statements gives me the correct value, but the second is "nil".
Does anyone have a suggestion on what i am doing wrong?
Technically @lubilis has answered but I couldn't fit this inside a comment so please bear with me.
Here's your viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
self.getBackendURLs { json in
backendURLs = self.extractJSON(JSON: json)
print(backendURLs)
}
print(backendURLs)
}
What will happen is the following:
viewDidLoad
is called, backendURLs
is nil getBackendURLs
, which starts on another thread in the background somewhere. print(backendURLs)
, which prints nil as backendURLs
is still nil because your callback has not been called yet as getBackendURLs
is still working on another thread. getBackendURLs
finishes retrieving data and parsing and executes this line completion(json)
print(backendURLs)
is called...and backendURLs
now has a value. To solve your problem you need to refresh your data inside your callback method.
If it is a UITableView
you could do a reloadData()
call, or maybe write a method that handles updating the UI for you. The important part is that you update the UI inside your callback, because you don't have valid values until then.
In your comments to this answer you say:
i need to access the variable backendURLs right after the completionHandler
To do that you could make a new method:
func performWhateverYouNeedToDoAfterCallbackHasCompleted() {
//Now you know that backendURLs has been updated and can work with them
print(backendURLs)
//do what you must
}
In the callback you then send to your self.getBackendURLs
, you invoke that method, and if you want to be sure that it happens on the main thread you do as you have figured out already:
self.getBackendURLs { json in
backendURLs = self.extractJSON(JSON: json)
print(backendURLs)
DispatchQueue.main.async {
self.performWhateverYouNeedToDoAfterCallbackHasCompleted()
}
}
Now your method is called after the callback has completed.
As your getBackendURLs
is an asynchronous method you can not know when it has completed and therefore you cannot expect values you get from getBackedURLs
to be ready straight after calling getBackendURLs
, they are not ready until getBackendURLs
has actually finished and is ready to call its callback method.
Hope that makes sense.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.