2

Pretty new to Swift and trying to learn SwiftUI this week. Attempting a GET request from a Joke API and eventually looking to get it to refresh every time you press a button.

I have a feeling I'm defining my structs incorrectly or maybe not decoding the returned data properly. I can print the string of data (as the json dictionary) but not access the values themselves through Swift.

import SwiftUI

struct Response : Codable {
    var joke: [Joke]
}

struct Joke : Codable {
    var setup: String
    var delivery: String
}

struct ContentView : View {
    @State private var joke = [Joke]()

    func loadData() {
        guard let url = URL(string: "https://sv443.net/jokeapi/v2/joke/Dark?type=twopart") else {
            print("Invalid URL")
            return
        }

        URLSession.shared.dataTask(with: url) { data, res, error in
            if let data = data {
                print("decoding...")
                let str = String(data: data, encoding: .utf8)
                print(str!)

                if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
                    print("decoded!")
                    // cannot get this to print
                    print(decodedResponse.joke)

                    // looks good, let's get out of here
                    return
                }
            }
            print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
        }.resume()
    }

    var body : some View {
        Text("test")
        .onAppear(perform: loadData)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
decoding...
{
    "category": "Dark",
    "type": "twopart",
    "setup": "When I was a kid, I made a really big sandcastle with my grandma.",
    "delivery": "Unfortunately that didn't impress anyone at the cremation...",
    "flags": {
        "nsfw": false,
        "religious": false,
        "political": false,
        "racist": false,
        "sexist": false
    },
    "id": 145,
    "error": false
}
Fetch failed: Unknown error

Probably just a dumb mistake I'm not catching but would love for some explanation or even some docs. The only docs/tutorials I've seen are showing how to work with specifically structured JSON responses. The one I'm pulling has a dictionary at root, so I guess that's where the discrepancy lies.

I assume I'd use @escaping for rendering in the UI?

1
  • Don't use try ? - that will hide any decoding error. Use do/try/catch then you can catch any decoding error and print it to see what is going wrong. Commented Jun 11, 2020 at 22:37

1 Answer 1

2

The problem is that you're trying to decode an array:

struct Response : Codable {
    var joke: [Joke]
}

struct Joke : Codable {
    var setup: String
    var delivery: String
}

However the JSON response is just an object.

The solution may be to decode it as an object and not an array:

struct Response : Codable {
    var setup: String
    var delivery: String
}

Or, alternatively, change the decoding part to decode a Joke:

let decodedResponse = try? JSONDecoder().decode(Joke.self, from: data)
Sign up to request clarification or add additional context in comments.

2 Comments

Doesn't his struct model also need to cover all fields of the JSON?
@rs7 You can specify only the fields you need. The synthesised init(from decoder:) implementation will look only for setup and delivery in the Decoder. The other JSON fields will be ignored.

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.