Published by Arunprasadh C on 25 Apr 2022Last Updated on 12 May 2022

Tuples and Optionals in Swift

Tuples

Tuples are used to group multiple values in a single compound value. The values can be of different Data Types. The basic syntax of Tuple is as follows:

var TupleName = (value1, value2, any number of values) // for Variables

OR

let TupleName = (value1, value2, any number of values) // for Constants

The elements of a Tuple can be accessed using Index value starting from 0 till n-1 where n is the number of elements in the Tuple.

Example :

let error = (404, "Not Found")
print("The error code is : \(error.0)")
print("The error description is : \(error.1)")

Output :

The error code is : 404
The error description is : Not Found

The elements of a Tuple can also be given Labels. When such Labels are used, the elements can be accessed with the Labels instead of Index Values. And even a combination of Index Values and Labels can be used.

Example :

var tup1 = (a: (c: 4,d: 5),"Hello")
print(tup1.a.d,tup1.1,separator: ", ")

Output :

5, Hello

There may be some situations where we might want to prefer using Tuple over struct or class to keep the code simpler. But, if we want to reuse the Tuple again and again, declaring a Tuple again and again might not be the best solution. Instead, we can use typealias for declaring the Tuple structure once and then reuse the structure like a data type anywhere. Consider the situation where we want to use Tuple for storing the attributes of a Circle. We can use typealias in the following way :

Example :

typealias Circle = (center: (x: Double, y: Double), radius: Double)
let unitCircle: Circle = Circle(center: (0.0, 0.0), radius: 1.0)
var circleTwo: Circle = Circle((4.5, 3.5), 10.5)
print(type(of: circleTwo))
print(unitCircle.center == circleTwo.center)

Output :

(center: (x: Double, y: Double), radius: Double)
false

Tuple is a compound type which does not conform to the Sequence protocol and hence, it cannot be traversed using a loop. Two tuples can be compared with each other using == and other comparison operators when they have the same number of elements with exactly matching types in the same order. However, this comparison can be done only when the tuples contain no more than 6 elements due to the implementation difficulties mentioned here.

Optionals

Optional is a Data Type which is used in situations where either there can be a Value or there can be no Value at all. When there is no value, it is represented as nil (Just like NULL in C/C++ or like null in Java. But nil is not a Pointer as in other languages). nil can be assigned only to variables/constants of Optional type.

Why there are no explicit pointers in Swift ?

There are no explicit pointers in Swift (just like other modern languages). The reasons are quite obvious :

But, Swift does provide some ways to access pointers (which is known as unsafe Swift) like UnsafeRawPointer class which should be used with caution.

General Syntax of declaring an Optional variable is as follows:

var name: DataType? 

The type of the variable will be Optional<DataType>.

Example 1 - Nillable String :

var nillableString: String?
print(nillableString)
nillableString = "Hello"
print(nillableString)
print(type(of: nillableString))
print(nillableString!)
print(type(of: nillableString!))

Output 1 :

nil
Optional("Hello")
Optional<String>
Hello
String

In the above example, when an Optional<String> is not initialized, nil is taken as the default value. But, if it is not Optional, then the program will not compile as the variable is not initialized. Notice that when I called the type(of: ) function on nillableString, it showed the Type as Optional<String>. But, when I called type(of: ) function on nillableString!, it showed the Type as String ! No, it was not magic or error of compiler. It happened because of the Forced Unwrapping Operator !. So, what is Unwrapping ? The Type Casting of the Optional object to the Actual Type of object is known as Unwrapping. Unwrapping can be done in many ways:

Using if-else Block :

Example 2 :

var variable:String? //evaluates to nil
if variable != nil {
 print("Not Nil")
}
else{
 print("Nil")
}

Output 2 :

Nil

Using Forced Unwrapping Operator ! :

Refer Example 1 for Forced Unwrapping Operator usage. If the Optional having nil value is tried to be unwrapped, an error Unexpectedly found nil while unwrapping an Optional value will be thrown at Runtime.

Using Automatic Unwrapping/Implicitly Unwrapped Optionals:

Declaring Optionals with ! instead of ? will make the variable to be automatically unwrapped when they are assigned

Example 3 :

var nillableString: String! = "Hello"
print(nillableString)
print(type(of: nillableString))
let finalString: String = nillableString  // no need of using ! here
print(finalString)
print(type(of: finalString))

Output 3 :

Optional("Hello")
Optional<String>
Hello
String

However, if nil is found in the implicitly optional variable, a fatal error will be thrown when it is assigned to non-optional variables.

