A reference architecture for SwiftUI applications, not a template with the serial numbers filed off. Clone it, rename it, delete the sample feature, and you have a production-shaped app on day one.
- Swift 6 language mode, strict concurrency
complete - iOS 17+, SwiftUI, SwiftData, Swift Testing
- Zero third-party dependencies — everything is Apple frameworks behind your own protocols
- Protocol-oriented boundaries and constructor injection throughout
- Router-based navigation with deep links and push-notification routing
- QA / Production environments driven by
.xcconfig+ schemes, not#if DEBUG - AI scaffolding skills for VS Code Copilot in
.github/prompts
git clone <your-fork> SwiftUIStarterKit
cd SwiftUIStarterKit
open SwiftUIStarterKit.xcodeprojPick a scheme and run:
| Scheme | Configuration | Backend | Bundle id |
|---|---|---|---|
SwiftUIStarterKit-Dev |
Debug | QA host, verbose logs | …swiftuistarterkit.dev |
SwiftUIStarterKit-QA |
QA | QA host, optimised | …swiftuistarterkit.qa |
SwiftUIStarterKit-Prod |
Release | Production host | …swiftuistarterkit |
All three install side by side, so a tester can hold QA and production at once.
The sample app talks to jsonplaceholder.typicode.com
— a free, public, no-key API — so it runs the moment you clone it. Sign in with
any email containing @ and a password of four or more characters; Debug and QA
builds use an offline DemoAuthService.
- Rename —
PRODUCT_BUNDLE_IDENTIFIER_BASEinConfig/Base.xcconfig. - Point at your API —
API_BASE_SCHEME/API_BASE_HOST/API_BASE_PATHinConfig/QA.xcconfigandConfig/Prod.xcconfig. - Real authentication — in
Core/DI/AppDependencies.swift, replace theDemoAuthServicebranch withRemoteAuthService, then deleteDemoAuthServiceand adjustNetwork/DTOs/AuthDTO.swiftto your payloads. - Delete the sample feature — remove
Features/TaskList,Features/TaskDetail,Data/Models/TaskItem.swift,Data/Repositories/TaskRepository.swift,Network/Endpoints/TaskEndpoints.swiftand theirAppRoute/FeatureFactorycases. If anything else breaks, the coupling was wrong and worth fixing. - Brand it — colours and spacing live in
DesignSystem/Theme/DesignTokens.swift; the accent colour is inResources/Assets.xcassets/AccentColor.colorset. - Push notifications — uncomment
CODE_SIGN_ENTITLEMENTSinConfig/Base.xcconfig, setDEVELOPMENT_TEAM, and enable the Push Notifications capability. Left off by default so a plain clone builds with no signing setup.
┌────────────────────────┐
│ SwiftUIStarterKitApp │ @main
└──────────┬─────────────┘
│ builds once
┌──────────▼─────────────┐
│ AppDependencies │ the composition root:
│ (the ONLY file that │ the one place concrete
│ names concrete types) │ types are chosen
└──────────┬─────────────┘
┌───────────────────┼────────────────────┐
│ │ │
┌────────▼────────┐ ┌────────▼────────┐ ┌─────────▼────────┐
│ AppRouter │ │ FeatureFactory │ │ RootView │
│ (routes as data) │ │ (route → view) │ │ (auth state → UI) │
└──────────────────┘ └────────┬────────┘ └───────────────────┘
│ injects protocols
┌─────────────────────────────▼──────────────────────────────┐
│ Feature View ──► ViewModel ──► RepositoryProtocol │
└──────────────────────────────────┬─────────────────────────┘
┌───────────────┴───────────────┐
┌──────────▼─────────┐ ┌──────────▼─────────┐
│ NetworkClientProtocol│ │ TaskLocalStore │
│ → NetworkClient │ │ → SwiftDataTaskStore│
└──────────┬─────────┘ └────────────────────┘
│ interceptors
┌──────────▼─────────┐
│ TokenProviderProtocol│ ◄── AuthManager, Keychain, biometrics
└────────────────────┘
Every arrow points at an abstraction. A feature never names a concrete service, which is what makes each piece replaceable, mockable and independently testable.
SwiftUIStarterKit/
├── App/ entry point, AppDelegate, RootView
├── Core/
│ ├── Configuration/ AppEnvironment, AppConfiguration
│ ├── DI/ AppDependencies, FeatureFactory, preview graph
│ ├── Navigation/ AppRoute, AppRouter
│ ├── Logging/ LoggerProtocol, OSLogLogger, redaction
│ ├── Analytics/ protocol, composite, vendor adapters
│ └── Extensions/ ViewState, UserFacingError, view helpers
├── Network/ APIEndpoint, NetworkClient, interceptors, DTOs
├── Data/ SwiftData models, LocalStore protocols, repositories
├── Security/ Auth, Biometrics, SecureStorage
├── Notifications/ permission, payloads, deep-link routing
├── DesignSystem/ tokens and shared components
└── Features/ one folder per screen: View + ViewModel
| Doc | What it covers |
|---|---|
| 01 Architecture | Layers, dependency rule, why MVVM here |
| 02 Dependency Injection | The composition root, and why not a service locator |
| 03 Networking | Endpoints, client, interceptors, error mapping |
| 04 Navigation | Routes as data, the router, deep links |
| 05 Security | Keychain, tokens, refresh, biometrics |
| 06 Push Notifications | Permission, tokens, routing a tap |
| 07 Analytics and Logging | Event catalogue, adapters, redaction |
| 08 Design System | Tokens, components, accessibility |
| 09 Testing | What to test, the mock library, patterns |
| 10 Environments and Schemes | xcconfig → Info.plist → AppConfiguration |
| 11 Adding a Feature | Step-by-step walkthrough |
| 12 Modularization | When and how to split into SPM packages |
| AI Engineering Prompt | The reusable prompt for building on this kit |
.github/ ships Copilot instructions and reusable prompt files. In VS Code,
open Copilot Chat and run:
/add-new-screen— asks for the screen name, feature folder and how it is reached, then generates view, view model, route, factory case and tests/api-integration-scaffold— asks for paths, verbs and sample JSON, then generates DTOs, endpoints, repository, DI wiring and tests/add-feature-module— a whole feature folder/write-unit-tests— Swift Testing suites using the kit's mocks
.vscode/settings.json already enables prompt files, instruction files and chat
modes. .github/copilot-instructions.md applies to every request; the files in
.github/instructions/ apply to matching paths.
xcodebuild test \
-project SwiftUIStarterKit.xcodeproj \
-scheme SwiftUIStarterKit-Dev \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=latest'The suite runs with no network, no Keychain entitlement and no on-disk database. That is a property of the architecture, not of the tests.
MIT — see LICENSE.