Let’s play with swift-Part 6

Encoding Decoding, Memory Management

Rejaul Hasan
8 min readSep 7, 2021

You can find better explanation in Swift Apprentice. So go through it if you want to.

JSONEncoder and JSONDecoder

struct Employee: Codable{
let name: String
let id: Int
var favoriteToy: Toy?
}
struct Toy: Codable{
let name: String
}
let toy = Toy(name: "Simple Toy")
let employee = Employee(name: "RX", id: 6, favoriteToy: toy)
let coder = JSONEncoder()
let data = try coder.encode(employee)
print(data)
let stringData = String(data: data, encoding: .utf8)
print(stringData!)

The name type should confirm Codable protocol to encode or decode data.

Renaming properties with CodingKeys

struct Employee: Codable{
let name: String
let id: Int
var favoriteToy: Toy?
enum CodingKeys: String, CodingKey {
case id = "employeeId"
case name
case favoriteToy
}
}

The output is

{"name":"RX","favoriteToy":{"name":"Simple Toy"},"employeeId":6}

Manual encoding and decoding

The encode function

What about making our json like billow.

{ "employeeId": 6, "name": "RX", "gift": "Simple Toy" }

How we can achieve it? We have to write our own encoder. Codable is nothing but a typealias.

public typealias Codable = Decodable & Encodable

So let’s first change our coding key which is used as json key.

struct Employee{var name: String
var id: Int
var favoriteToy: Toy?
enum CodingKeys: String, CodingKey {
case id = "employeeId"
case name
case gift
}
}

Here we use gift as our coding key instead of favoriteToy. Now we need to confirm Encodable protocol for our Employee struct to be able to encode it. Let’s make an extension.

extension Employee: Encodable{
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(id, forKey: .id)
try container.encode(favoriteToy?.name, forKey: .gift)
}
}

here we implement encode() function of Encodable protocol. We take our define CodingKeys as container and then do our encoding with related values for every case of our CodingKeys. The output is.

let toy = Toy(name: "Simple Toy")
let employee = Employee(name: "RX", id: 6, favoriteToy: toy)
let coder = JSONEncoder()
let data = try coder.encode(employee)
let stringData = String(data: data, encoding: .utf8)
print(stringData!) //{"name":"RX","gift":"Simple Toy","employeeId":6}

The decode function

let’s assume we get json like this.

{"name":"RX","gift":"Simple Toy","employeeId":6}

and we want to make our Employee instant from it. So we need to confirm Decoder this time.

extension Employee: Decodable{init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(Int.self, forKey: .id)
name = try values.decode(String.self, forKey: .name)
if let gift = try values.decode(String?.self, forKey: .gift){
favoriteToy = Toy(name: gift)
}
}
}

encodeIfPresent and decodeIfPresent

It turns out not all employees have a favorite toy. In this case, the encode method will create a JSON that looks like this:

{"name":"John Appleseed","gift":null,"employeeId":7}

In order to fix this, you can use encodeIfPresent so the encode method will look like this.

extension Employee: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(id, forKey: .id)
try container.encodeIfPresent(favoriteToy?.name,forKey: .gift)
}
}

Now the JSON won’t contain a gift key if the employee doesn’t have a favorite toy.

extension Employee: Decodable {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: .name)
id = try values.decode(Int.self, forKey: .id)
if let gift = try values.decodeIfPresent(String.self, forKey: .gift) {
favoriteToy = Toy(name: gift)
}
}
}

Writing tests for the Encoder and Decoder

Here just provide a small example. I will talk about testing letter in several post. Let’s see the code:

