Skip to content

kaanbiryol/init-revise-cli

Repository files navigation

init-revise-cli

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"))

Why?

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.

What it does

  • 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)

Requirements

  • Ruby with Bundler (bundle)

Quick start

make build
./init-revise-cli <source-file> -- <compiler-args...>

Usage

Single file

./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 -json

Look for swiftASTCommandArguments in the JSON output.

Batch mode (Xcode project)

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. Edit run.rb for other configurations.

Example project

The repo includes a committed Example/Example.xcodeproj fixture with .init(...) patterns across basic, optional, array, and nested cases.

make build
make test

Check Example/Targets/ExampleKit/Sources/ExampleKit.swift for the output. Reset with git restore Example/Targets.

Expected output

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")
])

How it works

  1. Type extraction - sends a SourceKit request with the file path and compiler args to resolve expression types at each byte offset
  2. AST rewriting - walks the syntax tree via SyntaxRewriter, matches .init( tokens to their resolved types, and replaces them in place
  3. File output - writes the transformed source back atomically (UTF-8)

Additional sources

Limitations

  • 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 transform Self.init() or already-explicit initializers
  • Silently skips expressions where SourceKit cannot determine the type

About

Swift CLI that rewrites type-inferred initializations to explicit ones using SwiftSyntax, reducing clean build time at scale

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors