Search code examples
iosswiftxcodeurlsession

URLSession does not call API. Though in Playground it works


So this code works for me in playground but for some reason URLSession.shared.dataTask(... doesn't call my flask API that I'm currently locally running. Any idea on what's wrong? So far I'm only concerned on why it does not enter the do{ in my project but it works properly in playground.

func getWords() -> [Word]{
    var words = [Word]()
    let url = URL(string: self.url)
    let request = URLRequest(url: url!)
    
    let group = DispatchGroup()
    
    print("XD")
    URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
            do {
                print("A")
                if let data = data{
                    print("B")
                    if let decodedResponse = try? JSONDecoder().decode([Word].self, from: data){
                        group.enter()
                        DispatchQueue.main.async(){
                            words = decodedResponse
                            print("C")
                            print(words)
                            group.leave()
                        }
                    }
                }
                print("DD")
            } catch {
                print("Words.swift Error in try catch")
            }
    group.enter()
    }).resume()
    group.leave()

    group.notify(queue: DispatchQueue.main, execute: {
           print(words)
         })
    print("ASDASD WORDS: \(words)")
    
    for _ in 1 ... 4 {
        // - to make sure there aren't duplicates -
        
        var wordId:Int = Int.random(in: 0..<words.count)
        while randomIds.contains(wordId){
            wordId = Int.random(in: 0..<words.count)
        }
        randomIds.append(wordId)
    }
    //returns 4 words
    return words
}

Solution

  • You aren't using DispatchGroup correctly; You should call enter before you start the asynchronous work and leave once it is complete. You can then use notify to perform some operation.

    However, you don't really need a DispatchGroup in this situation; You have that because you are trying to turn an asynchronous operation into a synchronous one;

    The correct approach is to accept that the operation is asynchronous and it isn't possible for this function to return [Word]. You will need to refactor the function to accept a completion handler closure and invoke that with the result.

    Something like this:

    func getWords(completionHandler:@escaping (Result<[Word], Error>) -> Void)  -> Void{
        var words = [Word]()
        let url = URL(string: self.url)
        let request = URLRequest(url: url!)  // Note you should use a guard and call the completion handler with an error if url is `nil`
        
        URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
            if let error = error {
                completionHandler(.failure(error))
            } else {
                do {
                    if let data = data {
                       let words = try JSONDecoder().decode([Word].self, from: data)
                       completionHandler(.success(words))
                    } else {
                       // TODO call completionHander with a .failure(SomeError)
                    }
                } catch {
                    completionHandler(.failure(error))
                }
            }
        }).resume() 
    }
    

    Then you can call it:

    getWords() { result in 
        switch result {
        case .success(let words):
            print(words)
        case .failure(let error):
            print(error.localizedDescription)
        }
    }