Organizing Data by Key in Swift Dictionaries

In Swift programs, it is often necessary to organize large amounts of data within collections.

Arrays are the most commonly used collection in programming. However, it is not always necessary to order data sequentially.

Frequently, data retrieval requires using a specific key, such as when searching for the definition of a word in a dictionary.



Architecting SwiftUI apps with MVC and MVVM

GET THE FREE BOOK NOW

Table of contents

What is a dictionary in Swift?

A dictionary is a collection type that stores key-value pairs, where each key is associated with a corresponding value. A dictionary can hold any number of key-value pairs as long as the keys are unique.

Keys can be of any type that conforms to the Hashable protocol and must all be of the same type. While strings are commonly used as keys in dictionaries, more complex types like enumerations and structures can also be used.

Values can be of any type and do not need to conform to a specific protocol, but they must also be of the same type. Once again, simple types like strings or numbers can be used, as well as complex types and even other collections such as arrays and dictionaries.

In other programming languages, a dictionary is also called a hash table, hash map, or associative array.

Dictionaries are widely used in Swift and help store and access data with logical relationships or structures, such as contact information, product details, settings, or preferences.

Creating dictionaries with content and empty dictionaries

There are several ways to create a dictionary in Swift. The most straightforward way is to use the literal syntax, which involves writing the key-value pairs inside square brackets [ and ], separated by commas ,.

For example, let’s create a dictionary to count how many fruits we have in our kitchen.

let fruits = [
	"apple": 5,
	"banana": 3,
	"orange": 4
]

Conventionally, constants and variables containing Swift collections are named using the plural form of what they contain, such as fruits.

Swift’s type inference system automatically detects that the keys have the type String and the values have the type Int. Therefore, the dictionary has the type [String: Int].

You can use an empty literal when you need to create an empty dictionary that you will fill with values later. However, you need to specify the type of the dictionary in the variable declaration because the compiler won’t be able to determine it through type inference.

Alternatively, you can use the initializer syntax by writing the type of the dictionary followed by parentheses (). However, it is preferred to use the literal syntax.

// Preferred
var fruits: [String: Int] = [:]

// Also valid syntax
var otherFruits = [String: Int]()

Accessing the content of a dictionary using subscripts

To access the value of a key in a dictionary, you utilize the subscript syntax by enclosing the key within square brackets [ and ] following the dictionary’s name. For instance:

let fruits = [
	"apple": 5,
	"banana": 3,
	"orange": 4
]

var appleCount = fruits["apple"]
// appleCount is an optional Int with value 5

It is important to note that the subscript syntax returns an optional value, which means it can either be a valid value or nil. This is because the key may not exist in the dictionary, or the corresponding value may be nil.

We have two options to safely unwrap the optional value: optional binding or the nil-coalescing operator. Within functions, optional binding is commonly employed alongside a guard statement.

Additionally, this technique can be employed to check if a dictionary contains a specific key without using complex methods. By utilizing an underscore _ character in optional binding, you can verify if retrieving a value yields nil while simultaneously disregarding the value when it is present.

let fruits = [
	"apple": 5,
	"banana": 3,
	"orange": 4
]

if let _ = fruits["apple"] {
	// The apple key is not present
} else {
	// The apple key is present
}

Adding, deleting, and updating the elements of a dictionary

To modify the value of a key in a dictionary, we can use the subscript syntax and assign a new value to it.

var fruits = [
	"apple": 5,
	"banana": 3,
	"orange": 4
]

fruits["apple"] = 6
// The dictionary is now ["apple": 6, "banana": 3, "orange": 4]

Adding a new key-value pair to a dictionary works the same way.

var fruits = [
	"apple": 5,
	"banana": 3,
	"orange": 4
]

fruits["pear"] = 2
// fruits is now ["apple": 5, "banana": 3, "orange": 4, "pear": 2]

If you want to add or update the value for a key while also checking the previous value, you can use the updateValue(_:forKey:) method, which returns the old value or nil if the key does not exist.

var fruits = [
	"apple": 5,
	"banana": 3,
	"orange": 4
]

let bananaCount = fruits.updateValue(7, forKey: "banana")
// bananaCount is an optional Int with value 3
// fruits now is ["apple: 5, "banana": 7, "orange": 4]

Finally, to remove a key-value pair from a dictionary, you can use the subscript syntax and assign nil to the key.

var fruits = [
	"apple": 5,
	"banana": 3,
	"orange": 4
]

fruits["apple"] = nil
// fruits now has ["banana": 3, "orange"]

Similar to the updateValue(_:forKey:) method, you can remove a value and test it simultaneously using the removeValue(forKey:) method.

Iterating over a dictionary using a for loop

To iterate over a dictionary, we can use a for-in loop, which gives us access to each key and value as a tuple.

var fruits = [
	"apple": 5,
	"banana": 3,
	"orange": 4
]

for (fruit, count) in fruits {
	print("\(fruit) has \(count) pieces")
}
// Output:
// apple has 5 pieces
// banana has 3 pieces
// orange has 4 pieces

Note that the order of the key-value pairs in a dictionary is not guaranteed, as dictionaries are unordered collections. If you need to iterate over a dictionary in a specific order, use the sorted(by:) method, which takes a closure that defines the sorting criteria.

var fruits = [
	"orange": 4,
	"apple": 5,
	"banana": 3
]

// Iterate over fruits in alphabetical order of keys
for (fruit, count) in fruits.sorted(by: {$0.key < $1.key}) {
	print("\(fruit) has \(count) pieces")
}

// Output:
// apple has 5 pieces
// banana has 3 pieces
// orange has 4 pieces


Swift dictionaries offer many practical methods and properties that allow us to count, access, and iterate over their contents.

  • The isEmpty and count properties help determine whether a dictionary contains any elements and how many there are.
  • The keys and values properties allow you to access keys and values as arrays, which you can then manipulate using any of the Array type methods.
  • The merge(_:uniquingKeysWith:) method merges another dictionary into the current one, using a closure to resolve conflicts for duplicate keys.
  • Finally, the filter(_:) and mapValues(_:) methods allow you to filter and transform the contents of a dictionary. Filtering and mapping are common functional programming operations.

Architecting SwiftUI apps with MVC and MVVM

It's easy to make an app by throwing some code together. But without best practices and robust architecture, you soon end up with unmanageable spaghetti code. In this guide I'll show you how to properly structure SwiftUI apps.

GET THE FREE BOOK NOW

Leave a Comment