Andromeda
Andromeda

Reputation: 673

Setting an @EnvironmentObject value when using AVAudioPlayerDelegate in a SwiftUI view hierarchy

I'm playing a sound in a SwiftUI view, and I'd like to set a value in an @EnvironmentObject when the sound completes.

So, I declare protocol compliance, and my completion handler is duly called. BUT, I can't set a value in my @EnvironmentObject, because the completion handler doesn't live in the SwiftUI view hierarchy.

My first thought was to pass the settings: EnvironmenObject to MyPlayerDelegate, but I was foiled because settings is an instance variable.

Any ideas?

Here's the code:

import SwiftUI
import AVFoundation
var audioPlayer: AVAudioPlayer!

import MediaPlayer
import AVKit

protocol PlayerDelegate: AnyObject {
    func soundFinished(_ sender: Any)
}


struct MeditationView: View {
    @EnvironmentObject var settings: Settings
    
    var myPlayerDelegate = MyPlayerDelegate()
    
    var body: some View {
        ZStack {
            settings.rainbowColour
                .animation(.easeInOut(duration: 1))
            VStack {
                meditationImage(colour: settings.rainbowColour)
                    .resizable()
                    .opacity(settings.showRainbow ? 0 : 1)
                    .scaledToFit()
                    .onAppear() {
                        if let audioFileURL =
                            Bundle.main.url(forResource: settings.meditation,
                                            withExtension: "mp3")
                        {
                            do {
                                audioPlayer = try AVAudioPlayer(contentsOf: audioFileURL)
                                guard let player = audioPlayer else {print("Unable to find " + settings.meditation); return}
                                player.prepareToPlay()
                                player.delegate = myPlayerDelegate
                                player.play()
                            } catch let error {
                                print("Unable to play the meditation sound" + settings.meditation)
                                print(error.localizedDescription)
                            }
                        } else {
                            print("Unable to load sound: " + settings.meditation)
                        }
                        
                    }    // .onAppear
                    .onTapGesture {
                        if let player = audioPlayer {
                            player.pause()
                        }
                        settings.showRainbow = true;
                    }
                Text("Tap to return to the rainbow")
            }
            
       }   // ZStack

    }
    
    class MyPlayerDelegate : NSObject, ObservableObject, AVAudioPlayerDelegate {
         
        func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
 //           settings.showRainbow = true;
        }
    }
}

Upvotes: 0

Views: 100

Answers (1)

Andromeda
Andromeda

Reputation: 673

Figured it out.

The EnvironmentObject is instantiated in the AppDelegate: let settings = Settings()

So, all I had to do was to move MyPlayerDelgate down a line so it wasn't part of MeditationView anymore. Then settings referred to the global instance of Settings, and everything worked.

Upvotes: 0

Related Questions