Swift Marshal

Inconsistent member ordering quietly compounds across a codebase. It increases cognitive load when navigating files, adds friction during code reviews, and creates needless inconsistency across teams. Swift Marshal makes that shape predictable — so reading code gets faster and reviews focus on what actually matters.

Install
brew tap ericodx/homebrew-tools
brew install swift-marshal

Before & after

Same type, two arrangements. The default ordering puts initializers and type-level state first, instance state next, and behavior at the bottom.

Before
struct UserSession {
    // MARK: - Instance methods
    func refresh() async throws {
        let snapshot = try await api.fetch(id: userId)
        applyUpdate(snapshot)
    }

    private func applyUpdate(_ snapshot: UserSnapshot) {
        firstName = snapshot.firstName
        lastName = snapshot.lastName
    }

    // MARK: - Instance properties
    var firstName: String
    var lastName: String

    // MARK: - Subtypes
    enum State {
        case active, expired, revoked
    }

    private let api: UserAPI

    // MARK: - Typealiases
    typealias UserID = UUID
    let userId: UserID

    // MARK: - Type properties
    static let anonymous = UserSession(userId: UUID(), api: .stub)

    // MARK: - Initializers
    init(userId: UserID, api: UserAPI) {
        self.userId = userId
        self.api = api
    }

    // MARK: - Type methods
    static func current() -> UserSession {
        anonymous
    }
}
After
struct UserSession {
    // MARK: - Typealiases
    typealias UserID = UUID

    // MARK: - Initializers
    init(userId: UserID, api: UserAPI) {
        self.userId = userId
        self.api = api
    }

    // MARK: - Type properties
    static let anonymous = UserSession(userId: UUID(), api: .stub)

    // MARK: - Instance properties
    var firstName: String
    var lastName: String
    private let api: UserAPI
    let userId: UserID

    // MARK: - Subtypes
    enum State {
        case active, expired, revoked
    }

    // MARK: - Type methods
    static func current() -> UserSession {
        anonymous
    }

    // MARK: - Instance methods
    func refresh() async throws {
        let snapshot = try await api.fetch(id: userId)
        applyUpdate(snapshot)
    }

    private func applyUpdate(_ snapshot: UserSnapshot) {
        firstName = snapshot.firstName
        lastName = snapshot.lastName
    }
}

Swift Marshal parses your source with SwiftSyntax, finds every type declaration, and reorders its members according to a canonical sequence defined in a small YAML config. Comments and other trivia attached to a declaration travel with it — if you have a // MARK: note above a method, it moves wherever the method moves. The output is byte-identical to the original except for member order.

Once enforced project-wide, every type tells the same story in the same order: what it requires (typealiases, initializers), what it holds (properties), what it does (methods). Reading a file becomes orientation rather than discovery, reviews stop arguing about where a new declaration should sit, and merge conflicts in the same area drop close to zero — because two developers writing in the same type land their changes in predictable positions.

Features

Automated fixes Detect issues with check, apply fixes with fix. Same engine, different intent.
CI-ready, config-driven A small YAML in your repo describes the shape. The CLI does the rest.
AST-based analysis Powered by SwiftSyntax — parses the real syntax tree instead of regexing source.
Preserves logic and formatting Only reorders declarations. Whitespace, comments, and behavior stay untouched.