2
votes

I am in the process of implementing a REST API with Swift. Of course, part of this API is using HTTP requests to retrieve and send data.

Full disclosure, I am inexperienced with Swift and am using this as a learning project to get my feet wet, so to speak. But it's turned into much more of a difficult project than I anticipated.

In implementing the first get method, I have (finally) gotten rid of all the compilation errors. However, when I call the function which utilizes the URLRequest, URLSession, dataTask, etc, it is never entered.

Upon debugging the program, I can watch the program execution reach the CompletionHandler, and skip over it right to "task.resume()."

A similar construction works in a Swift Playground, but does not work in the actual project proper.

So far I have tried a few things, namely making the function access a class instance variable, in hopes that that would force it to execute. But it does not.

I think the issue may be dealing with synchronicity, and perhaps I need to use a Semaphore, but I want to make sure I'm not missing anything obvious first.

import Foundation
/**
 A class to wrap all GET and POST requests, to avoid the necessity of repeatedly writing request code in each API method.
 */
class BasicRequest {
    private var url: URL
    private var header: [String: String]
    private var responseType: String

    private var jsonResponse: Any?


    init(url: URL, header: [String: String], responseType: String) {
        self.url = url
        self.header = header
        self.responseType = responseType
    } //END INIT

    public func requestJSON() -> Any {
        // Create the URLRequest object, and fill the header with the header fields as provided.
        var urlRequest = URLRequest(url: self.url)
        for (value, key) in self.header {
            urlRequest.addValue(value, forHTTPHeaderField: key)
        }

        let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
            print("Entered the completion handler")
            if error != nil {
                return
            }
            guard let httpResponse = response as? HTTPURLResponse, 200 == httpResponse.statusCode else {
                print("HTTP Request unsuccessful")
                return
            }
            guard let mime = response?.mimeType, mime == "application/json" else {
                print("Not a JSON response")
                return
            }
            do {
                let json = try JSONSerialization.jsonObject(with: data!, options: [])
                print(json)
                self.jsonResponse = json
            } catch {
                print("Could not transform to JSON")
                return
            }
        }
        task.resume()

        return "Function has returned"
    } //END REQUESTJSON
}

The expected result would be returning a JSON object, however that does not seem to be the case.

With respect to error messages, I get none. The only log I get in the debugger is the boilerplate "process exited with code 0."

To be truthful, I'm at a loss with what is causing this not to work.

1

1 Answers

1
votes

It appears you're writing this in a command-line app. In that case the program is terminating before the URLRequest completes.

I think the issue may be dealing with synchronicity, and perhaps I need to use a Semaphore, but I want to make sure I'm not missing anything obvious first.

Exactly.

The typical tool in Swift is DispatchGroup, which is just a higher-level kind of semaphore. Call dispatchGroup.enter() before starting the request, and all dispatchGroup.leave() at the end of the completion handler. In your calling code, include dispatchGroup.wait() to wait for it. (If that's not clear, I can add code for it, but there are also a lot of SO answers you can find that will demonstrate it.)