top of page

Swift Study Notes

Chapter 1: The Basics

This chapter introduces the fundamental building blocks of the Swift language, including constants, variables, numeric types, type safety, optionals, error handling, and assertions.

Constants and Variables

Constants and variables associate a name with a value.

A constant cannot be changed after it receives a value.

A variable can be assigned a different value later.

Declaring Constants and Variables

Use let to declare a constant.

Use var to declare a variable.

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0

In this example:

  • maximumNumberOfLoginAttempts is a constant because its value never changes.
  • currentLoginAttempt is a variable because its value changes over time.

If a value does not need to change, prefer a constant.

Constants make code safer and communicate intent more clearly.

Delayed Initialization

A constant or variable can be declared without an initial value and assigned later.

Before a value can be be read, Swift requires it to be initialized.

A constant can be assigned exactly once.

var environment = "development"

let maximumNumberOfLoginAttempts: Int

if environment == "development" {
    maximumNumberOfLoginAttempts = 100
} else {
    maximumNumberOfLoginAttempts = 10
}

In this example, the constant's value depends on the environment.

Swift guarantees that every execution path assigns a value before the constant can be used.

Multiple Declarations

Multiple constants or variables can be declared on a single line.

var x = 0.0, y = 0.0, z = 0.0

Type Annotations

A type annotation explicitly declares the type a constant or variable can store.

Use a colon followed by the type name.

var welcomeMessage: String

This can be read as:

Declare a variable named welcomeMessage of type String.

The declaration above creates a variable that can store String values.

The variable can be assigned a value later.

welcomeMessage = "Hello"

Multiple Variables

Multiple variables of the same type can share a single type annotation.

var red, green, blue: Double

Each variable in this declaration stores a Double value.

When To Use Type Annotations

Swift can often determine a type automatically using type inference.

Type annotations are useful when:

  • The type cannot be inferred clearly.
  • You want to make the intended type explicit.
  • You are declaring a value before assigning its initial value.
let maximumNumberOfLoginAttempts: Int

In this example, Swift knows the constant will store an Int value even though a value has not yet been assigned.

Naming Constants and Variables

Choose names that clearly describe the value they represent.

Swift names can contain almost any Unicode character, including non-Latin characters and emoji.

let π = 3.14159
let 🐶🐮 = "dogcow"

Naming Rules

Names can contain:

  • Letters
  • Numbers
  • Unicode characters
  • Emoji

Names cannot:

  • Contain whitespace characters
  • Begin with a number
let 1stPlace = "Gold"    // Error
let firstPlace = "Gold"  // Valid

Swift is a case-sensitive language.

These names are different:

var friendlyWelcome = "Hello!"
var FriendlyWelcome = "Hello!"

Reserved Keywords

Swift reserves certain words for its own use.

Examples include:

  • class
  • struct
  • enum
  • protocol

These keywords cannot normally be used as names.

let class = "Example" // Error

Using Keywords As Names

If necessary, a reserved keyword can be used as a name by surrounding it with backticks.

let `class` = "Example"

This allows the keyword to be treated as an identifier instead of a Swift language keyword.

Naming Conventions

Use names that communicate intent clearly.

A good name should help explain the purpose of the value without requiring additional comments.

let maximumNumberOfLoginAttempts = 10

Descriptive names are generally preferred over short or ambiguous names.

Printing Constants and Variables

Use print() to display the current value of a constant or variable.

var friendlyWelcome = "Hello!"
print(friendlyWelcome)
Output
Hello!

The value stored in friendlyWelcome is written to the console.

String Interpolation

String interpolation inserts a constant, variable, expression, or function call directly into a string.

Use the following syntax:

\(value)

Example:

print("The current value of friendlyWelcome is \(friendlyWelcome)")
Output
The current value of friendlyWelcome is Hello!

Swift evaluates the expression and inserts the result into the string.

Expressions in String Interpolation

String interpolation is not limited to variables.

Expressions can also be evaluated.

print("Two times two is \(2 * 2)")
Output
Two times two is 4

The expression is evaluated before the final string is produced.

Why String Interpolation Is Preferred

String interpolation is the standard way to combine values and text in Swift.

It improves readability and avoids manually joining strings together.

let score = 100

print("Score: \(score)")

Comments

Comments describe code and are ignored by the Swift compiler.

Use comments to explain intent, assumptions, or important implementation details.

Single-Line Comments

Single-line comments begin with //.

// This is a comment.

Everything after // on the current line is treated as a comment.

Multi-Line Comments

