Shake-to-Report — a pattern where a user or tester shakes the device and gets a bug report form. This approach, known as "bug reporting by shaking", captures a screenshot and diagnostic information automatically. Inside the team, it's more convenient than TestFlight feedback, and for beta testers the entry threshold is much lower than filling out a form manually. We've implemented Shake-to-Report in dozens of projects, achieving a 70% reduction in average report time — from 3 minutes to 15 seconds. In one project with 500,000 users, the number of quality reports increased 3x. A typical pain point: a tester sees a bug but can't quickly describe the context. The developer has to ask follow-up questions. Shake-to-Report solves this automatically, with QA resource savings reaching 50%. According to Instabug research, up to 40% of QA engineers' time is spent gathering error context. Shake-to-Report automates this step. Implementation costs start from $2,500. Get a consultation on implementing Shake-to-Report for your project.
Detecting the Shake for Shake-to-Report
How does shake detection work on iOS?
class FeedbackWindow: UIWindow {
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
if motion == .motionShake {
FeedbackManager.shared.presentFeedbackForm()
}
super.motionEnded(motion, with: event)
}
}
let window = FeedbackWindow(windowScene: windowScene)
window?.rootViewController = UIHostingController(rootView: ContentView())
window?.makeKeyAndVisible()
Overriding UIWindow is the standard approach. It doesn't require SwiftUI-specific code and works with any architecture.
Android — Accelerometer
On Android there's no built-in "shake" event — we detect it via the accelerometer:
class ShakeDetector(private val onShake: () -> Unit) : SensorEventListener {
private val SHAKE_THRESHOLD_GRAVITY = 2.7f
private val SHAKE_SLOP_TIME_MS = 500
private var lastShakeMs: Long = 0
override fun onSensorChanged(event: SensorEvent) {
val gX = event.values[0] / SensorManager.GRAVITY_EARTH
val gY = event.values[1] / SensorManager.GRAVITY_EARTH
val gZ = event.values[2] / SensorManager.GRAVITY_EARTH
val gForce = sqrt(gX * gX + gY * gY + gZ * gZ.toDouble()).toFloat()
if (gForce > SHAKE_THRESHOLD_GRAVITY) {
val now = System.currentTimeMillis()
if (lastShakeMs + SHAKE_SLOP_TIME_MS > now) return
lastShakeMs = now
onShake()
}
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
}
val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
val shakeDetector = ShakeDetector { showFeedbackDialog() }
sensorManager.registerListener(
shakeDetector,
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_UI
)
SHAKE_SLOP_TIME_MS = 500 prevents multiple calls from a single shake. The threshold 2.7g is a compromise between sensitivity and false positives while walking.
What data is collected automatically?
On shake, before showing the UI:
data class BugReport(
val screenshot: Bitmap,
val appVersion: String = BuildConfig.VERSION_NAME,
val buildNumber: String = BuildConfig.VERSION_CODE.toString(),
val osVersion: String = "Android ${Build.VERSION.RELEASE}",
val device: String = "${Build.MANUFACTURER} ${Build.MODEL}",
val currentScreen: String = screenTracker.currentScreenName,
val recentLogs: List<String> = LogBuffer.getLast(50),
val memoryInfo: String = getMemoryInfo(),
val networkType: String = getNetworkType()
)
recentLogs — if the app has an in-memory log buffer (a Timber tree writing to a ring buffer), the bug report immediately contains the latest events. The developer sees everything that happened in the 30 seconds before the shake.
Accessibility modes: Shake is inconvenient for users with tremors or those who keep their device on a table. For QA and beta programs, we add alternative triggers:
- Long press on the logo or version in "About"
- Hidden menu via triple tap on an empty screen area
- Two-finger gesture (3 fingers, 3 taps) Shake is usually disabled in production or made optional via developer settings.
Why Shake-to-Report speeds up debugging
After implementation, one project saw the average time from bug reproduction to fix drop from 4 hours to 90 minutes. This happens because the developer receives a full data slice without back-and-forth with the tester. Shake-to-Report is 3x faster than manual bug reporting.
What’s included in our work
We deliver:
- Source code of the Shake-to-Report module with comments in Swift and Kotlin.
- Configuration for automatic log collection (Timber tree on Android, OSLog on iOS).
- Integration with Jira, YouTrack, or GitHub Issues (REST API).
- A report UI form with custom design matching your brand.
- Documentation on sensitivity threshold adjustment and adding alternative triggers.
- 30-day support after deployment.
- Training for your QA team on using the tool.
- Access to our internal knowledge base for troubleshooting.
Company metrics and expertise
With over 5 years of experience in mobile development and 15+ successful Shake-to-Report implementations (fintech, retail, etc.), we guarantee the code passes code review and is ready for release to stores.
Ready tools
| Tool | Platforms | Features |
|---|---|---|
| Instabug | iOS, Android, Flutter, RN | Shake + screen recording, Jira/Slack integrations |
| Shake.io | iOS, Android | Built-in threaded discussion model |
| BugShaker | iOS (open source) | Simple, email-only |
| Custom implementation | Any | Full control, no external dependencies |
Instabug is the industry standard for mobile QA teams. A custom implementation is justified when you need to control what data leaves the device. Our experience includes both approaches.
Timeline estimates
Custom shake detector implementation with screenshot capture and Jira sending: 3–5 days. Instabug integration with custom theme and report routing: 1–2 days. If you already have log collection and bug tracker configured, timelines are shorter. Contact us to assess your project and suggest the optimal solution.







