0

I am currently writing an Alamofire HTTP request and am running into an issue where my view is not loading - likely because there is no data. The confusing part is that this was working yesterday. In the request I was able to do print(data) and the result was 506 bytes which, if my calculation is correct, is about the correct size given the JSON payload returned from the endpoint below.

@State var recipes = [Recipe]()

AF.request("http://localhost:3000/recipes").responseJSON { response in
    guard let data = response.data else { return }
    if let response = try? JSONDecoder().decode([Recipe].self, from: data) {
        DispatchQueue.main.async {
            self.recipes = response
        }
        return
    }
}

I can confirm that the endpoint that is being hit returns the following data...

[
   {
      "name":"Manhattan",
      "image":"https://images.unsplash.com/photo-1536935338788-846bb9981813?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=2486&q=80",
      "spirit":"Bourbon",
      "ice":"Crushed",
      "glass":"Coupe",
      "yield":"3.75",
      "description":"This is a good drink. You should make it.",
      "ingredients":[
         {
            "bottle":"High West Son Of Bourye",
            "amount":"2.5"
         },
         {
            "bottle":"Cocchi Vermouth Di Torino",
            "amount":"0.75"
         },
         {
            "bottle":"Simple Syrup",
            "amount":"0.083"
         }
      ]
   }
]

I also have my Recipe and Ingredient model here which should be able to decode based on the above JSON.

struct Recipe: Decodable, Identifiable {
    var id = UUID()
    var name: String
    var image: String
    var spirit: String
    var ice: String
    var glass: String
    var yield: String
    var description: String
    var ingredients: [Ingredient]
}

struct Ingredient: Decodable, Identifiable {
    var id = UUID()
    var bottle: String
    var amount: String
}

Is anybody able to spot an issue? I was trying to put a debugging print in the DispatchQueue but it is not printing which, to me, sounds like an error. However I am new to Swift/XCode/iOS and am not sure the best debugging practices for this.

  • Remove the `try?`, and do a property do/catch. Or even a `try!` to force a crash, but with a message in console explaining why it failed... Also, how did you confirm "I can confirm that the endpoint that is being hit returns the following data..."? You said `print(data) `, instead, do `print(String(data: data, encoding: .utf8)!)` – Larme Aug 31 '21 at 16:11

2 Answers2

1

If you can't debug yourself, NEVER USE try?. With more experience, I'd say that we tend to not use try?, but sometimes we do. But when we write try?, we are able to find an possible issue, ie debug if needed.

Let's do a proper try then, with a do/catch:

do { 
    let response = try JSONDecoder().decode([Recipe].self, from: data
    DispatchQueue.main.async {
        self.recipes = response
    }
} catch {
    print("Oops, there was en error while decoding: \(error)") // and not error.localizedDescription as it's more for users than developpers, so you'll skip all the useful informations
}

And read the output.

Going further?
Don't believe what's the API is supposed to return.
I've seen plenty and plenty of questions where the returned values was an error message, a XML Error message, a JSON Error message, an HTML Error message, and a JSON value missing, or of bad type, etc. And that, your JSONDecoder wasn't expecting it...
Reasons could be various, from bad/missing parameters, bad/missing APIKey, server down, bad/missing header, etc.
But, then, print the returned value.

print(String(data: data, encoding: .utf8) ?? "No data found")

So print it directly when you get it, or at least in the catch:

} catch {
    print("Oops, there was en error while decoding: \(error)") // and not error.localizedDescription as it's more for users than developpers, so you'll skip all the useful informations
    print("While getting response stringified: \(String(data: data, encoding: .utf8) ?? "No data found")")
}

If you don't understand the error message output, it's okay, there is no shame about it. But your first job is to get that error message. You can share it on SO if you don't understand it, you might get help with that. But currently, we can't guess what's wrong with your code.

Larme
  • 24,190
  • 6
  • 51
  • 81
  • Wish I could give bonus points for not only a great answer, but a kind answer. Thanks so much. –  Aug 31 '21 at 16:32
0

It's a good idea to drop some clues in your code when looking for a failure. If it were me I'd do something like this:

AF.request("http://localhost:3000/recipes").responseJSON { response in
    guard let data = response.data else { 
        print("Error trying to receive data in ", #file, #function)
        return
    }


    do {

        let response = try JSONDecoder().decode([Recipe].self, from: data) {
            DispatchQueue.main.async {
                self.recipes = response
            }
    } catch {
         print("Error failed to decode json data with error: \(error) in \(#file)", #function)
    }
}
SeaSpell
  • 678
  • 3
  • 9