Skip to content

Commit 88c8a74

Browse files
authored
Fix voice memos crasher (#183)
* Test case for voice memos crasher. * fix crasher * clean up * rename remove to delete
1 parent 207fdfe commit 88c8a74

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
@@ -28,6 +28,7 @@ struct VoiceMemo: Equatable {
2828
enum VoiceMemoAction: Equatable {
2929
case audioPlayerClient(Result<AudioPlayerClient.Action, AudioPlayerClient.Failure>)
3030
case playButtonTapped
31+
case delete
3132
case timerUpdated(TimeInterval)
3233
case titleTextFieldChanged(String)
3334
}
@@ -47,6 +48,12 @@ let voiceMemoReducer = Reducer<VoiceMemo, VoiceMemoAction, VoiceMemoEnvironment>
4748
memo.mode = .notPlaying
4849
return .cancel(id: TimerId())
4950

51+
case .delete:
52+
return .merge(
53+
.cancel(id: PlayerId()),
54+
.cancel(id: TimerId())
55+
)
56+
5057
case .playButtonTapped:
5158
switch memo.mode {
5259
case .notPlaying:
@@ -56,7 +63,8 @@ let voiceMemoReducer = Reducer<VoiceMemo, VoiceMemoAction, VoiceMemoEnvironment>
5663
environment.audioPlayerClient
5764
.play(PlayerId(), memo.url)
5865
.catchToEffect()
59-
.map(VoiceMemoAction.audioPlayerClient),
66+
.map(VoiceMemoAction.audioPlayerClient)
67+
.cancellable(id: PlayerId()),
6068

6169
Effect.timer(id: TimerId(), every: 0.5, on: environment.mainQueue)
6270
.map {
@@ -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 {}
@@ -196,10 +209,6 @@ let voiceMemosReducer = Reducer<VoiceMemosState, VoiceMemosAction, VoiceMemosEnv
196209
state.currentRecording?.duration += 1
197210
return .none
198211

199-
case let .deleteVoiceMemo(indexSet):
200-
state.voiceMemos.remove(atOffsets: indexSet)
201-
return .none
202-
203212
case let .finalRecordingTime(duration):
204213
state.currentRecording?.duration = duration
205214
return .none
@@ -256,6 +265,10 @@ let voiceMemosReducer = Reducer<VoiceMemosState, VoiceMemosAction, VoiceMemosEnv
256265
state.alertMessage = "Voice memo playback failed."
257266
return .none
258267

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

277284
struct VoiceMemosView: View {
@@ -289,7 +296,11 @@ struct VoiceMemosView: View {
289296
id: \.url,
290297
content: VoiceMemoView.init(store:)
291298
)
292-
.onDelete { viewStore.send(.deleteVoiceMemo($0)) }
299+
.onDelete { indexSet in
300+
for index in indexSet {
301+
viewStore.send(.voiceMemo(index: index, action: .delete))
302+
}
303+
}
293304
}
294305
VStack {
295306
ZStack {

Examples/VoiceMemos/VoiceMemosTests/VoiceMemosTests.swift

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

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

292324
extension VoiceMemosEnvironment {

0 commit comments

Comments
 (0)