1

I am experiencing no haptic output while testing the code below on my physical device (iPhone XR). I followed Apple Developer's "Playing a Single-tap Haptic Pattern" article, as well as double-checked with various other articles on the internet, and I am confident I have implemented it correctly. Moreover, there are no errors that are caught when running. What could be the reason why my device is not outputting the haptic pattern?

Side Notes:

  1. I can confirm that my phone does produce haptics for other apps, so it does not appear to be an issue with my physical device.
  2. I also implement an AVAudioPlayer separately; I doubt that would interfere, but I thought I'd mention it just in case.

Any help would be much appreciated--thanks!

var hapticEngine: CHHapticEngine!
var hapticPlayer: CHHapticPatternPlayer!

override func viewDidLoad() {
        super.viewDidLoad()
        
        // Create and configure a haptic engine.
        do {
            hapticEngine = try CHHapticEngine()
        } catch let error {
            fatalError("Engine Creation Error: \(error)")
        }
        
        // The reset handler provides an opportunity to restart the engine.
        hapticEngine.resetHandler = {

            print("Reset Handler: Restarting the engine.")

            do {
                // Try restarting the engine.
                try self.hapticEngine.start()

                // Register any custom resources you had registered, using registerAudioResource.
                // Recreate all haptic pattern players you had created, using createPlayer.

            } catch let error {
                fatalError("Failed to restart the engine: \(error.localizedDescription)")
            }
        }
        
        // The stopped handler alerts engine stoppage.
        hapticEngine.stoppedHandler = { reason in
            print("Stop Handler: The engine stopped for reason: \(reason.rawValue)")
            switch reason {
            case .audioSessionInterrupt: print("Audio session interrupt")
            case .applicationSuspended: print("Application suspended")
            case .idleTimeout: print("Idle timeout")
            case .systemError: print("System error")
            @unknown default:
                print("Unknown error")
            }
        }
        
        // Create haptic dictionary
        let hapticDict = [
            CHHapticPattern.Key.pattern: [
                [
                    CHHapticPattern.Key.event: [
                        CHHapticPattern.Key.eventType: CHHapticEvent.EventType.hapticTransient,
                        CHHapticPattern.Key.time: 0.001,
                        CHHapticPattern.Key.eventDuration: 1.0
                    ]
                ]
            ]
        ]
        
        // Create haptic pattern from haptic dictionary, then add it to the haptic player
        do {
            let pattern = try CHHapticPattern(dictionary: hapticDict)
            hapticPlayer = try hapticEngine.makePlayer(with: pattern)
        } catch let error {
            print("Failed to create hapticPlayer: \(error.localizedDescription)")
        }

        // ...
}

// ...

func playHaptic() {
        //audioPlayer?.play()

        // Start Haptic Engine
        do {
            try hapticEngine.start()
        } catch let error {
            print("Haptic Engine could not start: \(error.localizedDescription)")
        }
        
        // Start Haptic Player
        do {
            try hapticPlayer.start(atTime: 0.0)
            print("Why")
        } catch let error {
            print("Haptic Player could not start: \(error.localizedDescription)")
        }
        
        // Stop Haptic Engine
        hapticEngine.stop(completionHandler: nil)
}
Eric
  • 330
  • 3
  • 13

1 Answers1

2

The problem is this line:

hapticEngine.stop(completionHandler: nil)

Delete it and all will be well. You're stopping your "sound" the very instant it is trying to get started. That doesn't make much sense.

(Of course I am also assuming that somewhere there is code that actually calls your playHaptic method. You didn't show that code, but I'm just guessing that you probably remembered to include some. Having made sure that happens, I ran your code with the stop line commented out, and I definitely felt and heard the "pop" of the taptic tap.)

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 1
    Apart from that, your code is very nicely structured and written, congrats. – matt Nov 10 '20 at 23:14
  • 1
    First, thanks for the compliment! Second, In the "[Playing a Single-Tap Haptic Pattern](https://developer.apple.com/documentation/corehaptics/playing_a_single-tap_haptic_pattern)" that I linked to, under the subheading "Start the Engine and Play the Haptic Pattern", it mentions that "Unless you're playing several haptic patterns in succession, bookend haptic playback with starting and stopping the engine." So I don't believe this is the problem since this is what it recommends. However, I will try it out to confirm just in case. – Eric Nov 10 '20 at 23:15
  • 1
    Yes, bookend haptic _playback_ with starting and stopping. But you did not way for playback to complete. That is what the `notifyWhenPlayersFinished` handler is for — but you didn't implement that. – matt Nov 10 '20 at 23:23
  • 1
    Weird...it worked. I'm not sure I understand why they recommend that.... – Eric Nov 10 '20 at 23:23
  • 1
    See my preceding comment (they crossed in the mail). Also, they are probably thinking of a time when it is clear for other reasons that all playback is over. But as I say, you didn't wait for that; you stopped before playback even had a chance to begin. It's just like any sound or other asynchronous activity. You wouldn't cancel networking in the next line after starting it, after all. If you did, the networking would never happen. – matt Nov 10 '20 at 23:23
  • 1
    Aha... it all makes sense now. And yes I was wondering if there should be some sort of completion handler! I am newer to coding so I didn't intuitively know to wait for it to complete--I kind of assumed it did that automatically for some reason. Thanks so much Matt!! – Eric Nov 10 '20 at 23:36
  • 1
    No problem - again, your code was so nicely constructed that spotting and fixing the single flaw was easy! You really did your homework before asking this question. – matt Nov 10 '20 at 23:47
  • Well I spent a while on it to no avail... but thanks--appreciate it! – Eric Nov 11 '20 at 02:32
  • You might like to look at my example here: https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch14p655HapticFeedback/HapticFeedback/ViewController.swift – matt Nov 11 '20 at 03:05