0

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?

11
  • Declare your dictionary like this, var backendURLs: [String : String] = [:] Commented Nov 11, 2016 at 10:30
  • Call is asynchronous, need to wait response to get results. For this one of your print method returns nil. Commented Nov 11, 2016 at 10:31
  • @lubilis: thanks for the answer. i know it is asynchronous, but i thought the completionHandler would handle that Commented Nov 11, 2016 at 10:33
  • You say "The first print() statements gives me the correct value, but the second is "nil".", do you mean in viewDidLoad()? and is the first one the one inside your callback and the second one the one outside of the callback? Commented Nov 11, 2016 at 10:37
  • @pbodsk yes, first one inside the callback, second one outside. Commented Nov 11, 2016 at 10:38

1 Answer 1

1

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:

  1. viewDidLoad is called, backendURLs is nil
  2. you call getBackendURLs, which starts on another thread in the background somewhere.
  3. immediately after that your code continues to the outer 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.
  4. At some later point your getBackendURLs finishes retrieving data and parsing and executes this line completion(json)
  5. now your callback is executed with the array and your inner 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.

Update

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you very much for answer! But i am not sure how to update the data in the dictionary.
Is there a reason you have declared backendURLs outside your ViewController?
Isn't your backendURLs updated when you do this? backendURLs = self.extractJSON(JSON: json) print(backendURLs). I mean, don't you see the correct values printed there?
i need to access the variable backendURLs right after the completionHandler (where my second print statement is placed). Some time after that i need it in another class. I tried updating the dictionary through DispatchQueue.main.async { backendURLs = URLs } but that did not work. I see the correct values in the completionHandler. But i am not able to update the dictionary outside of it

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.