Setup
My iOS application is communicating with a BLE device. As a response to certain events within this communication, I would like to provide haptic feedback to the user.
In an attempt to support older phones like the iPhone 7, I decided to use UIFeedbackGenerator
, specifically UIImpactFeedbackGenerator
for this purpose.
Now, when receiving said events over BLE, I call feedbackGenerator.impactOccurred()
.
Behavior
At the time of the call, no haptic feedback takes place.
But the system remembers these calls to UIFeedbackGenerator
nevertheless.
Because, when the user interacts with a UI element afterwards, all calls tofeedbackGenerator.impactOccurred()
are triggered at once and haptic feedback is provided as a response to that UI interaction. Which is quite confusing for the user, who did not expect haptic feedback at that point. Additionally, the haptic feedback can be quite intense if enough calls accumulated over time.
When enough of these forgotten hapticfeedback calls accumulate, the system seems to be overloaded; the following logs are generated in the console a couple dozen times:
CAReportingClient.mm:295:-[CAReportingClient init]_block_invoke: Couldn’t communicate with a helper application
Anyway, this could be expected behaviour according to the apple docs for UIFeedbackGenerator
:
Note that calling these methods does not play haptics directly. Instead, it informs the system of the event. The system then determines whether to play the haptics based on the device, the application’s state, the amount of battery power remaining, and other factors.
My question
Do you have experience in wether the usage of Core Bluetooth changes the behavior of CoreHaptics
or UIFeedbackGenerator
? That is my starting point in debugging, because when I simulate all parts of the communication locally, haptic feedback works just fine.
What I already tried
- calling
UIFeedbackGenerator
from main/background thread - Using
UINotificationFeedbackGenerator
instead ofUIImpactFeedbackGenerator
- Calling
prepare()
on theUIFeedbackGenerator
in advance.
Turns out, I didn't pay enough attention in my CoreBluetooth implementation. The worker that I scheduled for constantly sending data to the characteristic was running in a
DispatchQueue.global(qos: .userInitiated)
.And because I block this queue with my worker, CoreHaptics decides it's best to not play the feedback right away.
Moral of the story: Be aware of which queues you use.