Sample CodeiOS 14.0+, iPadOS 14.0+, Mac Catalyst 14.0+Reviewed 2026-07-21View on Apple Developer

WWDC21 Challenge: Large Text Challenge

At a glance

Item Summary
Purpose Teach Dynamic Type by letting people change layout properties and inspect exercise-specific feedback.
Architecture A code-light, single-target SwiftUI challenge organized around JSON scenarios and per-exercise observable state objects.
Main pattern ScenarioFeedbackProtocol gives one feedback view a common interface over different exercise models.
Boundary No extension, persistence layer, network service, or reusable package.

Project structure

LargeTextChallenge/
├── Models/                 # Scenario, Texture, JSON loader
├── Views/
│   ├── Scenarios/          # ExerciseOne ... ExerciseFive
│   ├── Supporting/         # AdaptiveStack and shapes
│   └── ScenarioFeedbackView.swift
└── Extensions/

Each exercise intentionally keeps its property model and view variants together.

Overall architecture

Reference code

LargeTextChallenge/Views/ScenarioList.swift:29 — data keys select a concrete teaching view (abridged).

List(scenarioData) { scenario in
    if scenario.exercise == .textWithButton {
        NavigationLink(destination: ExerciseOne(scenario)) { ... }
    } else if scenario.exercise == .textWithImage {
        NavigationLink(destination: ExerciseThree(scenario)) { ... }
    }
}

Ownership and state

Ownership evidence

LargeTextChallenge/Views/Scenarios/ExerciseOne.swift:128 — the exercise creates and retains its observable state.

struct ExerciseOne: View {
    let scenario: Scenario?
    @StateObject private var viewModel: ExerciseOneProperties

    init(_ scenario: Scenario? = nil) {
        self.scenario = scenario
        self._viewModel = StateObject(
            wrappedValue: ExerciseOneProperties(scenario)
        )
    }
}

The exercise model is the mutation authority for choices. JSON-backed Scenario values are immutable descriptions shared by value.

Class and protocol design

Each Exercise...Properties class adopts ObservableObject and ScenarioFeedbackProtocol. The protocol exposes only results(), so ScenarioFeedbackView accepts any exercise without knowing its individual toggles (LargeTextChallenge/Views/ScenarioFeedbackView.swift:10). Views remain value types; reusable AdaptiveStack<Content> uses a generic @ViewBuilder instead of type erasure.

Access control

Symbol Access Rationale
Exercise viewModel private Only its root view creates the StateObject (LargeTextChallenge/Views/Scenarios/ExerciseOne.swift:130).
Scenario fixture globals internal default All exercises in the single app target read them (LargeTextChallenge/Models/Data.swift:12).
Exercise/property types internal default They are target-local teaching components.
Public/file-private API none No cross-module contract exists; file-level widening is unnecessary.

Logic ownership and placement

Logic Owner Placement reason
Bundle decoding Data.swift generic loader One small fixture-loading boundary.
Exercise routing ScenarioList The scenario key maps directly to destination views.
Choice mutation and evaluation Exercise...Properties Published choices and feedback calculation change together.
Adaptive horizontal/vertical layout AdaptiveStack Reusable layout behavior is isolated from exercise content (LargeTextChallenge/Views/Supporting/AdaptiveStack.swift:17).

Design patterns

Pattern Evidence Purpose
Data-driven exercise catalog Codable scenarios and enum keys Keeps copy/feedback outside view code.
Strategy protocol ScenarioFeedbackProtocol One result UI works for multiple exercise models.
Observable view model Per-exercise property classes Shares toggles across the menu, content, and feedback views.
Generic composition AdaptiveStack<Content> Switches layout without duplicating child content.

Naming conventions

  • Exercise families share numbered prefixes, making related state/menu/view types easy to scan.
  • Model types use nouns (Scenario, Texture); feedback outputs add Result.
  • Boolean properties read as decisions (allowsScrolling, stackVertically).

Architecture takeaways

  • Keep lesson configuration immutable and user choices in an observable owner.
  • A tiny protocol can unify feedback without forcing common storage.
  • Generic view builders are effective protocol-oriented composition for SwiftUI layout.
  • The code contains deliberate challenge variants; they are teaching states, not production fallback paths.

Source map

Source Role
LargeTextChallenge/LargeTextChallengeApp.swift:10 App entry point
LargeTextChallenge/Models/Scenario.swift:11 Scenario schema
LargeTextChallenge/Models/Data.swift:17 Generic fixture decoder
LargeTextChallenge/Views/ScenarioFeedbackView.swift:58 Shared result UI
LargeTextChallenge/Views/Scenarios/ExerciseOne.swift:13 Observable strategy implementation
LargeTextChallenge/Views/Supporting/AdaptiveStack.swift:43 Adaptive composition