Swift Failable Initializer with SwiftyJSON

360 Views Asked by At

I’m trying to initialise a simple data model object with some JSON from SwiftyJSON. I’d like the initialiser to fail and return nil if any of the required JSON values aren’t present. Here’s my code:

class ProductCategory: NSObject {

  let id: String
  let sortOrder: Int
  let name: String
  let imageURL: String
  let ranges: [String]

  init?(json: JSON) {
    if let jsonID = json["id"].string,
       jsonSortOrder = json["sortOrder"].int,
       jsonName = json["name"].string,
       jsonImageURL = json["imageURL"].string {
      id = jsonID
      sortOrder = jsonSortOrder
      name = jsonName
      imageURL = jsonImageURL
      ranges = json["ranges"].arrayValue.map { $0.string! }
    } else {
      return nil
    }
  }
}

I’d expect this to work. In the event that we didn’t hit all those json values, simply return nil and bail out. However, I get an error on the return nil, stating:

All stored properties of a class instance must be initialized before returning nil from an initializer.

I’m confused: isn’t the point of a failable initializer that I can bail out without setting it up if something goes wrong? The object returned would be nil, why would there be any value in setting up its properties?

2

There are 2 best solutions below

1
Greg Leszek On BEST ANSWER

Failable Initializers for Classes:

"For classes, however, a failable initializer can trigger an initialization failure only after all stored properties introduced by that class have been set to an initial value and any initializer delegation has taken place."

So

init?(json: JSON) {
    self.id = json["id"].string
    self.sortOrder = json["sortOrder"].int
    ...
    if ... { return nil }
}
0
Luke On

So here’s what I ended up doing – Greg was right but I ended up switching to a struct as a result:

struct ProductCategory {

    let id: String
    let sortOrder: Int
    let name: String
    let imageURL: String
    let ranges: [String]

    init?(json: JSON) {
        guard let jsonID = json["id"].string,
              let jsonSortOrder = json["sortOrder"].int,
              let jsonName = json["name"].string,
              let jsonImageURL = json["image"].string else {
            return nil
        }

        self.id = jsonID
        self.sortOrder = jsonSortOrder
        self.name = jsonName
        self.imageURL = jsonImageURL
        self.ranges = json["ranges"].arrayValue.map { $0.string! }
    }

}