UIKit Tap Instrumentation
Generates: Events (Log Records)
Automatically captures user taps in UIKit apps and emits app.widget.click events with touch coordinates, element identity, and rage-click detection.
Setup
UIKit Tap instrumentations is enabled and configured from the Pulse dashboard — it cannot be enabled from initialize(). The remote config controls:
| Setting | Description |
|---|---|
captureContext | Whether to extract a human-readable label (app.click.context) from the tapped view |
| Rage click — time window | Sliding window (ms) in which taps are counted for rage detection |
| Rage click — threshold | Number of taps within the window and radius that triggers a rage event |
| Rage click — radius | Radius (pt) within which taps count as targeting the same element |
Tap Target Detection
After a touch ends, the SDK walks from the hit view up the superview chain and attributes the tap to a semantic target — the element that best represents user intent (for example standard controls, table/collection cells, views exposed as interactive in accessibility, and common discrete gesture targets). Scroll views are not treated as the tap target, so scrolling is not misclassified as a widget click.
If no suitable target is found, the tap is recorded as a dead click (click.type = "dead").
Heuristics may change between SDK releases. For stable naming in telemetry, prefer accessibilityIdentifier (and meaningful accessibilityLabel) on the views you care about.
Generated Telemetry
Type: Log Record (Event)
Event Name: app.widget.click (set via event.name attribute — body is empty)
pulse.type: app.click
Attributes
| Attribute | Type | Example | Present |
|---|---|---|---|
click.type | String | "good" / "dead" | ✅ Always |
click.is_rage | Bool | true | ⚠️ Rage events only |
click.rage_count | Int | 5 | ⚠️ Rage events only |
app.widget.name | String | "UIButton" | ⚠️ Good clicks |
app.widget.id | String | "checkout_btn" | ⚠️ If accessibilityIdentifier is set |
app.click.context | String | "label=Add to Cart" | ⚠️ If captureContext=true and label found |
app.screen.coordinate.x | Int | 142 | ✅ Always |
app.screen.coordinate.y | Int | 380 | ✅ Always |
device.screen.width | Int | 390 | ✅ Always |
device.screen.height | Int | 844 | ✅ Always |
app.screen.coordinate.nx | Double | 0.364 | ✅ Always |
app.screen.coordinate.ny | Double | 0.451 | ✅ Always |
Sample Payload: Good Click
{
"body": "",
"event.name": "app.widget.click",
"attributes": {
"pulse.type": "app.click",
"click.type": "good",
"app.widget.name": "UIButton",
"app.widget.id": "checkout_btn",
"app.click.context": "label=Checkout",
"app.screen.coordinate.x": 142,
"app.screen.coordinate.y": 380,
"device.screen.width": 390,
"device.screen.height": 844,
"app.screen.coordinate.nx": 0.364,
"app.screen.coordinate.ny": 0.451,
"session.id": "f40364c92b85ec0c19c35a65be42b97f",
"screen.name": "CartViewController"
}
}
Sample Payload: Rage Click
{
"body": "",
"event.name": "app.widget.click",
"attributes": {
"pulse.type": "app.click",
"click.type": "good",
"click.is_rage": true,
"click.rage_count": 5,
"app.widget.name": "UIButton",
"app.widget.id": "submit_btn",
"app.screen.coordinate.x": 195,
"app.screen.coordinate.y": 650,
"device.screen.width": 390,
"device.screen.height": 844,
"app.screen.coordinate.nx": 0.5,
"app.screen.coordinate.ny": 0.771
}
}
Label Extraction (captureContext)
When captureContext(true), the SDK builds a human-readable label from the tapped view and nearby text (for example segment titles, UILabel text, UIButton title labels, and accessibilityLabel). A bounded scan of subviews may contribute fragments when needed.
PII safety: UITextField, UITextView, and UISearchBar never expose typed text — only their accessibilityLabel is used.
SwiftUI Limitation
This instrumentation is UIKit-only. SwiftUI apps have best-effort coverage:
- The UIKit view tree is inspected, but the SwiftUI component hierarchy (Button vs Text vs onTapGesture) is not reflected in UIKit — you get the UIKit class names, not SwiftUI view types.