Vocabulary for this course

The terms the chapters assume, in plain words. You can open this from any chapter.

array
An ordered list of values of one type, reached by position starting at zero.
block
A group of lines of code that run together, often inside curly brackets.
closure
A piece of code you can store in a value and pass to other code, remembered with the names around it.
compiler
The program that turns the code you write into something the machine can run, reporting mistakes before it does.
dictionary
A collection of key and value pairs, where each key appears once and finds its value.
enum
A type whose value is one of a fixed set of named cases, which may each carry extra data.
expression
A piece of code that produces a value, such as a sum or a call to a function.
function
A named piece of code that takes values and returns a result when called.
guard
A check that must pass before the code after it runs, leaving the scope early when it fails.
method
A function that belongs to a type and is called on a value of that type.
nil
The absence of a value, allowed only where a type says a value may be missing.
operator
A symbol such as plus or equals that combines values into a new one.
optional
A type that either holds a value or holds nothing, written with a question mark after the type.
optional chaining
Writing a question mark before something you reach into, so the result is nil when the value is missing.
property
A named value that belongs to a type or to one of its values.
protocol
A list of requirements that a type promises to meet, so code can work with any type that meets them.
scope
The region of code where a name can be used before it goes out of reach.
string interpolation
Putting a value inside text with a backslash and parentheses, so the value becomes part of the text.
struct
A type that groups related values and makes a copy whenever it is given another name or passed along.
subscript
A way to read or write an item in an array or dictionary by putting a key or position in square brackets.
type
What kind of value something is, such as a whole number or text, and what it can do.
unwrap
To get the value out of an optional after checking that something is there.

Swift from the ground up

Structs and protocols

A struct that models a thing, properties it computes on demand, and a protocol that states what a type can do.

Part 10 of 12

Advanced19 min readBack to Courses

A struct is a small model

A struct gathers related values under a name. struct Rectangle with a width and a height describes a rectangle, and Swift writes a ready-made initialiser so you can build one with Rectangle(width: 3, height: 4).

Stored properties are the values the struct remembers. Methods are functions that live inside the struct and work on those values.

The member-wise initialiser is generated only when the struct declares no initialiser of its own. Write your own init and the free one disappears, so a struct with validation usually needs an initialiser that checks each value before storing it. A property with a default can still be left out of the call, and a private property makes the generated initialiser private too.

Properties you compute

A computed property looks like a stored one at the point of use, but it works the value out fresh each time you ask. An area read as box.area has no parentheses, yet it always reflects the current width and height.

Because it is computed, it can never drift out of step with the values it is built from. Change the width, and the next read of area uses the new number.

A computed property has no storage of its own. It runs its block on every read, so box.area twice is two multiplications, not one saved answer. That is cheap here and important when the computation is expensive: cache the result in a stored property if it is read far more often than the values change.

A protocol is a promise

A protocol lists what a type can do without saying how. protocol Named might only require a label property that can be read. Any type that declares it conforms must supply that property.

A function can then accept any value that conforms, using the protocol name as the parameter type, and call the promised members without caring what the underlying type is. That is how one piece of code serves many kinds of thing.

Conformance is declared on the type, not on the protocol: struct Point: Named is the promise. The compiler checks the member’s name, type and access level. A protocol can also require a method with arguments, and the conforming type must match the parameter labels exactly, because the protocol is describing the call site as well as the body.

Structs are copied, not shared

A struct is a value type. Assigning it to a new name copies the values, and changing the copy leaves the original alone. That makes a struct predictable to pass around, because no other part of the program can change it behind your back.

A method that changes a stored property must be marked mutating, and it can only be called on a var instance. The compiler asks for that word so a reader can see, at the definition, which methods write.

Passing a struct to a function copies it, so the function cannot change the caller’s value by default. An inout parameter is the explicit exception: the caller writes &value, and the changes are written back on return.

What goes wrong

A missing protocol member is reported on the type declaration, not at the call that needs it. The message names the requirement and the type, so read it as a checklist: the property is not readable, the method has the wrong label, or conformance was never declared.

A mutating method on a let instance fails with a message about an immutable value. The fix is not to mark the method non-mutating; it is to hold the instance in a var if it really changes. If it does not change, remove the assignment and the keyword together, because a method that claims to mutate but does not is misleading.

A struct that keeps a promise

protocol Named {
    var label: String { get }
}

struct Point: Named {
    var x: Int
    var y: Int

    var label: String {
        return "(\(x), \(y))"
    }
}

let p = Point(x: 3, y: 4)
print(p.label)

Three mistakes follow from value semantics. A let instance of a struct cannot be mutated, so the variable that holds it must be a var. A method that assigns to a property needs the mutating keyword; without it the compiler treats the assignment as a change to a constant. And a type that declares conformance must supply every member the protocol names, with the exact type, or the compiler rejects the type itself.

A rectangle that knows its area

Run it and check the area and the label. Then change the width and predict how the area and the label each change.

Output
Click "Run" to execute your code

Try these next

  • Add a mutating method to a struct, call it twice, and print the property after each call.
  • Copy a struct into a var, change the copy, and print both values to show they are independent.
  • Write a second type that conforms to the same protocol and pass both types to one function.

A counter, a copy and a promise

Run it and compare the original with the copy. Then increment the copy twice more and predict both summaries before you run it again.

Output
Click "Run" to execute your code

A protocol is a contract, not a parent. It does not share code, it shares a promise, so a function written against the protocol keeps working when a new conforming type appears.

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

Where does Rectangle(width: 3, height: 4) come from?