1

I have a problem when adding audio to haptic feedback using core haptics in swifui. The system keeps showing the error "can't find audioID" in scope as audioID should be a registered audio resource ID parameter created by the function registerAudioresource of CHHapticEngine.

Here are the two functions that I have written:

//get engine ready
func prepareHaptics() {
  guard CHHapticEngine.capablitiesForHardware().supportHaptics else { return }
  do {
    engine = try CHHapticEngine()
    try engine?.start()
} catch {
    print(“There was an error creating the engine: \(error.localizedDescription)”)
}
}

//play haptic and audio pattern
func Haptictransient() {
  guard CHHapticEngine.capablitiesForHardware().supportHaptics else { return }
  guard let sound = Bundle.main.url(forResource: "tapsound", withExtension: "wav") else { return }
  
  do {
    let audioID = try engine?.registerAudioresource(sound)
} catch {
    print("Couldn't create the audio ID")
}
  var events = [CHHapticEvent]()
//The code below get me an error "cannot find 'audioID' in scope"
  let audio = CHHapticEvent(audioresourceID: audioID, parameters: [], relativeTime: 0)
  events.append(audio)
  let haptic = CHHapticEvent(evnetType: .hapticTransient, parameters: [], relativeTime: 0)
  events.append(haptic)

  do {
    let pattern = try CHHapticPattern(events: evnets, parameters: [])
    let player = try engine?.makePlayer(with: pattern)
    try player?.start(atTime: 0)
} catch { print("Failed to play pattern")}
}

I tried different ways but none have worked. Any idea or help will be much appreciated!

Edit 2 This is the full version of my codes. Although no error received, no feedback received.

import SwiftUI
import CoreHaptics

struct TapView: View {
  @State var engine: CHHapticEngine?

  var body: some View {
    Button(action: {
        prepareHaptics()
        hapticTransient()
    }) { Text("PRESS") }
  }
  
//get haptic engine ready
  func prepareHaptics() {
    guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else { return }
    do {
      engine = try CHHapticEngine()
      try engine?.start()
    } catch {
      print("There was an error creating the engine: \(error.localizedDescription)")
    }
  }

//add haptic and audio feedback
  func hapticTransient() {
    guard CHHapticEngine.capabilitiesForHardware().supportsHaptics, let engine else { return }
    guard let sound = Bundle.main.url(forResource: "tapsound", withExtension: "wav") else {return}
    let audioID: CHHapticAudioREsourceID
    do { audioID = try engine.registerAudioResource(sound) }
    catch { print("Failed to create the audio") return }
    
    var events = [CHHapticEvent]()
    let audioEvent = CHHapticEvent(audioResourceID: audioID, parameters: [], relativeTime: 0)
    let hapticEvent = CHHapticEvent(eventType: .hapticTransient, parameters: [], relativeTime: 0)
    events.append(audioEvent)
    events.append(hapticEvent)

    do { 
      let pattern = try CHHapticPattern(events: events, parameter: [])
      let player = try engine.makePlayer(with: pattern)
      try palyer.start(atTime: 0)
   } catch {
       print("Failed to play pattern \(error.localDescription)")
     }
  }
}
Yunjia You
  • 11
  • 2

1 Answers1

0

The problem is here:

do {
    let audioID = try engine.registerAudioResource(sound)
} catch {
    print("Couldn't create the audio ID")
}

audioID is out of scope when you try to use it later. You can fix it by moving the declaration of audioID before the do/catch:

var engine: CHHapticEngine?

//get engine ready
func prepareHaptics() {
    guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else { return }
    do {
        engine = try CHHapticEngine()
        try engine?.start()
    } catch {
        print("There was an error creating the engine: \(error.localizedDescription)")
    }
}

//play haptic and audio pattern
func hapticTransient() {

    // Also check you have an engine before continuing 
    guard CHHapticEngine.capabilitiesForHardware().supportsHaptics, let engine else { return }
    
    guard let sound = Bundle.main.url(forResource: "tapsound", withExtension: "wav") else { return }
    
    //Declare audioID here so it's in scope
    let audioID: CHHapticAudioResourceID    
    do {
        audioID = try engine.registerAudioResource(sound)
    } catch {
        print("Couldn't create the audio ID")

        // return if it fails
        return
    }

    var events = [CHHapticEvent]()
    //The code below get me an error "cannot find 'audioID' in scope"
    let audio = CHHapticEvent(audioResourceID: audioID, parameters: [], relativeTime: 0)
    events.append(audio)
    let haptic = CHHapticEvent(eventType: .hapticTransient, parameters: [], relativeTime: 0)
    events.append(haptic)
    
    do {
        let pattern = try CHHapticPattern(events: events, parameters: [])
        let player = try engine.makePlayer(with: pattern)
        try player.start(atTime: 0)
    } catch { print("Failed to play pattern")}
}

Notice I've added a return if registerAudioResource fails

I've also added let engine to your guard as it makes no sense if it's nil

Ashley Mills
  • 50,474
  • 16
  • 129
  • 160
  • Thank you Ashley, but with your codes I had another problem. The build succeeded but the output was always "Couldn't create the audio ID". It seems like the audioID was never created by the do/catch command. The initial idea is to provide haptic and audio feedback everytime the user presses the button, so I link two functions (prepareHaptics and hapticTrasient) to the action attribute of the button component, but now neither haptic nor audio feedback was given after tapping on the button – Yunjia You Feb 22 '23 at 01:42
  • Now I fixed this problem by replacing the audio resource with a mp3 file, instead of using a wav file – Yunjia You Feb 22 '23 at 03:37
  • Glad you got it working. Feel free to accept my answer if it helped. – Ashley Mills Feb 22 '23 at 08:19