| name | datasource |
| description | Creates DataSources for data access. Use when creating RemoteDataSource for REST APIs or GraphQL, MemoryDataSource for in-memory storage, LocalDataSource for UserDefaults persistence, or DTOs for API responses. |
Skill: DataSource
Guide for creating DataSources following the Repository pattern.
Before Creating — Clarify Requirements
CRITICAL: Do NOT assume the API schema or operations. Ask the user before writing any code:
- Operations: Which operations does the contract need and with what parameters? (e.g., paginated list, single item by ID, both, with filters, etc.)
- Entity fields: What fields does the DTO need? For GraphQL, the user can provide the query directly — derive DTOs from its response shape. Otherwise, ask for the fields or API documentation. Never fetch external API docs on your own.
Only proceed to implementation after the user confirms these details.
DataSource Types
| Type | Transport | Contract | Implementation | Error Mapper |
|---|
| REST | HTTP | nonisolated protocol: Sendable | nonisolated struct with HTTPClientContract | HTTPErrorMapper |
| GraphQL | HTTP/GraphQL | nonisolated protocol: Sendable | nonisolated struct with GraphQLClientContract | GraphQLErrorMapper |
| Memory | In-memory | : Actor | actor with dictionary storage | — |
| UserDefaults | Local | : Actor | actor with UserDefaults | — |
Templates
Each reference contains full templates: contract, implementation, DTO, mock, fixtures, and tests.
File Structure
Features/{Feature}/
├── Sources/
│ └── Data/
│ ├── DataSources/
│ │ ├── Remote/
│ │ │ ├── {Name}RemoteDataSourceContract.swift
│ │ │ └── {Name}RESTDataSource.swift (or {Name}GraphQLDataSource.swift)
│ │ └── Local/
│ │ ├── {Name}LocalDataSourceContract.swift
│ │ ├── {Name}MemoryDataSource.swift
│ │ └── {Name}UserDefaultsDataSource.swift # Optional: UserDefaults
│ └── DTOs/
│ └── {Name}DTO.swift
└── Tests/
├── Unit/Data/
│ └── DataSources/
│ ├── Remote/
│ │ └── {Name}RESTDataSourceTests.swift (or {Name}GraphQLDataSourceTests.swift)
│ └── Local/
│ └── {Name}MemoryDataSourceTests.swift (or {Name}UserDefaultsDataSourceTests.swift)
├── Shared/Fixtures/
│ ├── {name}.json
│ └── {name}s_response.json
└── Shared/Mocks/
Key Principles
Contracts
- Internal visibility, separate file from implementation
- Remote contracts:
nonisolated protocol: Sendable with @concurrent on async methods. Local contracts (Memory, UserDefaults): : Actor
- Transport-agnostic: same contract for REST or GraphQL
- Remote DataSource implementations:
nonisolated struct with @concurrent on async methods
- Transport clients (
HTTPClientContract, GraphQLClientContract) also use @concurrent for off-MainActor execution
ChallengeNetworking uses nonisolated default isolation — networking types don't need nonisolated annotations
- DataSources only work with DTOs — parameters and return types must be DTOs, never domain objects
- Remote:
@concurrent async throws. Local (Memory, UserDefaults): methods are actor-isolated (implicitly async from caller)
Sendable vs Actor contracts: Use : Actor when the DataSource has its own mutable state to protect (Memory, UserDefaults, ImageDiskCacheContract). Use : Sendable with nonisolated methods only for stateless wrappers around thread-safe APIs (e.g., FileSystem wrapping FileManager). See /concurrency skill "Actor Reentrancy" section for when and why to choose : Sendable.
DTOs (Data Transfer Objects)
"A Data Transfer Object is one of those objects our mothers told us never to write. It's often little more than a bunch of fields and the getters and setters for them."
— Martin Fowler, PoEAA
DTOs are intentionally anemic — they exist purely to transfer data between systems.
Rules:
Decodable, Equatable
- Internal visibility, properties match JSON keys
- NO behavior — only data (anemic by design)
- NO
toDomain() methods — mapping belongs in the Repository
- REST IDs are
Int, GraphQL IDs are String
Error Mapping
DataSources catch transport errors and map them to APIError:
- REST:
HTTPError → APIError via HTTPErrorMapper
- GraphQL:
GraphQLError → APIError via GraphQLErrorMapper
Repositories and upper layers only see APIError, never transport-specific errors.
Visibility Summary
| Component | Visibility | Location |
|---|
| Remote Contract | internal | Sources/Data/DataSources/Remote/ |
| Remote Implementation | internal | Sources/Data/DataSources/Remote/ |
| Local Contract | internal | Sources/Data/DataSources/Local/ |
| Local Implementation | internal | Sources/Data/DataSources/Local/ |
| DTO | internal | Sources/Data/DTOs/ |
| Mocks | internal | Tests/Shared/Mocks/ |
Checklists
RemoteDataSource (REST)
RemoteDataSource (GraphQL)
MemoryDataSource
LocalDataSource (UserDefaults)