Skip to content

Commit 776c718

Browse files
committed
Fix voice memos crasher (#183)
* Test case for voice memos crasher. * fix crasher * clean up * rename remove to delete
1 parent 4387eb8 commit 776c718

File tree

2 files changed

+59
-16
lines changed

2 files changed

+59
-16
lines changed

Examples/VoiceMemos/VoiceMemos/VoiceMemos.swift

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct VoiceMemo: Equatable {
2929
enum VoiceMemoAction: Equatable {
3030
case audioPlayerClient(Result<AudioPlayerClient.Action, AudioPlayerClient.Failure>)
3131
case playButtonTapped
32+
case delete
3233
case timerUpdated(TimeInterval)
3334
case titleTextFieldChanged(String)
3435
}
@@ -48,6 +49,12 @@ let voiceMemoReducer = Reducer<VoiceMemo, VoiceMemoAction, VoiceMemoEnvironment>
4849
memo.mode = .notPlaying
4950
return .cancel(id: TimerId())
5051

52+
case .delete:
53+
return .merge(
54+
.cancel(id: PlayerId()),
55+
.cancel(id: TimerId())
56+
)
57+
5158
case .playButtonTapped:
5259
switch memo.mode {
5360
case .notPlaying:
@@ -57,7 +64,8 @@ let voiceMemoReducer = Reducer<VoiceMemo, VoiceMemoAction, VoiceMemoEnvironment>
5764
environment.audioPlayerClient
5865
.play(PlayerId(), memo.url)
5966
.catchToEffect()
60-
.map(VoiceMemoAction.audioPlayerClient),
67+
.map(VoiceMemoAction.audioPlayerClient)
68+
.cancellable(id: PlayerId()),
6169
Effect.timer(id: TimerId(), every: .milliseconds(500), on: environment.mainQueue)
6270
.map { date -> VoiceMemoAction in
6371
.timerUpdated(
@@ -121,7 +129,6 @@ enum VoiceMemosAction: Equatable {
121129
case alertDismissed
122130
case audioRecorderClient(Result<AudioRecorderClient.Action, AudioRecorderClient.Failure>)
123131
case currentRecordingTimerUpdated
124-
case deleteVoiceMemo(IndexSet)
125132
case finalRecordingTime(TimeInterval)
126133
case openSettingsButtonTapped
127134
case recordButtonTapped
@@ -140,6 +147,12 @@ struct VoiceMemosEnvironment {
140147
}
141148

142149
let voiceMemosReducer = Reducer<VoiceMemosState, VoiceMemosAction, VoiceMemosEnvironment>.combine(
150+
voiceMemoReducer.forEach(
151+
state: \.voiceMemos,
152+
action: /VoiceMemosAction.voiceMemo(index:action:),
153+
environment: {
154+
VoiceMemoEnvironment(audioPlayerClient: $0.audioPlayerClient, mainQueue: $0.mainQueue)
155+
}),
143156
.init { state, action, environment in
144157
struct RecorderId: Hashable {}
145158
struct RecorderTimerId: Hashable {}
@@ -199,10 +212,6 @@ let voiceMemosReducer = Reducer<VoiceMemosState, VoiceMemosAction, VoiceMemosEnv
199212
state.currentRecording?.duration += 1
200213
return .none
201214

202-
case let .deleteVoiceMemo(indexSet):
203-
state.voiceMemos.remove(atOffsets: indexSet)
204-
return .none
205-
206215
case let .finalRecordingTime(duration):
207216
state.currentRecording?.duration = duration
208217
return .none
@@ -257,6 +266,10 @@ let voiceMemosReducer = Reducer<VoiceMemosState, VoiceMemosAction, VoiceMemosEnv
257266
state.alertMessage = "Voice memo playback failed."
258267
return .none
259268

269+
case let .voiceMemo(index: index, action: .delete):
270+
state.voiceMemos.remove(at: index)
271+
return .none
272+
260273
case let .voiceMemo(index: index, action: .playButtonTapped):
261274
for idx in state.voiceMemos.indices where idx != index {
262275
state.voiceMemos[idx].mode = .notPlaying
@@ -266,13 +279,7 @@ let voiceMemosReducer = Reducer<VoiceMemosState, VoiceMemosAction, VoiceMemosEnv
266279
case .voiceMemo:
267280
return .none
268281
}
269-
},
270-
voiceMemoReducer.forEach(
271-
state: \.voiceMemos,
272-
action: /VoiceMemosAction.voiceMemo(index:action:),
273-
environment: {
274-
VoiceMemoEnvironment(audioPlayerClient: $0.audioPlayerClient, mainQueue: $0.mainQueue)
275-
})
282+
}
276283
)
277284

278285
struct VoiceMemosView: View {
@@ -290,7 +297,11 @@ struct VoiceMemosView: View {
290297
id: \.url,
291298
content: VoiceMemoView.init(store:)
292299
)
293-
.onDelete { viewStore.send(.deleteVoiceMemo($0)) }
300+
.onDelete { indexSet in
301+
for index in indexSet {
302+
viewStore.send(.voiceMemo(index: index, action: .delete))
303+
}
304+
}
294305
}
295306
VStack {
296307
ZStack {

Examples/VoiceMemos/VoiceMemosTests/VoiceMemosTests.swift

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,12 +280,44 @@ class VoiceMemosTests: XCTestCase {
280280
)
281281

282282
store.assert(
283-
.send(.deleteVoiceMemo(IndexSet(integer: 1))),
284-
.send(.deleteVoiceMemo(IndexSet(integer: 0))) {
283+
.send(.voiceMemo(index: 0, action: .delete)) {
285284
$0.voiceMemos = []
286285
}
287286
)
288287
}
288+
289+
func testDeleteMemoWhilePlaying() {
290+
let store = TestStore(
291+
initialState: VoiceMemosState(
292+
voiceMemos: [
293+
VoiceMemo(
294+
date: Date(timeIntervalSinceNow: 0),
295+
duration: 10,
296+
mode: .notPlaying,
297+
title: "",
298+
url: URL(string: "https://www.pointfree.co/functions")!
299+
),
300+
]
301+
),
302+
reducer: voiceMemosReducer,
303+
environment: .mock(
304+
audioPlayerClient: .mock(
305+
play: { id, url in .future { _ in } }
306+
),
307+
mainQueue: self.scheduler.eraseToAnyScheduler()
308+
)
309+
)
310+
311+
store.assert(
312+
.send(.voiceMemo(index: 0, action: .playButtonTapped)) {
313+
$0.voiceMemos[0].mode = .playing(progress: 0)
314+
},
315+
.send(.voiceMemo(index: 0, action: .delete)) {
316+
$0.voiceMemos = []
317+
},
318+
.do { self.scheduler.run() }
319+
)
320+
}
289321
}
290322

291323
extension VoiceMemosEnvironment {

0 commit comments

Comments
 (0)