Enable Sentry performance tracing in a SwiftUI iOS app, instrument view rendering and network calls, and read the transaction data so you can find real-world slowness your users experience.
Sentry's performance monitoring is built on the concept of transactions and spans. A transaction represents a meaningful unit of work, such as loading a screen or handling a user action, and it contains a tree of spans that break that work into finer pieces like a network request, a database query, or a view render. Each span records a start time and duration, so a completed transaction is effectively a timeline of everything that happened during that operation. Sentry collects these transactions from real devices in production, which is the key difference from profiling in Instruments on your own machine. Instruments shows you how your app behaves on your hardware under your conditions; Sentry shows you how it behaves across the messy variety of real user devices, network conditions, and data sizes. For a SwiftUI app this is valuable because rendering performance and data-loading latency vary enormously in the field. To get this data you enable tracing at SDK initialization and then let auto-instrumentation and your own manual spans populate the transactions.
Performance monitoring is off until you tell the SDK to sample transactions. In your SentrySDK.start configuration, set a traces sample rate. The simplest option is options.tracesSampleRate, a value between 0.0 and 1.0 representing the fraction of transactions to capture. Setting it to 1.0 captures every transaction, which is fine in development but generates far too many events, and too much cost, in production. A production value such as 0.1 or 0.2 captures a representative sample while keeping event volume manageable. For finer control you can instead provide options.tracesSampler, a closure that returns a rate per transaction so you can sample critical flows more heavily and ignore noisy ones. Enable this alongside your existing DSN configuration in your App struct's init. The SDK's automatic instrumentation for app launch and UIViewController lifecycle begins producing transactions immediately once a nonzero rate is set. Start with a low rate, confirm data appears, and adjust. Because transactions count against your event quota, treat the sample rate as a deliberate cost decision rather than leaving it at the maximum.
SwiftUI does not use UIViewController lifecycle the way UIKit does, so Sentry provides dedicated helpers for view-level tracing. Add the SentrySwiftUI product to your target, either through Swift Package Manager or CocoaPods, in addition to the main Sentry SDK. Then wrap the views you care about with the SentryTracedView helper, which creates a transaction or span measuring how long that view takes to produce its body and appear. You use it by wrapping a view, for example SentryTracedView("HomeScreen") { HomeView() }, giving each traced view a clear name that will show up in the dashboard. Focus this instrumentation on screens where rendering cost matters, such as list-heavy views, screens with complex layouts, or views that do work in their body. Over-instrumenting every small view adds noise and events without insight. The traced view integrates with the surrounding transaction so that a view render appears as a span within the larger screen-load transaction, giving you a clear picture of how much of a slow screen is rendering versus data loading.
Much of the latency users feel comes from network calls, and Sentry can instrument these automatically. When you enable the networking auto-instrumentation, requests made with URLSession are captured as spans within the active transaction, showing you request duration, and you can see which endpoints are slow in the field. For work the automatic instrumentation does not cover, create manual spans. Within an active transaction you can start a child span around any block of code, give it an operation name and description, and finish it when the work completes. For example, you might wrap an expensive JSON decode, an image resize, or a Core Data fetch in a span so it shows up on the transaction timeline. You start a transaction explicitly with SentrySDK.startTransaction when you want to measure a custom flow that is not tied to a screen, then create child spans on it and call finish on each. This manual instrumentation is how you turn Sentry from a generic monitor into a tool that measures exactly the operations that matter in your app's specific hot paths.
Once transactions are flowing, open the Performance section of your Sentry project. It lists your transactions grouped by name, with aggregate metrics such as throughput, average and percentile durations, and how they trend over time. Percentiles matter more than averages here; the p75 or p95 duration tells you what your slower users experience, which is usually where the real problems hide. Click a transaction to see individual samples, and open a sample to view its span waterfall, a visual timeline showing each span's start and duration nested under the transaction. This waterfall is where you diagnose slowness: you can see at a glance whether a slow screen load is dominated by a network call, a render, or a decode step. You can filter by release and environment to compare performance across versions and confirm whether an optimization actually helped in production. Combined with release health, this lets you treat performance as a measurable, trackable quality attribute of each build rather than a vague impression.
Performance monitoring is powerful but not free, in two senses. First, every captured transaction counts against your Sentry event quota, and a busy app can generate a very large number of transactions, so a high sample rate can drive up cost quickly. This is why choosing a sensible tracesSampleRate, or a tracesSampler that captures important flows more and routine ones less, is essential rather than optional. Second, instrumentation has a small runtime cost on the device, because the SDK is measuring and recording timing. In practice this overhead is minor for reasonable instrumentation, but it is a real reason not to wrap every trivial view or every tiny function in a span. Be selective: instrument the screens and operations where performance genuinely matters to users, and sample the rest lightly or not at all. The goal is representative insight into real-world latency, not a complete recording of everything. A useful pattern is to combine a modest global tracesSampleRate with a tracesSampler that raises the rate for a handful of business-critical flows, such as checkout or sign-in, and lowers it for high-frequency background transactions, so you keep detailed data where it matters and avoid paying for noise everywhere else. Tuning these two dials, what you instrument and how much you sample, is the difference between useful, affordable performance data and an expensive firehose.
The most reliable way to adopt performance monitoring is incrementally. Start by enabling a low traces sample rate with only the automatic app-launch and lifecycle instrumentation, ship it, and confirm transactions appear in the dashboard with sensible durations. Next, add SentryTracedView around two or three of your most important or most complained-about screens and watch how they perform across real devices. Then enable network instrumentation and identify your slowest endpoints, which are often the true cause of screens that feel sluggish. Only after that add manual spans around specific expensive operations you suspect. At each step, check the dashboard, confirm the data is meaningful, and adjust your sample rate to balance insight against cost. This staged approach prevents the two common failure modes: instrumenting so little that the data is useless, and instrumenting so much that you drown in events and cost. Because you build and ship the app entirely in Xcode as usual, none of this changes your release process; it simply adds a production lens on top of it that shows how your SwiftUI app really performs for the people using it.
Set a traces sample rate in your SDK configuration. In SentrySDK.start, set options.tracesSampleRate to a value between 0.0 and 1.0, or provide options.tracesSampler for per-transaction control. Any nonzero value enables tracing and the automatic app-launch and lifecycle instrumentation begins producing transactions.
A transaction is a top-level unit of work, like loading a screen. A span is a smaller piece of work within it, like a network request or a render. A transaction contains a tree of spans, forming a timeline you can view as a waterfall in the dashboard.
Yes, for view-level rendering spans. Add the SentrySwiftUI product alongside the main SDK and wrap views with SentryTracedView to measure how long they take to render and appear. The core SDK handles app launch and network tracing, but SwiftUI view rendering needs the SwiftUI helper.
Not 1.0. Every transaction counts against your event quota, so a busy app at full sampling gets expensive fast. A value like 0.1 or 0.2 captures a representative sample. For finer control, use tracesSampler to sample critical flows more heavily and routine ones less.
Yes. With network auto-instrumentation enabled, URLSession requests are captured as spans within the active transaction, so you can see request durations per endpoint across real user devices. Open a transaction's span waterfall to see whether network calls dominate a slow screen load.