Convert JSON to model value in Swift

In my quest to learn more about Swift, I am looking for ways to improve my application and have noticed several places where I make assumptions, where maybe I shouldn't be.

When creating a new object, say, β€œstudent,” they need things like name ( String ), age ( Int ), and grade ( Float ). I read them from a JSON file and put them in an object like this:

 // note, details is a [String:Any] type let name = details["name"] as! String let age = details["age"] as! Int let score = Float(details["score"]) self.student = Student(name: name, tutor_group: tutor_group, score: score) 

So my questions are: 1. How can I change my code to verify that if the value is not a number, where it should be, the variable just becomes nil or even better than 0? 2. What if the key in the dictionary does not exist? 3. Are there different ways to do this, and if so, what is best?

Note that I want this code to be as short as possible - if / else for each line is not what I'm looking for.

Thank you so much in advance!

+6
source share
3 answers

Solution proposed by the Swift team

Apple recently described a suggested way to solve this problem.

You can define a Student structure (or class) this way

 struct Student { let name: String let age: Int let score: Float init?(json: [String:Any]) { guard let name = json["name"] as? String, let age = json["age"] as? Int, let score = json["score"] as? Float else { return nil } self.name = name self.age = age self.score = score } } 

The advantages of this approach

  • You encapsulate the logic for converting JSON to Student inside the Student structure itself
  • If the JSON does not contain valid data (for example, there is no age field with a valid Int ), then the initializer fails and returns nil . This means that there is no way to create a Student with missing fields (which is good), and this scenario will not crash.

More details

The post linked above also describes a more advanced approach when a missing field / value throws an exception. However, if you do not need to register (or notify the caller) why you do not need an initialization failure.

+8
source

So my questions are: 1. How can I change my code to verify that if the value is not a number, where it should be, the variable only becomes zero or even better than 0? 2. What if the key in the dictionary does not exist? 3. Are there different ways to do this, and if so, what is best?

 let age = (details["age"] as? Int) ?? 0 
  • In all cases, age will be of type Int
  • If the key does not exist, details["age"] will return nil , as? Int as? Int will return Int? with the value nil , and the union operator nil ?? will set the value to 0 .
  • If the type is not Int , conditional listing as? Int as? Int will return nil and the value will be set to 0 .
  • In the expected case, age will have the value Int , which was stored in details["age"] .

For other fields:

 let name = (details["name"] as? String) ?? "" // If score is stored as a String let score = Float(details["score"] as? String ?? "") ?? 0 

OR

 // If score is stored as a number let score = (details["score"] as? Float) ?? 0 
+4
source

Instead, you can use security:

 guard let name = details["name"] as? String else { return } print("\(name)") 

Thanks!

+1
source

All Articles