Chapters

Hide chapters

macOS Apprentice

Third Edition · macOS 26 · Swift 6 · Xcode 26

Section II: Building With SwiftUI

Section 2: 6 chapters
Show chapters Hide chapters

Section III: Building With AppKit

Section 3: 6 chapters
Show chapters Hide chapters

2. Beginning Swift
Written by Sarah Reichelt

In the first chapter, you installed Xcode and configured it. Then, you learned how to create a Mac app from the standard template.

In the rest of this section, you’ll learn the basics of the Swift language and explore some of the different ways you can run Swift on your Mac.

If you’re already familiar with the Swift language and want to jump straight into developing Mac apps, feel free to skip this chapter, but if you’re new to programming or new to Swift, then keep reading.

What is Swift?

Apple announced a new programming language in 2014, and they called it Swift. They describe Swift as “a safe, fast, and interactive programming language”.

Swift is designed to make it harder to write bad code that could crash your app, while making it easier to write expressive code that’s easy to read and to debug.

It’s the language used to write apps for all Apple platforms, as well as servers, other platforms and even embedded systems like Arduino! But enough talk — time to code.

Running Swift in the Terminal

When you installed Xcode in the previous chapter, you also installed a suite of tools called the Xcode Command Line Tools. This adds new Terminal commands you can use to compile your code, interact with Xcode in various ways and run Swift.

Open Terminal by going to Applications ▸ Utilities ▸ Terminal.app or by pressing Command-Space to open Spotlight and searching for Terminal. The default Terminal window opens as a small window in the top left of your screen. The width is fine, but expand the window so that it’s as tall as your screen. This makes it much easier to see the sequence of commands that you’re about to run.

In Terminal, type swift and press Return:

Swift in the Terminal.
Swift in the Terminal.

You’re looking at a list of the subcommands that you can run from the swift command line tool. Most of these are to do with packages, which are beyond the scope of this book. The one you’re interested in is repl so that you can “experiment with Swift code interactively”.

Type swift repl and press Return to start the Swift REPL. So what is a REPL? It stands for Read-Evaluate-Print-Loop. You type in Swift code, the Swift REPL reads it, evaluates the result, prints that result and loops back to its command prompt ready for another command.

Note: In earlier versions of the Xcode Command Line Tools, running swift without any extra arguments automatically started the Swift REPL, but since Swift 5.7, you need to select this manually.

To get out of the REPL and back to your usual Terminal, enter :exit or press Control-D. Pressing Command-K at any time clears the Terminal screen.

If you exited the REPL, press Up-Arrow to return to the previous command and run swift repl again.

Swift Types

Swift is a strictly-typed language, but what does that mean? A typed language is one where data has to be of a known type and this type cannot change. Swift infers the data type depending on what you give it and the Swift REPL makes it easy to see the assigned type.

In the REPL, type 1 and press Return:

First look at the Swift REPL.
First look at the Swift REPL.

You only entered one character, but there’s a lot of information on this screen:

  1. The first line shows your standard Terminal prompt — in my case, it’s showing my user name and computer name — followed by the command you entered to start the REPL.
  2. You get to see what version of Swift you’re running. Don’t worry if it’s not exactly the same as mine.
  3. The REPL has its own prompt which numbers the lines as you enter them. You typed 1 on line 1.
  4. This is where the REPL printed the result. First, you see $R0 which is an internal label that the REPL assigns to your result. Then you see Int = 1. This tells you that Swift has evaluated what you entered and found it to be an integer with the value of 1. Swift uses Int to indicate an integer, which is a whole number with no decimal point.
  5. Finally, the REPL loops back and gives you another prompt.

Now that you know how to read what the REPL tells you, enter these lines, pressing Return after each one:

4.87
"Swift"
true

Swift types
Swift types

