Sample CodeReviewed 2026-07-21View on Apple Developer

Using JSON with custom types

At a glance

Item Summary
Purpose Demonstrate array decoding, key renaming, nested source shapes, and dynamic JSON keys with Codable.
Deliverable A multi-page Xcode playground, not an app with a runtime UI or shared object graph.
Architecture Each page is an independent executable lesson with local JSON, local types, encoder/decoder calls, and printed output.

Project structure

Using-JSON-with-Custom-Types.playground/Pages/
├── Read Data from Arrays.xcplaygroundpage/Contents.swift
├── Change Key Names.xcplaygroundpage/Contents.swift
├── Access Nested Data.xcplaygroundpage/Contents.swift
├── Merge Data from Different Depths.xcplaygroundpage/Contents.swift
└── Table of Contents.xcplaygroundpage/Contents.swift

Declarations with the same names on different pages are isolated by playground-page execution; they do not form one module-wide model layer.

Overall architecture

Reference code

Using-JSON-with-Custom-Types.playground/Pages/Access Nested Data.xcplaygroundpage/Contents.swift:143 — the domain initializer deliberately removes transport-only aisle/shelf nesting.

extension GroceryStore {
    // ...
}

Ownership and state

Ownership evidence

Using-JSON-with-Custom-Types.playground/Pages/Merge Data from Different Depths.xcplaygroundpage/Contents.swift:87 — encoding creates nested containers from domain values without persistent shared state.

    func encode(to encoder: Encoder) throws {
        // ...
    }

All state is value-semantic and page-local. Decoders create DTO/domain values; adapter initializers copy the needed data. Encoders borrow values to write containers. There is no owner with a lifecycle beyond playground execution.

Class and protocol design

Type technique Responsibility
GroceryProduct: Codable Use synthesized array element coding when names and shape align.
Private CodingKeys enum Map wire keys such as product_name to idiomatic Swift properties.
GroceryStoreService: Decodable Mirror a nested transport shape exactly.
GroceryStore.init(from:) Convert transport values into a flatter domain shape.
ProductKey: CodingKey struct Represent unbounded product names as dynamic JSON keys.
Manual encode / init(from:) Merge key names and nested values into one product model.

Protocol-oriented design here means conformance to Codable, Encodable, Decodable, and CodingKey; there is no repository or service interface.

Access control

Boundary Effect and rationale
private CodingKeys Keeps wire-field names inside the type whose representation they define.
Page types and values are implicit internal They are lesson-local implementation, not a public package API.
public init(from:) on the internal GroceryStore extension Satisfies/illustrates decoding visibility, but the internal enclosing type caps external access.
Nested DTO types Names transport-only aisle/shelf concepts without exposing them to the flattened domain model.

Using-JSON-with-Custom-Types.playground/Pages/Change Key Names.xcplaygroundpage/Contents.swift:47 shows the private key map; the explicitly public decoder initializer is at Using-JSON-with-Custom-Types.playground/Pages/Merge Data from Different Depths.xcplaygroundpage/Contents.swift:124.

Logic ownership and placement

Logic Owner
Wire-name mapping Nested CodingKeys
Transport-shape mirroring GroceryStoreService and nested DTOs
Transport-to-domain flattening GroceryStore.init(from:)
Dynamic-key generation ProductKey
Manual nested encode/decode GroceryStore protocol conformances
Lesson execution and output Page-level statements

Design patterns

Pattern Evidence Purpose
Synthesized Codable Using-JSON-with-Custom-Types.playground/Pages/Read Data from Arrays.xcplaygroundpage/Contents.swift:38 Handles aligned homogeneous array data with minimal code.
Coding-key mapping Using-JSON-with-Custom-Types.playground/Pages/Change Key Names.xcplaygroundpage/Contents.swift:42 Separates idiomatic Swift names from wire names.
DTO / translation boundary Using-JSON-with-Custom-Types.playground/Pages/Access Nested Data.xcplaygroundpage/Contents.swift:115 Prevents transport nesting from shaping the app model.
Custom dynamic key Using-JSON-with-Custom-Types.playground/Pages/Merge Data from Different Depths.xcplaygroundpage/Contents.swift:74 Supports data-dependent object keys.

Naming conventions

  • GroceryStoreService signals a source-shaped intermediary; GroceryStore is the app-facing domain value.
  • Nested types (Aisle, Shelf, ProductKey) keep names scoped to the representation that needs them.
  • Initializers use init(from:) for translation and Codable decoding, with the parameter type disambiguating their roles.

Architecture takeaways

  • Model transport JSON exactly in a DTO when its shape is not suitable for app code.
  • Convert at one initializer boundary and keep the domain representation simple.
  • Keep wire keys private to their coding type.
  • Treat each playground page as an independent architecture example, not one combined application.

Source map

Source Role
Using-JSON-with-Custom-Types.playground/Pages/Read Data from Arrays.xcplaygroundpage/Contents.swift:38 Synthesized array coding
Using-JSON-with-Custom-Types.playground/Pages/Change Key Names.xcplaygroundpage/Contents.swift:42 Static key mapping
Using-JSON-with-Custom-Types.playground/Pages/Access Nested Data.xcplaygroundpage/Contents.swift:115 Source-shaped DTO
Using-JSON-with-Custom-Types.playground/Pages/Access Nested Data.xcplaygroundpage/Contents.swift:143 DTO-to-domain adapter
Using-JSON-with-Custom-Types.playground/Pages/Merge Data from Different Depths.xcplaygroundpage/Contents.swift:73 Dynamic coding keys