About this book

Take your first foray into programming for Apple devices with Swift.Swift is fundamentally different from Objective-C, as it is a protocol-oriented language. While you can still write normal object-oriented code in Swift, it requires a new way of thinking to take advantage of its powerful features and a solid understanding of the basics to become productive.

Publication date:
May 2018
Publisher
Packt
Pages
196
ISBN
9781789534313

 

Chapter 1. Swift Basics

Swift is a relatively new programming language designed by Apple Inc., and was initially made available to Apple developers in 2014—primarily intended as a replacement for the aging Objective-C language that was the foundation of OS X and iOS software development at the time.

Unlike many object-oriented languages, which are based on older procedural languages—for example, C++ and Objective-C are based on C—Swift was designed from the ground up as a new, modern, object-oriented language that makes programming faster and easier, and helps developers produce expressive code that's less prone to errors than many languages.

While not based on an older language, Swift, in the words of its chief architect, Chris Lattner, " was inspired by drawing ideas from Objective-C, Rust, Haskell, Ruby, Python, C#, CLU, and far too many others to list." (Chris Lattner home page: http://nondot.org/sabre/).

Swift was initially a proprietary language, but was made open source software in December 2015 as of its version 2.2. While Swift remains primarily used by developers targeting the Apple macOS and iOS platforms, Swift is also fully supported on Linux, and there are unofficial ports under development for Windows as well.

The objective of this lesson is to learn the fundamentals of using the Swift programming language. In this lesson, you'll learn basic Swift syntax and program structure. You'll also learn how to use Swift built-in data types and enums, and how to declare and use Swift variables and constants. Let's get started.

 

Lesson objectives


By the end of this lesson, you will be able to:

  • Explain the program structure and syntax of Swift programs

  • Declare and use Swift variables and constants

  • Use the various built-in Swift data types

  • Use the Swift enum language syntax

 

Swift Program Structure


In this first section, we'll look at the basic language syntax for Swift, and you'll write your first fully functional Swift program.

Like many modern programming languages, Swift draws its most basic syntax from the programming language C. If you have previous programming experience in other C-inspired languages, such as C++, Java, C#, Objective-C, or PHP, many aspects of Swift will seem familiar, and many Swift concepts you will probably find quite familiar.

We can say the following about Swift's basic syntax:

  • Programs are made up of statements, executed sequentially

  • More than one statement are allowed per editor line when separated by a semicolon (;)

  • Units of work in Swift are modularized using functions and organized into types

  • Functions accept one or more parameters, and return values

  • Single and multiline comments follow the same syntax as in C++ and Java

  • Swift data type names and usage are similar to that in Java, C#, and C++

  • Swift has the concept of named variables, which are mutable, and named constants, which are immutable

  • Swift has both struct and class semantics, as do C++ and C#

If you have prior experience in other C-inspired languages, such as Java, C#, or C++, Swift has some improvements and differences that will take some time and practice for you to become accustomed to:

  • Semicolons are not required at the end of statements—except when used to separate multiple statements typed on the same line in a source file.

  • Swift has no main() method to serve as the program's starting point when the operating system loads the application. Swift programs begin at the first line of code of the program's source file—as is the case in most interpreted languages.

  • Functions in Swift place the function return at the right-hand side of the function declaration, rather than the left.

  • Function parameter declaration syntax is inspired by Objective-C, which is quite different and often at first confusing for Java, C#, and C++ developers.

  • The difference between a struct and a class in Swift is similar to what we have in C# (value type versus reference type), but not the same as in C++ (both are the same, except struct members are public by default).

For those coming to Swift from Java, C++, C#, and similar languages, your previous experience with other C-inspired languages will help accelerate your progress learning Swift. However, be sure to study the language syntax carefully and be on the lookout for subtle differences.

Hello, World!

When learning a new language, it's traditional for a first program to make sure the development environment is installed and properly configured by writing a program that outputs something to the screen. That's what we'll do next.

Now, let's use an Xcode playground to create a simple Swift program to display the string Hello, World to the playground console, by following these steps:

  1. Begin by launching Xcode. You should be presented with a Welcome to Xcode screen with the following commands listed on the left:

    1. Get started with a playground

    2. Create a new Xcode project

    3. Clone an existing project

  2. Since we'll be writing code but not building an application in this lesson, choose the Get started with a playground option to open an interactive code window.

    Note

    Xcode playgrounds are provided to allow developers to quickly experiment with Swift code. In addition to learning Swift, as we are in this lesson, you can use playgrounds to develop functions and test whether a specific fragment of Swift code will do what you expect.

  3. Choose Blank as the playground template, and then press the Next button.

  4. Next, Xcode will prompt where to save the playground. This will save your code in a file with a playground file extension. Name the playground HelloWorld, and save it to your desktop.

  5. When Xcode creates a new playground, it adds some default code to the editing window. Press A on your keyboard and then the Delete key on the keyboard to delete the sample code.

  6. In the now-blank editor window, add the following two lines of code:

    let message = "Hello, World."
    print(message)

Congratulations! You've just written your first Swift program. If you see the text Hello, World. output in the bottom pane of the playground window, your program has worked.

Before we move on, let's look at the structure of the playground window:

Note the following regions in the playground window, as indicated by the numbers within the red circles:

  • 1: At the top of the window is a status bar which tells you the state of the playground.

  • 2: The editing pane of the window is where you type the code to run in the playground.

  • 3: The right-hand pane of the playground window shows information about the effect of each line of code. In this simple program, it shows the value message has been set to ("Hello, World."), and the text that was sent to the console ("Hello, World.\n"). Note the right pane discloses that the print() function added a newline (\n) character to the output.

  • 4: The output pane of the playground window shows the debug console, which in this case displays what the Swift program has output. If your code has errors, the debug console will output information about those errors as well.

Now that we have a development environment up and running where we can create and run Swift code, let's move on to learning about and using the Swift language.

 

Swift Variables and Constants


Virtually all programming languages include the ability for programmers to store values in memory using an associated name chosen by the programmer. Variables allow programs to operate on data values that change during the run of the program.

Declaring Swift Variables

A Swift variable declaration uses the following basic syntax:

var <variable name> : <type> = <value>

Given this syntax, a legal declaration for a Pi variable would be the following:

                        var pi : Double = 3.14159

This declaration means: create a variable named Pi , which stores a Double data type, and assign it an initial value of 3.14159.

Note

The Swift Standard Library has Pi built in, accessed by using the Float.pi and Double.pi properties.

Variables Versus Constants

You may want to store a named value in your program that will not change during the life of the program. In the previous example, the value of Pi should never change during the course of a program. How can we ensure that, once defined, this named value can never be accidentally changed by our code?

Swift variables are declared using the var keyword, while Swift constants are declared using the let keyword, for example:

var pi1 = 3.14159
let pi2 = 3.15159

In this code, the named value pi1 is a variable, and its value can be changed by the code after it is declared. The following line of code later in the program would be legal, even though it would result in an invalid value for pi1:

pi1 = pi1 * 2.0

On the other hand, since pi2 was declared as a constant, using the let keyword, the following line of code later in the program would result in a compile-time error, since changing a let constant is illegal:

pi2 = pi2 * 2.0

Generally, any time you create a named value that will never be changed during the run of your program, you should use the let keyword to create a constant. The Swift compiler enforces this recommendation by creating a compile-time warning whenever a var is created that is not subsequently changed.

Note

Other than the restriction on mutating the value of a constant once declared (for safety), Swift variables and constants are used in virtually identical ways, and you usually won't think about whether a symbol is a variable or a constant after declaring it.

Type Inference

In the previous example, we created the variable pi1 without specifying its data type. We took advantage of a Swift compiler feature called type inference.

When you assign the value of a variable or constant as you create it, the Swift compiler will analyze the right-hand side of the assignment, infer the data type, and assign that data type to the variable or constant you're creating. For example, in the following declaration, the compiler will create the variable name as a String data type:

var name = "George Smith"

As a type-safe language, once a data type is inferred by the compiler, it remains fixed for the life of the variable or constant. Attempting to assign a non-string value to the name variable declared above would result in a compile-time error:

name = 3.14159  // Error: "Cannot assign value of type 'Double' to 'String'

While Swift is a type-safe language, where variable types are explicit and do not change, it is possible to create Swift code that behaves like a dynamic type language using the Swift Any data type. For example, the following code is legal in Swift:

var anyType: Any
anyType = "Hello, world"
anyType = 3.14159

While this is legal, it's not a good Swift programming practice. The Any type is mainly provided to allow bridging between Objective-C and Swift code. To keep your code as safe and error-free as possible, you should use explicit types wherever possible.

Variable Naming

Swift variables and constants have the same naming rules as most C-inspired programming languages:

  • Must not start with a digit

  • After the first character, digits are allowed

  • Can begin with and include an underscore character

  • Symbol names are case sensitive

  • Reserved language keywords may be used as variable names if enclosed in backticks (for example, `Int`:Int = 5)

When creating variable and constant names in Swift, the generally accepted naming convention is to use a camelCase naming convention, beginning with a lowercase letter. Following generally accepted naming conventions makes code easier for others to read and understand (https://swift.org/documentation/api-design-guidelines/#follow-case-conventions).

For example, the following would be a conventional variable declaration:

var postalCode = "48108"

However, the following would not be conventional, and would be considered incorrect by many other Swift developers:

var PostalCode = "48108"
var postal_code  = "48108"
var POSTALCODE = "48108"

Unlike many other programming languages, Swift is not restricted to the Western alphabet for its variable name characters. You may use any Unicode character as part of your variable declarations. The following variable declarations are legal in Swift:

var helloWorld = "Hello, World"
var 你好世界 = "Hello World"
var 😊 = "Smile!"

Note

Just because you can use any Unicode character within a variable name, and can use reserved words as variables when enclosed in backticks, it doesn't mean you should. Always consider other developers who may need to read and maintain your code in the future. The priority for variable names is that they should make code easier to read, understand, and maintain.

Working with Variables

In this section, you'll use an Xcode playground to create a variable and constant, and observe the difference between them. So, let's get started.

To work with variables, follow these steps:

  1. Launch Xcode as before, and create a new playground named Create a Variable.playground.

  2. Add the following code to the playground to create a constant (that is, an immutable variable) named name, and a variable named address:

    let name = "John Doe"
    var address = "201 Main Street"
    print("\(name) lives at \(address)")

    In this code, both name and address store string text in named memory locations. And we can include them both in the print statement in the same way.

  3. Now add the following code to change John Doe's address and print the new information to the console:

    address = "301 Fifth Avenue"
    print("\(name) lives at \(address)")

    In the console output, the address is changed as expected.

  4. Finally, let's try to change the string stored in the name variable:

    name = "Richard Doe"

    In this case, the Swift compiler generates a compile-time error:

    Cannot assign to value: 'name' is a 'let' constant

By declaring name as an immutable variable with let, we let the compiler know no code should be allowed to change the content of the variable after its value is initially set.

Tuples

One of Swift's unique language features is its inclusion of tuples. By default, variables and constants store a single value. Tuples allow a variable or constant name to refer to a set of values. While tuples do not exist in many languages, you can think of them as compound values, and they function almost identically to a structure, which is a single named object which can store more than one variable embedded within it.

By using a tuple, we could take the following variable declaration:

var dialCode = 44
var isoCode = "GB"
var name = "United Kingdom"

We could combine it to the following:

var country = (44, "GB", "United Kingdom")

Then we can access the individual members of the tuple as follows:

print(country.0)  // outputs 44
print(country.1)  // outputs GB
print(country.2)  // outputs United Kingdom

Tuple members can also be given individual names, as follows:

var country = (dialCode: 44, isoCode: "GB", name: "Great Britain")

print(country.dialCode)  // outputs 44
print(country.0)              // also outputs 44!
print(country.isoCode)   // outputs GB
print(country.name)       // outputs United Kingdom

Swift functions can accept multiple input parameters, but return only one value. A common use case for a tuple variable type is to include more than one value from a function:

func getCountry() -> (dialCode: Int, isoCode: String, name: String) {
    let country = (dialCode: 44, isoCode: "GB", name: "United Kingdom")
    return country
}

let ret = getCountry()

print(ret)

A second way to return multiple values from a function is to use inout parameters, which allows a function to change the value of an input parameter within that function.

While there are valid use cases for changing inout parameter values, returning a tuple has the advantage of returning a value type—rather than modifying input values.

Note

Tuples behave much like structures—which are predefined compound data types in Swift and many other languages. You may be tempted to use tuples rather than making the extra effort to create structures since they provide similar utility. Be careful not to overuse tuples. They are convenient for ad hoc, lightweight composite data types, but when used in complex programming, use cases can result in code that's more difficult to understand and harder to maintain. Use tuples as they're intended, as a means to bundle a few related components of a data element.

Creating a Tuple

Let's look at creating a tuple. We'll use an Xcode playground to create and use a tuple. Here are the steps:

  1. Launch Xcode as before, and create a new playground named Create a Tuple.playground.

  2. Add the following code to the playground to create a tuple containing a person's name, address and age:

    let person1 = ("John Doe", "201 Main Street", 35)
    print("\(person1.0) lives at \(person1.1) and is \(person1.2) years old.")

    This code is very similar to the previous , except that we've used a tuple to group together values describing John Doe—rather than using separate variables for each element.

    While this syntax is legal, acceptable, and common, it can begin to result in difficult to understand and maintain code—especially when a tuple contains more than two simple values. To make a tuple more maintainable, you can give variable names to each of its components.

  3. Add the following to the playground:

    let person2 = (name: "Jane Doe", address: "301 Fifth Avenue", age: 35)
    print("\(person2.name) lives at \(person2.address) and is \(person2.age) years old.")

    In this second approach, each member of the tuple has a descriptive name, making it easier for the reader of the program to understand and maintain the code.

Optionals

Another unique language feature Swift provides is the optional. In most programming languages, all variables and constants must hold some value. But, in the real world, sometimes a value is unknown. For example, an address may or may not contain a second address line, and more than 60 countries in the world don't use postal codes. Optionals allow variables to indicate whether their value is missing (that is, not assigned), or is truly a  blank value.

Note

When variables are declared optional in Swift, they behave very similarly to column values in SQL database such as Oracle, SQL Server, and MySQL.

Optionality for Swift variables is optional (pun intended). To declare a variable as an optional, add a question mark (?) to the end of its data type (or assign another optional variable's value to it so the optional property is inferred from the existing variable).

The following variable name is not an optional:

var name: String = "Hello"

This next variable name is an optional, and has an initial value of nil:

var name: String?

The presence of the question mark intuitively expresses that the variable may—or may not—contain a string. If the optional is not assigned a value, it will automatically be set to nil, meaning it has no value.

Declaring an Optional

Earlier in this lesson, we declared variables with initial values assigned. These variables are not optional, have a value, and can never be assigned a nil value, or an unwrapped optional variable's value.

In this section, we define a variable as an optional by adding a question mark to the type name, which makes it subject to the Swift compiler's optional validation rules.

A third possibility is to declare a force unwrapped variable—a variable that can be nil, but is not optional. This type of variable is declared by placing an exclamation point (!) after the type (rather than the question mark (?) for the optional), for example:

var customerAge: Int!

When a variable is declared in this fashion, the compiler will allow the variable to be assigned a nil value at any time, but will not warn the programmer at compile time when the variable's value is (or could be) assigned a nil value.

There are limited circumstances where this technique is required, and in general it should be avoided.

Note

Why don't we make all variables optional? Optional is a powerful Swift feature, but working with optional variables requires more code as they are used, primarily to check for nil values before accessing the optional value. In general, you should use optional variables when variables may be missing values, but not use optional variables when you know a variable will always have a value.

Working with Optionals

As mentioned, the simplest way to declare a variable as an optional is to append the data type with a question mark, for example:

var name: String?

Because of Swift's type inference, the following line of code will create a second variable of optional type:

var nameCopy = name

The syntax to assign a value to this variable is the same as it would be if the variable was not declared as optional:

name = "Adam Smith"

The difference between optional and non-optional variables is primarily when you access the value of an optional, which we'll cover next.

Optional nil Values

Optional variables in Swift can be directly compared to the absence of value (nil) and assigned a nil value. For example, in the following two statements, variable a initially has a value of 4, then is assigned a nil value, and then is checked for having a nil value:

var a: Int? = 4
a = nil
if a == nil {
  print("a is nil")
}

While the presence or absence of a value within an optional can be directly tested, extracting and using the value contained within an optional requires that the optional (the envelope) be unwrapped, and the content (value) extracted. We'll learn how to do this next.

Accessing Optional Values

Think of an optional as a value wrapped in an envelope. You cannot access the contents of an envelope without opening it (unwrapping it), and then removing the contents.

You can primarily unwrap an optional and use its value in two ways:

  • Force unwrap

  • Conditional unwrap

We'll learn each of these techniques next.

Force Unwrapping an Optional

Look at the two optional Int variables:

var a: Int?
var b: Int = 4

You could attempt to assign a to b, for example:

b = a

But this would result in a compile-time error:

Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?

As the error indicates, accessing the value of an unwrapped optional variable is (always) illegal. One approach to solving this problem is to force unwrap the variable as we use it. To force unwrap a variable, simply place an exclamation mark (!) after the variable name, for example:

b = a!

Force unwrapping is similar to using a type cast in many languages. In Swift, a force unwrap tells the compiler to assume that the optional contains a value.

However, a force unwrap shifts all the responsibility to the programmer for ensuring optionals actually have values. The above example, b = a!, would allow the code to compile, but would generate the following runtime error, and the application will crash:

Fatal error: Unexpectedly found nil while unwrapping an Optional value

Because variable a is an optional with no value, there is no value to extract from it to assign to b.

Note

Force unwrapping should not be viewed as a way to get around compiler type-safety features. Only use force unwrapping when you're absolutely certain that it's impossible for an optional variable to contain a nil value. In the following code, a force unwrap would be acceptable:

var a: Int? = 2
var b: Int = 4
b = a!

Conditionally Unwrapping Optionals

While there are times when force unwrapping variables is safe, you should typically take advantage of Swift's type-safety features by using conditional unwrapping.

With conditional unwrapping, we ask the compiler to first check whether the optional has a value, and return the value if present, or nil if not.

For example, to assign the value of optional a to a new, non-optional variable b, we can use the following code:

var a: Int? = 4
if let b = a {
   print(b)
}

This code snippet would print the value 4 to the console. If we had not assigned the initial value 4 to a, then nothing would have been printed.

Using Optionals

Use an Xcode playground to create and use an optional, by performing the following steps:

  1. Launch Xcode as before, and create a new playground named Using Optionals.playground.

  2. Add the following code to the playground to create an optional containing a person's name:

    var name: String? = nil
  3. Now add the following code to check whether the optional is nil:

    if name == nil {
        print("name is nil")
    } else {
        print("name is not nil")
    }

    Of course, since we assigned the value nil, it is nil.

    A more common way to check for a non-nil optional is to use the if/let syntax covered previously.

  4. Add the following code to assign a value to the optional content, then print it to the console:

    name = "John Doe"
    if let n = name {
        print(n)
    } else {
    print("the name is still nil")

    Because you assigned a value to the variable name, the string John Doe is printed to the console.

  5. Finally, comment out the variable assignment. The output will now change to the name is still nil, because the if/let syntax detected that the variable name contains no value.

The Swift guard Statement

It's very common that Swift functions should only execute when parameters passed to them are in an expected state. In early versions of Swift, the conditional unwrapping technique was often used to provide this type of safety checking. For example, a function that accepts an optional Int value, but should only proceed when the parameter is not nil might look as follows:

func doubleValue(input: Int?) -> Int? {
   if let i = input {
      return i * 2
   }
   return nil
}

While this function is only a few lines of code, imagine if the work done on the unwrapped variable was more complex. To allow parameter and other data state checking to be concisely done at the beginning of functions, Swift includes a guard keyword.

The following is a version of doubleValue that uses the guard syntax to place data state checks at the top of the function:

func doubleValue(input: Int?) -> Int? {
   guard let i = input else { return nil }
   return i * 2
}

This is the end of this section. Here, we have had a deep look at how to declare variables and constants in Swift. We also worked with tuples and optionals.

Activity: Variable Summary

In Swift, variables are declared before being used. Variables can be declared in various ways, and may not even need to have their type explicitly stated when the compiler can infer data type from initial assignment.

Use an Xcode playground to practice how to declare variables, constants, and tuples.

  1. Launch Xcode as before, and create a new playground named Topic B Summary.playground.

  2. Add the following code to the playground to create three variables storing values related to the weather conditions in Berlin:

    let cityName = "Berlin"
    var humidityPercentage: Double?
    var temperatureCentigrade: Double?

    Note that cityName is a constant, non-optional variable, with an initial string value. Since we know the name of the city in advance, and it doesn't change for this program, it's most appropriate to use let to declare this value as a constant.

    humidityPercentage and temperatureCentigrade are declared as optional, since we do not yet know the weather conditions in Berlin at the start of this program.

  3. Next, add the following line of code to create a tuple to collect the weather report data into a single variable named weather:

    var weather = (city: cityName, humidityPercentage: humidityPercentage, temperature: temperatureCentigrade)

    Recall that providing reference names for each tuple member is optional, but is included here to make the remaining part of the program clearer to other programmers who may need to read this program later.

  4. Next, set the value of humidity within the tuple:

    weather.1 = 0.70

    Note that even though you created a reference name for humidity (humidityPercentage), you can still set the value using the ordinal position within the tuple. The following line of code would probably be better in this case:

    weather.humidityPercentage = 0.70
  5. Now print the tuple to the console. On noticing that the variable provided is a tuple, the console print() function prints all members of the tuple—along with the reference names provided:

    print(weather)

    The output of the print statement is as follows:

    (city: "Berlin", humidityPercentage: Optional(0.69999999999999996), temperature: nil)
  6. Finally, print each of the tuple's components, each on its own line:

    print("City: \(weather.city)")
    print("Humidity: \(String(describing:weather.humidityPercentage))")
    print("Temperature: \(String(describing:weather.temperature))")

    The output of this code is as follows:

    City: Berlin
    Humidity: Optional(0.69999999999999996)
    Temperature: nil
 

Swift Data Types


Like most programming languages, Swift includes a full complement of built-in data types that store numbers, characters, strings, and Boolean values.

Note

In the previous section, we covered the use of Swift optionals, and worked through several examples declaring an Int variable as optional and non-optional. Keep in mind that any Swift variable, of any type, can be declared as an optional.

Numeric Data Types

Like most programming languages, Swift provides built-in numeric data types that represent either integer or floating-point values.

Int on 64-Bit Versus 32-Bit Platforms

While it's likely you'll develop Swift applications exclusively on 64-bit platforms, it's important to know that Swift is available on both 32-bit and 64-bit platforms. When using a generic integer numeric type ( Int or UInt), the generic type will be mapped to an underlying, specific equivalent that matches the current platform's word size. For example, on a 64-bit platform, Int is mapped to Int64; on a 32-bit platform, the same Int type is mapped to an Int32.

Built-In Numeric Data Types

The following table summarizes the available Swift numeric data types:

Type

Min value

Max value

Int8

-128

127

Int16

-32768

32767

Int32

-2.1 x 109

2.1 x 109

Int64

-9.2 x 1018

9.2 x 1018

UInt8

0

255

UInt16

0

65535

UInt32

0

4.3 x 109

UInt64

0

1.8 x 1019

Double

-1.8 x 10308

1.8 x 10308

Float

-3.4 x 1038

3.4 x 1038

Choosing the Appropriate Numeric Data Type

Conceptually, a UInt64 variable will consume four times more RAM than a UInt8 variable, so you may ask, " Should I tune my variables by selecting the smallest number of bits needed to meet requirements?"

While it may seem intuitive to select the numeric type that uses the least RAM to store the variable's expected range of values, it's usually preferable to use the generic integer types (for example, Int when declaring integers and Double when declaring floating-point numbers).

Note

This is a reference from The Swift Programming Language (Swift 4): " Unless you need to work with a specific size of integer, always use Int for integer values in your code. This aids code consistency and interoperability." Visit https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ for the official documentation.

Declaring and Assigning Integer Variables

Integer values may be instantiated using base 10 (decimal), base 2 (binary), base 8 (octal), or base 16 (hexadecimal) literal values, or by assigning another Int variable of the same type to the new variable.

For example, assigning the number 100 to a new Int variable holding a duration in minutes can be done in any of the following ways:

let minutes = 100         // decimal
let minutes = 0b1100100   // binary
let minutes = 0o144       // octal
let minutes = 0x64        // hexadecimal

Declaring and Assigning Floating Point Numbers

Floating-point numbers are represented by either Float or Double data types. In general, you should use Double—and employ Float only when specific circumstances require using the smaller, 32-bit numeric variable.

Declaring and assigning value to floating-point variables follows the same syntax rules as with integer variables. For example, the following statement creates a new Double variable interestRate, and assigns an initial value to it:

var interestRate = 5.34

Numeric Literal Grouping

When assigning constant values to numeric types, Swift provides a handy format to make code more readable: the underscore character is ignored when parsing numeric literals.

This feature is most commonly used to provide groupings of thousands in a large integer or floating-point assignments, but actually can be used to provide any grouping separation that makes code more readable. For example, the following statements all assign the value 100,000 to the variable minutes:

var minutes = 100000
var minutes = 100_000
var minutes = 10_00_00
var minutes = 0b110_000110_101000_00

Using the underscore for readability can also be used for floating-point literal values. For example, the following statements are equivalent:

var balance = 10000.44556
var balance = 10_000.44_556

Numeric Type Conversions

Like many fully compiled languages, Swift is a strongly typed language, and requires explicit type conversions (or casts) when assigning the value from one variable type to a variable of a different type.

Many new Swift programmers find that Swift is even stricter than languages they've used before. In many programming languages, the compiler will implicitly convert between data types during an assignment so long as the value contained within the variable being assigned (on the right of the equals sign) could not overflow the variable being assigned to (on the left of the equals sign).

In other words, in many languages, the following code would be legal, since an Int8 is known to always fit into an Int16 without a numeric overflow:

Int8 smallNumber = 3;
Int16 mediumNumber = smallNumber;

However, this equivalent code in Swift would result in a compile-time error:

var smallNumber: Int8 = 3
var mediumNumber: Int16 = smallNumber

This code would generate the following error:

error: cannot convert value of type 'Int8' to specified type 'Int16'

In Swift, it's always the programmer's responsibility to ensure that assignments have the same data type on the left and right of the assignment operator (that is, the equals sign). The following code corrects the compile-time error:

var smallNumber: Int8 = 100
var mediumNumber: Int16 = Int16(smallNumber)

Note

This requirement for explicit type assignment is one reason why most Swift programming uses the generic numeric variables Int and Double, except when specific usage requires tuning for numeric range or memory storage size.

Using Numeric Types

Now, let's see how to use various numeric variable types by following these steps:

  1. Launch Xcode as before, and create a new playground named Topic B Using Numeric Types.playground.

  2. Add the following code to the playground to create three Int variables, using binary, base10, and base16 literal notation, respectively:

    var base2 = 0b101010
    var base10 = 42
    var hex = 0x2A
  3. Now add the following three corresponding lines to print the data type and value for each of the variables you just created.

    print("Printing \(type(of: base2)): \(base2)")
    print("Printing \(type(of: base10)): \(base10)")
    print("Printing \(type(of: hex)): \(hex)")

    Examining the output, note that the three variables all have the same data type (Int) and same value (42 in base 10).

  4. Add the following lines of code to create two more variables, and to print the types and values for each:

    var scientific = 4.2E+7
    let double = 4.99993288828
    print("Printing \(type(of: scientific)): \(scientific)")
    print("Printing \(type(of: double)): \(double)")

    Note that both variables were created as Double types—even though the value of the first is actually an Integer. Swift's inference system doesn't always look at the actual value. In this case, the presence of scientific notation in the literal value caused Swift to assume the value should be a Double.

  5. Now add the following lines to cast and round the variable named double to an Int:

    var castToInt = Int(double)
    var roundToInt = Int(double.rounded())
    print("Printing \(type(of: castToInt)): \(castToInt)")
    print("Printing \(type(of: roundToInt)): \(roundToInt)")

    As you probably expected, the castToInt discarded the fractional value of the original double variable. For the roundToInt variable, we called the .rounded() function on the variable double, and then cast that value. Since 4.999 was rounded up to 5 before being cast, the Int contains the rounded value.

  6. Finally, add the following lines to create a very large unsigned integer and then print its type and value:

    var bigUnsignedNumber:UInt64 = 18_000_000_000_000_000_000
    print("Printing \(type(of: bigUnsignedNumber)): \(bigUnsignedNumber)")

    This code works as expected—printing an integer with 20 digits (the underscore is added to help count how many digits there are).

    Note that in this case, we specified UInt64 should be the data type for this variable. Had we not made the type explicit, Swift's type inference rules would have assigned the smaller Int data type to the variable, and it would have overflowed.

Again, keep in mind the inference engine examines the format of a constant perhaps more than the value of the numeric value being assigned. You should rely on the inference engine by default, but keep in mind you may sometimes need to be explicit when you know more about how a variable will be used than Swift can infer.

Boolean

In Swift, the Boolean data type is Bool, and stores a value of true or false. As with other data types, in the case that a Bool value is not yet known, a Bool can be declared as optional, for example, Bool?.

For example, the following code declares a Boolean in Swift, and then changes its value:

var isChecked = false
isChecked = true

Testing for the value of a Bool value is similar to how we do it in other C-inspired languages, for example:

if isChecked {
   // statements to execute if isChecked is true
}
if isChecked == true {
   // statements to execute if isChecked is true
}
if !isChecked {
   // statements to execute if isChecked is false
}

Character

The Character data type in Swift is an extended grapheme cluster.

What does that mean?

An extended grapheme cluster is an ordered sequence of one or more Unicode scalars (that is, values) that, when taken together, produce a human-readable character.

Most important to understand is that, unlike ASCII or ANSI character representations many programmers have worked with before, a Character in Swift may be made of more than one Unicode value.

In Swift 4, the underlying complexities of Unicode, scalar values, and extended grapheme clusters are largely managed for you, but as you begin to work natively with Unicode characters and strings, bear in mind that the Swift Character/String architecture was developed from the ground up around Unicode character representation—not ANSI/ASCII as many other languages were.

Assigning a Character

The following are two examples creating new Character variables, and assigning literal values:

let ch1:Character = "A"
let ch2:Character = "😎"

Note the following regarding this assignment:

  • In Swift, a Character literal is delimited by a double quote, rather than the single quote that's common in most C-inspired languages.

  • Because the Swift compiler's type inference rules will assume double quotes around a literal imply a string variable, the above ch1 assignment must explicitly declare the variables as Character type—otherwise the Swift compiler will create ch1 as a string.

Constructing a Character Literal

To construct a Character type using Unicode values, you can assign an escape sequence, or use the UnicodeScalar struct to create a Character using numeric Unicode values as input.

The following line of code creates a UnicodeScalar from the value 65 (the ASCII value for the English letter A), and then assigns it to the immutable variable ch1:

let ch1 = Character(UnicodeScalar(65))

In this case, there is no ambiguity with regards to double quotation marks, so it's not necessary to explicitly assign the Character type during this assignment.

It's also common to construct a Character using a UnicodeScalar escape sequence within double quotation marks. The following creates a character variable containing an emoji character represented by the UnicodeScalar 1F601:

let ch3 = "\u{1F601}"  // sets ch3 to "😁"

While Unicode scalars are conceptually similar to ASCII/ANSI value encoding, Swift Characters may be made of more than one numeric value, while ASCII and ANSI use only one numeric value to represent each character.

For example, an accented Western letter is expressed by providing a UnicodeScalar containing two character values.

We can construct the Unicode representation of an accented e character as follows:

let ch4 = "e\u{301}"   // é

The expression on the right of the assignment contains the literal letter e, followed by the escaped value for the accent modifier (301). The Swift compiler combines these two elements into a single extended grapheme cluster.

String

Strings in Swift are very similar to strings in other programming languages. As string handling is so central to any application development project, we'll dedicate an entire subsequent lesson to Swift's powerful string handling capabilities. In this section, we'll discuss the basics for declaring and using a string.

Fundamentally, strings are arrays of the Character types, supporting the familiar assignment operator (=), substrings, concatenation, and C-inspired escape characters.

Instantiating a String

Instantiating a string variable is highly intuitive. The following statements create string variables:

var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
let macCharacters = "⌘⌃⌥⇧ ⏎⌫⇪⎋⇥"
let emoji = "😎😂🎃🐳🍎😜😆"

String Concatenation

As in many languages, Swift strings can be concatenated using the plus (+) operator:

let alphaMac = alphabet + macCharacters

String also supports the unary addition operator:

alphabet += macCharacters

Extracting Characters

One difference between Swift strings and strings in many languages is how individual elements of strings are accessed. Specifically, the following syntax with Swift strings is illegal:

let ch = alphabet[4]
error: 'subscript' is unavailable: cannot subscript String with an Int, see the documentation comment for discussion

In Swift, the input to the subscript operator (that is, what's between the [] characters) is expected to be of type String.Index, not Int.

In practice, you will construct an Index, then pass the index to the substring operator, for example:

let idx = alphabet.index(alphabet.startIndex, offsetBy: 4)
let ch = alphabet[idx]  // ch is assigned the character "E"

String Length

Obtaining the length of string is quite easy—simply call the count property of a string:

var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
let alphabetLength = alphabet.count  // 26

We have now reached the end of this section. Here, we worked with the different data types in Swift, specifically numeric, Boolean, character, and string data types.

Activity: Data Type Summary

Now that you've learned about the various data types available with Swift, let's put this knowledge into practice by using various types together, and also using the Apple Foundation framework.

Use an Xcode playground to practice various data types. You'll be using numeric data types, formatting them as strings, and using string interpolation to print string values from various data types.

  1. Launch Xcode as before, and create a new playground named Data Type Summary.playground.

  2. Add the following code to the playground to create an immutable Double with an initial value:

    let dVal = 4.9876
  3. Next, create a Boolean mutable variable with an initial value of true, and another variable set to the Double variable after rounding to a whole number:

    var iValRounded = true
    var iVal = Int(dVal.rounded())
  4. Next, we're going to use a class from Foundation to create a string representation of the Double value, rounded to two digits. If you're not familiar with NumberFormatter, don't worry. This is just one of the many utility classes Apple provides in its expansive SDK for macOS and iOS:

    var formatDigits = 2
    let nf = NumberFormatter()
    nf.numberStyle = .decimal
    nf.maximumFractionDigits = formatDigits
    let formattedDouble = nf.string(from: NSNumber(value: dVal)) ?? "#Err"

    Because NumberFormatter.string returns an optional, we need either to check it (with if/let, or as here, provide a default value ("#Err") in case the function does return nil.

  5. Now add the following line to print a statement about the values we've created:

    print("The original number was \(formattedDouble) (rounded to \(formatDigits) decimal places), while the value \(iValRounded ? "rounded" : "unrounded") to Integer is \(iVal).")

    The output of this code is as follows:

    The original number was 4.99 (rounded to 2 decimal places), while the value rounded to Integer is 5.
  6. Finally, add the following lines to change the rounding strategy, and print a sentence about the result of the new string conversions:

    formatDigits = 0
    nf.maximumFractionDigits = formatDigits
    formattedDouble = nf.string(from: NSNumber(value: dVal)) ?? "#Err"
    iValRounded = false
    iVal = Int(dVal)
    print("The original number was \(formattedDouble) (rounded to \(formatDigits) decimal places), while the value \(iValRounded ? "rounded" : "unrounded") to Integer is \(iVal).")

    The output of this second sentence is as follows:

    The original number was 5 (rounded to 0 decimal places), while the value unrounded to Integer is 4.
 

Enums


Enums are frequently used in Swift to create custom data types that have a predefined set of possible values to select from. Enums serve to make code more readable and maintainable, and also provide compile-time checking for parameters and value assignments which yield higher quality, more robust code.

Many languages provide built-in enum features, and Swift's implementation of the enum is very similar to other languages. Swift does have some unique enum features, which we'll cover in this section.

Basic Enum Syntax

Consider the following code, which creates and uses a basic enum:

enum DayOfWeek {
    case monday, tuesday, wednesday, thursday, friday
}

var today = DayOfWeek.wednesday

if today == .friday {
    print("Today is Friday")
} else {
    print("Today is not Friday")
}

Defining the enum DayOfWeek declares a new data type, which can be used just like any other data type. Because the variable today is of the type DayOfWeek, which can only be assigned one of the seven listed values, we could not assign anything else. For example, the following code would generate a compile-time error, because Saturday is not included in the predefined values:

Var today = DayOfWeek.saturday

The preceding example illustrates the two most important advantages of enums:

  • Possible values are restricted to a predefined list, making assignment of invalid values something that is tested at compile time rather than at runtime.

  • Code that uses enums become self-documenting and easier to understand.

Enum with Raw Values

In the preceding enum example, the enum values (.monday, .tuesday, and so on) have no underlying data type. For example, we might want to calculate the day of week by subtracting the ordinal number for the today variable from .monday.

However, with the enum as defined, there is no numeric value associated, so the following code will fail to compile:

var nthDay = today - DayOfWeek.Monday

This code generates the following error:

Binary operator – cannot be applied to two 'DayOfWeek' operands

This is by design, because unlike some languages, a Swift enum need not be mapped to a native data type (and should not be, if there's no reason to do so).

However, Swift enums can be mapped to any underlying data type. In the following revision, we map the day of week to the Int data type, which enables the nth day of the week calculation mentioned above:

enum DayOfWeek: Int {
    case monday, tuesday, wednesday, thursday, friday
}

var today = DayOfWeek.Wednesday // DayOfWeek.wednesday
var nthDay = today.rawValue - DayOfWeek.monday.rawValue + 1 // 3
var tomorrow = DayOfWeek(rawValue: today.rawValue + 1) // DayOfWeek.thursday

In this case, all we needed to do was add a native data type (Int) to the enum declaration. The Swift compiler then holds a .rawValue property. When an enum has an underlying value, it also becomes possible to create an enum member by passing it to the rawValue: parameter of the enum initializer.

Note

Use care with raw values. Passing a rawValue: to an enum initializer that does not match a defined case within the enum results in the creation of a nil optional.

In the preceding example, we used Int as the raw value for the revised DayOfWeek enum. Swift allows any data type to serve as the underlying value of an enum. For example, we could use String instead of Int to enable the following use case:

enum DayOfWeek: String {
    case monday = "Monday"
    case tuesday = "Tuesday"
    case wednesday = "Wednesday"
    case thursday = "Thursday"
    case friday = "Friday"
    case saturday = "Saturday"
}

var today = DayOfWeek.Wednesday // DayOfWeek.wednesday
let dayString = today.rawValue  // "Wednesday"

In this section, we have looked at enums in detail. We saw its syntax and how to define an enum with raw values. We will now work through an activity where we will use enums to implement error codes.

Activity: Using Swift Enums

Enumerations are a powerful construct available in many programming languages. Enumerations make code more robust and easier for others to understand and maintain.

Use Xcode to define error codes using conventional error number techniques, and alternatives that use Swift enums.

  1. Launch Xcode as before, and create a new playground named Activity D - Using Numeric Types.playground.

  2. Add the following lines of code to create a set of error codes using simple integer values:

    // Store an error condition as an integer
    let success = 0
    let ioFailure = 1
    let timeoutFailure = 2
  3. Now create the same set of error codes using an enum without a raw value:

    // Store an error condition as an enum type
    enum Result {
        case success
        case ioFailure
        case timeoutFailure
    }
  4. Finally, create the same set again, this time using an enum with a raw Integer value associated with each result code:

    // Store an error condition as an enum type with raw value
    enum ResultWithRawValue: Int {
        case success = 0
        case ioFailure = 1
        case timeoutFailure = 2
    }
  5. Now let's use these error values by creating a new variable, assigning the ioFailure error condition to each one:

    let error1 = ioFailure
    let error2 = Result.ioFailure
    let error3 = ResultWithRawValue.ioFailure
  6. Finally, use the console print function to output the content of each error variable. Note how each one is represented to the console:

    // Now print out the error result from each case.
    print("File access resulted: \(error1)")
    print("File access resulted: \(error2)")
    print("File access resulted: \(error3)")
    print("File access resulted: \(error3.rawValue)")
 

Summary


In this lesson, we've learned the basic language structure and syntax for the Swift programming language. We've now understood the following concepts:

  • The fundamental structure of Swift programs, and how to use an Xcode playground to develop simple and complex programs

  • How to create and use mutable and immutable Swift variables

  • The built-in data types available to Swift programs, and how to select the appropriate data type depending on circumstance

  • Swift's powerful optional construct for detecting and branching program flow when data values are not available

  • Swift's type inference and strict type safety syntax and usage

Now that you have the basics well in hand, we're ready to move on to the next lesson, where we'll learn how to use these language elements in complex Swift programs. Specifically, we'll look at the control flow structures and operators offered by Swift.

About the Authors
  • Rob Kerr

    Rob Kerr is a mobile software strategist, designer, and developer, with over 20 years of software development experience. He has been developing iOS applications using Swift since the language's introduction. Rob is the founder of Mobile Toolworks, a software development consultancy creating innovative software designed for a mobile-first world.

    Browse publications by this author
  • Kåre Morstøl

    Kåre Morstøl is an independent software developer from Norway, who has programmed almost exclusively in Swift since it was announced. He has created and contributed to numerous open source projects, mainly focused on command-line applications. He thinks Swift is a great language that just keeps getting better.

    Browse publications by this author
Latest Reviews (1 reviews total)
Don't Know - haven't received it
Beginning Swift
Unlock this book and the full library FREE for 7 days
Start now