- Published on
Constants and Variables in Swift
- Authors
- Name
- Mario Jackson
- @iAmMarioJackson
Unveiling the Power of Swift: A Journey into Constants and Variables
Welcome to the dynamic world of Swift programming, where the mastery of constants and variables lays the foundation for building powerful and flexible applications on iOS, macOS, watchOS, and tvOS. As you embark on this journey, you'll discover the significance of these fundamental building blocks and how they shape the behavior of your code.
- Variables
- Constants
- Type Inference
- Key Differences
- Diving Deeper
- Scope and Lifetime of Constants and Variables
- Type Safety in Swift
- Value and Reference Types
- Type Conversion
- Optionals
- Wrapping Up
Swift, like most programming languages, utilizes variables
and constants
.
Variables
are for storing values that can change.Constants
hold those values that shouldn't change.
If you're acquainted with JavaScript, you'll know that constants are declared using const
and variables can be declared using either var
or let
.
In contrast, Swift has a slightly different approach:
- Declare a
constant
withlet
- Declare a
variable
withvar
Variables
A variable in Swift provides you with a named storage place that allows your code to manipulate its stored value. A variable's name (also referred to as an identifier) is associated with a value that we can change during program execution.
var intValue: Int = 10
intValue = 20 // It's permissible. As intValue is a variable, we can change its value.```
In the above example, intValue is a variable of type Int
, initially assigned the value 10. Later, we can modify its value to 20 or any other integer value.
Constants
Unlike a variable, a constant, once declared in Swift, maintains its value throughout the execution and cannot be altered after its initial set-up.
let constantValue: Int = 100
constantValue = 200 // It's not permissible. As constantValue is a constant, we cannot change its value.
Above, constantValue is a constant that's been assigned the value 100. If you attempt to modify the constantValue, a compile-time error will occur because altering the value of a constant is prohibited in Swift.
Type Inference
Swift employs type inference, a feature that allows the compiler to deduce the type of an expression automatically. Because of this, when you're declaring constants or variables, you don't have to explicitly indicate the type, as long as it can be inferred from the initial value.
let inferredDouble = 3.14159 // Inferred as double
var inferredString = "Hello" // Inferred as String
In the examples above, inferredDouble
is inferred as Double and inferredString
as String automatically, eliminating the need for explicit type declarations.
Key Differences
The main differences between constants and variables can be boiled down to two essential points:
- Mutability: The crucial difference lies in their respective natures. Variables are mutable, meaning they can amend their values at any point, whereas constants are immutable—once their value is set, it doesn't change
- Performance: Constants can be more performance efficient. In programming, the predictability brought by constants allows the compiler to make specific optimizations leading to better-performing code.
Swift encourages you to use constants (let) wherever the value doesn't need to be changed. This practice enhances safety by preventing inadvertent value modification. Variables (var), on the other hand, should be used for values that may need to be altered.
let pi = 3.14159 // The value of pi never changes, so using `let` is appropriate.
var username = "Mario" // The username might change, so we use `var`.
username = "Swift Mario" // You can manipulate the value since it's a variable.
Diving Deeper
Scope and Lifetime of Constants and Variables
Every variable or constant in Swift has a scope
and a lifetime
. Scope implies where a variable or constant can be accessed or used in your code. A variable declared outside of all function bodies has a global scope and can be used anywhere in the code. However, a variable declared within a function has local scope and can only be used within that function. The lifetime of a variable or constant begins when it is declared and ends when it is no longer used and is reclaimed by the system. Understanding the scope and lifetime of variables and constants is crucial to avoid errors and memory leaks.
In Other Words
So, in basic terms, every variable or constant in Swift has a scene
— called scope — where it can make an appearance, and it also has a lifespan — called lifetime. Just think of your variable as a movie star; it can only be seen where the script (your program) allows it. So, if it's decided that our star should only be seen in a garden scene (a function), then it just can't suddenly turn up in a shopping mall (another function)! Also, just like our star, our variable is not immortal — it has a lifetime, starting when it's created and ending when the system sees it has no more use and politely shows it the exit. Keeping track of this is crucial to ensure your code doesn't trip over its own feet.
Type Safety in Swift
Swift is touted for being a type-safe language. It means the Swift language is very strict about the data types you use with each variable or constant. For instance, if you declare a variable as an Int
, Swift won't allow you to later use that variable as a String
This type-safety feature vastly minimizes the likelihood of unexpected behavior or crashes. It allows for most type checking errors to be caught at compile-time, before any problematic code is run.
In Other Words
Swift is known for its strict nature — once it decides something, it sticks to it. If you're telling Swift your variable should be an Int
, Swift won't let you just randomly use it as a String
. It's like telling someone they're going to be eating an apple and handing them a pretzel. That won't work, and Swift knows it. That's called type safety! It stops your code from going haywire and limits unexpected errors, which is always a good thing.
Value and Reference Types
In Swift, variables and constants can store values of value types (like structures and enumerations) and reference types (like classes). When you assign a value type to a variable, constant, or pass it to a function, Swift creates a new, separate copy of the value. In the case of reference types, Swift doesn't create a copy but instead passes a reference to the existing object. Hence, changes made from one reference point affect the same object from another reference point. Understanding the difference between value and reference types in Swift is crucial for managing mutable state and avoiding common programming mistakes.
Structs are value types. When you make a copy of a value type, it creates an entirely new object:
struct Cookie {
var flavor: String
}
var myCookie = Cookie(flavor: "Chocolate Chip") // We have a cookie with Chocolate Chip flavor
var yourCookie = myCookie // The cookie gets copied for you
yourCookie.flavor = "Oatmeal Raisin" // You changed your cookie's flavor
print(myCookie.flavor) // Outputs: Chocolate Chip
print(yourCookie.flavor) // Outputs: Oatmeal Raisin
As you can see, changing yourCookie
did not affect myCookie
. This is because they are separate instances.
In contrast, classes in Swift are reference types. Let's consider Car as a class. This makes it a reference type in Swift:
class Car {
var isCarOpen: Bool
init(isCarOpen: Bool) {
self.isCarOpen = isCarOpen
}
}
Now, let's imagine you have your mainKey
and spareKey
:
class Car {
var isCarOpen: Bool
init(isCarOpen: Bool) {
self.isCarOpen = isCarOpen
}
}
var mainKey = Car(isCarOpen: false) // The car is initially locked
var spareKey = mainKey // The spare key refers to the same car
You can use the spare key to open the car:
class Car {
var isCarOpen: Bool
init(isCarOpen: Bool) {
self.isCarOpen = isCarOpen
}
}
var mainKey = Car(isCarOpen: false) // The car is initially locked
var spareKey = mainKey // The spare key refers to the same car
spareKey.isCarOpen = true // The car is now opened using the spare key
Since both mainKey
and spareKey
refer to the same instance (your car), any action performed on the car via the spareKey
will reflect when accessed via the mainKey
.
class Car {
var isCarOpen: Bool
init(isCarOpen: Bool) {
self.isCarOpen = isCarOpen
}
}
var mainKey = Car(isCarOpen: false) // The car is initially locked
var spareKey = mainKey // The spare key refers to the same car
spareKey.isCarOpen = true // The car is now opened using the spare key
print(mainKey.isCarOpen) // Outputs: true
print(spareKey.isCarOpen) // Outputs: true
This exactly mimics the behavior of reference types. Both mainKey
and spareKey
point to the same instance of the Car class, so changes made via spareKey
also affect mainKey
.
NOTE In this example, we made a simplified demonstration to illustrate the concept of reference types. However, in a more realistic or complex program, it would probably be clearer to have a Car
class with a carKey
property that refers to a CarKey
class. That way, different keys could have different properties (like whether they're a main key or a spare), but all could still interact with the Car
instance. This would help us model more complex real-world relationships more accurately in our code. This distinction further emphasizes the flexibility and control we can have over data structures when understanding and utilizing differences between value and reference types.
In Other Words
In Swift, variables and constants can hold values described as value types and reference types. Think of a value type like a firsthand story — every time you pass it around, a fresh copy is made. But a reference type is more like a game of 'Telephone'. Instead of creating a new story, everyone just refers back to the original. So, any tweaks that happen affect the same original storyline. It's handy to know which is which to keep your code neat and your variables behaving.
Type Conversion
Swift does not implicitly convert types. This means you must perform explicit type conversion. Here is an example showing how to convert an Int
to a Double
:
let intVal: Int = 10
let doubleVal: Double = Double(intVal) // the value of doubleVal is now 10.0
In this example, we have successfully converted an Int
to a Double
. We did this by using the Double()
initializer, passing in our integer.
NOTE There is also the process of Type Casting which is different from Type Conversion.
Optionals
An important Swift feature to discuss in conjunction with variables and constants is the concept of optionals
. Optionals are used to represent the absence of a value and are in essence a type-safe way to say a variable can be nil
. When declaring a variable or constant, you can use a question mark ?
to denote an optional type, meaning it can hold either a value or nil. There is also the concept of optional binding and optional chaining that deal with safely handling and unwrapping these optional values. Optionals, though initially might seem complex, play a key role in Swift's app safety by preventing unexpected crashes due to nil
values.
In Other Words
Consider a jar of jam here as a variable in Swift. When the jar is full of jam, it represents that the variable contains a value. However, at times you just run out of jam, leaving the jar empty. In terms of programming, this is represented as nil, the absence of value. Now, if we assume, not every jar in your pantry is guaranteed to be full, how do you represent this in Swift? That's when we bring in the 'optionals'! In Swift, an optional is like a jar that could either be full of jam ('value') or completely empty ('nil'). By defining our jar of jam as optional, we are accepting that it isn't always guaranteed to have jam inside - sometimes, it might be empty.
var jarOfJam: String? // Declaring an Optional, our jar could have jam or not!
jarOfJam = "Strawberry" // Filling the jar with delicious Strawberry jam
jarOfJam = nil // Uh-oh, we just ran out of jam. Now it's empty.
When you want to use the value (jam) inside the optional (jar), you first need to check if there's jam inside. This process is known as unwrapping
. You'd hate to plan on making a PB&J sandwich only to find out last minute there's actually no jam in the jar, right?
Swift gives us optional binding or optional chaining - basically, clever, safe ways to check our jar for jam before we start spreading it on our sandwich, thus protecting our code from crashes (or our metaphorical sandwich session from heartbreaking disappointment!).
Wrapping Up
I hope that this guide has clarified the workings of constants and variables in Swift for you. Essential to remember is that variables are mutable (their values can change), while constants are immutable (their values are set for their lifetime).
You can read more about the basics in Apple's documentation