Screens, elements and masking
A report is only as useful as its location. The component cannot see which element was tapped by itself, so you (or your agent) give names to the screens and the elements that matter.
Screens
A screen name is stored in the screen field of every report made on that screen. Keep names short and stable: Home, Settings, Checkout.
- SwiftUI:
.nitpickScreen("Home")on the root view of the screen. - Expo Router: not automatic. Put
useNitpickScreen(usePathname())in a small component inside the provider in the root layout, so the route path becomes the screen name (see Install for Expo). - React Navigation: use
useNitpickScreenwith the navigation container's current route name. - Tabs or a stack: also pass the focus of the screen, see the next section.
Tabs and stacks: pass the focus
A tab bar or a stack keeps the screens that are not shown. Their marked elements still exist, so a tap on the screen could be matched to a hidden element, even a smaller one on the same place. Pass the focus of each screen: elements inside a screen with focused set to false never count when the user points, and that screen does not set the screen name. Without the option nothing changes.
Expo Router: wrap each tab or stack screen in <NitpickScreen> and pass useIsFocused(). The name is up to you; the route path is a good one.
// app/(tabs)/settings.tsx
import { useIsFocused } from "expo-router";
import { NitpickElement, NitpickScreen } from "@nitpickhq/react-native";
export default function Settings() {
const focused = useIsFocused();
return (
<NitpickScreen name="/settings" focused={focused}>
<NitpickElement name="Settings/ProfileCard">{/* ... */}</NitpickElement>
</NitpickScreen>
);
}
React Navigation without Expo Router: the same, with useIsFocused from @react-navigation/native. If you only want the screen name and not the elements, use useNitpickScreen("/settings", { focused }); the elements below it then still count, so wrap the screen in <NitpickScreen> when it has marked elements.
SwiftUI: give each screen of a TabView its own .nitpickScreen and pass whether its tab is selected.
TabView(selection: $selection) {
HomeView()
.nitpickScreen("Home", focused: selection == .home)
.tabItem { Label("Home", systemImage: "house") }
.tag(Tab.home)
SettingsView()
.nitpickScreen("Settings", focused: selection == .settings)
.tabItem { Label("Settings", systemImage: "gear") }
.tag(Tab.settings)
}
The focused parameter is optional and defaults to true. A screen inside a screen without focus has no focus either.
Elements
An element name goes into the element field. Mark the buttons, fields and cards that users are likely to point at. Prefer names that lead to the file: Home/StartButton, Checkout/PayButton.
- SwiftUI:
.nitpickElement("Home/StartButton"). - React Native:
<NitpickElement name="...">or atestID.
SwiftUI: elements inside a List
In a List or Section, a Button is often the row itself. A marker on such a Button is not found when the user points at it: the report gets element_match: none and no frame. Put the marker on the content of the row instead, for example a VStack or HStack inside the button's label:
List {
// Not found: the marker sits on the Button that is the row
Button("Mark all done", action: markAll)
.nitpickElement("Home/MarkAllDoneButton")
// Found: the marker sits on the content of the row
Button(action: markAll) {
HStack {
Text("Mark all done")
Spacer()
}
.nitpickElement("Home/MarkAllDoneButton")
}
}
If a marked element in a list is still not found, mark a view that is not itself the row (a VStack with the card inside works), and check with nitpick feedback list that element_match is exact.
How a tap is matched
When the user points at something, the component looks at the tap and sets element_match:
element_match |
Meaning |
|---|---|
exact |
The tap was inside a marked element. The smallest marked element that contains the tap wins. |
nearest |
No marked element contains the tap, but one is within 44 points. That one is used. |
none |
Nothing marked nearby. The report still has the screen, the screenshot and the tap position. |
The report also carries element_frame (the element's box in points) and tap, so your agent can see exactly where on the screenshot the user pointed.
Masking
Masked parts show up as a black box on the screenshot. The user sees the preview before sending and can also remove the screenshot completely.
- Password fields are masked where the platform allows it.
- Everything else is up to you: card numbers, addresses, health data, anything personal. Mark it with
.nitpickMask()or<NitpickMask>. - Automatic masking in SwiftUI is only partial, so do not rely on it. Mark sensitive views yourself. Your agent does this during install.
- Screens that you flag as secure by the platform's own means are not captured.
What the component never does
It captures nothing before the user opens it. The only thing it does earlier is ask Nitpick for your settings when the app starts. It records no screen, reads no photo library, collects no device name and uses no private APIs. See Privacy and store forms.