Swift Programming

Chapter 4
Collections and Data Processing

Certificate in IOS Development
Swift Programming   •   Certificate

Collections and Data Processing

Introduction

Most applications process groups of related values rather than isolated values. A weather app stores temperatures, a shopping app stores products, and a messaging app stores users or conversations. Swift provides three principal collection types—arrays, sets, and dictionaries—each designed around a different relationship between data items.

Choosing an appropriate collection is a design decision. Arrays preserve order and permit duplicate values; sets represent unique, unordered membership; dictionaries associate keys with values. Swift also provides powerful operations for traversing and transforming collections, including iteration, subscripting, sorting, filtering, mapping, and reduction.

This chapter develops practical foundations for using these tools safely and efficiently in iOS applications.

Learning Objectives

By the end of this chapter, you should be able to:

  • create, modify, and access arrays, sets, and dictionaries;
  • explain ordering, uniqueness, and key–value association;
  • use iteration and subscripting appropriately;
  • sort and transform collection data;
  • apply filter, map, and reduce to common programming tasks;
  • select a collection type based on application requirements; and
  • identify common errors involving optionals, mutation, and collection performance.

Collection Types and Their Design Meaning

A collection stores multiple values of the same element type, although a dictionary may have different key and value types. The choice of collection communicates the intended meaning of the data.

TypeOrderDuplicatesAccess modelTypical use
ArrayPreservedAllowedInteger indexOrdered lists, sequences
SetNot guaranteedNot allowedMembershipTags, permissions, unique identifiers
DictionaryKey-based associationKeys uniqueKeyLookup tables, records

Swift collections are generic. For example, Array<String> contains strings, while [Int] is shorthand for an array of integers. Strong typing helps detect incompatible values before an application runs.

Swift collections are also value types. Assigning a collection to another variable creates an independent logical value. Swift uses copy-on-write optimization, so physical copying is generally delayed until one copy is modified. This provides value semantics without unnecessarily copying storage.

Arrays: Ordered Sequences

An array is an ordered collection whose elements are accessed using integer indices beginning at zero.

var tasks: [String] = ["Plan", "Code", "Test"]
tasks.append("Deploy")
tasks.insert("Design", at: 0)

print(tasks[1])

The array now contains "Design", "Plan", "Code", "Test", and "Deploy". The expression tasks[1] returns the second element because indexing starts at zero.

Arrays can be initialized in several ways:

let emptyScores = [Int]()
let repeatedScores = Array(repeating: 0, count: 4)
let scores = [72, 88, 91, 65]

let prevents structural mutation, while var permits operations such as append, remove, and replacement.

var scores = [72, 88, 91, 65]
scores[0] = 75
scores.remove(at: 3)
scores.removeLast()

Accessing an invalid index causes a runtime error. Therefore, code should validate an index before subscripting when its safety is uncertain:

let index = 2
if scores.indices.contains(index) {
    print(scores[index])
}

Useful array properties and methods include count, isEmpty, first, last, contains, and randomElement(). The last two return information safely; first, last, and randomElement() return optionals because the array may be empty.

if let firstScore = scores.first {
    print("First score: \(firstScore)")
}

Array Complexity and Practical Selection

Appending to the end of an array is normally efficient because array storage is contiguous and may reserve extra capacity. Inserting or removing near the beginning can require later elements to move. Consequently, arrays are appropriate for ordered data and frequent indexed access, but not always for frequent insertion at the front.

Sets: Unique Membership

A set stores distinct values without providing a meaningful positional order. Elements must conform to Hashable, allowing Swift to determine membership efficiently.

var categories: Set<String> = ["Swift", "iOS", "Programming", "Swift"]
print(categories.count)       // 3
categories.insert("Testing")
categories.remove("iOS")

The duplicate "Swift" is retained only once. Set membership is tested with contains:

if categories.contains("Swift") {
    print("Swift is selected")
}

A set is preferable when uniqueness and membership are more important than order. For example, a user’s selected permissions should not contain the same permission twice.

Sets support mathematical operations:

let iosTopics: Set = ["Swift", "UIKit", "Testing"]
let sharedTopics: Set = ["Swift", "Testing", "Git"]

