<!--
{
  "documentType" : "article",
  "framework" : "Xcode",
  "identifier" : "/documentation/Xcode/diagnosing-performance-issues-early",
  "metadataVersion" : "0.1.0",
  "role" : "article",
  "title" : "Diagnosing performance issues early"
}
-->

# Diagnosing performance issues early

Diagnose potential performance issues in your app during development and testing with the Thread Performance Checker tool in Xcode.

## Overview

Identifying potential performance issues during development saves testing time later. Resolving performance
issues, such as priority inversions and non-UI work on the main thread, makes your app
responsive. Priority inversions occur when a low-priority thread blocks a higher-priority
thread, which can lead to an unresponsive app. Similarly, non-UI work, such as synchronous
networking or I/O on the main thread, can block the main thread for hundreds of
milliseconds, which prevents people from interacting with your app.

The Thread Performance Checker tool detects priority inversions and non-UI work on the
main thread. It doesn’t require any recompilation. Use the Thread Performance Checker tool
to detect, diagnose, and resolve performance issues.

### Understand the detected issues

The Thread Performance Checker tool surfaces issues in the Issue navigator and the source editor. Read the diagnostic message carefully.

![A screenshot of a Priority Inversion issue text in the source editor.](images/com.apple.Xcode/diagnosing-performance-issues-early-1@2x.png)

To understand the issue in depth, expand the backtrace of the issue in the Issue navigator.

![A screenshot of the expanded backdtrace in the Issue navigator.](images/com.apple.Xcode/diagnosing-performance-issues-early-2@2x.png)

Click Generate in the description of an issue to generate a fix using intelligence in Xcode.
For more information, see <doc://com.apple.documentation/documentation/Xcode/writing-code-with-intelligence-in-xcode>.

