6.4 KiB
Tiledown Conventions
The Swift coding conventions for Tiledown. They keep the codebase consistent, testable, and portable. Read this before opening a PR. When in doubt, read the surrounding files and match what is already there: consistency with existing code outranks personal preference.
These are the Swift domain conventions: the language- and platform-specific
craft. They sit on top of a vendored core spine of cross-cutting engineering
discipline (the optimal-path ethic, proof and verification, testing discipline,
systematic debugging, commits, and git process). That spine lives in
core/; read it first, the Swift rules build on it and do not
repeat it.
This page is the overview. The full per-area Swift rules live at the repository
root (index at README.md), and the cross-cutting rules in
core/.
Language
Swift for everything. The single exception is JavaScript, and only where it is intrinsic to the output: client-side tiles (Mermaid, charts, forms, polls) emit HTML and JS that run in the visitor's browser. JS is never used for build logic or tooling.
Engineering principles
- Optimal over fast. Respect existing code and idioms. Clarify ambiguity before coding rather than assuming requirements. When a real trade-off exists, surface two or three options instead of guessing.
- Progressive architecture. Start with the simplest thing that works. A protocol is earned by a real cross-target seam or by multiple implementations; do not wrap a lone same-target concrete type preemptively. Generalise only when a pattern has actually emerged.
- Make impossible states unrepresentable. Use exhaustive enums with
associated values. Never force-unwrap (
!,try!) in shipping code. Errors carry both a human-readable reason and an actionable recovery path. - Testable by design. Inject every collaborator through the initialiser. Test behaviour through the public API, not implementation details.
- Profile, then optimise. Value semantics by default. Pick the right data structure first. Optimise only with a profile in hand.
Dependency injection
- No singletons. No
static let shared, no process-wide config reached through static accessors. Every dependency appears at theinitsite so coupling is visible, testable, and removable. - Every external collaborator goes through
init. Not method parameters at the call site, not static fallbacks, not environment lookup. Pure free functions that compute from their arguments are fine. - Cross-module seams are protocols, not concrete imports. A library module does not reach into another module's concrete types. The executable (the composition root) is the only place that wires concretes together.
- No closure typealiases for named cross-module contracts. Use a named
protocol. Closures as ordinary method parameters (
onProgress:) are fine.
Namespacing and file layout
- Every public implementation type lives under an
enum/structnamespace that mirrors its folder. The only top-level public contracts are protocols and supporting value/null/preview types in the foundation-onlySharedProtocolstarget, where the module itself is the namespace. ReadingModule.Sub.Leafshould tell you where every other type lives and what it does. - Namespace anchors are caseless
enum. Usestructonly when the type is also a value. Neverclassfor a namespace. Shared mutable state is anactoror an injected value, never aclass. - Drop redundant context. Under
Availability, the error isAvailability.Error, notAvailabilityError. - Concrete types are declared via extensions on the leaf namespace. Conformances may be separate extensions on the qualified path.
- One non-private type per file. Private helper types may co-locate when they exist only to support the main type.
- File naming. A file declaring
extension Foo.Bar { public struct X }is namedFoo.Bar.X.swift(dots, matching the qualified name). Anchor files contain only the namespace declaration, no implementation.
Concurrency
- Swift 6 strict concurrency is on. Types crossing concurrency boundaries are
Sendable; prove it, do not silence it with@uncheckedunless you have a documented reason. - Shared mutable state goes in an
actor. UI-affine state is@MainActor. - Use structured concurrency. No arbitrary
Task.sleepto paper over ordering.
Cross-platform
Tiledown builds and runs on Apple platforms and Linux, with Windows where explicitly noted. The TileKit engine itself targets macOS and Linux; the broader cross-platform discipline (and the CI matrix in cross-platform.md) extends to Windows for code that opts into it.
- Guard platform-divergent code and abstract platform-specific dependencies behind a protocol seam: one implementation per platform, wired by the composition root. Subprocess use is allowed.
- Prefer pure-Swift implementations over Foundation-only conveniences in the core.
- On the server side, prefer cross-platform libraries (for example AsyncHTTPClient over URLSession, swift-log over os.log).
Testing
- Use the Swift Testing framework for unit and integration tests:
@Test,@Suite,#expect. Use XCTest only where the platform harness requires it, notably XCUITest, or while migrating an existing suite that cannot move in one change. - One behaviour per test. Descriptive names. Deterministic data. No live network or filesystem dependence; inject test doubles.
- One test target per source target, named
<SourceTarget>Tests. - Use parameterised tests (
@Test(arguments:)) for families of similar cases.
Verification before "done"
The discipline lives in core/verification.md: no
claim of done, fixed, or passing without fresh command output in the same response.
For Swift, match the command to the claim:
| Claim | Command |
|---|---|
| Build succeeds | swift build |
| Tests pass | swift test (cite the count, e.g. 42 / 42) |
| Bug fixed | the test that reproduced it, now passing |
"Looks good" and "should pass" are not evidence.
Commits, branches, PRs
See core/commits.md and
core/git-discipline.md, and the project's
CONTRIBUTING.md. In short: Conventional Commits, one focused
change per PR, a CHANGELOG.md entry for any change touching shipping source, and
no AI attribution or em dashes in any committed text.