Using Optional Binding : if-let/var

Optional Binding is similar to using an if-else block. The only difference is that if the Optional value is not nil, the unwrapped value is assigned to a new constant and further operations are performed on the constant. It can be done using if-let block :

Example 4 :

var sample:String? = "Hello"
if let unwrappedSample = sample
{
print("Sample is \(unwrappedSample)")
}

Output 4 :

Sample is Hello

If you want to mutate unwrappedSample, you can replace let with var. However, the constant/variable unwrappedSample can’t be accessed out of the scope of if. So, what should you do if you want to use/mutate unwrappedSample after if block ? You have to instead prefer to use guard-let/var statement.

Using Optional Binding : guard-let/var

It is similar to using an if-let/var block. The key difference is that the guard code block is executed only when the given condition is false. The guard statement transfers the constrol out of scope when the condition is false. It must use return or throw to get out of scope when the condition is false. If the condition is true, it executes the succeeding statements. It literally guards the code from undesired scenarios. Also, on both if-let/var and guard-let/var Optional Binding, additional predicates can be checked by specifying them separated by comma. If the predicate turns out to be false, the else. block will only get executed even when the Optional has a non-nil value.

Example 5.1 :

var sample: String? = "Hello"
var isOkay = true
guard var unwrappedSample = sample, isOkay else
{
    throw fatalError("sample had nil value or isOkay was false")
}
unwrappedSample += " World !"
print(unwrappedSample)

Output 5.1 :

Hello World !

Let us make the value of variable isOkay as false. Now, we will get fatalError even though unwrappedSample got non-nil value, as isOkay is false.

Example 5.2 :

var sample: String? = "Hello"
var isOkay = false
guard var unwrappedSample = sample, isOkay else
{
    throw fatalError("sample had nil value or isOkay was false")
}
unwrappedSample += " World !"
print(unwrappedSample)

Output 5.2 :

helloworld/main.swift:36: Fatal error: sample had nil value or isOkay was false
2022-05-06 12:13:03.177763+0530 helloworld[13685:124273] helloworld/main.swift:36: Fatal error: sample had nil value or isOkay was false

Using nil Coalescing Operator ??

The nil Coalescing Operator ?? is like a shorthand for if-else Block. If the Optional has a value, that value will be used. Else, a provided Default Value will be used. Example 6 :

var str1: String?
var str2: String = str1 ?? "Default"
print(str2)
str1 = "Provided"
str2 = str1 ?? "Default"
print(str2)

Output 6 :

Default
Provided

Using switch statement

switch case will be explained in detail in Decision-making Constructs. switch statement can be used to unwrap optionals in the following manner:

Example :

var intOpt: Int? = 5
switch intOpt
{
case .some(let value):
    print("The value of intOpt is \(value)")
case .none:
    print("intOpt is nil")
}

Output :

The value of intOpt is 5

Using if-case-let statement

The Swift if-case-let statement is a convenient shortcut to avoid a full switch statement when you only want to match a single case.

Example :

var boolOpt: Bool? = true
if case .some(let val) = boolOpt
{
    print(type(of: val))
    print(val)
}
boolOpt = nil
if case .none = boolOpt
{
    print("nil")
}

Output :

Bool
true
nil

Using Optional Chaining

Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil. If the optional contains a value, the property, method, or subscript call succeeds; if the optional is nil, the property, method, or subscript call returns nil. Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil. The ? symbol is suffixed to the Optional variable to perform Optional Chaining. The benefit of this over Forced Unwrapping is that it doesn’t throw error if nil is found. The result is always given as an Optional. Instead, it returns nil value. The following example shows Optional chaining using nested Tuples as we haven’t seen about classes and structs in Swift.

Example :

var a: Int? = 1, b: Int? = 8, c: Int? = 5, d: String? = "Hello"
var innerTup1: (a: Int?, b: Int?, c: Int?)? = (a, b, c)
var innerTup2: (c: Int?, d: String?)? = (c, d)
var outerTup: (one: (a: Int?, b: Int?, c: Int?)?, two: (c: Int?, d: String?)?)? = (innerTup1, innerTup2)
print(outerTup?.one?.b?.isMultiple(of: 4))
outerTup?.two = nil
print(outerTup?.two?.d)

Output :

Optional(true)
nil //since two is set as nil

Now that we have seen about Optionals and Tuples in Swift, we can see about Variables, Literals and Constants in Swift.

← Back to Index
← Characters and Strings in Swift Variables, Literals and Constants →