Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do-catch error in init(from decoder:Decoder) from a Codable struct?

let jsonString = """
                    {
                        "name":1,
                        "gender":"male",
                    }
                    """

struct Person: Codable {
    var name: String
    var gender: String

    public init(from decoder: Decoder) throws {
        do {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            name = try container.decode(String.self, forKey: .name)
            gender = try container.decode(String.self, forKey: .gender)
        } catch {
            print("XXXXXX \(error)")
        }
    }
}

From the code above, it won't compile because it complains,

Return from initializer without initializing all stored properties.

I want to throw some errors but how do I do that without,

  1. making default value for every properties.
  2. making them all optional.
like image 246
Tony Lin Avatar asked Nov 29 '25 11:11

Tony Lin


1 Answers

You don't need a do-catch in init(from decoder: Decoder) because it is already marked as throws. So just do:

public init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    name = try container.decode(String.self, forKey: .name)
    gender = try container.decode(String.self, forKey: .gender)
}

Whatever does the decoding can use a do-catch to see any exception thrown inside the above init(from:) method, like in the following example:

struct Person: Codable {
    var name: String
    var gender: String

    // Note: This is not a very good example because this init method
    // is not even necessary in this case, since the automatically-
    // synthesized `init(from:)` method does exactly the same thing. I've
    // left it here to illustrate that you don't need to have a `do-catch`
    // in this method and can instead just use `try`, since the method
    // is marked as `throws`.
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        gender = try container.decode(String.self, forKey: .gender)
    }
}

class PersonDecoder {
    func person(decodedFrom data: Data) -> Person? {
        do {
            // The `JSONDecoder.decode(_:from:)` method calls
            // `Person.init(from:)`, which can throw, which is why
            // `JSONDecoder.decode(_:from:)` also throws.
            let person = try JSONDecoder().decode(Person.self, from: data)
            return person
        } catch {
            // Inspect any thrown errors here.
            print(error)

            return nil
        }
    }
}

let personData = Data("""
{
    "name": 1,
    "gender": "male"
}
""".utf8)

let personDecoder = PersonDecoder()

// Prints: "The data couldn’t be read because it isn’t in the correct format."
// and `person` is nil.
let person = personDecoder.person(decodedFrom: personData)
like image 93
TylerP Avatar answered Dec 02 '25 01:12

TylerP



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!