Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: Creating Model Class from JSON with Failable Initializer

I'm trying to implement some classes which can be initialized with JSON data [String : AnyObject]. Failable initializers appear to be perfect for this use case, but I can't seem to get the syntax correct (without creating ugly code).

Is it possible to do something like this?:

class Person {
  let firstName: String
  let middleName: String?

  init?(JSONData data: [String : AnyObject]) {
    do {
      try {
        self.firstName = data["firstName"] as! String
        self.middleName = data["middleName"] as? String
      }
    } catch {
      return nil
    }
  }
}
like image 644
user1084447 Avatar asked Mar 13 '23 22:03

user1084447


1 Answers

This is how I usually define model types with a failable initializer.

struct Person {
    let firstName: String
    let middleName: String?

    init?(JSONData data:[String:AnyObject]) {
        guard let firstName = data["firstName"] as? String else { return nil }
        self.firstName = firstName
        self.middleName = data["middleName"] as? String
    }
}

Why a Struct?

As you can see I use a Struct instead of a Class, there are at least 2 big reasons for this:

  • failable initializers work better with structs because you can return nil without having initialized all the stored properties
  • they are faster (because saved on the Stack instead of the Heap)

Why guard let?

I also prefer using the guard let construct because it makes clear that if some mandatory value is not retrieved then the initialisation must fail.

More

My version of the initializer do not require middleName. I decided so because you defined middleName as an optional. However if you want to make middleName mandatory just change Person as shown below

struct Person {
    let firstName: String
    let middleName: String

    init?(JSONData data:[String:AnyObject]) {
        guard let
            firstName = data["firstName"] as? String,
            middleName = data["middleName"] as? String else { return nil }
        self.firstName = firstName
        self.middleName = middleName
    }
}

Wait, Structs are value types! I'm going to waste lots of memory with every copy 😱

Nope!

[...] Swift only performs an actual copy behind the scenes when it is absolutely necessary to do so. Swift manages all value copying to ensure optimal performance, and you should not avoid assignment to try to preempt this optimisation.

The Swift Programming Language

like image 120
Luca Angeletti Avatar answered Apr 27 '23 17:04

Luca Angeletti