Update state after audio player stops playing #2018
-
I send an action from button to reducer to start playing and I want to update icon to stop icon and once it's finished I want to automatically update it to play icon again. First, is straightforward and I update it but what's the best practice to update it back to "Start" once audio is finished? Here's the simplified code just for an example ignoring all the complex logic. Just, start and stop stuff. Plus I use struct MyReducer: ReducerProtocol {
@Dependency(\.player) var player
struct State: Equatable {
// properties
}
enum Action {
case start
case startResult(TaskResult<Void>)
case stop
}
var body: some ReducerProtocol<State, Action> {
Reduce { state, action in
switch action {
case start:
// set item boolean prop to is playing
return .task {
await .startResult(
.init(try await player.play())
)
}
// *** handling start result
case stop:
// set item boolean prop to not playing
return .fireAndForget { await player.stop() }
}
}
}
} Let's say player returns Package version: 0.50.2 |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 9 replies
-
Just in case you're interested in more complex implementation I'll provide it here as well. I haven't yet added functionality on audio player to return result when audio is ended as I'm not quite sure what would be the best way of doing it. struct ProjectsReducer: ReducerProtocol {
@Dependency(\.fileManager) var fileManager
@Dependency(\.simpleAudioPlayer) var audioPlayer
struct State: Equatable {
var title: String
var icon: ImageSource
var items: OrderedSet<Item>
}
enum Action {
case refresh
case refreshResult(TaskResult<[State.Item]>)
case playAudio(State.File)
case playAudioResult(TaskResult<State.File>)
case stopAudio(State.File)
case stopAudioResult(TaskResult<State.File>)
}
var body: some ReducerProtocol<State, Action> {
Reduce { state, action in
switch action {
case .refresh:
return .task {
await .refreshResult(
.init {
let result = try await fileManager.getContent(.documentDirectory)
return await result.asyncMap { url in
if url.hasDirectoryPath {
return .directory(.init(url: url, title: url.lastPathComponent))
} else {
return await .file(.init(url: url))
}
}
}
)
}
case let .refreshResult(.success(items)):
state.items = .init(items)
return .none
case let .refreshResult(.failure(error)):
print(error.localizedDescription)
return .none
// MARK: - Playback
case let .playAudio(file):
return .task {
await .playAudioResult(
.init {
try await audioPlayer.play(file.url)
return file
}
)
}
case let .playAudioResult(.success(file)):
return updateFile(file: file, isRunning: true, state: &state)
case let .playAudioResult(.failure(error)):
print(error.localizedDescription)
return .none
case let .stopAudio(file):
return .task {
await .stopAudioResult(
.init {
try await audioPlayer.stop(file.url)
return file
}
)
}
case let .stopAudioResult(.success(file)):
return updateFile(file: file, isRunning: false, state: &state)
case let .stopAudioResult(.failure(error)):
print(error.localizedDescription)
return .none
}
}
}
private func updateFile(
file: State.File,
isRunning: Bool,
state: inout State
) -> EffectTask<Action> {
if let index = state.items.firstIndex(where: { $0.id == file.id }) {
var runningFile = file
runningFile.isRunning = isRunning
state.items.remove(at: index)
state.items.insert(.file(runningFile), at: index)
}
return .none
}
} |
Beta Was this translation helpful? Give feedback.
-
I made it work by using case let .playAudio(file):
return .run { send in
await send(
.playAudioResult(
.init {
try await audioPlayer.play(file.url)
return file
}
)
)
try await audioPlayer.waitForStop(file.url)
await send(.audioFinished(file))
}
.cancellable(id: file.id, cancelInFlight: true) I could not manage to hook |
Beta Was this translation helpful? Give feedback.
-
Hi @kakhaberikiknadze, the "Voice Memos" demo application in this repo might help you. We have an return .run { send in
self.audioPlayer.play(…)
await send(.audioFinished)
} |
Beta Was this translation helpful? Give feedback.
I made it work by using
.run
.I could not manage to hook
AVAudioPlayer
's end of stream publisher so far but testing out withTask.sl…