The issues surfaced point to code in your project that can cause hangs. Hangs
occur when your app is unresponsive for hundreds of milliseconds. To learn more about hangs,
see [Improving app responsiveness](/documentation/Xcode/improving-app-responsiveness) and the WWDC session video
[Understand and eliminate hangs from your app](https://developer.apple.com/videos/play/wwdc2021/10258/).

### Diagnose and resolve priority inversions

If you use concurrency primitives, such as
<doc://com.apple.documentation/documentation/Dispatch/dispatch_semaphore_wait> and
<doc://com.apple.documentation/documentation/Dispatch/dispatch_group_wait>, in
your code or invoke APIs that use them, your app is
susceptible to priority inversions if there is a mismatch in the quality-of-service (QoS)
class of the dispatch queues your app uses. When you
use these primitives, the system can’t automatically propagate priority from the
higher-priority thread to the lower-priority thread. You can take these precautions to
avoid priority inversions in your code:

- Don’t use `dispatch_semaphore_wait` and `dispatch_group_wait` to
  emulate synchronous behavior when calling an asynchronous
  internal method or API. Remove the code if the underlying functionality is
  unnecessary.
- Ensure that the QoS of the waiting thread is the same as or lower than the QoS of
  the signaling thread when a synchronous variant isn’t available.
  Explicitly classify the QoS of the work when you create a
  <doc://com.apple.documentation/documentation/Dispatch/dispatch-queue> or an
  <doc://com.apple.documentation/documentation/Foundation/OperationQueue>.

The code in `initiateBackgroundWork` below explicitly creates a dispatch queue with
background QoS. `doBackgroundWorkAsync` signals the completion of the background work it
does asynchronously at background QoS. After the background work completes, it updates
the UI label on the main thread at <doc://com.apple.documentation/documentation/Dispatch/DispatchQoS/userInteractive>
QoS.

```swift
func initiateBackgroundWork() {
    let dispatchSemaphore = DispatchSemaphore(value: 0)
    let backgroundQueue = DispatchQueue(label: "background_queue", 
                                        qos: .background)
    
    backgroundQueue.async {
        // Perform work on a separate thread using the quality-of-service level 
        // for background maintenance or cleanup tasks and signal when the work completes.
       doBackgroundWorkAsync {
           dispatchSemaphore.signal()
       }
       
       _ = dispatchSemaphore.wait(timeout: DispatchTime.distantFuture)
       
       DispatchQueue.main.async { [weak self] in
           self?.label.text = "Background work completed"
       }
    })
}
```

To learn more about priority inversions and QoS, see 
[Modernizing Grand Central Dispatch
Usage](https://developer.apple.com/videos/play/wwdc2017/706/) and 
[Energy Efficiency Guide for iOS
Apps](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/EnergyGuide-iOS/PrioritizeWorkWithQoS.html).

### Diagnose and eliminate non-UI work on the main thread

Long-running synchronous I/O and networking on the main thread can make your app unresponsive. For example, to perform
real-time capture, you instantiate an <doc://com.apple.documentation/documentation/AVFoundation/AVCaptureSession> object and add
appropriate inputs and outputs. Invoking the <doc://com.apple.documentation/documentation/AVFoundation/AVCaptureSession/startRunning()>
method of an `AVCaptureSession` object on the main thread of your app can lead to hangs.
You can take these precautions to avoid non-UI work on the main thread:

- Don’t synchronously read from or write to files and I/O devices on the main thread.
  Instead, do the work on a separate serial dispatch queue, and notify the completion of I/O by enqueuing a block onto the main queue.
- Use the asynchronous variant of an API that performs I/O to do that work off the main
  thread.
- Don’t perform synchronous networking on the main thread of your app. Instead, use an
  asynchronous networking API, such as
  <doc://com.apple.documentation/documentation/Foundation/URLSession>.

### Detect runtime issues in tests

Xcode detects runtime issues in tests you write using <doc://com.apple.documentation/documentation/Testing> or <doc://com.apple.documentation/documentation/XCTest>, including UI tests that use <doc://com.apple.documentation/documentation/XCUIAutomation> to automatically control your app’s UI.

By default, Xcode reports runtime issues in your tests as warnings.
To fail tests when runtime issues occur, follow these steps:

1. Choose Product > Test Plan > Edit Test Plan. Follow any prompts that Xcode displays.
2. Switch to the Configurations pane in the Test Plan editor.
3. Change the configuration values in the Runtime API Checking section to On (as Failure). You can set each value separately, for example, to report main thread issues as failures so you can work on resolving those without also causing failures from other runtime issues.

![A screenshot of the Test Plan editor, highlighting the Main Thread Checker setting.](images/com.apple.Xcode/diagnosing-performance-issues-early-5@2x.png)

To turn runtime issues back into warnings, change the configuration values to On (as Warnings).
For more information on configuring test plans, see [Improving code assessment by organizing tests into test plans](/documentation/Xcode/organizing-tests-to-improve-feedback).

If you configure Xcode to report runtime issues in tests as warnings, Xcode shows the issues in a Runtime Warnings section in the Test Report, in the Test navigator, and in the Issues navigator.

![A screenshot of the Test Report showing a runtime issue that Xcode detected during a test.](images/com.apple.Xcode/diagnosing-performance-issues-early-4@2x.png)

### Follow profiling guidance with the Thread Performance Checker tool

Xcode enables the Thread Performance Checker tool by default for the Run action. The
Thread Performance Checker tool works by inserting checks to detect issues such as priority
inversions and dependencies between operations of different quality-of-service levels. This
overhead is minimal in most cases, but some apps can trigger conditions that cause that
overhead to appear in their call stacks. If you profile your app and want to be sure that the
Thread Performance Checker tool’s overhead never appears in your call stacks, do one of the following:

- Use the Profile action instead of the Run action.
- Launch the tool from Instruments instead of attaching to a process that you already launched from Xcode.

For higher confidence in your profiling results, disable the Thread Performance Checker tool.
Choose Product > Scheme > Edit Scheme to display the scheme editor. Select the Run schemes,
navigate to the Diagnostics section, and unselect the Thread Performance Checker tool checkbox.

![A screenshot of the Scheme Editor with the Thread Performance Checker checkbox selected.](images/com.apple.Xcode/diagnosing-performance-issues-early-3@2x.png)

In addition to the Thread Performance Checker tool, always test your code using a comprehensive set of performance tests.
For more information about testing your code, see [Testing](/documentation/Xcode/testing).

> Important: The Thread Performance Checker tool is currently supported only on macOS and iOS.

Resolution of certain performance issues may require significant code refactoring or redesign of the underlying logic. To suppress the
warning for issues you intend to address at a later time, set the `PERFC_SUPPRESSION_FILE` environment variable to provide a list of
classes and methods in a suppression file. The Thread Performance Checker tool only shows issues that don’t involve those classes and
methods. Use the following format for your suppression file:

```other
class:UIActivityViewController
class:NSThread
method:-[UIViewController view]
method:readv
```

To disable runtime issues in tests, edit the test plan and set the Runtime API Checking configuration values to Off.

## See Also

[Addressing watchdog terminations](/documentation/Xcode/addressing-watchdog-terminations)

Identify the signature of an unresponsive app terminated by the watchdog, and address
the issue.

[Improving app responsiveness](/documentation/Xcode/improving-app-responsiveness)

Create a user experience that feels responsive by removing hangs and hitches from your app.



---

Copyright &copy; 2026 Apple Inc. All rights reserved. | [Terms of Use](https://www.apple.com/legal/internet-services/terms/site.html) | [Privacy Policy](https://www.apple.com/privacy/privacy-policy)