Skip to main content

Xcode Stuck on "Indexing"? 5 Tricks to Fix Slow Build Times

 There is no workflow killer quite like the infinite "Indexing..." progress bar in Xcode. You open your project, ready to implement a feature, but the editor features—syntax highlighting, autocomplete, and "Jump to Definition"—are dead. Your fans are spinning at max RPM, SourceKitService is consuming 100% CPU, and Xcode has effectively become a glorified text editor.

For iOS developers, this isn't just an annoyance; it is a significant drain on productivity. While the immediate impulse is to restart Xcode, that rarely solves the underlying issue. The problem usually lies deeper in the interaction between the Swift compiler, the build system, and the derived data cache.

Here is a technical deep dive into why Xcode hangs on indexing and five distinct strategies to resolve it permanently.

The Root Cause: Why Indexing Hangs

To fix the problem, we must understand the architecture of the Xcode build system. The "Indexing" phase is powered primarily by SourceKitService, a background daemon responsible for parsing your Swift and Objective-C source code to provide editor features.

The Type Inference Trap

Swift is a strongly typed language with aggressive type inference. When you write code, the compiler constructs an Abstract Syntax Tree (AST) and attempts to solve constraints to determine types.

In complex expressions—particularly those involving chained operators, large literals (like dictionaries), or heavy usage of Swift generics—the constraint solver can hit exponential time complexity. If a single expression takes 15 seconds to type-check, the indexer hangs while waiting for that resolution. If SourceKitService cannot resolve the tree, it enters a race condition or a deadlock, causing the permanent "Indexing..." status.

The Corrupted Module Cache

Xcode caches build artifacts and indexed databases in DerivedData. This folder contains the Module Cache, a binary representation of the compiled modules. If the compiler is interrupted, or if there is a version mismatch between Swift toolchains, this cache can become corrupted. When Xcode attempts to read a corrupted index file, the process stalls indefinitely.


1. The Nuclear Option: Surgical DerivedData Removal

While every developer knows to "delete DerivedData," doing it via the Xcode UI (Product > Clean Build Folder) is often insufficient because it does not always clear the Module Cache or the Index Data Store.

To ensure a completely clean state, you must remove the directory manually via the terminal. This forces Xcode to regenerate the index database from scratch.

The Fix

Close Xcode completely to release any file locks, then run the following command:

# Nuke the entire DerivedData directory
rm -rf ~/Library/Developer/Xcode/DerivedData

# Optional: Clear the module cache specifically if you want to be precise
rm -rf ~/Library/Developer/Xcode/DerivedData/ModuleCache.noindex

Restart Xcode. The initial index will take time, but it should complete successfully rather than hanging.


2. Identify Slow Compilers with Diagnostic Flags

If you clear DerivedData and the hanging returns shortly after, your code is likely too complex for the type checker to resolve quickly. You need to identify exactly which function or expression is choking the compiler.

Swift provides diagnostic flags to warn you when type-checking an expression or function exceeds a specific time threshold.

The Fix

  1. Navigate to your Project Target.
  2. Go to Build Settings.
  3. Search for Other Swift Flags.
  4. Add the following flags:
-Xfrontend -warn-long-expression-type-checking=100
-Xfrontend -warn-long-function-bodies=100

These flags tell the compiler to emit a warning for any expression or function body that takes longer than 100ms to compile.

Analyzing the Results

Build your project (Cmd + B). Look at the build log (Report Navigator). You will see warnings like:

warning: expression took 250ms to type-check (limit: 100ms)

Locate these areas. They are the primary culprits stalling your indexer.


3. Refactoring Complex Expressions

Once you have identified the slow code using the flags above, you must refactor it. The goal is to reduce the work the compiler's constraint solver has to do.

The most common cause of hangs is Collection Literals without explicit types, or complex SwiftUI bodies.

The "Bad" Code

This dictionary relies entirely on inference. The compiler must check every key and value against every other key and value to ensure they conform to a common type (e.g., [String: Any]).

// Heavy inference load
let analyticsPayload = [
    "event": "user_signup",
    "timestamp": Date().timeIntervalSince1970,
    "properties": [
        "method": "email",
        "device": UIDevice.current.name,
        "screen_width": UIScreen.main.bounds.width
    ]
]

The Fix: Explicit Typing

By explicitly defining the type, you short-circuit the inference engine. The compiler no longer needs to solve the type; it only needs to verify it.

// Zero inference required
let analyticsPayload: [String: Any] = [
    "event": "user_signup",
    "timestamp": Date().timeIntervalSince1970,
    "properties": [
        "method": "email",
        "device": UIDevice.current.name,
        "screen_width": UIScreen.main.bounds.width
    ] as [String: Any]
]

Similarly, break up long chained function calls (like mapfilterreduce) into intermediate variables with explicit types.


4. Resetting the SourceKitService Daemon

Sometimes the code is fine, but the SourceKitService process has entered a zombie state. It may have crashed silently or deadlocked, leaving Xcode waiting for a response that will never come.

Instead of restarting the heavy Xcode application, you can simply kill the background service. Xcode is resilient enough to restart it automatically.

The Fix

Open your terminal and run:

killall SourceKitService

Watch your Xcode status bar. You will likely see "Indexing..." flash briefly and then finish immediately. This is the fastest way to recover functionality without a full IDE reboot.


5. Optimizing Build Settings for Debugging

Certain build settings are computationally expensive and unnecessary during the development/indexing phase. Adjusting these can free up CPU cycles for the indexer.

Disable dSYM Generation for Debug Builds

Generating dSYM (debug symbol) files is essential for crash reporting in production (Release), but it adds significant I/O overhead during development.

  1. Go to Build Settings.
  2. Search for Debug Information Format.
  3. Expand the setting to see Debug and Release.
  4. Change Debug to DWARF (default is often DWARF with dSYM File).
  5. Keep Release as DWARF with dSYM File.

Verify Architecture settings

Ensure you are only building the active architecture.

  1. Search for Build Active Architecture Only.
  2. Set Debug to Yes.

This prevents Xcode from attempting to index and build arm64 and x86_64 slices simultaneously during development, cutting the workload in half.


Common Pitfalls: Generated Code

A massive edge case for indexing issues is Auto-Generated Code. Tools like Sourcery, Apollo (GraphQL), or Mockolo often generate files containing thousands of lines of code.

If Xcode tries to index a 10,000-line GraphQL model file, it will hang.

Solution: If you do not need autocomplete inside these generated files, mark them as "Binary Frameworks" or wrap them in a separate Swift Package. If that isn't possible, ensure the generated code uses explicit types everywhere. The most effective method is to modularize your app (using Swift Package Manager) and move generated code into a package that rarely changes. Xcode will compile it once, cache the module, and stop trying to re-index it on every keystroke in your main app.

Conclusion

Xcode's "Indexing" hang is rarely random; it is a symptom of resource contention or compiler bottlenecks. By systematically clearing corrupted data, identifying complex code via diagnostic flags, and assisting the type inference engine with explicit types, you can reclaim your CPU and your productivity.

Stop waiting for the progress bar. Kill the daemon, fix the types, and get back to shipping code.