Checking the results of these lines, by line number:

  1. Double is short for double precision floating point and means a number with decimals. As you can see from the printed result, computers aren’t very good at floating point numbers and often get them a bit wrong, although rarely enough to make any significant difference.

  2. String describes any text and, in Swift, you surround text with double-quotes.

  3. Bool (short for Boolean) is a value that can only be either true or false.

These four entries demonstrate the basic or primitive types used by Swift. Later, you’ll see how to combine these into more complex types for your own data needs.

Operating on Data

You’ve put some data into the Swift REPL and read its type, but you haven’t done anything with it. You’ll use operators to perform operations on your data. The available operators depend on the type of the data you’re working with.

Numbers

Numeric operators include the standard mathematical operators with the most usual being addition (+), subtraction (-), multiplication (*) and division (/).

Evaluate these commands one at a time in the Swift REPL:

67 + 3
1.45 - 2
9 * 4
7 / 3

Which gives these results:

Numerical operators
Numerical operators

Looking at these by line number:

  1. Adding 2 Int values returns another Int .
  2. The first value here is a Double. You can only subtract a Double from another Double, so Swift has assigned a type of Double to the 2. The result is a Double and again shows the problems a computer can have with decimals.
  3. Again, this operator started with two Int values and gives another Int.
  4. This one may have surprised you with what appears to be the wrong answer. Shouldn’t 7 divided by 3 equal 2.3333333? Not in this case, because you started with two Int values and so Swift performed an integer operation and returned an integer value. To get the correct answer, enter 7.0 / 3. To our human eyes, 7.0 is exactly the same as 7, but to Swift, one is a Double and one is an Int.

Note: The spaces either side of the operator make the expression easier to read. Omitting both spaces — 9*4 — will work, but if you put a space on one side, make sure there’s a space on the other side. 9 *4 or 9* 4 won’t work. If you use inconsistent spacing, Swift shows a very unhelpful error message: error: consecutive statements on a line must be separated by ‘;’, so if you see that, check your spaces.

Order of Operations

When doing multiple calculations on a single line, Swift evaluates operators in a certain order. Type in this operation, but before pressing Return, try to work out what result you expect:

2 + 5 * 3

If you worked from left to right, you may have expected 21 (2 + 5 = 7, 7 * 3 = 21). But the order of operations dictates that multiplication and division happen first and then addition and subtraction. So for this sum, 5 * 3 = 15, 2 + 15 = 17.

If you want to change the order, wrap sections in parentheses. Swift evaluates these sections first and then evaluates the rest of the line. To get 21, you’d need to do this:

(2 + 5) * 3

While you can rely on the order of operations, you can always use parentheses to make your meaning clear.

Strings

You can operate on String data too. The + operator concatenates strings. Run this command:

"Hello" + " Swift!"

You’ll get a single String with the two parts run together. There’s a space before the S in the second string to make sure the words aren’t run together. The + operator does not add a space.

Booleans

The only operator that works on Booleans is the negative or not operator !. You can see it in action like this:

!true

It toggles true to false and false to true.

Storing Data

So far, you’ve used the Swift REPL in a very transitory way. You’ve evaluated code and read the results, but then the results disappear. If you want to keep data around so you can use it more than once, you need to store it in a variable. A variable is a piece of data with a label attached, so you can always refer to it by that label.

Still in the REPL, enter:

var language = "Swift"

Declaring a variable.
Declaring a variable.

The REPL reports back that language is a String, which is correct. It also shows that instead of giving it a temporary $R label, it has assigned it to a variable called language. Now you can use that variable:

"Hello " + language

You’ve printed your String variable with a prefix, but you haven’t changed the language variable, which you can prove by evaluating language on its own.

To change a variable, store the result of your operation back into the same variable, like this:

language = "Hello " + language

This line says to set the contents of the language variable to “Hello “ followed by whatever was in there before.

Now when you evaluate language (you can press Up-Arrow to bring back your previous commands), you’ll see that it has changed:

Editing a variable.
Editing a variable.

You already know that Swift assigns types to data. It does the same to variables, and once assigned, these types can’t be changed.

Typing Variables

