JSON data serialization is one of the biggest challenges in Swift development. Swift is type-safe, which makes it difficult to handle weak types like JSON. One of the new features Swift 4 brings is the Codable protocol.
However, Codable didn’t fully meet our requirements, including no automatic conversion of types and unfriendly default support. So, if we solve these problems, won’t it be perfect
Codable pit point 1: Type conversion is not supported
// JSON: { "uid":"123456", "name":"Harry", "age":10 } // Model: struct Dog: Codable{ var uid: Int var name: String? var age: Int? } Duplicate codeCopy the code
In the json conversion process, we often encounter situations where the type model is inconsistent with the type of JSON, such as the uid field above. Uid in JSON is String, but our model is Int. Since Swift is type-safe, the conversion will not succeed.
Codable pit point 2: Default values are not supported
Without further ado, go to the code
struct Activity: Codable { enum Status: Int {case start = 1// Case processing = 2// Case end = 3// Case end} var name: String var status: statusCopy the code
If you are interviewing for a job, or are looking to move on, take a look at my carefully summarized interview materials: gitee.com/Mcci7/i-ose… To obtain a detailed interview information for your job hopping salary more than a guarantee
There’s an activity, there’s an activity in three different states, and so far, so good. One day, it suddenly said that the activity needs to add the removed status, what?
//JSON {"name": "New Year's day activity ", "status": 4}Copy the code
Using the Activity to parse the JSON file above will result in an error. How can we avoid it, as shown below
var status: Status?
Copy the code
The answer is no, no, no, because the decoding of the optional value says’ set to nil if it doesn’t exist ‘, not ‘set to nil if decoding fails’.
The solution
Is there a better way to deal with both of these problems? The answer is to use property Wrapper. See ObjMapper for the code, and here’s a brief description of how to use it.
Conversion between Model and JSON
// JSON: { "uid":888888, "name":"Tom", "age":10 } // Model: struct Dog: Codable{// If the field is not an optional type, use Default, providing a Default value, like the following @default < int.zero > var uid: Int // If it is an optional type, use Backed @var name: String? @Backed var age: Int? } //JSON to model let dog = Dog.decodeJSON(from: json) //model to json let json = dog.jsonStringCopy the code
When the object type in the JSON/Dictionary is inconsistent with the Model property, ObjMapper will perform the following automatic conversion. Values that are not supported by automatic conversion will be set to nil or default.
JSON/Dictionary | Model |
---|---|
String | String, Number (integer, floating point), Bool |
Number type (integer, floating point) | Number type, String, Bool |
Bool | Bool, String, Number (including integer, floating point) |
nil | nil,0 |
The Model of nested
{" author ": {" id" : 888888, "name" : "Alex", "age" : "10"}, "title" : "model with json turn", "the subTitle" : "how elegant transformation"} / / model: struct Author: Codable{ @Default<Int.Zero> var uid: Int @Default<String.Empty> var name: String // Backed, if the types do not match, the type is converted @backed Var age: Int? } struct Article: Codable {// if the title is nil or not in json, the title is set to a Default @default < string.empty > var title: String var subTitle: String? var author: Author } //JSON to model let article = Article.decodeJSON(from: json) //model to json let json = article.jsonStringCopy the code
The default value for type
Struct Activity: Codable {///Step 1: make Status follow the DefaultValue protocol enum Status: Int, Codable, DefaultValue {case start = 1 case processing = 2 case end = 3 case unknown = 0// DefaultValue, Static let DefaultValue = status.unknown} @default <String.Empty> var name: static let DefaultValue = status.unknown} @default <String.Empty> var name: static let DefaultValue = status.unknown} @default <String. String ///Step 3: use default@default <Status> var Status: Status// active Status} // support for optional values let json = """ {"name": DecodeJSON (from: json) decodeJSON(from: json)! /// Print ("json: \(activity.jsonString??)") /// Print ("json: \(activity.jsonString?? "")")Copy the code
Set different defaults for common types
ObjMapper already has a lot of default values built in, such as Int.Zero, Bool.True, string.empty… If we want to set different defaults for fields, see the following code:
public extension Int { enum One: DefaultValue { public static let defaultValue = 1 } } struct Dog: Codable{ @Backed var name: String? @default <Int.One> var age: Int} @default <Int.One> var age: Int}Copy the code
Reference documentation
- Fast JSON parsing with the Codable protocol
- Swift 4 Tap holes for Codable protocols
- Set defaults for Codable decoding using Property Wrapper