class EncoderDecoderTests: XCTestCase {
var jsonEncoder: JSONEncoder!
var jsonDecoder: JSONDecoder!
var toy1: Toy!
var employee1: Employee!
override func setUp() {
super.setUp()
jsonEncoder = JSONEncoder()
jsonDecoder = JSONDecoder()
toy1 = Toy(name: "Teddy Bear")
employee1 = Employee(name: "John Appleseed", id: 7,favoriteToy: toy1)
}
func testEncoder() {
let jsonData = try? jsonEncoder.encode(employee1)
XCTAssertNotNil(jsonData, "Encoding failed")
let jsonString = String(data: jsonData!, encoding: .utf8)!
XCTAssertEqual(jsonString, "{\"name\":\"John Appleseed\",
\"gift\":\"Teddy Bear\",\"employeeId\":7}")
}
func testDecoder() {
let jsonData = try! jsonEncoder.encode(employee1)
let employee2 = try? jsonDecoder.decode(Employee.self, from:jsonData)
XCTAssertNotNil(employee2)
XCTAssertEqual(employee1.name, employee2!.name)
XCTAssertEqual(employee1.id, employee2!.id)
XCTAssertEqual(employee1.favoriteToy?.name, employee2!.favoriteToy?.name)
}
}

Memory Management

Reference cycles

We have a Tutorial class and it has a property name editor which is an object of Editor class. In real scenario one editor is responsible to looks after a tutorial before publish.

class Tutorial {let title: String
var editor: Editor?
init(title: String) {
self.title = title
}
deinit {
print(“Good by tutorial \(title)”)
}
}
class Editor{
let name: String
var tutorials:[Tutorial] = []
init(name: String) {
self.name = name
}
deinit {
print(“Good by Editor \(name)!”)
}
}
do {
let tutorial = Tutorial(title: “Memory management”)
let editor = Editor(name: “Ray”)
tutorial.editor = editor
editor.tutorials.append(tutorial)
}

Here we declare our tutorial and editor within a scope. So when pass through the scope compiler should deinit the objects but nothing happen. Because both class object have property of another class object. So reference cycle happen.

Weak

Using weak we can break the retain cycle. Weak variable do not increase the automatic reference count(ARC) and it also look after it’s current reference count. So when it found 0 in ARC it will automatically make it nil.

You can’t define a weak reference to be a constant because it will change to nil during runtime when the underlying object goes away.

So the code should be

class Tutorial {
let title: String
weak var editor: Editor?
init(title: String) {
self.title = title
}
deinit {
print(“Good by tutorial \(title)”)
}
}

Unowned Reference

Unowned also behave like weak. It do not increase ARC count but the small difference is that it can not be an optional. It always needs a value.

So earlier we talk about Tutorial class and Editor class. Now let’s add an Author class. Every Tutorial must need an Author because without him who written the article or Tutorial. So in Tutorial class we have to add property of Author and it can not be optional. So here we can not use weak. We have to use Unowned then because it always need value.

class Tutorial {
let title: String
weak var editor: Editor?
unowned let author: Author
init(title: String, author: Author) {
self.title = title
self.author = author
}
deinit {
print(“Good by tutorial \(title)”)
}
}

and here is the Author class

class Author{
let name: String
var tutorials:[Tutorial] = []
init(name: String) {
self.name = name
}
deinit {
print(“Good by Author \(name)!”)
}
}

Closure

little fact on using lazy:

class Test{
var count = 0
var printCount = {print(self.count)}
}

makes an error. “Cannot find ‘self’ in scope or self is used before initialization”. But making this variable lazy makes it run perfectly because as we know lazy initially do not run it’s code. It will run when you use them first time.

NOTE:- CLOSURE ARE REFERENCE TYPE

Whenever you assign a function or a closure to a constant or a variable, you are actually setting that constant or variable to be a reference to the function or closure.

Note:- closures extend the lifetime of any object they use to guarantee those objects are alive and valid.

So what we get from this 2 note that closure itself is a reference and whatever value or object we use inside the closure it’s ARC will increase by 1 as closure guarantee that those objects are alive and valid.

So what about this code

class Test{
var count = 0
lazy var printCount = {print(self.count)}
deinit {
print(“destroy object”)
}
}
do{
let test = Test()
test.printCount()
print(test.printCount)
}