Run these commands, each of which will give you an error:

language = false
language = 42
language = "Swift " + 6.2

The first two errors say that you cannot assign a value of a different type to a String variable. The last one tells you that a Double cannot be converted to the expected argument type String. As you saw earlier, + is a valid String operator and a valid numeric operator, but it only works if it gets the same type of data on both sides.

Swift uses Type Inference to decide what sort of data you’re using. You can set the types manually if you want to, but it’s better practice to let Swift infer the types for you whenever possible.

To specify the type, follow the variable name with a colon and then the data type, like this:

var userName: String = "admin"
var isLoggedIn: Bool = true
var counter: Int = 0
var width: Float = 5.64

In these examples, the first three type declarations are redundant since Swift would have allocated those exact types. In the fourth line, the type is different to the default Double type for a floating point number, so this is an example of where you do need to specify the type manually.

A Float is a single precision floating point number so it’s similar to a Double but it can’t hold numbers as big or as small. It takes up less memory but unless you’re writing code for a system where memory usage is critical, it’s easier to let Swift make the choices and stick to using Double.

Changing Data Types

What if you wanted to produce a String that combined text and a number — for example, if you wanted to show the Swift version number?

You use String Interpolation. This is a way of inserting the result of any Swift operation into a String. You use it by creating a quoted string as usual, and then adding the insertion wrapped in \( and ) like this:

var version = 6.2
language = "Swift \(version)"

Now when you print out the language variable, you’ll see that it contains “Swift 6.2”.

You can insert almost anything inside a string interpolation, including numbers, other strings, calculations and variables.

So that covers creating strings, but what about converting numbers between the different numeric formats?

Evaluate these three lines and see what happens:

var score = 6.8
var bonusPoints = 3
score += bonusPoints

Mixing number types.
Mixing number types.

Stepping through these lines as numbered in the screenshot:

  1. score is a Double and is almost equal to the number you entered.
  2. bonusPoints is an Int.
  3. You’re trying to add an Int to a Double.

The += operator is a shorthand way of saying score = score + bonusPoints. You can also use -=, *= and /=. They look strange at first, but once you get used to them, these operators allow you to write code that is neat and efficient.

As before, the last line gives a “cannot convert” error because you’ve got two different types on either side of the operator. To make this work, you have to change one of these values to the same type as the other. You could use Int(score) to convert score to an integer. It would lose precision and become 6, but the addition would work.

In this case, a better solution is to convert bonusPoints to a Double using Double(bonusPoints). This command works:

score += Double(bonusPoints)

Converting number types.
Converting number types.

These Int() and Double() commands are initializers. They’re initializing a new instance of their data type starting with the supplied value.

Keeping Data Constant

Variables are very useful for data that may change, but not all data has to have that ability. Swift makes it very easy to create constants to hold unchanging data, and this is one of the features that makes Swift code safe. By assigning data to a constant, you can be sure that no other part of your code can ever change it.

Assigning a constant is similar to assigning a variable, but you use the let keyword instead of var:

let userID = "ABCD1234"

Note: If you’ve come from the JavaScript world, this is the opposite of what you expect, but let in Swift is the same as const in JavaScript, while var in Swift is like let in JavaScript.

A good rule of thumb when coding in Swift is to start with let for every piece of data and only change to var if Xcode complains.

Trying to edit a constant gives a series of errors, which helpfully tell you what to do if you actually need to edit the value:

Trying to edit a constant.
Trying to edit a constant.

Naming Variables

When you’re writing code, it’s important to make it readable. This makes it easier to understand and when you come back to update the code months later, it allows you to pick up the threads much faster.

Swift encourages code that reads well and a big part of that is choosing good variable names. Compare these two chunks of code:

let u = "ABCD1234"
var n = "Jane Doe"
var pw = "super_secret"
var p = 36
let userID = "ABCD1234"
var userName = "Jane Doe"
var password = "super_secret"
var parkingSpaceNumber = 36

Which one would you rather read if you were unfamiliar with the code? Xcode and the Swift REPL have excellent auto-complete options. To see this in action, go back to the Swift REPL, type l and press Tab:

Auto-complete
Auto-complete

Because you previously defined the language variable, auto-complete suggests it, along with two key words that also match what you typed.

Type an after the l it filled in for you on the next line and this time, pressing Tab gives your full variable name. Press Return to evaluate it.

So there’s no excuse for writing short and unhelpful names as they don’t actually save you any time when writing and they’ll cost you a lot more time later.

You can use this auto-complete technique for the rest of this chapter to make your life easier as you type in the REPL.

The convention for writing variable and constant names in Swift is lowerCamelCase. Names start with a lowercase letter and the subsequent words in the name start with an uppercase letter.

Swift allows for a great deal of variety in variable names including the use of Emoji, but in the interests of clarity, this isn’t a great idea. Variable names must start with a letter, but after that, they can contain letters, numbers, underscores and dashes.

Collecting Data

So far, each variable or constant has held a single primitive data point, but you’ll often want to gather a collection of data points together under a single variable name.

Arrays

Swift has several ways to do this, and the most commonly used one is an array. An array is an ordered collection of items of the same type.

To create an array, you wrap the data in square brackets and separate the individual items by commas:

var things = [ "pear", "banana", "grape", "zebra" ]

Creating an array.
Creating an array.

When you run this line in the REPL, you’ll see that things has the type [String]. The square brackets indicate that it’s an array and the String says that the elements in the array are all of type String. To us, “grape” and “zebra” are very different, but to Swift, they’re both strings and that makes them similar enough to have in the same array.

Arrays have operations, too. The + operator combines two arrays:

things += [ "aardvark", "artichoke" ]
print(things)

things now has six elements:

Merging arrays.
Merging arrays.

Note: In the REPL, you can check the value of a variable by entering its name and pressing Return. That gives a lot of information about the variable and its contents. For a more compact display, you can use print. You’ll find print very useful later when it comes to debugging your apps in Xcode.

To access a single item in an array — commonly referred to as an element — you use its position, or index, in the array. The things array has six elements, but computers like to start counting from zero, so the first element has an index of zero.

Run these commands one at a time, but beware — one of them will crash the REPL:

things[0]
things[5]
things[6]

Accessing an array.
Accessing an array.

The last line gave Fatal error: Index out of range which is what happens when you try to read an element that doesn’t exist. The key point to remember is that the last element has an index that’s equal to the number of elements minus one.

Array Properties and Methods

There are other manipulations that you can perform on arrays, but they don’t use operators. Instead, they use methods and properties. Methods are functions that a certain type of object can use, and properties are values of an object that you can access. You’ll learn more about these in later chapters, but for now, check these different array properties and methods:

var companions: [String] = []
companions.append("Sarah Jane")
companions += ["Amelia", "Rose", "Martha", "Donna"]
print(companions)
companions.count
companions.remove(at: 2)
print(companions)

Array properties and methods.
Array properties and methods.

Going through these different commands:

  1. When you create an array with starting data, Swift uses the initial elements to decide the type of the whole array. When starting with an empty array, you must declare the type inside square brackets and then initialize the array as empty with the second set of square brackets after the equals sign.
  2. The append method adds a new element on to the end of the array. The new element has to match the declared type for the array, in this case a String.
  3. As you saw already, the + operator merges two arrays, and you can use the += shorthand here too.
  4. The print command displays the contents of the array.
  5. Query the count property to see how long the array is.
  6. Use remove(at:) to delete the element at the specified index. If you use an index that doesn’t exist, you’ll get another fatal error.
  7. Bonus points if you spotted the Doctor Who reference. :]

Arrays are a useful data structure for storing a list of items of the same type and accessing them by index, but what if you wanted to be able to access the elements by name? For that, you need to use a dictionary.

Dictionaries

In a real world dictionary, you look up a key word and get back a definition for that word. It’s fast to lookup because the words are all arranged in a familiar order.

