There are three different ways to work with JSON (JavaScript Object Notation) in Swift, including using the built-in JSONSerialization class, the Codable protocol, and third-party libraries like SwiftyJSON, ObjectMapper, CodableAlamofire or something like this. We’ll look at each approach.
For example, make a JSON file
{
"ownerName" : "Anna",
"posts" : [
{
"title" : "Async requests",
"isPublished" : false,
"counts" : {
"likes" : 0,
"views" : 0
}
},
{
"title" : "SwiftUI",
"isPublished" : true,
"counts" : {
"likes" : 20,
"views" : 1500
}
}
]
}
and create models for the view layer converted from JSON
struct Owner {
let name: String
let posts: [Post]
}
struct Post {
let title: String
let isPublished: Bool
let counts: Counts
}
struct Counts {
let likes: Int
let views: Int
}
JSONSerialization is a built-in class from Apple for parsing JSON in Swift. It’s available in the Foundation framework.
do {
let jsonDict = try JSONSerialization.jsonObject(with: jsonData)
let model = try makeModelFromSerializing(jsonDict as! [String: Any])
let data = try JSONSerialization.data(withJSONObject: makeJSON(fromModel: model))
print("RESULT Serialization: \(model)")
print("RESULT Convert: \(String(data: data, encoding: .utf8))")
} catch {
print("ERROR: \(error)")
}
Where jsonData - it’s just Data
from JSON file.
First, serialize the JSON data into any object using JSONSerialization.jsonObject
, and if you print that object, you will see that the object is a dictionary [String: Any]
. The next step is to convert this dictionary into our model Owner
. To do this, we will create a utility method makeModelFromSerializing.
func makeModelFromSerializing(_ dict: [String: Any]) throws -> Owner {
func modelPost(_ dict: [String: Any]) throws -> Post {
guard let title = dict["title"] as? String,
let isPublished = dict["isPublished"] as? Bool,
let counts = dict["counts"] as? [String: Any],
let likes = counts["likes"] as? Int,
let views = counts["views"] as? Int
else { throw NSError() }
return Post(
title: title,
isPublished: isPublished,
counts: Counts(likes: likes, views: views)
)
}
guard let ownerName = dict["ownerName"] as? String,
let posts = dict["posts"] as? [[String: Any]]
else { throw NSError() }
return Owner(name: ownerName, posts: try posts.map(modelPost(_:)))
}
And finally, convert from our model Owner
JSON into Data
. For this, convert our Owner
model into a JSON dictionary using the makeJSON
method, and after that, create the data using JSONSerialization.data
.
func makeJSON(fromModel model: Owner) -> [String: Any] {
func makePostJSON(_ model: Post) -> [String: Any] {
[
"title": model.title,
"isPublished": model.isPublished,
"counts": ["likes": model.counts.likes, "views": model.counts.views]
]
}
return ["ownerName": model.name, "posts": model.posts.map(makePostJSON(_:))]
}
Pros:
Cons:
An encoded protocol was introduced in Swift 4. With this one, you can easily convert between JSON and Swift types. You define a struct or class that conforms to Codable and use JSONDecoder or JSONEncoder to encode or decode JSON.
Firstly make DTO (Data Transfer Object)
struct OwnerDTO: Codable {
let ownerName: String
let posts: [PostDTO]
}
extension OwnerDTO {
var convert: Owner {
Owner(name: ownerName, posts: posts.map(\.convert))
}
init(model: Owner) {
self.ownerName = model.name
self.posts = model.posts.map(PostDTO.init(model:))
}
}
struct PostDTO: Codable {
let title: String
let isPublished: Bool
let counts: CountsDTO
}
extension PostDTO {
var convert: Post {
Post(title: title, isPublished: isPublished, counts: counts.convert)
}
init(model: Post) {
self.title = model.title
self.isPublished = model.isPublished
self.counts = CountsDTO(model: model.counts)
}
}
struct CountsDTO: Codable {
let likes: Int
let views: Int
}
extension CountsDTO {
var convert: Counts {
Counts(likes: likes, views: views)
}
init(model: Counts) {
self.likes = model.likes
self.views = model.views
}
}
This DTO’s conforms Codable
(which is a type alias for the two protocols: Decodeable and Encodable) and creates a computed property convert that transforms our DTO into a Model for the view layer and a custom initialization that helps transform the model from the view layer into a DTO.
do {
let dto = try JSONDecoder().decode(OwnerDTO.self, from: jsonData)
let model = dto.convert
let json = try JSONEncoder().encode(OwnerDTO(model: model))
print("RESULT Decodable: \(dto)")
print("RESULT Model: \(model)")
print("RESULT Encodable: \(String(data: json, encoding: .utf8))")
} catch {
print("ERROR: \(error)")
}
To convert JSON data to a DTO model, use JSONDecoder().decode
, to convert the DTO model to JSON data - JSONEncoder().encode
Pros:
Cons:
There are several third-party libraries available for JSON parsing in Swift, such as SwiftyJSON, ObjectMapper, or CodableAlamofire. These libraries often provide more flexibility and convenience compared to the built-in solutions.
Let's look at a SwiftyJSON example:
do {
let json = try JSON(data: jsonData)
let model = makeModel(json: json)
let data = try JSON(makeJSON(fromModel: model)).rawData()
print("RESULT Third Party Lib: \(json)")
print("RESULT Third Party Lib Model: \(model)")
print("RESULT Third Party Lib JSON: \(String(data: data, encoding: .utf8))")
} catch {
print("ERROR: \(error)")
}
To make model for the view layer using the method makeModel.
func makeModel(json: JSON) -> Owner {
func makePost(json: JSON) -> Post {
Post(
title: json["title"].stringValue,
isPublished: json["isPublished"].boolValue,
counts: Counts(likes: json["counts"]["likes"].intValue, views: json["counts"]["views"].intValue)
)
}
return Owner(name: json["ownerName"].stringValue, posts: json["posts"].arrayValue.map(makePost(json:)))
}
And to convert the model from the view layer into dictionary using the method makeJSON
from 1 point
Pros:
Cons:
Each of these methods has its pros and cons. JSONSerialization is lightweight and built-in, Codable is type-safe and easy to use, while third-party libraries have more advanced features and convenience. Which one you choose depends on the specific requirements and preferences of your project.
All examples in this repository: https://github.com/Ze8c/SerializeJSON