Thumbs in iOS and Android Apps
Add thumbs, a marked-up screenshot and screen recordings to a native app with real SwiftUI and Compose views.
Add the QaidThumbs Swift package or dev.qaid:thumbs, configure it with your embed key, and call QaidThumbs.present from a Send feedback button. The person marks up a screenshot or records the screen in a native sheet, and the report lands in your inbox with the app version, device and recent errors.
App reports and site reports share one inbox, one key and one plan. The sheet, the markup editor and the record button are drawn with SwiftUI on iOS and Jetpack Compose on Android.
What you need
| iOS | Android | |
|---|---|---|
| Package | https://github.com/qaiddev/thumbs-native, product QaidThumbs |
dev.qaid:thumbs:0.3.0 from Maven Central |
| Minimum | iOS 15 | minSdk 26, compileSdk 35, the Compose compiler plugin |
| Key | Your project’s embed key, under Settings → API Keys | Same key |
The key only sends feedback, so it is safe inside an app.
Add the package
iOS: add the Swift package
In Xcode choose File → Add Package Dependencies, paste
https://github.com/qaiddev/thumbs-native, and add the QaidThumbs product to your app target. Screen recording uses ReplayKit, which needs no permission entry.Android: add the dependency
Add
implementation("dev.qaid:thumbs:0.3.0")to your app module. The library merges a recording service and the permissions it needs into your manifest. Google Play asks apps that declareFOREGROUND_SERVICE_MEDIA_PROJECTIONto explain it in the Play Console, so say it records the screen for bug reports.Configure once at launch
Call
QaidThumbs.configurein your app delegate,Appinit orApplication.onCreate. Android also takes the application, so reports saved offline can send at the next launch.Call present from a button
Put a Send feedback item in a menu or settings screen. Pass
screento say where the person was.
import QaidThumbs
QaidThumbs.configure(QaidThumbsConfiguration(
apiKey: "YOUR_EMBED_KEY",
appName: "My App"
))
QaidThumbs.present(screen: "Settings")
QaidThumbs.configure(this, QaidThumbsConfig(
apiKey = "YOUR_EMBED_KEY",
appName = "My App",
))
QaidThumbs.present(activity, screen = "Settings")
The project’s Customize page prints both with your key in place, under the iOS and Android tabs of THUMBS CODE.
What the person sees
The SDK takes the screenshot before the sheet opens, so the sheet is never in it. The sheet holds the screenshot, the thumbs, a message box and Send. Mark up opens an editor with box, arrow, pen and redact tools, undo and clear. Redact paints solid black onto the image’s own pixels, on the phone, before anything is sent.
Record screen closes the sheet and records the app. When the person stops, the sheet comes back with the clip and whatever they had typed.
Recordings need Pro, as on the web. On Free the sheet says screen recordings aren’t available and offers a screenshot instead.
What reaches your inbox
Open a report and Details lists the app as CinemaCrew 1.4 (812), the device as iPhone16,1 · iOS 18.2, the screen and the locale. Tell it who is signed in and anything else you want beside each report:
QaidThumbs.setUser(id: "u_42", email: "sam@example.com", name: "Sam")
QaidThumbs.setMetadata(key: "plan", value: "pro")
QaidThumbs.log("Sync failed", level: .error)
Console Errors holds recent warnings and errors from the app’s own log plus your log lines. Network Errors holds failed calls you report. Only the method, the address without its query string, and the status are kept.
val client = OkHttpClient.Builder()
.addInterceptor(QaidThumbs.networkInterceptor())
.build()
On iOS, call QaidThumbs.record(request:response:error:) where your requests finish.
Hide private screens
Mark a view as sensitive and it is blacked out in screenshots and covered while recording. Password fields are covered without being marked.
cardNumberField.qaidSensitive = true
Open a quest after a report
Link quests to the thumbs with quests: QaidQuestLinks(up:, down:, video:). Once qaid accepts the report, the sheet closes and onLinkedQuest hands you the quest. Show it with the quests SDK, passing the feedback id so the answers are joined to the report:
QaidThumbs.onLinkedQuest = { link in
QaidQuests.present(questId: link.questId, metadata: ["feedbackId": link.feedbackId])
}
The two SDKs don’t depend on each other; an app that never sets the hook just shows “Sent”.
Domain Restriction
A project with a Domain Restriction checks app reports too. By default the SDK names the app by its bundle id, and qaid reads the id back to front: com.example.myapp passes a restriction of example.com. If your app’s id doesn’t match your domain, give the SDK an address on the domain instead:
QaidThumbsConfiguration(apiKey: "YOUR_EMBED_KEY",
pageUrl: URL(string: "https://example.com/app/ios"),
appName: "My App")
Offline, shake and your own words
A report sent with no connection is saved and sent later: at the next launch, when the app comes back to the front, or when the network returns. QaidThumbs.enableShakeToReport() opens the sheet on a shake, and setScreen names the screen it came from.
Every string on the sheet and in the editor can be replaced through text, so the sheet can match your app’s language. Coming from 0.2? QaidFeedback is now QaidThumbs, and reports queued by 0.2 still send after the upgrade. The full API is in the SDK repository.