Cleaner inits - ExpressibleBy... protocols
Sometimes structures or classes we create in code can be easily expressed by built-in Swift types. Consider the following code:
struct Person {
let name: String
}
let ana = Person(name: "Ana")
I created simple Person
a struct that holds a person's name. To create this structure we need to explicitly call its auto-generated constructor.
There is a better way, Swift provides a couple of protocols that can make initialization nicer and simpler. In our case, we need ExpressibleByStringLiteral
. Swift requires to implement custom init
(stringLiteral value: Self.StringLiteralType)
. We are using String
.
Let's refactor our code:
struct Person: ExpressibleByStringLiteral {
init(stringLiteral value: String) {
name = value
}
let name: String
}
let ana: Person = "Ana"
Now instead of calling initializer directly, we can make Swift call it for us - I'm creating Person
by assigning String
. Swift will create Person
for us because we used ExpressibleByStringLiteral
.
We can use the power of expressible protocols with generics and extensions. I'll create a generic stack in Swift:
struct Stack<T> {
private var array = [T]()
func peek() -> T? {
array.last
}
mutating func pop() -> T? {
array.popLast()
}
mutating func push(_ element: T) {
array.append(element)
}
}
Now, I want to be able to create a stack from an array. To do this we need to add a new initializer:
extension Stack {
init(from array: [T]) {
array.forEach {
push($0)
}
}
}
var stack = Stack(from: [1, 2, 3, 4])
stack.pop() // gives 4
Well... I don't like it! I can improve it by using the power of ExpressibleByArrayLiteral
and extensions:
extension Stack: ExpressibleByArrayLiteral {
init(arrayLiteral elements: T...) {
self.init(from: elements)
}
}
var stack: Stack = [1, 2, 3, 4]
And you don't even have to specify the type of stack in <>
! Swift will figure it all by itself!
Swift standard library provides protocols for a couple of built-in types - more information can be found here.
If you like my posts, check out my Twitter!