Here deinit will not call because when we create test object it increase arc by 1. But we use closure as a property. Inside closure we use self so as closure ensure the object exist so it increase ARC. Now the arc = 2. When we finish the do scope we reduce our arc to 1. So now arc = 1 that is why the deinit will not call.

What you think about this code

class Test{
var count = 0
lazy var printCount = {print(“ok”)}
deinit {
print(“destroy object”)
}
}
do{
let test = Test()
test.printCount()
}

Yes, this time the deinit call and you will see destroy object in the console. because this time you do not use self inside closure. So the object arc will remain 1 inside the do scope and when you pass the do scope it decrease it’s value into 0.

Capture List

Capture lists are a language feature to help you control exactly how a closure extends the lifetime of instances it references. Capture lists are a list of variables captured by a closure and appears at the beginning of the closure before any arguments.

What will be the output of this code.

var count = 0
let f = {
print(count)
count = 10
}
count = 1
f()
print(count)

The output is 1 and 10. So the count inside the closure pass as an immutable reference type. If it’s immutable then how we change it’s value? Well, we do not change it’s reference and the reference or address is immutable, not the value inside it. That’s why we can change it’s value.

var count = 0
let f = {[count] in
print(count)
//count = 10
}
count = 1
f()

what do you think will be the output?

The output is 0. Here we use capture list at the beginning of the closure. The capture list creates an immutable local variable name count(you can use any name you want) and copy the value of global count into it. So when you change global count variable latter the closure will not get the update. So it print 0. Is the local count inside closure immutable? Yes, it is. Try to remove comment from count = 10 line. You will get an error.

Now let’s break the retain cycle of closures:

our old code

class Test{
var count = 0
lazy var printCount = {print(self.count)}
deinit {
print(“distroy object”)
}
}
do{
let test = Test()
test.printCount()
}

Unowned self

So, here we can use capture list and make one unowned self inside the closure. Using unowned prevent object to increase it’s ARC. So the replace code will be like.

lazy var printCount = { [unowned self] in
print(self.count)
}

this time deinit will call.

Weak self

There are certain times when you can’t capture self as an unowned reference, because it might become nil. Consider the following example:

class Test{
var count = 0
lazy var printCount = { [unowned self] in
print(self.count)
}
deinit {
print(“distroy object”)
}
}
let closureHolder:()-> Void
do{
let test = Test()
test.printCount()
closureHolder = test.printCount
}
closureHolder()

This time you will get an error. Because here, when do scope finish we already deallocate our test object but we store or closure into closureHolder and we try call it outside of out do scope. So when we call the closure. The closure will try to find self but it’s already gone. We capture it as unowned self. We know that unowned can not be nil. There has to be a value. So the error happen. Try to see the error message. it explained itself very well.

//Fatal error: Attempted to read an unowned reference but object 0x6000039f9260 was already deallocated

So here we can use weak self instead of unowned self.

lazy var printCount = { [weak self] in
print(self?.count)
}

The output is

Optional(0)
distroy object
nil

We get warning this time as there is no guarantee that you get an instance all the time inside closure. Here you do not get. We can solve it using Weak strong pattern.

Weak strong pattern

The weak-strong pattern (sometimes affectionately called the weak-strong-dance) also does not extend the lifetime of self but converts the weak reference to a strong one after it enters the closure:

lazy var printCount = { [weak self] in
guard let self = self else {
print(“No object”)
return
}
print(self.count)
}

guard makes self strong if it isn’t nil, so it’s guaranteed to live until the end of the closure.

Those who follow or read comment down if you have questions. I am planning to write on testing from iOS Unit Testing by Example. They write for UIKit. I will try to write it in SwiftUI. I also love to write more algorithm topic soon. So stay positive and we will learn many thing together. If you want to connect, knock me at LinkedIn. Happy coding and stay safe.

--

--

Rejaul Hasan

I work as a Sr. software engineer for iOS platform. Available to discuss about any good opportunities or projects.