Multi-line comments begin with /* and end with */.

/*
This is a comment.
*/

Multi-line comments can span multiple lines.

Nested Comments

Unlike many programming languages, Swift supports nested multi-line comments.

/*

    This is the start of the first comment.

    /* This is a nested comment. */

    This is the end of the first comment.

*/

Nested comments allow blocks of code that already contain comments to be commented out safely.

This is particularly useful when debugging or temporarily disabling sections of code.

Semicolons

Semicolons are optional in Swift.

Most Swift code places one statement per line and does not use semicolons.

let cat = "🐱"
print(cat)

Multiple Statements On One Line

Use a semicolon to separate multiple statements written on the same line.

let cat = "🐱"; print(cat)

In this example, the semicolon separates two statements.

Modern Swift Style

Modern Swift code rarely uses semicolons.

Writing one statement per line is generally preferred because it improves readability.

Integers

An integer is a whole number with no fractional component.

Examples include:

  • 0
  • -42
  • 1024

Swift provides signed and unsigned integer types of different sizes.

Integer Bounds

Every integer type has a minimum and maximum value it can store.

Use the min and max properties to access these values.

print(UInt8.min)
Output
0
print(UInt8.max)
Output
255

A UInt8 value can store any number between 0 and 255.

Attempting to store a value outside the valid range results in an error.

Int

Int is Swift's default integer type.

Its size matches the native word size of the current platform.

Modern Apple platforms use a 64-bit architecture, so Int is typically a 64-bit integer.

In most situations, Int should be your preferred integer type.

UInt

UInt is Swift's default unsigned integer type.

Unsigned integers can store only positive values and zero.

let score: UInt = 100

Because UInt cannot represent negative values, assigning a negative number produces an error.

let score: UInt = -1 // Error

Swift recommends using Int unless you have a specific reason to use an unsigned integer type.

Floating-Point Numbers

Swift provides two floating-point types:

  • Double
  • Float

Double

Double is a 64-bit floating-point number.

It provides greater precision than Float.

Swift recommends using Double unless a specific reason exists to use Float.

Float

Float is a 32-bit floating-point number.

It uses less memory but stores fewer decimal digits of precision.

Type Safety and Type Inference

Swift is a type-safe language.

Type safety prevents values of incompatible types from being used together accidentally.

Type Inference

Swift can often determine a value's type automatically.

This process is known as type inference.

let meaningOfLife = 42

Swift infers that meaningOfLife is an Int.

let pi = 3.14159

Swift infers that pi is a Double.

Mixed Numeric Values

When integer and floating-point literals appear together, Swift infers a type that can represent both values.

let anotherPi = 3 + 0.14159

Swift infers that anotherPi is a Double.

Type inference reduces repetition while preserving type safety.

Numeric Literals

Numeric literals are values written directly in source code.

Decimal

17

Binary

0b10001

Octal

0o21

Hexadecimal

0x11

All four examples represent the decimal value 17.

Readable Numeric Literals

Underscores improve readability and are ignored by the compiler.

1_000_000
1_000.000_1

Numeric Type Conversion

Swift does not perform implicit numeric conversions between different numeric types.

Values must be converted explicitly.

Integer Conversion

Different integer types cannot be mixed automatically.

let twoThousand: UInt16 = 2_000
let one: UInt8 = 1

let twoThousandAndOne = twoThousand + UInt16(one)

Integer and Floating-Point Conversion

Integers and floating-point values must also be converted explicitly.

let three = 3
let pointOneFourOneFiveNine = 0.14159

let pi = Double(three) + pointOneFourOneFiveNine

Why Explicit Conversion Exists

Swift requires conversions to be visible in code so that changes in precision or range are never hidden.

Type Aliases

A type alias creates an alternative name for an existing type.

Use the typealias keyword.

typealias AudioSample = UInt16

Type aliases improve readability by expressing intent without creating a new type.

Booleans

Swift provides the Bool type for working with logical values.

A Boolean value can be either:

  • true
  • false

Conditional Statements

Conditional statements require a Boolean value.

if turnipsAreDelicious {
    print("Mmm, tasty turnips!")
} else {
    print("Eww, turnips are horrible.")
}

Type Safety and Booleans

Unlike some languages, Swift requires actual Boolean values in conditional statements.

let i = 1

if i {
    // Error
}

Integers are not automatically treated as Boolean values.

Tuples

Tuples group multiple values into a single compound value.

let http404Error = (404, "Not Found")

Decomposing Tuples

let (statusCode, statusMessage) = http404Error

