Swift
custom objects
arrays
value modification
coding tutorial

Find an item and change value in custom object array - Swift

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Updating one item inside an array of custom objects is a common Swift task in view models and state reducers. The best method depends on whether your model is a value type or reference type. For value types, find the index and replace or mutate through the index.

Update Pattern for Struct Based Models

With struct models, array elements are values, so mutate by index after searching with firstIndex(where:).

swift
1import Foundation
2
3struct Task {
4    let id: Int
5    var title: String
6    var done: Bool
7}
8
9var tasks = [
10    Task(id: 1, title: "Write docs", done: false),
11    Task(id: 2, title: "Review PR", done: false),
12]
13
14if let idx = tasks.firstIndex(where: { $0.id == 2 }) {
15    tasks[idx].done = true
16}
17
18print(tasks)

This pattern is explicit and keeps mutation localized.

Safer Reusable Update Helper

If you update arrays often, create a helper that encapsulates search and mutation. This prevents duplicated lookup logic and reduces mistakes.

swift
1extension Array where Element == Task {
2    mutating func updateTask(id: Int, mutate: (inout Task) -> Void) {
3        guard let index = firstIndex(where: { $0.id == id }) else { return }
4        mutate(&self[index])
5    }
6}
7
8tasks.updateTask(id: 1) { task in
9    task.title = "Write final docs"
10    task.done = true
11}

The closure based helper keeps call sites concise and easy to read.

Reference Type Models and Side Effects

If elements are classes, mutation semantics differ because array stores references. Changing properties through a found instance updates shared references too. This may be desired or dangerous depending on architecture.

swift
1final class User {
2    let id: Int
3    var score: Int
4
5    init(id: Int, score: Int) {
6        self.id = id
7        self.score = score
8    }
9}
10
11let users = [User(id: 10, score: 3), User(id: 11, score: 7)]
12if let user = users.first(where: { $0.id == 10 }) {
13    user.score += 5
14}

When using reference types, document ownership rules so unexpected shared mutations do not leak across features.

firstIndex(where:) is perfect for moderate collections, but repeated updates on large arrays can become expensive because each lookup scans from the start. If updates are frequent, maintain an index map from identifier to array position.

swift
1var indexByID: [Int: Int] = [:]
2for (idx, task) in tasks.enumerated() {
3    indexByID[task.id] = idx
4}
5
6if let idx = indexByID[2] {
7    tasks[idx].done = true
8}

When items are inserted or removed, update the map accordingly or rebuild it in one pass after batch operations. This tradeoff adds bookkeeping but can reduce update cost significantly in state heavy applications.

For concurrent contexts, avoid mutating shared arrays directly from multiple threads. Route updates through one serial queue or actor so search and mutation remain consistent.

In SwiftUI or reducer architectures, immutable updates are also common: create a new array with the modified item and publish it as new state. This approach can simplify change tracking and replay behavior in complex UI flows.

Choose one update style per module and document it. Consistency reduces subtle bugs when multiple developers touch state mutation code simultaneously.

Common Pitfalls

  • Mutating a copy instead of the array element for struct models.
  • Performing repeated linear searches in hot loops instead of indexing strategy.
  • Ignoring missing element cases and assuming lookups always succeed.
  • Using reference types without clear ownership expectations.
  • Spreading update logic across many call sites with inconsistent rules.

Summary

  • Use firstIndex(where:) plus index mutation for struct arrays.
  • Encapsulate repeated update logic in helpers.
  • Understand reference versus value semantics before mutating.
  • Handle missing elements gracefully.
  • Consider dictionary indexing for performance critical updates.

Course illustration
Course illustration

All Rights Reserved.