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.
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.
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
}
} 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.