In Swift, dictionaries are very similar. Each entry in a dictionary has a key and a value. The key is like the word that you look up and the value is the definition. Internally, Swift stores the keys very efficiently so it can look them up very fast.

These keys and values can be of any type so long as all the keys are the same type and all the values are the same type. The keys and values don’t have to be the same type. Commonly, the keys are strings.

Imagine you’re out on a bird-spotting walk carrying your Mac with you. Run these examples one by one in the REPL:

var birdsSeen: [String: Int] = [:]
birdsSeen["Robin"] = 3
birdsSeen["Blue Jay"] = 27
print(birdsSeen)

Creating a dictionary.
Creating a dictionary.

Following this sequence:

  1. When creating an empty dictionary you have to set the types for both the keys and the values. Inside the square brackets, the type before the colon is for the key and the type after is for the value. After the equals sign, the square brackets with a colon inside set up an empty dictionary, or as the REPL reports, a dictionary with 0 key/value pairs.
  2. You add an element to an array by setting a particular key for the dictionary to a value of the correct type.
  3. Lines 9 and 10 in the image above set integers as the values for two different keys.
  4. Printing out the dictionary shows the entered data — yours may appear in a different order.

You retrieve values using their keys:

birdsSeen["Blue Jay"]
birdsSeen["Albatross"]

Accessing dictionary values.
Accessing dictionary values.

Interestingly, unlike with an array, you can request an item that doesn’t exist without causing a crash. You’ll get back nil if the dictionary doesn’t contain that key.

Looking closely at the result, you’ll see that the type is Int?. This indicates an optional integer which is a value that might be nothing — nil — and might be an integer. You’ll learn more about optionals in the next chapter.

Modifying a Dictionary

You’ve seen how to add values to a dictionary and how to query them, but what about editing existing values? You can overwrite them, using their key:

birdsSeen["Blue Jay"] = 2
print(birdsSeen)

That’s fine if you know the new value, but what if you want to edit the existing value? What happens if you see another robin?

birdsSeen["Robin"] += 1

Incrementing a value in a dictionary.
Incrementing a value in a dictionary.

This fails because Swift can’t be sure that there is a value for the key “Robin”. You’ll learn all about optionals and unwrapping later, so for now, accept that this can’t work.

But there’s a convenient way to tell a dictionary to use the value it has, or to use a default value if it doesn’t have one already:

birdsSeen["Robin", default: 0] += 1
birdsSeen["Penguin", default: 0] += 1
print(birdsSeen)

Dictionary default values.
Dictionary default values.

In this example, there was an existing value for “Robin” so Swift was able to increment it by one. There was no value for “Penguin” so Swift incremented the default value of zero.

The number of elements in a dictionary is accessible using the count property and you can remove an element by setting its value to nil:

birdsSeen.count
birdsSeen["Penguin"] = nil
print(birdsSeen)
birdsSeen.count

Counting and deleting from a dictionary.
Counting and deleting from a dictionary.

It did seem unlikely that you’d seen a penguin on your walk!

Now that you know about Swift’s data types, constants, variables and collections, you’re ready to jump back to Xcode in the next chapter.

Key Points

  • The Swift REPL allows you to run Swift code in the Terminal. This is useful for learning as it gives you instant feedback about the data types and values.
  • Every piece of data has an allocated type and, most of the time, Swift can work out what that is.
  • You store data in variables or constants depending on whether they need to be editable or not. In computer terms, variables are mutable and constants are immutable.
  • Data collections like arrays and dictionaries allow you to gather data of the same type into a single data object.

Where to Go From Here?

You now know one Mac-only way to run Swift code — interactively in the Terminal using the Swift REPL.

In the next chapter, you’ll expand on your knowledge as you learn a different way to run Swift, this time using Xcode.

For the official Swift information and guides, go to Swift.org.

To read about Swift in more depth, check out our Swift Apprentice: Fundamentals book.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.