Reputation: 8832
I am using the UIFeedback Haptic Engine
with swift 2.3 like:
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.Warning)
and
let generator = UIImpactFeedbackGenerator(style: .Heavy)
generator.impactOccurred()
Today I got a new kind of error like this, and couldn't find the problem. Do you have any idea?
UIFeedbackHapticEngine _deactivate] called more times than the feedback engine was activated
Details:
Fatal Exception: NSInternalInconsistencyException
0 CoreFoundation 0x1863e41c0 __exceptionPreprocess
1 libobjc.A.dylib 0x184e1c55c objc_exception_throw
2 CoreFoundation 0x1863e4094 +[NSException raise:format:]
3 Foundation 0x186e6e82c -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:]
4 UIKit 0x18cc43fb8 -[_UIFeedbackEngine _deactivate]
5 UIKit 0x18cad781c -[UIFeedbackGenerator __deactivateWithStyle:]
Upvotes: 13
Views: 3922
Reputation: 67
Just to complete an answer that was already given: what you want to do is either have an OperationQueue or a DispatchQueue that will always be used to call on a FeedbackGenerator's functions. Keep in mind that for your use case, you might have to release the generators, but a minimal example would be:
class HapticsService {
private let hapticsQueue = DispatchQueue(label: "dev.alecrim.hapticQueue", qos: .userInteractive)
typealias FeedbackType = UINotificationFeedbackGenerator.FeedbackType
private let feedbackGeneator = UINotificationFeedbackGenerator()
private let selectionGenerator = UISelectionFeedbackGenerator()
func prepareForHaptic() {
hapticsQueue.async {
self.feedbackGeneator.prepare()
self.selectionGenerator.prepare()
}
}
func performHaptic(feedback: FeedbackType) {
hapticsQueue.async {
self.feedbackGeneator.notificationOccurred(feedback)
}
}
func performSelectionHaptic() {
hapticsQueue.async {
self.selectionGenerator.selectionChanged()
}
}
}
This pretty much solved our related crashes in production.
Upvotes: 3
Reputation: 6526
Calling generator.impactOccurred()
will crash on iOS 11.*. You need to call that on the main thread async
.
let generator = UIImpactFeedbackGenerator(style: style)
generator.prepare()
DispatchQueue.main.async {
generator.impactOccurred()
}
Upvotes: 6
Reputation: 4209
UIImpactFeedbackGenerator
is not thread safe, so make sure you are calling the generator.impactOccurred()
synchronously and not in a dispatch_async
or in another async thread.
Upvotes: 15