4.
Running Swift in a Playground
Written by Sarah Reichelt
In the previous chapters, you used basic Swift types and collections. You learned how to loop, evaluate conditions and write functions. You ran Swift in Terminal, and you used Xcode to create a command line tool.
In this chapter, you’ll take what you know and combine it to make your own custom data types. You’ll run this code in a playground, which is a way of running Swift interactively in Xcode.
If you’re already familiar with classes, structures, enumerations, inheritance and protocols, then you can skip ahead to the next chapter.
Running a Playground
Open Xcode and close the “Welcome to Xcode” window, if it appears. Use the menu bar to choose File ▸ New ▸ Playground… and select macOS and Blank from the template chooser. Name your playground CustomTypes.playground and save it in your Developer folder or wherever you prefer.
Your playground opens looking like this:
There are three main sections in this window:
- The navigator: Playgrounds allow you to have multiple pages and to bring in external code files or assets. You’ll see them all listed here.
- The code editor where you’ll type all your Swift.
- The results panel that shows the result of each line of code.
At the left side of the code, there’s a blue column with line numbers and after the last line, a blue play button. Click it to run the default code:
“Hello, playground” appears in the results panel as the playground executes that line. Mouse over it to see two new controls. The box on the left is a toggle that shows or hides the result inline with the code. The eye icon opens a larger popup displaying the value.
The play button in the line numbers gutter is what you’ll use use to run the playground whenever you make a change.
Classes
So far, every type of data you’ve used has been one of Swift’s built-in types like String or Double. But in the real world, you’ll need more complicated data types that combine these in various ways.
Consider a contacts list that stores names, addresses, phone numbers and email addresses. Or a pizza shop menu that stores pizza types with their bases, sauces and toppings. You can construct these by mixing the types that you already know.
For this section, imagine you’re writing an app for a fruit seller who wants to keep track of their stock items.
Clear everything out of your playground and enter this code:
// 1
class StockItem {
// 2
let name: String
var numberInStock: Int
}
This uses a new keyword:
- A class is a custom type. To create one, you start with the
classkeyword and then set a name. Class names conventionally use UpperCamelCase. - Inside the class, you declare a constant and a variable. When these are in a class, they’re referred to as properties. So the
StockItemclass has two properties: one of them is a constant and the other is editable.
Xcode now shows a nasty red error. Click on the red dot to read the full message. The problem is that your new class has no way of setting these properties when it’s first used.
To solve this, you’ll write an initializer. Add a blank line after the properties but before the closing curly brace and insert this code:
// 1
init(name: String, numberInStock: Int) {
// 2
self.name = name
self.numberInStock = numberInStock
}
This looks almost like a function, but not quite:
- There’s no
funckeyword becauseinitis a special method that the class calls automatically whenever you make a new object of that type. The arguments to theinitfunction are the starting information sent to fill in the properties. It’s common to use the property names for these arguments. - In the body of this function, you use the argument values to set the properties. Because the same names appear twice,
selfdistinguishes the properties from the arguments.
A class doesn’t do anything by itself. It provides a set of instructions for making an instance of the class. You’ll use this StockItem initializer to create instances of the StockItem class, each with their own values for the properties.
Creating a Class Instance
To create your first instance of the class, add this outside the class declaration:
var bananas = StockItem(name: "banana", numberInStock: 12)
Click the play button under the new line, and when it has run, click the box beside StockItem in the results panel:
This shows that the line of code created a new instance of the StockItem class, setting its name property to “banana” and its numberInStock property to 12.
Now you can work with these properties:
// 1
bananas.name
// 2
bananas.numberInStock += 1
// 3
bananas
Looking at these lines:
- You access properties using dot-notation that uses the name of the object, a dot and then the name of the property.
- The
numberInStockproperty is a variable that you can edit. - This line is here to show the current value of
bananasin the results panel.
Run the playground and then toggle the inline display on the last line to see this:
Now you have the ability to create a complex object with more than one property, and you can assign it to a variable.
So far, this looks like a super-dictionary. You have a single variable called bananas and it has two properties, which are like the keys of a dictionary. It’s more flexible than a dictionary because the properties can have different types and it’s easier to access them, but what makes classes better?
The answer is methods. Method is the name given to a function that’s inside a class. You call a method on an instance of the class, and it operates on its own instance properties.
Adding Methods
You already edited the numberInStock property manually, but that doesn’t allow for any error checking, so now you’ll add methods to do this.
Add a blank line after the end of the init method and type in these new methods:
// 1
func buy(number: Int) {
// 2
numberInStock += number
}
// 3
func sell(number: Int) {
// 4
if number > numberInStock {
numberInStock = 0
} else {
numberInStock -= number
}
}
Here’s what you added to the class:
- You declare a method called
buy(number:)that takes anIntargument. A method is a function, so the structure of a method is the same as the structure of a function. - You use the argument to increase the
numberInStockproperty. - The
sell(number:)method starts off the same asbuy(number:), but it adds some validation. - You can’t sell more than you have in stock, so this method checks to make sure
numberInStocknever goes below zero.
Time to test this by adding these lines at the end of the playground:
// 1
bananas.buy(number: 4)
bananas.numberInStock
// 2
bananas.sell(number: 2)
bananas.numberInStock
// 3
bananas.sell(number: 1000)
bananas.numberInStock
Each of these sections calls a method and displays the result:
- Call
buy(number:)on yourbananasinstance ofStockItemto increase the number in stock. - Use
sell(number:)to reduce the stock by a number that you know is less than the current stock. - Now, try to sell a large number of bananas and confirm that your validation works.
Running the playground shows these results:
Reference Types
It would be a very boring shop that sold nothing but bananas, so create some new instances:
let apples = StockItem(name: "apple", numberInStock: 0)
let oranges = StockItem(name: "orange", numberInStock: 24)
Now for an interesting twist. These two instances of StockItem are both constants, declared using the let keyword. Does this mean you can’t edit them?
Try it:
apples.buy(number: 16)
oranges.sell(number: 3)
Running the code, this all works!
Why? Isn’t let supposed to keep things from changing?
When you made the apples instance, the value stored in apples was not the data, it was the address of the data in memory. In computer terms, classes are reference types and not value types.
A String is a value type, so when you create a string constant or property using let, you can never change it. But StockItem is a reference type, so even if it’s declared as a constant, you can still edit its variable properties.
You can’t edit its constant properties, which you can prove by trying this:
apples.name = "Golden Delicious"
Running the playground now gives this error:
The error message not only tells you what’s wrong, but how to fix it. In fact, clicking the Fix button will go into your class definition and change name from let to var, but that’s not what you want here, so remove the error-causing line.
Now you know the basics of defining a class, initializing instances of the class and working with properties and methods, it’s time to learn about another way of creating custom types.
Structures
You’re going to use another playground page for this, so go to File ▸ New ▸ Playground Page or press Option-Command-N to add a page.
Xcode adds the new page and selects the title in the navigator so you can edit it. Set the name of this page to Structures and press Return.
Your first page now appears in the navigator as “Untitled Page”. Select it, press Return to make the name editable and set it to Classes.
Click the Structures page in the navigator to get back to it. Your playground now looks like this:
The comments at the top and bottom of the code are for navigation. To see this in action, select Editor ▸ Show Rendered Markup (all the way down at the bottom of the menu) and use the Next link to jump to the Classes page, which has become page 2. When you’ve seen how this works, choose Editor ▸ Show Raw Markup. Get back to the Structures page and delete all the code in it.
To make it easier to compare classes and structures, you’ll create a StockItem structure and make it do the same as the class did.
Start by adding this code to your blank Structures page:
// 1
struct StockItem {
// 2
let name: String
var numberInStock: Int
}
// 3
So far, it’s very similar to the class:
- For structures, you start with the
structkeyword. Again, the name is in UpperCamelCase. - You define the two properties as before.
- Xcode isn’t showing an error! This is because structures automatically get an initializer, so you don’t need to write your own. Hurray! Less code is good code.
To check if it works, add this line to make an instance:
var peaches = StockItem(name: "peach", numberInStock: 6)
Run the playground (only the current page runs) to see what happens:
So far, structures are looking great — set up the properties and it all just works. But what about methods?
Adding Structure Methods
Add a blank line after the structure’s properties and insert these methods:
func buy(number: Int) {
numberInStock += number
}
func sell(number: Int) {
if number > numberInStock {
numberInStock = 0
} else {
numberInStock -= number
}
}
This is the same as you used in the class, so why is Xcode so upset?
This looks like a sea of red, but it’s only one error repeated. Click the first red dot for more detail and you’ll see that ‘self’ is immutable. The numberInStock property isn’t the issue. It really is mutable because you set it up as a var. The problem is that the entire structure is immutable — that’s what self is referring to. This is a feature of structures.
Xcode helpfully offers to solve the problem, so click the Fix button on the first and second red dots. This changes the errors to gray while Xcode thinks for a few seconds before removing them.
Now you’ll see the mutating keyword before func in both your methods, indicating that these methods can mutate the structure.
As with deciding between var and let, always start without mutating and only add it if Xcode insists. Like let, this is a feature that makes Swift a safer language because you can’t change a structure’s properties without permission.
With this in place, you can operate on your structure like you did on your class:
peaches.name
peaches.numberInStock += 1
peaches.buy(number: 4)
peaches.numberInStock
peaches.sell(number: 2)
peaches.numberInStock
peaches.sell(number: 1000)
peaches.numberInStock
Running the playground after adding these lines shows:
Before moving on from structures, there’s one more difference you need to be aware of.
Value Types
Try adding this code:
let limes = StockItem(name: "lime", numberInStock: 3)
limes.buy(number: 9)
With a class, this worked even though the class instance was a constant. But with a structure, it gives an error:
This is actually more logical. When you initialize a structure instance as a constant, it really is a constant and you can’t change its properties. You already learned that classes are reference types. Structures are value types, so the variable limes actually holds the data, not an address in memory. This is another case of Swift making your code safer.
Delete these two lines to clear the error.
Classes and structures are the main two custom types you’ll work with, but there’s another type that’s useful when you have a limited number of options. That type is an enumeration.
Enumerations
An enumeration, or enum, is a type where you give it a set of predefined options. Each instance must be one of those options. Imagine you’re implementing a login system. What are the possible states for a user?
- Logged in
- Logged out
- Banned
- Logged in as admin
This is a perfect use case for an enumeration because your user has to be in one of these states.
Create a new playground page, set its name to Enumerations and clear all its content.
Enter this code to set up your new enumeration:
// 1
enum LoginState {
// 2
case loggedIn
case loggedOut
case banned
case admin
}
How does this work?
- Start with the
enumkeyword followed by the name in UpperCamelCase. - For each possibility, add a
caseand a label. The labels use lowerCamelCase.
LoginState is now a type like String, Int or StockItem and you use it like this:
var userStatus = LoginState.loggedOut
Now that the type of userStatus is set to LoginState, you can edit it less verbosely like this:
userStatus = .admin
Running your playground page shows:
This is a very convenient way to handle a set of options, but you can also add methods to an enumeration. They can step through the possible options to give a result.
Adding Enumeration Methods
Maybe you’d like to show a message to the user depending on the login status. Add this method before the closing curly brace in LoginState:
// 1
func displayMessage() -> String {
// 2
switch self {
}
}
This looks like it’s missing a few parts:
- You declare the
displayMessagemethod. It takes no arguments and returns aString. - You start a switch statement that switches on
self, which is the current instance of the enumeration.
Xcode now complains that “Switch must be exhaustive” but since you have a fixed number of options, Xcode can fill in the missing code and save a lot of typing. Click the red dot and press the Fix button beside “Do you want to add missing cases?”
Note: If you don’t see an error flag, switch to another playground page, then back to Enumerations, or close the playground and reopen it.
Xcode adds a case for each of the possibilities with a code placeholder for each one. There’s an error because the method doesn’t return anything yet, but you’ll fix that now.
Fill in the code blocks so that your switch looks like this:
switch self {
case .loggedIn:
return "Welcome, user"
case .loggedOut:
return "Log in to access this site."
case .banned:
return "*** BANNED ***"
case .admin:
return "Welcome, mighty administrator!"
}
It now returns an appropriate message for each state. Xcode isn’t always good at formatting, so to tidy things up, delete the blank line after the switch line, then press Command-A to select all your code and Control-I to re-indent it.
A method is one way to provide this message, but another possibility is a computed property. When you have a method that takes no arguments but produces output, consider using one of these instead.
Replace the method declaration with:
var displayMessage: String {
And now, you’ve got a computed property, instead of a method, and you can access it with:
userStatus.displayMessage
These also work in classes and structures.
You have seen three ways to make a custom type in Swift. Now, it’s time to go back to classes and learn some more features.
Inheritance
You may have heard of Object-Oriented Programming or OOP. This is a system of programming that uses classes and subclasses. A subclass is a class that inherits from a parent class.
Open the Classes page in your playground and scroll down to the bottom. Your fruit seller has decided to branch out and sell soft drinks, too. In many ways, a soft drink is a StockItem like a piece of fruit but it has an extra property: Is the drink fizzy or not?
This make it a perfect case for a subclass. Your SoftDrinkItem class can have everything that StockItem has, plus more.
Add the subclass like this:
// 1
class SoftDrinkItem: StockItem {
// 2
var isFizzy: Bool
// 3
init(name: String, numberInStock: Int, isFizzy: Bool) {
// 4
self.isFizzy = isFizzy
// 5
super.init(name: name, numberInStock: numberInStock)
}
}
There’s a lot going on here:
- You declare the new class but, after the class name, type a colon and the name of the parent class.
- You define the
isFizzyproperty, which is unique to this class and not part of the parent. - You create an initializer that has both the parent properties and the subclass property as its arguments.
- You set the subclass property first. You must assign all the subclass properties before moving on to the next step.
- You call
super.initto initialize the parent with its properties.
Initializing a Subclass Instance
This class has inherited everything from its parent, so you can initialize it and use the parent’s methods like this:
// 1
let mineralWater = SoftDrinkItem(
name: "Mineral water",
numberInStock: 12,
isFizzy: false)
// 2
mineralWater.sell(number: 3)
How does this work?
- You call the initializer with all three properties. It’s perfectly valid to split method calls or declarations over multiple lines, and if you have a lot of properties, this makes your code a lot more readable.
- You call a parent method on your instance of the subclass.
Run the playground and click the square to show the final result inline:
Use the disclosure triangle in the inline result to show the parent class properties separated from the subclass property.
The SoftDrinkItem class has no sell(number:) of its own so it uses its parent’s sell(number:).
Add these two lines to your playground and run:
mineralWater is SoftDrinkItem
mineralWater is StockItem
They’ll both give warnings because they can never be false, but they demonstrate that mineralWater is both a SoftDrinkItem and a StockItem. Select these lines and press Command-/ to comment them out and get rid of the warnings.
One benefit of inheritance is that you can have an array containing StockItem instances and SoftDrinkItem instances without a problem:
Enter this line in your playground:
var stocks = [bananas, apples, mineralWater]
And then Option-click on the variable name stocks:
This confirms that Swift considers it an array with a single type of object in it, but the type is StockItem, even though one of the elements is a subclass of StockItem.
Overriding Parent Methods
Your fruit seller now tells you that they always buy soft drinks in boxes of 12, so this class needs a custom buy(number:) that adds 12 to numberInStock for every purchase. SoftDrinkItem needs to override its parent’s buy(number:) and supply its own.
Insert this in the SoftDrinkItem class definition:
// 1
override func buy(number: Int) {
// 2
numberInStock += number * 12
}
Looking at this method:
- Since this replaces a parent method, start with the
overridekeyword. Xcode will complain and offer to insert it if you forget. - Then, insert your custom logic that increments
numberInStockby 12 for each purchase.
Test your new method like this:
mineralWater.numberInStock
mineralWater.buy(number: 2)
mineralWater.numberInStock
And run the playground to see the result:
This covers the key features of inheritance, but it only works for classes. With structures, you’ll use a different approach.
Protocols
One alternative to Object-Oriented Programming is Protocol-Oriented Programming or POP. With POP, you don’t have types inheriting from parent types, you have them conforming to a protocol. So what is a protocol?
A protocol is a contract — a list of properties and methods that any conforming type must provide.
Implementing the fruit selling app using POP needs a few changes, so create a new playground page and call it Protocols.
Clear the page and enter this:
// 1
protocol StockItemProtocol {
// 2
var name: String { get }
// 3
var numberInStock: Int { get set }
// 4
mutating func buy(number: Int)
mutating func sell(number: Int)
}
This is different:
- You declare a protocol by starting with the
protocolkeyword followed by the name in UpperCamelCase. It isn’t necessary to have Protocol as part of the name — that’s here to make the example clearer. - Any type conforming to this protocol must have a
Stringproperty calledname. Thegetshows that the type must make it readable, but it doesn’t have to be editable. - The type also needs an
Intproperty callednumberInStock. Thegetandsetshow that it can be both read and edited. - All conforming types must supply two methods called
buy(number:)andsell(number:)that both accept anIntargument and return nothing. These methods can mutate the structure’s properties.
The methods look very strange since the protocol lists the method declarations but no code, but that’s how protocols work. Where class definitions give a blueprint for creating instances, protocols provide a set of rules.
Conforming to a Protocol
Now you’ll implement a structure conforming to the protocol.
Type this outside the protocol:
struct StockItem: StockItemProtocol {
}
You created a structure with a name, and then, you added a colon and the protocol name. This guarantees that StockItem will supply all the properties and methods listed in the StockItemProtocol.
Right now, it doesn’t do any of that, and Xcode is not happy. Click the red dot and then the Fix button to get Xcode to fill in what’s needed. This supplies the properties and adds method stubs, which are methods with the correct declarations, but using a code placeholder.
Note: If you don’t see an error flag, switch to another playground page, then back to Protocols, or close the playground and reopen it.
Replace the buy(number:) code placeholder with:
numberInStock += number
And the sell(number:) placeholder with:
numberInStock -= number
The sell(number:) method doesn’t include the validation, but you already know how to do that.
It’s a great idea to let Xcode do as much work as possible. You avoid typos, you won’t leave anything out and you save yourself time and effort. But as you’ve already seen, Xcode isn’t great at formatting, so get rid of the excess blank lines and press Command-A followed by Control-I to format your code.
Using your new structure works as before:
var mangos = StockItem(name: "mango", numberInStock: 5)
mangos.buy(number: 3)
mangos.sell(number: 1)
mangos.numberInStock
So far, this seems a bit pointless. The StockItem structure is much the same, and there’s an extra protocol that just adds extra code. But what about the soft drinks? That structure can’t inherit from StockItem but it can conform to StockItemProtocol.
Add another structure definition:
struct SoftDrinkItem: StockItemProtocol {
var name: String
var numberInStock: Int
var isFizzy: Bool
mutating func buy(number: Int) {
numberInStock += number * 12
}
mutating func sell(number: Int) {
numberInStock -= number
}
}
As you can see, this is very similar to StockItem but it contains the new isFizzy property. A protocol says what must be in a type — it doesn’t care if there are extra properties or methods.
Next, create an instance like this:
var lemonades = SoftDrinkItem(
name: "lemonade",
numberInStock: 24,
isFizzy: true)
Like with the classes and subclasses, you can add different types conforming to the same protocol to an array, if you set up the array correctly.
Try this code:
var stocks: [StockItemProtocol] = [mangos, lemonades]
The elements in the array are different types, but they conform to the same protocol, so when you declare the array as containing items that conform to that protocol, you can gather them together.
Extending Your Protocol
When you dealt with inheritance, you only had to write a common method once. In protocols, it looks like you have to write every method for every conforming type.
To get around this, you’ll use an extension. Extensions let you add to existing types or protocols. Often you’ll use them as a convenient way of grouping similar methods together, but in protocols they can supply default methods.
Outside the protocol declaration, type this:
// 1
extension StockItemProtocol {
// 2
mutating func buy(number: Int) {
numberInStock += number
}
mutating func sell(number: Int) {
numberInStock -= number
}
}
What have you added here?
- You create an extension using the
extensionkeyword followed by the name of the class, structure or protocol that it extends. - This extension provides default implementations of
buy(number:)andsell(number:).
Scroll to where you declared StockItem and remove its two methods. It can use the defaults.
In SoftDrinkItem, delete sell(number:), but leave buy(number:) because it’s different from the default.
Now your code is shorter, neater and easier to maintain.
To confirm that this still works and that each type is using the correct buy(number:), add and run these lines:
mangos.numberInStock = 0
mangos.buy(number: 2)
mangos.sell(number: 1)
mangos.numberInStock
lemonades.numberInStock = 0
lemonades.buy(number: 1)
lemonades.sell(number: 1)
lemonades.numberInStock
Which shows you:
Protocols work with both structures and classes. Inheritance only works with classes. So, which should you use?
What to Use?
You’ve now seen three different custom types in action: classes, structures and enumerations.
Enumerations are for when you have a predefined set of possibilities.
Deciding between classes and structures is more complex:
- Classes are reference types; structures are value types. If you want to pass an object to something else and have the changes flow back to the original, use a class. If you want to pass along isolated instances, use a structure.
- Classes can inherit from another class. Often, especially when working with interface elements, you’ll start with existing classes that you want to customize, so you’ll have to use a class.
- Both structures and classes can conform to protocols. In fact, a single object can conform to multiple protocols where a subclass can only inherit from one parent class.
- You don’t have to pick one only. Any project can have a mixture of classes and structures.
Generally speaking, structures are safer because you know exactly what can change their data. So start with a structure, but if you need to inherit or you need the properties to be more mutable, then switch to a class.
In Section 2 of this book, you’ll build an app using SwiftUI. SwiftUI has definite rules about using classes in some cases. A common pattern is to have a main data class that contains an array of data structures.
As you progress to section 3, you’ll get into AppKit, which is the Mac’s older user interface framework. It uses classes and inheritance extensively.
Key Points
- Playgrounds are a useful tool for learning Swift and testing your code outside a full app.
- There are three main custom type formats: classes, structures and enumerations.
- Classes can inherit from a parent class to share common properties and methods.
- Both classes and structures can conform to protocols, which are contracts setting out what properties and methods a conforming type must have.
- Enumerations allow for predefined sets of options.
Where to Go From Here
This is the end of your brief introduction to Swift and how you can run it on your Mac. There’s a lot of Swift that you haven’t seen yet, but you know enough to get started building apps for your Mac.
In the next section, you’re going to build a full game using Swift and SwiftUI.
For the official Swift information and guides, go to Swift.org.
To read about Swift in more depth, check out our Swift Apprentice book.