Markers
How swiftspawn safely edits four files in your project, and where its boundaries are.
swiftspawn only ever edits four files after new. Every edit happens between two specific marker comments. Anything outside the markers is yours, forever.
The contract
Section titled “The contract”Each managed file contains a pair of // MARK: - comments:
// MARK: - Cases (auto-generated)// (CLI writes here)// MARK: - End auto-generatedWhen a generator needs to add a route, register a service, or wire a screen, it:
- Reads the file.
- Locates the matching pair of markers.
- Replaces only the text between them.
- Writes the file back.
Code outside the markers is read but never modified. You can rename functions, add comments, reorder properties, or rewrite anything else; the CLI doesn’t care.
If the markers aren’t found (legacy projects, hand-written files, or markers accidentally deleted), the generator does not edit the file. It prints the snippet you would need to paste in instead, so nothing gets corrupted.
The four files
Section titled “The four files”Infrastructure/Routing/Route.swift
Section titled “Infrastructure/Routing/Route.swift”Holds the navigation enum.
enum Route: Hashable { // MARK: - Cases (auto-generated) case home case movieList case movieDetail // MARK: - End auto-generated
// your hand-written cases (with parameters, etc.) go here case settings(tab: SettingsTab)}Cases inside the marker region are kept sorted alphabetically. Cases you write outside are untouched.
Affected by:
generate screen <Name>(unless--no-route)generate route <name>recipe crud <Resource>generate feature --screens <names>
Exit code if markers are missing: 7 (routeMarkersMissing).
App/RootView.swift
Section titled “App/RootView.swift”The root NavigationStack switches on Route to choose a view.
struct RootView: View { @State private var router = Router() var body: some View { NavigationStack(path: $router.path) { HelloView() .navigationDestination(for: Route.self) { route in switch route { // MARK: - Routes (auto-generated) case .home: HomeView() case .movieList: MovieListView() case .movieDetail: MovieDetailView() // MARK: - End auto-generated
// hand-written destinations (with parameters) go here case .settings(let tab): SettingsView(tab: tab) } } } }}Affected by the same commands as Route.swift.
App/DIContainer.swift
Section titled “App/DIContainer.swift”A Factory Container extension where service factories live.
extension Container { // MARK: - Service Factories (auto-generated) var movieService: Factory<MovieService> { self { MovieServiceImpl() } } var userService: Factory<UserService> { self { UserServiceImpl() } } // MARK: - End auto-generated
// your hand-tuned factories (with .singleton scope, custom args, etc.) go here var analytics: Factory<Analytics> { self { Analytics(env: .production) }.singleton }}Affected by:
generate service <Name>generate screen <Name> --uses <services>recipe crud <Resource>generate feature --service <name>
Package.swift
Section titled “Package.swift”The SPM manifest. Dependencies for OpenAPI clients are added here.
let package = Package( name: "MyApp", dependencies: [ // MARK: - Dependencies (auto-generated) .package(url: "https://github.com/hmlongco/Factory.git", from: "2.4.0"), .package(url: "https://github.com/apple/swift-openapi-generator.git", from: "1.0.0"), // MARK: - End auto-generated ], // ...)Affected by:
generate api
Exit code if markers are missing: 16 (packageMarkersMissing).
Restoring missing markers
Section titled “Restoring missing markers”If you accidentally delete a marker pair, the affected generators will start printing snippets instead of editing. To restore:
- Open the file.
- Add the marker pair back at the right location (see the four examples above).
- Re-run the generator.
There’s no swiftspawn fix-markers today; manual restoration is the workflow.
Why markers and not full-file regeneration
Section titled “Why markers and not full-file regeneration”Some scaffolders regenerate entire files from templates whenever you add something. That’s fast for greenfield projects but loses any custom code the moment you write some. The marker contract is a deliberate trade: the CLI stays in its lane, and your code stays yours.