A memberwise initializer is an initializer that is automatically generated by the compiler for structs that don't define a custom initializer in their declaration. That sound complicated. Let me show you a few examples to explain this in more detail. Create an empty playground if you want to follow along.
We define a struct with name Book
. The Book
struct declares two constant properties of type String
, title
and author
.
import Foundation
struct Book {
let title: String
let author: String
}
Because the Book
struct doesn't define a custom initializer in its declaration, the compiler generates a memberwise initializer. Xcode confirms this when we try to initialize a Book
struct. The list of suggestions Xcode displays include the memberwise initializer.
Default Values
We only scratched the surface with the Book
struct. Let's take a look at a more complex example. The ShoppingCart
struct declares two variable properties, totalPrice
of type Double
and numberOfItems
of type Int
.
import Foundation
struct ShoppingCart {
var totalPrice: Double
var numberOfItems: Int
}
You know from the previous example that the compiler generates a memberwise initializer for the ShoppingCart
struct. If we assign a default value to the properties, the options we have to create a ShoppingCart
object increases. Xcode gives us four options to create a ShoppingCart
object.
We can pass no values to the initializer. This is possible because each property has a default value.
let shoppingCart = ShoppingCart()
We can pass a value for the totalPrice
property or a value for the numberOfItems
property.
let shoppingCart2 = ShoppingCart(totalPrice: 10.0)
let shoppingCart3 = ShoppingCart(numberOfItems: 10)
We can also pass values for both properties.
let shoppingCart4 = ShoppingCart(totalPrice: 10.0, numberOfItems: 10)
How does this work? The memberwise initializer the compiler generates looks something like this. The compiler uses the default value of each property as the default value of the corresponding parameter of the memberwise initializer. There is no magic involved.
init(totalPrice: Double = 0, numberOfItems: Int = 0) {
self.totalPrice = totalPrice
self.numberOfItems = numberOfItems
}
When Is a Memberwise Initializer Not Available?
It is important to know that the compiler only generates a memberwise initializer for a struct if the type declaration doesn't define a custom initializer. Let's revisit the Book
struct. We update the example by adding a custom initializer to the Book
declaration.
import Foundation
struct Book {
let title: String
let author: String
init(data: (String, String)) {
title = data.0
author = data.1
}
}
Notice that Xcode's list of suggestions no longer includes the memberwise initializer. The only option we have to create a Book
struct is the custom initializer we defined.
A Workaround for a Missing Memberwise Initializer
It can happen that you define a custom initializer, but you also want the convenience of a memberwise initializer. This is surprisingly simple and you don't need to implement the memberwise initializer yourself. The solution is to define the custom initializer in an extension for the struct. Take a look at the updated example.
import Foundation
struct Book {
let title: String
let author: String
}
extension Book {
init(data: (String, String)) {
title = data.0
author = data.1
}
}
This gives us the best of both worlds.
How to Create a Memberwise Initializer for Classes?
Even though the compiler only generates memberwise initializers for structs, it is possible to create one for classes. This is a little known feature of Xcode. Take a look at this example.
import Foundation
class Book {
let title: String
let author: String
}
I have converted the Book
struct to a class. Because the compiler doesn't generate a memberwise initializer for classes, it throws an error. The error tells us that the Book
class has no initializers. That is correct, but we can fix this with a few clicks.
Note that this solution only works in a project, not in a playground. This is a limitation of Xcode. Right-click the Book
class to bring up the contextual menu. Choose Refactor > Generate Memberwise Initializer.
This is the result.
import Foundation
class Book {
internal init(title: String, author: String) {
self.title = title
self.author = author
}
let title: String
let author: String
}