Metzler
Metzler

Reputation: 15

Making a GET request with swift 4 works in playgrounds but not in project

Using Swift 4 and Xcode 10

I am trying to make a GET request to an API and get the results in json, and my code works perfectly in my playground, but when I copy it over to my app, I get a "Program ended with exit code: 0" error.

I'd like to make this a function I can call and change up the headers, httpMethod, credentials, actionURL, etc.. so I can reuse it for different calls to this API.

This is my first shot at this, and have been searching all over.

1) This is where I borrowed most of the code for this part of the project. Error parsing JSON in swift and loop in array

2) I tried using the advice in this video to build a struct for the data. https://www.youtube.com/watch?v=WwT2EyAVLmI&t=105s

Not sure if it is something on the swift side, or the xcode side...

import Foundation
import Cocoa
// removed in project, works in playgrounds
//import PlaygroundSupport

func makeGetCall() {
    // Set up the URL request
    let baseURL: String = "https://ws.example.net/v1/action"
    guard let url = URL(string: baseURL) else {
        print("Error: cannot create URL")
        return
    }

    // set up the session
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)

    // set up auth
    let token = "MYTOKEN"
    let key = "MYKEY"
    let loginString = String(format: "%@:%@", token, key)
    let loginData = loginString.data(using: String.Encoding.utf8)?.base64EncodedString()


    // make the request
    var request = URLRequest(url: url)
    request.httpMethod = "GET"
    request.setValue("application/json", forHTTPHeaderField: "Accept")
    request.addValue("Basic \(loginData!)", forHTTPHeaderField: "Authorization")

    let task = session.dataTask(with: request) {
        (data, response, error) in
        // check for any errors
        guard error == nil else {
            print("error calling GET")
            print(error!)
            return
        }
        // make sure we got data
        guard let responseData = data else {
            print("Error: did not receive data")
            return
        }
        // parse the result as JSON, since that's what the API provides
        do {
            guard let apiResponse = try JSONSerialization.jsonObject(with: responseData, options: [])
                as? [String: Any] else {
                    print("error trying to convert data to JSON")
                    return
            }
            // let's just print it to prove we can access it
            print(apiResponse)

            // the apiResponse object is a dictionary
            // so we just access the title using the "title" key
            // so check for a title and print it if we have one
            //guard let todoTitle = todo["title"] as? String else {
            //    print("Could not get todo title from JSON")
            //    return
            //}
            //print("The title is: " + todoTitle)
        } catch  {
            print("error trying to convert data to JSON")
            return
        }
    }
    task.resume()
}

makeGetCall()
// removed in project, works in playgrounds
//PlaygroundPage.current.needsIndefiniteExecution = true

In playgrounds I receive the json response as expected, but when I copy the code over to my project I receive an error.

Expected output example:

["facet": <__NSArrayI 0x7fb4305d1b40>(
asnNumber,
assetPriority,
assetType,
etc...

Upvotes: 1

Views: 1114

Answers (1)

matt
matt

Reputation: 535557

"Program ended with exit code: 0" is not an error; it just means the program came to an end. In fact, it is the opposite of an error, since it means the program came to an end in good order.

So, what kind of project template did you use? I'm guessing you used a Cocoa command-line tool. In that case, the problem is that simply you have no runloop, so we reach the last line and come to an end before any asynchronous stuff (e.g. networking) can take place.

This is exactly parallel to what you had to do in your playground. You couldn't network asynchronously without needsIndefiniteExecution; the playground needs to keep running after it has reached the last line, to give the networking a chance to happen. In the same way, you need the runloop in the command-line tool to keep going after it has reached the last line, to give the networking a chance to happen.

For how to give your command-line tool a runloop, see for example Run NSRunLoop in a Cocoa command-line program

Alternatively, don't use a command-line tool. Make an actual app. Now the app persists (until you quit it), and asynchronous networking just works.

Upvotes: 1

Related Questions