← All topics/Architecture & design patterns

Practical interview questions

Scenario-style prompts with sample answer outlines. Focus is on how you would design and reason in real codebases.

Question 6

Single source of truth & duplicated state

Users see different counts on the list and detail screens after an edit. Architecturally, how do you prevent multiple ‘caches of truth’ for the same entity?

Follow-ups

  • What about SwiftUI @State vs shared store?

Answer outline

This happens when the same entity is held in multiple mutable places — a list screen caches one copy, the detail screen another, and writes to one don't reach the other.

The fix is a single source of truth: one place owns the entity, all screens read from it, and all writes go through it.

A helpful mental model: separate canonical state from view-local state. Canonical state is the shared model that must stay consistent across screens — a post count, a user profile, a cart total. View-local state is scoped to one view and temporary — draft text, loading flags, sheet visibility.

Once an edit is committed it should update the shared store; every view then derives its display from that store rather than holding its own copy.

Principles

  • Store each entity once — every screen reads from the same source, never a local copy.
  • Route all writes through the store; views never mutate shared data directly.
  • Separate canonical state (shared, must be consistent) from view-local state (ephemeral, scoped to one view).
  • @State is right for view-local ephemeral state; cross-screen entities belong in a shared observable store.

The store owns the array; views call update() rather than mutating their own copies:

Shared store — single write point
@Observable
final class PostStore {
    private(set) var posts: [Post] = []

    func update(_ post: Post) {
        guard let index = posts.firstIndex(where: { $0.id == post.id })
        else { return }
        posts[index] = post   // one write; every observer sees it
    }
}

Both views receive the same PostStore instance; neither holds its own copy of the data:

Two views, one store — counts stay in sync
struct PostListView: View {
    let store: PostStore

    var body: some View {
        List(store.posts) { PostRow(post: $0) }
    }
}

struct PostDetailView: View {
    let store: PostStore
    let postId: Post.ID

    var post: Post? { store.posts.first { $0.id == postId } }

    var body: some View {
        Text("Likes: \(post?.likeCount ?? 0)")
        Button("Like") {
            if var p = post {
                p.likeCount += 1
                store.update(p)   // list view updates automatically
            }
        }
    }
}

Follow-up angles

  • @State is the right fit for sheet visibility, draft text fields, and selection — state that lives and dies with one view and doesn't need to be consistent elsewhere.
  • For persistence-backed entities, let SwiftData or Core Data be the store and query from there — view models should not cache results in separate properties.