Impressia/Vernissage/Haptics/HapticService.swift

71 lines
2.2 KiB
Swift

//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import CoreHaptics
public final class HapticService: ObservableObject {
public static let shared = HapticService()
private let hapticEngine: CHHapticEngine?
private var needsToRestart = false
/// Fires a transient haptic event with the given intensity and sharpness (0-1).
public func touch(intensity: Float = 0.75, sharpness: Float = 0.5) {
do {
let event = CHHapticEvent(
eventType: .hapticTransient,
parameters: [
CHHapticEventParameter(parameterID: .hapticIntensity, value: intensity),
CHHapticEventParameter(parameterID: .hapticSharpness, value: sharpness)
],
relativeTime: 0)
let pattern = try CHHapticPattern(events: [event], parameters: [])
let player = try hapticEngine?.makePlayer(with: pattern)
if needsToRestart {
try? start()
}
try player?.start(atTime: CHHapticTimeImmediate)
} catch {
print("Error \(error.localizedDescription)")
}
}
private init() {
hapticEngine = try? CHHapticEngine()
hapticEngine?.resetHandler = resetHandler
hapticEngine?.stoppedHandler = restartHandler
hapticEngine?.playsHapticsOnly = true
try? start()
}
/// Stops the internal CHHapticEngine. Should be called when your app enters the background.
public func stop(completionHandler: CHHapticEngine.CompletionHandler? = nil) {
hapticEngine?.stop(completionHandler: completionHandler)
}
/// Starts the internal CHHapticEngine. Should be called when your app enters the foreground.
public func start() throws {
try hapticEngine?.start()
needsToRestart = false
}
private func resetHandler() {
do {
try start()
} catch {
needsToRestart = true
}
}
private func restartHandler(_ reasonForStopping: CHHapticEngine.StoppedReason? = nil) {
resetHandler()
}
}