Swift 3 Tutorial
In this Swift 3 tutorial, we’ll focus on how beginners may approach going from complete beginner to having a basic grasp on Swift, and we’ll be working with Swift 3. We chose to write this tutorial because newcomers will find many tutorials out there that are out of date, so it’s not appropriate to simply write a “what’s new in Swift 3” post. Instead we’ll approach this from the perspective of someone who has some programming experience in at least one other language, and we’ll teach you what’s unique about Swift, and how to use it effectively. Ready? Let’s go!
Constants and Variables
Any Swift variable is either a constant or not. Not to be confused with the type of the variable, such as Float or Int, constants and variables are just ways to describe terms that hold a value which can change (are mutable), and constants that can not be changed (becuase they are immutable).
To define a constant, use the let keyword.
Example:
let
name =
"Jameson"
If you were to try and change the value of name, you would be unable to do so, and the Swift compiler would produce an error.
let
name =
"Jameson"
name =
"Bob"
error: cannot assign to value:
'name'
is
a
'let'
constant
name =
"Bob"
~~~~ ^
On the other hand, by using the var keyword, we define a variable that can change:
var
name =
"Jameson"
name =
"Bob"
This code does not produce an error.
In general, you should always default to using the let
keyword, unless you know you need a var
keyword. This leads to code that is ultimately safer. If you define a constant, and later attempt to modify it, you will get an error and at that time can determine if you should switch to use the var
keyword, or if the error is giving you a hint that maybe you should rethink the current logic flow. In general, immutability is preferred over mutability; it simply leads to less programmers errors and makes it easier to reason about your code.
Basic Types
In Swift, a type is indicated by declaring a variable, then putting a colon, folowed by the type name. For example to declare an integer, which is of Swift type Int
, you could use the following:
let
age:
Int
= 5
Or similarly, if you want to declare a string:
let
name:
String
=
"Jameson"
Swift supports type inference, so you can usually omit the type information, and let the compiler determine what the type should be based on it’s initial value.
let
age = 5
let
name =
"Jameson"
The types for age
and name
are still Int
and String
respectively, but we can skip the type annotation, because it is obvious that 5
is an Int
, and "Jameson"
is a String
.
Remember, the let
keyword simply makes these values constant. If we expected the value of age to change, but not name, we might write these declarations like so:
var
age = 5
let
name =
"Jameson"
Now if we want to update the age
value, it’s possible to do:
var
age = 5
let
name =
"Jameson"
age = 25
print
(age)
25
Working with Strings
It’s frequently useful to print a command to the console or otherwise evaluate a String
containing other variables. For example, I might want to form a sentence with my variables age
and name
and print it to the console. I can do this using the +
operator between each String
.
let
age =
"15"
let
name =
"Robb"
let
sentence = name +
" is "
+ age
print
(sentence)
Robb
is
15
A shortcut for this is to write your String
as you normally would without the +
operator seperating each string, and put each variable inside of a set of parentheses, preceeded by a backslash \
.
let
sentence =
"\(name) is \(age)"
print
(sentence)
Robb
is
15
As you can see this has the same effect, but is much easier to read and compose sentences.
One thing you may have noticed is that age
is now of type String
because it was assigned the value "15"
instead of just 15
without the quotes. This is because concatenating a String
and an Int
will not automatically cast the Int
to String
, which is a neccessary step before concatenating is possible.
Or in other words, this code will produce an error:
let
age = 15
let
name =
"Robb"
let
sentence = name +
" is "
+ age
print
(sentence)
Error
:
Binary
operator
'+'
cannot be applied to operands of type
'String'
and
'Int'
So what we have to do is get age
converted to a String
before using it here. Thing is done by casting
the variable. Simply call the String constructor and pass in the Int
value:
let
age = 15
let
name =
"Robb"
let
stringAge =
String
(age)
let
sentence = name +
" is "
+ stringAge
print
(sentence)
Robb
is
15
We created a new variable called stringAge
here, but we also could have performed the cast in-place, because the string interpolation will evaluate each expression separately, and the same goes for the contents of the parenthesis when interpolating strings that way:
let
age = 15
let
name =
"Robb"
let
sentence = name +
" is "
+
String
(age)
print
(sentence)
print
(
"\(name) enjoys being \(String(age))"
)
Robb
is
15
Robb
enjoys being
15
Optionals
In Swift, there is also the concept of the optional. An optional is just a variable that can be nil, null, or otherwise not set to any value. In general, you can think of any variable in most other programming languages as being an optional. The “optionality” of a variable is declared by appending a question mark (?) on to the end of the type name in a type annotation. So continuing the example above, where we know age and name will always be set, we might want to add another variable that could be nil
, because it is possible that it just isn’t present. Let’s use favoriteColor
as an example. Many people have a favorite color, but it’s possible someone doesn’t, or we just don’t have the data. We would declare this variable as an optional, and not assign it to any value.
var
favoriteColor:
String
?
Implicit in the declaration of an optional with no value set, is the assignment to nil. We can verify this by examining the value of favoriteColor after declaring it as an optional by printing it to the console using the print()
function.
var
favoriteColor:
String
?
print
(favoriteColor)
nil
We can later assign something to favoriteColor
and see that it is no longer nil
.
var
favoriteColor:
String
?
favoriteColor =
"Blue"
print
(favoriteColor)
Optional
(
"Blue"
)
Note that instead of just getting the string "Blue"
, we get Optional("Blue")
. This is because the value is still “wrapped” inside of the optional.
You can think of optionals like a birthday gift. The box the gift comes in, wrapped up in some paper with pretty balloons on it, could actually be empty. A rather cruel gift to give someone on their birthday, but it’s possible to do. It could also have something inside of it. But either way, if we pick it up and look at it, what we have is not the thing inside of of it in our hands, but it’s just the wrapped box itself.
If we want to get at the thing inside, we need to unwrap the gift first. This is the same way optionals work. When we pass around optional variables and interact with them, we’re really working with a container that may or may not have anything inside of it. Similar to our gift, the optional must be unwrapped before it can be used.
Declaring our optional with no value is valid Swift and will compile just fine. But, if we tried to declare this variable without the optional syntax, it would present an error.
There are also variables in Swift that are not optional. They always have a value. If you tried to assign nil to a non-optional variable, you will get a compiler error:
var
favoriteColor =
"Blue"
favoriteColor =
nil
error:
nil
cannot be assigned to type
'String'
Similarly, non-optional values can not be assigned to nil during their declaration:
var
favoriteColor:
String
error: variables must have an initial value
Unwrapping Optionals
So we know what optionals are, and that they allow for a variable to become nil, and we know that optionals are containers rather than values themeselves. So, in our programs when we want to access the contents of an optional, how do we do it? There are several ways so let’s go over them now.
First, and most commonly, you will unwrap optionals using optional binding. In optional binding, you will assign a new variable to the value of an optional within an if statement. If the optional contains a value, this variable will be set, and the code block following this statement will be executed.
Let’s look at an example. Here we will declare two optionals, one called favoriteAnimal
which is set to Fox
, and one set to favoriteSong
, which we will not set (it will remain nil
)
var
favoriteAnimal:
String
?
var
favoriteSong:
String
?
favoriteAnimal =
"Fox"
Let’s employ optional binding to discover if each variable is set, and if so we’ll print a sentence containing the value to the console. First we’ll do it with favoriteAnimal
.
if
let
unwrappedFavoriteAnimal = favoriteAnimal {
print
(
"Favorite animal is: "
+ unwrappedFavoriteAnimal)
}
Favorite
animal
is
:
Fox
In the event that the value is not set, we simply will trigger whatever is in the else
block, or nothing at all if an else
block isn’t specified.
if
let
unwrappedFavoriteSong = favoriteSong {
print
(
"Favorite song is: "
+ unwrappedFavoriteSong)
}
else
{
print
(
"I don't know what your favorite song is!"
)
}
I don't know what your favorite song
is
!
If we need to unwrap multiple optionals, and we require all of them to proceed with a bit of logic, we need to check each one:
var
favoriteAnimal:
String
?
var
favoriteSong:
String
?
favoriteAnimal =
"Fox"
favoriteSong =
"Shake it Off"
if
let
unwrappedFavoriteSong = favoriteSong {
if
let
unwrappedFavoriteAnimal = favoriteAnimal {
print
(unwrappedFavoriteSong +
" "
+ unwrappedFavoriteAnimal)
}
}
This gets kind of messy kind of fast, so Swift offers a shortcut to unwrap multiple variables at once:
var
favoriteAnimal:
String
?
var
favoriteSong:
String
?
favoriteAnimal =
"Fox"
favoriteSong =
"Shake it Off"
if
let
unwrappedFavoriteSong = favoriteSong,
let
unwrappedFavoriteAnimal = favoriteAnimal {
print
(unwrappedFavoriteSong +
" "
+ unwrappedFavoriteAnimal)
}
Collections
Swift has multiple collection types, the most common of which being arrays, sets, and dictionaries.
Array
Let’s take a look at an example of an Array
let
starks: [
String
] = [
"Eddard"
,
"Catelyn"
,
"Robb"
,
"Sansa"
]
Here we have a basic Array
, which is of type [String]
. The square brackets indicate that this is an array of String
objects, rather than just being a single String
. As usual, Swift can infer this type data too, just by examining the initial assignment:
let
starks = [
"Robb"
,
"Sansa"
,
"Arya"
,
"Jon"
]
We can access elements of this array in a variety of ways, such as using an Int
index, calling the various collections methods.
let
starks = [
"Robb"
,
"Sansa"
,
"Arya"
,
"Jon"
]
print
( starks[0] )
print
( starks[2] )
print
( starks.first! )
Robb
Arya
Robb
You’ll note that arrays are zero-indexed, so the first element in the array "Robb"
is accessed using starks[0]
Additionally, you may notice that while the first
method returns an optional (and therefore is being force-unwrapped with the !
symbol), the indexed accessors does not return an optional. If you try to access an index in an array that is not present, your program will fail at runtime! So always check the length of arrays when accessing by index:
if
starks.count >= 4 {
print
( starks[3] )
}
There are ways to make this type of checking automated, but it is not done by default for performance reasons.
Hashable Types / Dictionary
Dictionaries are able to store values based on a key, typically the key is of type String
, but it could actually be many different Swift types. In this example we create a basic Dictionary
with String
keys and Int
values for the age of each person:
let
ages = [
"Robb"
: 15,
"Sansa"
: 12,
"Arya"
: 10,
"Jon"
: 15]
We can access these values by their String
keys:
print
( ages[
"Arya"
]! )
print
( ages[
"Jon"
]! )
10
15
Note that we’re unwrapping these because they are optional values, and could potentailly be nil. It is generally safer to use optional binding to unwrap the value from a Dictionary
, especially if you have reason to believe the value could often be nil
.
if
let
aryasAge = ages[
"Arya"
] {
print
(
"Arya is \(aryasAge) years old"
)
}
Arya
is
10 years old
We can also store arrays inside of dictionaries, or dictionaries inside of arrays, or a mix of both.
let
families = [
"Stark"
: [
"Robb"
: 15,
"Sansa"
: 12,
"Arya"
: 10,
"Jon"
: 15],
"Baratheon"
: [
"Joffrey"
: 13,
"Tommen"
: 8]
]
let
tommensAge = families[
"Baratheon"
]![
"Tommen"
]!
print
(
"Tommen is \(tommensAge) years old"
)
Tommen
is
8 years old
The type of houses
here would be [String: [String: Int]]
Or in other words it is a dictionary with a String
key, and it’s values are [String: Int]
, another dictionary with String
keys and Int
values.
Set
A Swift 3 Set
is similar to an Array
, except the values in a Set
are unique and unordered.
Initializing a Set
looks almost exactly like intitializing an Array
, the only different is the type:
let
colors:
Set
<
String
> = [
"Blue"
,
"Red"
,
"Orange"
,
"Blue"
]
This code creates a new set of type String
. The greater than / less than symbols <
and >
are used to indicate Swift generic types, including the types of a Set
as shown here.
You’ll notice we included "Blue"
twice in our list, but if we print out the contents of colors
, we only see it once:
let
colors:
Set
<
String
> = [
"Blue"
,
"Red"
,
"Orange"
,
"Blue"
]
print
(colors)
[
"Orange"
,
"Red"
,
"Blue"
]
You may also notice that the ordering is inconsistent. Sets do not maintain any particular order for their contents.
We can not access members of a Set
using indexes as we can with arrays, but instead we use the methods built-in to the Set
type to add and remove objects. We can also call the contains
method to check if the Set
includes something.
var
colors:
Set
<
String
> = [
"Blue"
,
"Red"
,
"Orange"
,
"Blue"
]
colors.insert(
"Black"
)
colors.insert(
"Black"
)
colors.remove(
"Red"
)
print
(colors)
print
(colors.contains(
"Black"
))
print
(colors.contains(
"Red"
))
[
"Black"
,
"Orange"
,
"Blue"
]
true
false
Constructing sets of objects is a common way to catalogue what is included or excluded in a list of things, as long as there is no need to order or have duplicates of the objects.
There are many methods I have not mentioned, and I would encourage you to read through Apple’s documentation on each of these three classes to further familiarize yourself with them.
Tuples
Tuples are not technicaly a collection, but instead simply multiple variables that can be passed around with a single identifier.
let
fullName = (
"Jameson"
,
"Quave"
)
The type of the tuple
here is (String, String)
, and we can manually access each numbered tuple element using dot-syntax, followed by the index:
let
fullName = (
"Jameson"
,
"Quave"
)
print
(fullName.1)
print
(fullName.0)
Quave
Jameson
Tuples can also be deconstructed in to new variable names:
let
(first, last) = (
"Jameson"
,
"Quave"
)
print
(first)
Jameson
Since we’re not using the last name here, we could just ignore that value by using an underscore _
and still deconstruct the first name:
let
(first, _) = (
"Jameson"
,
"Quave"
)
print
(first)
Jameson
Tuples are useful when you have a method that you want to return multiple values.
Control Flow
Control flow in Swift looks pretty similar to other languages. At the most basic level we have the if
and else
statements.
if
10 > 5 {
print
(
"10 is greater than 5."
)
}
else
{
print
(
"10 is not greater than five."
)
}
10
is
greater than 5
You can alternatively put your conditions for an if statement in parenthesis:
if
(10 > 5) {
...
Swift also support the switch statement, and checks at compile for whether or not you have exhaustively covered all possibilities. If you do not (or can not) specifically handle all possibiities, then you can use the default:
case to handle everything not explicitly handled.
let
name =
"Jameson"
switch
(name) {
case
"Joe"
:
print
(
"Name is Joe"
)
case
"Jameson"
:
print
(
"This is Jameson"
)
default
:
print
(
"I don't know of this person!"
)
}
This
is
Jameson
Here because the value of name
is "This is Jameson"
, we match the 2nd case and execute the line
...
print
(
"This is Jameson"
)
...
If however we set the name to be something not present in our list of cases, such as "Jason"
, the switch would fall back to the default
case.
let
name =
"Jason"
switch
(name) {
case
"Joe"
:
print
(
"Name is Joe"
)
case
"Jameson"
:
print
(
"This is Jameson"
)
default
:
print
(
"I don't know of this person!"
)
}
I don't know of this person!
Loops and Collections
Swift 3 does not support the classical C-style for loops you may be used to, and instead opts for enumeration and for-each style loops for the for element in array
syntax.
For example if we have an array names
, and we want to print each one seperately, we can do so with a for loop:
let
names = [
"Robb"
,
"Sansa"
,
"Arya"
,
"Jon"
]
for
name
in
names {
print
(
"Name: \(name)"
)
}
Name
:
Robb
Name
:
Sansa
Name
:
Arya
Name
:
Jon
This is great if you happen to want to loop over an array, but without C-style arrays, how would we loop over a series of numbers? The answer comes in the form of Swift’s Range
and Stride
. Let’s say we wanted to count to 10, by threes, we could do that by using a Range
from 1 to 10 using the syntax 1...10
. Then we could only print each number that is evenly divisible by three by using the modulo %
operator and checking for a remainder of 0.
for
i
in
1...10 {
if
i % 3 == 0 {
print
(i)
}
}
3
6
9
There is another option however, to only iterate every third item (or any arbitrary delta), known as a stride
. A stride
can be created using a variety of methods, but the most common is stride(from: , to:, by:)
where the from
value is where the stride starts, to
is where it ends, and by
is how much each value changes to approach the to
. If that sounds a little confusing just look at this code sample
let
byThrees = stride(from: 3, to: 10, by: 3)
for
n
in
byThrees {
print
(n)
}
3
6
9
It’s almost readable in english, you might say you are “counting” from 3 to 10 by 3. Here we create a stride
and store it in a variable named byThrees
, but we could use it directly in the loop as well:
for
n
in
stride(from: 3, to: 10, by: 3) {
print
(n)
}
3
6
9
Collections also all have an indices
property that can be used to loops. This returns an array of the indices a collection has, useful for iterating or filtering some but not all of a collection. For example, back in our name collection we may want only the first three names, which we can retrieve like so:
let
names = [
"Robb"
,
"Sansa"
,
"Arya"
,
"Jon"
]
for
nameIndex
in
names.indices {
if
(nameIndex < 3) {
print
(names[nameIndex])
}
}
Robb
,
Sansa
,
Arya
There is also the enumerate
method on collections, that allow you to get both the index and value from an array as you loop over it:
let
names = [
"Robb"
,
"Sansa"
,
"Arya"
,
"Jon"
]
for
(index, name)
in
names.enumerated() {
print
(
"\(index): \(name)"
)
}
0:
Robb
1:
Sansa
2:
Arya
3:
Jon
There are still more ways to enumerate loop over objects in Swift 3, but these are the most commonly used.
You may notice in our for loop we are assigning to two variables at once, both index
and name
. These are seperated by commas and surrounded by parenthesis in orer to delineate there are two named variable we expect returned from the enumerated()
method. These are techincally deconstructed tuples
.
This concluded the fundamentals portion of the Swift 3 tutorial. Next, I’ll show you how to use what you’ve learned here in real-world scenarios. Keep up with these posts by subscribing below. You’ll be emailed when I make new posts, and post video tutorials and other neat stuff, never spam 🙂
game of thrones fan ?
A bit 🙂