A Swift CLI that rewrites .init(...) calls to explicit type initializers using SourceKit type resolution - reducing Swift type-checker overhead in large codebases.
// before
doSomething(model: .init(value: "1"))
// after
doSomething(model: Test.ViewModel(value: "1"))Implicit .init(...) forces the Swift type checker to infer types from context. In complex expressions with generics or deeply nested calls, this can increase build times. (not always!)
Replacing .init(...) with explicit types gives the compiler a direct resolution path.
- Queries SourceKit for resolved expression types using your project's compiler arguments
- Parses Swift source files into an AST via swift-syntax
- Replaces
.init(...)tokens with fully-qualified type names - Handles optionals, arrays, and nested types
- Rewrites files in place (atomic writes)
- Ruby with Bundler (
bundle)
make build
./init-revise-cli <source-file> -- <compiler-args...>./init-revise-cli MyFile.swift -- <compiler-args...>Compiler args are the same flags passed to swiftc. Extract them from Xcode:
xcodebuild -project MyApp.xcodeproj -alltargets -arch arm64 \
-sdk iphonesimulator -showBuildSettingsForIndex -jsonLook for swiftASTCommandArguments in the JSON output.
run.rb processes all Swift files across targets and schemes, resolving per-file compiler arguments automatically:
bundle exec ./run.rb <project-path>
# or, for workspace-based projects:
bundle exec ./run.rb <workspace-path> <project-path>Defaults to
arm64/iphonesimulator. Editrun.rbfor other configurations.
The repo includes a committed Example/Example.xcodeproj fixture with .init(...) patterns across basic, optional, array, and nested cases.
make build
make testCheck Example/Targets/ExampleKit/Sources/ExampleKit.swift for the output. Reset with git restore Example/Targets.
Before:
doSomething(model: .init(value: "1"))
doSomethingOptional(model: .init(value: "2"))
doSomethingArray(model: [
.init(value: "3"),
.init(value: "4"),
.init(value: "5")
])After:
doSomething(model: Test.ViewModel(value: "1"))
doSomethingOptional(model: Test.ViewModel(value: "2"))
doSomethingArray(model: [
Test.ViewModel(value: "3"),
Test.ViewModel(value: "4"),
Test.ViewModel(value: "5")
])- Type extraction - sends a SourceKit request with the file path and compiler args to resolve expression types at each byte offset
- AST rewriting - walks the syntax tree via
SyntaxRewriter, matches.init(tokens to their resolved types, and replaces them in place - File output - writes the transformed source back atomically (UTF-8)
- Why does adding a type name here speed up typechecking so much?
- Surprising compilation performance of nested .init() vs Constructable()
- Requires correct compiler arguments - without them, SourceKit cannot resolve types
- In-place only - no dry-run mode; use version control
- Only targets
.init()syntax (prefix-dot form) - does not transformSelf.init()or already-explicit initializers - Silently skips expressions where SourceKit cannot determine the type