Skip to content

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.

Each managed file contains a pair of // MARK: - comments:

// MARK: - Cases (auto-generated)
// (CLI writes here)
// MARK: - End auto-generated

When a generator needs to add a route, register a service, or wire a screen, it:

  1. Reads the file.
  2. Locates the matching pair of markers.
  3. Replaces only the text between them.
  4. 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.

Holds the navigation enum.

Infrastructure/Routing/Route.swift
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).

The root NavigationStack switches on Route to choose a view.

App/RootView.swift
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.

A Factory Container extension where service factories live.

App/DIContainer.swift
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>

The SPM manifest. Dependencies for OpenAPI clients are added here.

Package.swift
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).

If you accidentally delete a marker pair, the affected generators will start printing snippets instead of editing. To restore:

  1. Open the file.
  2. Add the marker pair back at the right location (see the four examples above).
  3. 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.