0

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;
        }
    }
}

1 Answer 1

0

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.