Codable is a great protocol available in Swift. It makes it possible to create a type safe JSON representations of structures used within our application with zero boilerplate.
1
2
3
4
5
struct Person : Codable {
let name : String
let email : String
let age : Int
}
Once the structure conforms to Codable
everything works out of the box. There’s a nice way to test those structures and make sure that everything gets the exact JSON format that we aligned with backend.
Let’s create the following protocol in the test bundle:
1
2
3
4
protocol JSONTestable {
init ? ( _ json : String )
func json () -> String ?
}
After that we can immediately provide an extension which conforms to that protocol in the test bundle:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension JSONTestable where Self : Codable {
init ? ( _ json : String ) {
guard
let data = json . data ( using : . utf8 ),
let decoded = try ? JSONDecoder (). decode ( Self . self , from : data )
else { return nil }
self = decoded
}
func json () -> String ? {
guard let data = try ? JSONEncoder (). encode ( self ) else { return nil }
return String ( data : data , encoding : . utf8 )
}
}
Then there’s only one simple step which needs to be done in the test bundle in order to test the structure:
PersonTests.swift
:
1
extension Person : JSONTestable {}
That’s it! Now we can easily test the result of the serialization and deserialization :)
Example tests:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
final class PersonTests : XCTestCase {
func testJsonSerialization () {
let person = Person ( name : "John Appleased" , email : "john@appleased.com" , age : 30 )
XCTAssertEqual ( personJson , person . json ()) // ✅
}
func testJsonDeserialization () {
let person = Person ( name : "John Appleased" , email : "john@appleased.com" , age : 30 )
XCTAssertEqual ( person , Person ( personJson )) // ✅
}
}
private let personJson = """
{ "name" : "John Appleased" , "email" : "john@appleased.com" , "age" : 30 }
"""
extension Person : JSONTestable , Equatable {}
What do you think about this method? Is there anything which could be improved in this implementation? Please let us know :)