gshaw / notes

Issues and solutions I find during software development.
https://gshaw.ca
MIT License
1 stars 0 forks source link

AudioServicesPlayAlertSound(kSystemSoundID_Vibrate) doesn't work in silent mode #13

Open gshaw opened 1 year ago

gshaw commented 1 year ago

If you want a strong vibration the original vibrate sound effect works well using

AudioServicesPlayAlertSound(kSystemSoundID_Vibrate)

But it won't play if the device is silent mode.

The new AVAudioPlayer APIs allow you to specify the app category so you can explicitly state that you are app is using audio in a playback category to play audio while the device is in silent mode. This is great except that there is no option to use the old kSystemSoundID_Vibrate effect.

gshaw commented 1 year ago

To work around this problem I created a helper service that builds a custom haptic similar to the old school long vibration.

import CoreHaptics
import UIKit

class HapticService {
    private let lightImpactGenerator = UIImpactFeedbackGenerator(style: .light)
    private let notificationGenerator = UINotificationFeedbackGenerator()

    private var engine: CHHapticEngine?
    private var vibratePlayer: CHHapticPatternPlayer?

    init() {
        lightImpactGenerator.prepare()
        notificationGenerator.prepare()
        prepareHapticEngine()
    }

    private func prepareHapticEngine() {
        guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else {
            LoggerService.default.warning("HapticService: Device does not support haptics")
            return
        }
        do {
            let engine = try CHHapticEngine()
            vibratePlayer = try buildVibratePlayer(engine: engine)
            try engine.start()
            self.engine = engine
        } catch {
            LoggerService.default.error("HapticService: prepareHapticEngine: \(error)")
        }
    }

    private func buildVibratePlayer(engine: CHHapticEngine) throws -> CHHapticPatternPlayer {
        let event = CHHapticEvent(eventType: .hapticContinuous, parameters: [], relativeTime: 0, duration: 0.4)
        let pattern = try CHHapticPattern(events: [event], parameters: [])
        let player = try engine.makePlayer(with: pattern)
        return player
    }

    func performLightImpact() {
        lightImpactGenerator.impactOccurred()
    }

    func performSuccessNotification() {
        notificationGenerator.notificationOccurred(.success)
    }

    func performVibration() {
        guard let vibratePlayer else {
            return
        }
        do {
            try vibratePlayer.start(atTime: 0)
        } catch {
            LoggerService.default.error("HapticService: performVibration: \(error)")
        }
    }
}