let common = iosTopics.intersection(sharedTopics)
let all = iosTopics.union(sharedTopics)
let onlyIOS = iosTopics.subtracting(sharedTopics)

common contains values in both sets, all contains values in either set, and onlyIOS contains values in the first set but not the second. Because sets are unordered, use sorted() when a predictable display order is required.

Dictionaries: Key–Value Association

A dictionary stores associations between unique keys and values. It is useful when the application needs to find a value by an identifier rather than by position.

var prices: [String: Double] = [
    "Book": 18.50,
    "Cable": 9.99
]

prices["Case"] = 24.00
prices["Cable"] = 11.49

Dictionary subscripting returns an optional value because a key may not exist:

if let price = prices["Book"] {
    print(price)
}

The nil value can remove a key–value pair:

prices["Case"] = nil

An alternative is updateValue(_:forKey:), which returns the previous value if one existed. This is useful when an application needs to know whether an update replaced existing data.

Dictionaries can be traversed as key–value pairs:

for (product, price) in prices {
    print("\(product): \(price)")
}

Dictionary iteration order should not be treated as a user-interface ordering rule. If sorted output is needed, explicitly sort the keys or pairs.

for product in prices.keys.sorted() {
    print("\(product): \(prices[product]!)")
}

Force unwrapping is safe here only because product came from the same dictionary. In general, optional binding is safer.

Iteration

The basic for-in loop visits each element:

let names = ["Ava", "Ben", "Chen"]
for name in names {
    print(name)
}

Use enumerated() when both the position and value are required:

for (index, name) in names.enumerated() {
    print("\(index): \(name)")
}

Use where to restrict iteration:

for score in scores where score >= 70 {
    print("Passing: \(score)")
}

A while loop is sometimes suitable for index-based processing, but direct collection iteration is usually clearer and less vulnerable to index errors.

Subscripts and Safe Access

Subscript syntax provides concise access to collection elements. Arrays use integer indices, dictionaries use keys, and strings use specialized indices rather than ordinary integers.

var profile = ["name": "Mina", "role": "Developer"]
profile["role"] = "iOS Developer"

A useful safety practice is to avoid assuming that data exists. Dictionary access, first, and similar operations produce optionals precisely because absence is a valid state. Handle that state explicitly with if let, guard let, nil-coalescing (??), or optional mapping.

let displayName = profile["name"] ?? "Guest"

Sorting and Ordering

sorted() returns a new collection and leaves the original unchanged. sort() mutates an array in place.

let values = [7, 2, 9, 4]
let ascending = values.sorted()
let descending = values.sorted(by: >)

For custom records:

struct AppUser {
    let name: String
    let age: Int
}

let users = [
    AppUser(name: "Ava", age: 28),
    AppUser(name: "Ben", age: 21)
]

let byAge = users.sorted { $0.age < $1.age }

Sorting generally requires comparison of elements and has typical time complexity O(n log n), although exact performance depends on implementation and input. Sorting should therefore be performed deliberately, especially for large data sets or repeated user-interface updates.

Filtering, Mapping, and Reduction

Swift’s higher-order collection methods express common data-processing operations clearly.

Filtering

filter returns elements satisfying a condition.

let scores = [55, 72, 91, 64, 88]
let passing = scores.filter { $0 >= 60 }

The result is [72, 91, 64, 88]. The original array is unchanged.

Mapping

map transforms every element and returns one output for each input.

let percentages = scores.map { "\($0)%" }

The result is an array of strings. Mapping does not remove elements; if one input must produce zero or more outputs, flatMap may be appropriate.

Reduction

reduce combines all elements into one result. It requires an initial value and a combining operation.

let total = scores.reduce(0) { partialTotal, score in
    partialTotal + score
}

The initial value is 0, and each score is added to the running total. The average can then be calculated safely:

let average = scores.isEmpty ? 0.0 : Double(total) / Double(scores.count)

The conditional is important because division by zero is not valid. Here, total is an integer, so conversion to Double is necessary when a fractional average is desired.

A reduction can also build a dictionary, although a loop may be clearer when the logic is complex:

let words = ["Swift", "iOS", "Swift"]
let frequencies = words.reduce(into: [String: Int]()) { result, word in
    result[word, default: 0] += 1
}

