-2

I have a JSON response from my api that returns this:

[
    {
        "id": 1,
        "chapter": 5,
        "amount": 28,
        "texts": [
            {
                "lyric": "lorem ipsum",
                "number": 1
            },
            {
                "lyric": "lorem foo bar",
                "number": 2
            }
        ],
        "book": 1
    }
]

I tried

struct Chapter: Decodable, Identifiable {
    var id: Int
    var chapter: Int
    var amount: Int

    struct Lyrics: Codable {
         var lyricText: String
         var lyricNumber: Int
     }

    enum Codingkeys: String, CodingKey {
        case lyricText = "lyric"
        case lyricNumber = "number"
    }
}

But I get the following error upon making the call


dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))

My API call looks like this:

...
    @Published var chapters = [Chapter]()
    func fetchBookDetails() {
        if let url = URL(string: url) {
            let session = URLSession(configuration: .default)
            let task = session.dataTask(with: url) { (data, response, error) in
                if error == nil {
                    if let safeData = data {
                        do {
                            
                            let response = try JSONDecoder().decode([Chapter].self, from: safeData)
                            DispatchQueue.main.async {
                                self.chapters = response
                            }
                        } catch {
                            print(error)
                        }
                        
                    }
                }
            }
            task.resume()
        }
    }

The struct looks fine I guess, but the api call is complaining - any idea what it could be? Or is it the struct that is done incorrectly

erikvm
  • 858
  • 10
  • 30

1 Answers1

0

texts is a sub structure (an array of properties), so you need to define a second container for it, for example

struct Text: Codable {
    let lyric: String
    let number: Int
}

Then you can update Chapter to reference the sub structure something like...

struct Chapter: Decodable {
    let id: Int
    let chapter: Int
    let amount: Int
    let book: Int
    
    let texts: [Text]
}

And finally, load it...

let chapters = try JSONDecoder().decode([Chapter].self, from: jsonData)

But what about the error message?

dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))

Oh, right, but the error message is telling there is something wrong with what you've downloaded. I like to, in these cases, convert the data to String and print it if possible, that way, you know what is been returned to you.

For example:

let actualText = String(data: safeData, encoding: .utf8)

The print this and see what you're actually getting


The Playground test code

import UIKit

let jsonText = """
[
    {
        "id": 1,
        "chapter": 5,
        "amount": 28,
        "texts": [
            {
                "lyric": "lorem ipsum",
                "number": 1
            },
            {
                "lyric": "lorem foo bar",
                "number": 2
            },
        ],
        "book": 1
    }
]
"""

struct Text: Codable {
    let lyric: String
    let number: Int
}

struct Chapter: Decodable {
    let id: Int
    let chapter: Int
    let amount: Int
    let book: Int
    
    let texts: [Text]
}

let jsonData = jsonText.data(using: .utf8)!
do {
    let chapters = try JSONDecoder().decode([Chapter].self, from: jsonData)
} catch let error {
    error
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Few minor things: 1) Converting to data is usually easier with `Data(jsonText.utf8)`. 2) In do-try-catch, `let error` isn't needed as it automatically 'creates' an `error` variable to use. 3) You should probably print out that error with `print(error)`. – George Sep 13 '21 at 22:20
  • @George 2) Leaves no doubt and removes confusion on "where did that come from?"; 3) It's a playground, so I'm printing in the right bar directly :P – MadProgrammer Sep 13 '21 at 22:24
  • Thanks a ton for your help, the typo was in my endpoint - wrote bok instead of book – erikvm Sep 13 '21 at 22:25