Each value is assigned to a separate constant or variable.

Ignoring Values

Use an underscore (_) to ignore values that are not needed.

let (statusCode, _) = http404Error

Accessing Values by Position

http404Error.0
http404Error.1

Named Tuples

let http200Status = (
    statusCode: 200,
    description: "OK"
)

Named values can be accessed directly.

http200Status.statusCode
http200Status.description

Tuples provide a lightweight way to group related values together without creating a custom type.

Optionals

An optional represents a value that may be present or absent.

A non-optional value must always contain a value.

An optional can contain:

  • A value
  • nil
var serverResponseCode: Int? = 404

The question mark (?) indicates that the value is optional.

nil

Assign nil to indicate the absence of a value.

serverResponseCode = nil

Only optional values can be assigned nil.

Optional Binding

Optional binding safely extracts a value from an optional.

if let actualNumber = Int(possibleNumber) {
    print("The string has an integer value of \(actualNumber)")
}

If the optional contains a value, the binding succeeds.

If it contains nil, the binding fails.

Multiple Optional Bindings

if let firstNumber = Int("4"),
   let secondNumber = Int("42"),
   firstNumber < secondNumber {
    print("\(firstNumber) < \(secondNumber)")
}

Each condition must succeed before the body executes.

Providing a Fallback Value

Use the nil-coalescing operator (??) to provide a default value when an optional contains nil.

let displayName = name ?? "Guest"

If the optional contains a value, that value is used.

Otherwise, the fallback value is used.

Force Unwrapping

Force unwrapping accesses the value stored inside an optional.

Use an exclamation mark (!).

print(convertedNumber!)
Important

Force unwrapping assumes that a value exists.

If the optional contains nil, the application terminates at runtime.

let name: String? = nil

print(name!) // Runtime error

Only use force unwrapping when you are certain a value exists.

Thinking About Force Unwrapping

Force unwrapping can be viewed as a shorthand way of saying:

A value must exist here.

print(name!)

Conceptually, this behaves similarly to:

if let name {
    print(name)
} else {
    fatalError("Expected name to contain a value.")
}

If the optional contains a value, execution continues normally.

If the optional contains nil, execution terminates with a runtime error.

Implicitly Unwrapped Optionals

An implicitly unwrapped optional is still an optional value.

Use an exclamation mark (!) in the type declaration.

let possibleString: String! = "An optional string."

Unlike a normal optional, Swift automatically force unwraps an implicitly unwrapped optional each time it is accessed.

This allows the value to be used without writing ! explicitly.

let assumedString: String = possibleString
Important

Every access performs an implicit force unwrap.

If the value contains nil when it is accessed, a runtime error occurs.

Conceptually, every access behaves as though Swift had written:

possibleString!

Use implicitly unwrapped optionals only when a value may be nil initially but is guaranteed to contain a value before it is first accessed.

Error Handling

Error handling allows code to respond to recoverable failures at runtime.

Functions that can throw errors are marked with the throws keyword.

func makeASandwich() throws {
    // ...
}

Use do-catch to handle errors.

do {
    try makeASandwich()
} catch {
    print("Failed to make sandwich.")
}

If an error is thrown, execution transfers to the matching catch block.

Assertions and Preconditions

Assertions and preconditions allow assumptions to be expressed directly in code.

If an assumption is violated, execution stops and reports the problem.

Assertions

Use assertions to detect programming mistakes during development.

assert(age >= 0, "A person\'s age cannot be less than zero.")

Assertions help identify bugs closer to their source.

Preconditions

Use preconditions when a requirement must be true before execution can continue safely.

precondition(index > 0, "Index must be greater than zero.")

Examples include:

  • Valid array indexes
  • Required input values
  • Conditions that would make further execution unsafe

Assertions vs Preconditions

Use an assertion to detect mistakes during development.

Use a precondition to enforce requirements that must always be satisfied for the program to continue safely.

3DaysOfSwift_final_cleaned_Logo-transparent-v3 ⭐️-tiny.png
3DaysOfSwift_final_cleaned_Logo-transparent-v3 ⭐️-tiny.png

Last edited on 28th May 2026

version 1.4

42 playgrounds

5  Xcode projects

0 programs

Website Line Art-tiny.png

Our Site is under construction - v1.4 - Works best on desktop.

© Copyright www.3DaysOfSwift.com.

All rights reserved.

By interacting with our site you agree to our terms & conditions.

You are not authorised to use our learning materials for A.I. training.

FIN.

🎉

bottom of page