reduce(into:) mutates the accumulating result efficiently within the operation.

Chaining Operations

Operations can be chained when each stage has a clear purpose:

let highScoreLabels = scores
    .filter { $0 >= 70 }
    .sorted(by: >)
    .map { "Score: \($0)" }

The processing order matters: values are filtered, then sorted, then transformed. Chaining improves readability when names describe the result and closures remain simple.

Choosing the Appropriate Collection

A practical selection process asks three questions:

  1. Does order matter?
  2. Can duplicates occur?
  3. How will values be located?

Choose an array when order, duplicates, or positional access matters. Choose a set when values must be unique and the principal operation is membership testing or set algebra. Choose a dictionary when each value is identified by a unique key.

For example, an online store might use:

  • an array for the ordered items in a shopping cart;
  • a set for product categories selected by a user; and
  • a dictionary keyed by product ID for quick product lookup.

Sometimes more than one collection is appropriate. A dictionary can provide lookup by ID, while an array maintains display order. Such duplication requires a clear synchronization strategy to avoid inconsistent data.

Applied Case: Processing Assessment Results

Background

Consider an illustrative iOS learning application that records quiz scores for a class. The application must identify passing learners, display results from highest to lowest, calculate the class average, and prevent duplicate course tags.

Problem

The raw data consists of learner names and scores. The app also receives course tags from several screens, some of which may be repeated.

Analysis and Solution

A dictionary associates each learner with a score. A set removes duplicate tags. Collection methods process the scores:

let results: [String: Int] = [
    "Ava": 86,
    "Ben": 59,
    "Chen": 74,
    "Dina": 91
]

let passingLearners = results
    .filter { $0.value >= 60 }
    .sorted { $0.value > $1.value }

let total = results.values.reduce(0, +)
let average = results.isEmpty ? 0.0 : Double(total) / Double(results.count)

let tags: Set<String> = ["swift", "ios", "swift", "collections"]

passingLearners is a sorted array of key–value pairs. The set contains three unique tags. The average is computed using:

average = total ÷ number of learners

For these results, the total is 310 and the number of learners is 4, so the average is 77.5 points. The solution separates concerns: the dictionary models association, the set models uniqueness, and array-producing methods support ordered presentation.

Outcome and Learning Points

The application can display passing learners in descending score order, report the average, and show each course tag once. The case demonstrates that collection choice should reflect domain meaning rather than personal preference. It also shows why optional and empty-collection cases must be considered before production use.

Best Practices, Challenges, and Limitations

Prefer immutability when possible. Declare collections with let when they do not need mutation. This reduces accidental state changes and makes code easier to reason about.

Avoid force unwrapping by default. A missing dictionary key or empty array is normal in many applications. Use safe handling unless the invariant is genuinely guaranteed.

Do not rely on unordered output. Sets and dictionaries should be sorted before presentation when users require stable ordering.

Choose expressive transformations. filter, map, and reduce are valuable, but a conventional loop may be more readable for multi-stage validation, error handling, or complex side effects.

Consider memory and performance. Collection operations often create intermediate collections. For very large or lazily processed data, lazy sequences can defer work, but they should be introduced only when their behavior is understood and measured.

Keep UI work responsive. Expensive sorting or processing should not block the main thread in an iOS application. Large data sets may require background processing and carefully coordinated UI updates.

Validate external data. Collection contents received from networks, files, or users should be decoded and validated before use. A dictionary is not a substitute for a validated data model.

Contemporary Relevance

Modern Swift development frequently combines collections with Codable, asynchronous APIs, and Swift concurrency. Network responses are decoded into arrays or dictionaries, filtered before display, and transformed into view models. With concurrency, collection processing may occur away from the main actor, while UI updates must respect thread-safety rules. These developments do not change the fundamental principles: model data accurately, handle absence safely, and select structures according to required operations.

Chapter Summary

Arrays model ordered, indexable sequences and permit duplicates. Sets model unique membership without dependable positional order. Dictionaries model key–value associations and return optionals when a key may be absent. Swift supports safe and expressive processing through iteration, subscripting, sorting, filtering, mapping, and reduction. sorted() creates an ordered result, filter selects elements, map transforms every element, and reduce combines elements into one result. Collection selection should follow the data’s meaning and the operations the application performs. Robust code also considers empty collections, invalid indices, optionals, ordering requirements, mutation, performance, and UI responsiveness.

Key Terms

Array: An ordered collection that permits duplicate elements.

Set: A collection of unique elements with no meaningful positional order.

Dictionary: A collection associating unique keys with values.

Element: An individual value stored in a collection.

Index: A position used to access an array element.

Key: An identifier used to access a dictionary value.

Value: Data associated with a dictionary key.

Subscript: Syntax used to access or modify collection data.

Iteration: Repeatedly visiting elements in a collection.

Mutation: Changing a collection after it has been created.

Optional: A type representing either a value or absence of a value.

filter: An operation that retains elements satisfying a condition.

map: An operation that transforms every element.

reduce: An operation that combines elements into one result.

Sorting: Arranging elements according to a comparison rule.

Hashable: A protocol enabling values to be used efficiently in hashed collections such as sets and dictionary keys.

Value semantics: Behavior in which a collection variable represents an independent logical value.

Copy-on-write: An optimization that delays physical copying until mutation occurs.

Review Questions

Short Answer Questions

  1. What is the principal difference between an array and a set?
  2. Why does dictionary subscripting return an optional value?
  3. What does the filter method return?
  4. When should sorted() be preferred to sort()?
  5. Why is an initial value required by reduce?

Long Answer Questions

  1. Compare arrays, sets, and dictionaries in terms of order, uniqueness, access, and suitable applications.
  2. Explain how filter, map, and reduce differ, using a Swift example for each.
  3. Discuss safe subscripting and optional handling in collection-based programs.
  4. Explain why an application might use both an array and a dictionary for the same domain data.
  5. Describe performance and readability considerations when chaining collection operations.

Analytical / Application Questions

  1. Design collection types for a shopping cart, a unique permission list, and a product lookup service. Justify each choice.
  2. Write Swift code that receives an array of integers, retains even values, sorts them descending, and converts them into display strings.
  3. A learner-result dictionary may be empty and may contain failing scores. Explain how you would calculate a safe average and produce a sorted list of passing learners.

Multiple Choice Questions with Answers

Question 1

Which collection preserves order and allows duplicate elements?

A. Array

B. Set

C. Dictionary

D. Tuple

Correct Answer: A. Array

Question 2

What does filter do to a collection?

A. Sorts every element

B. Retains elements satisfying a condition

C. Combines all elements into one value

D. Associates elements with keys

Correct Answer: B. Retains elements satisfying a condition

Question 3

Why is a dictionary lookup such as prices["Book"] optional?

A. Dictionary values are always integers

B. The key may not exist

C. Dictionaries cannot store strings

D. Keys are accessed by array index

Correct Answer: B. The key may not exist

Question 4

Which operation returns a new sorted collection without modifying the original array?

A. sort()

B. insert()

C. sorted()

D. shuffleInPlace()

Correct Answer: C. sorted()

Question 5

What is the result of inserting a duplicate value into a Set?

A. A second copy is always added

B. The set becomes an array

C. The value is stored only once

D. The program must crash

Correct Answer: C. The value is stored only once

Question 6

Which operation transforms every input element into an output element?

A. map

B. filter

C. reduce

D. remove

Correct Answer: A. map

Question 7

What is the total produced by [3, 5, 7].reduce(0, +) ?

A. 10

B. 12

C. 15

D. 21

Correct Answer: C. 15

Question 8

Which collection is most appropriate for looking up a product by a unique product ID?

A. Array

B. Set

C. Dictionary

D. Range

Correct Answer: C. Dictionary

Question 9

What does scores.first provide when scores is an array?

A. Always the first integer

B. An optional first element

C. The array’s last index

D. A Boolean membership result

Correct Answer: B. An optional first element

Question 10

Which statement about sort() is correct?

A. It always returns a dictionary

B. It mutates the array in place

C. It removes duplicate elements

D. It can be used only with sets

Correct Answer: B. It mutates the array in place

Swift Programming
Chapter 4 of 14