Skip to content

Commit 0328f76

Browse files
committed
[Chore] #221 - 컨벤션 준수, 낙관적 업데이트 정합성 제어
1 parent 855f347 commit 0328f76

2 files changed

Lines changed: 34 additions & 33 deletions

File tree

Neki-iOS/Features/Pose/Sources/Presentation/Sources/Feature/PoseDetailFeature.swift

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ struct PoseDetailFeature {
2626
case didTapBackButton
2727

2828
// Internal Actions
29-
case scrapResponse(PoseID, Result<Void, Error>)
29+
case scrapResponse(Pose, Result<Void, Error>)
3030

3131
// Delegate Actions
3232
case delegate(Delegate)
@@ -49,17 +49,17 @@ struct PoseDetailFeature {
4949
// MARK: - View Actions
5050
case .onTapScrap:
5151
guard var pose = state.currentPose else { return .none }
52+
let originalPose = pose
5253
pose.isScrapped.toggle()
5354
state.poses[id: pose.id] = pose
5455

55-
let trackingEffect: Effect<Action> = .run { _ in analytics.logEvent(event: PoseAnalyticsEvent.poseBookmark) }
56-
let scrapEffect: Effect<Action> = .run { [pose] send in
57-
await send(.delegate(.poseUpdated(pose)))
58-
await send(.scrapResponse(pose.id, Result { try await poseClient.scrapPose(poseID: pose.id) }))
56+
let scrapEffect: Effect<Action> = .run { [originalPose, updatedPose = pose] send in
57+
await send(.delegate(.poseUpdated(updatedPose)))
58+
await send(.scrapResponse(originalPose, Result { try await poseClient.scrapPose(poseID: originalPose.id) }))
5959
}
6060
.cancellable(id: CancelID.scrap(pose.id), cancelInFlight: true)
6161

62-
return .merge(trackingEffect, scrapEffect)
62+
return scrapEffect
6363

6464
case let .pageChanged(newID):
6565
state.selectedID = newID
@@ -69,18 +69,18 @@ struct PoseDetailFeature {
6969
return .run { _ in await dismiss() }
7070

7171
// MARK: - Internal Actions
72-
case let .scrapResponse(id, .failure(error)):
72+
case .scrapResponse(_, .success):
73+
return .run { _ in analytics.logEvent(PoseAnalyticsEvent.poseBookmark) }
74+
75+
case let .scrapResponse(originalPose, .failure(error)):
7376
if error is CancellationError { return .none }
77+
Logger.presentation.error("Error occured while scrapping pose: ID-\(id) / Error: \(error)")
7478

75-
if var pose = state.poses[id: id] {
76-
pose.isScrapped.toggle()
77-
state.poses[id: id] = pose
78-
return .send(.delegate(.poseUpdated(pose)))
79+
if state.poses[id: originalPose.id] != nil {
80+
state.poses[id: originalPose.id] = originalPose
81+
return .send(.delegate(.poseUpdated(originalPose)))
7982
}
80-
Logger.presentation.error("Error occured while scrapping pose: ID-\(id) / Error: \(error)")
81-
return .none
8283

83-
case .scrapResponse:
8484
return .none
8585

8686
case .delegate:

Neki-iOS/Features/Pose/Sources/Presentation/Sources/Feature/RandomPoseCarouselFeature.swift

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import os
1313
struct RandomPoseCarouselFeature {
1414
enum SlideDirection { case previous, next, none }
1515

16-
private enum CancelID { case poseRequest }
16+
private enum CancelID: Hashable {
17+
case poseRequest
18+
case scrap(PoseID)
19+
}
1720

1821
@ObservableState
1922
struct State {
@@ -43,7 +46,7 @@ struct RandomPoseCarouselFeature {
4346

4447
// Internal Actions
4548
case poseResponse(Result<Pose, Error>)
46-
case scrapResponse(PoseID, Result<Void, Error>)
49+
case scrapResponse(Pose, Result<Void, Error>)
4750
case flushResources
4851

4952
// Delegate Actions
@@ -63,10 +66,7 @@ struct RandomPoseCarouselFeature {
6366
switch action {
6467
// MARK: - Lifecycle & View Actions
6568
case .onAppear:
66-
if state.currentPose != nil {
67-
return .none
68-
}
69-
69+
guard state.currentPose == nil else { return .none }
7070
state.isLoading = true
7171
return .run { [count = state.activePeopleCount] send in
7272
await send(.poseResponse(Result { try await poseClient.initializeRandomPose(peopleCount: count) }))
@@ -105,16 +105,17 @@ struct RandomPoseCarouselFeature {
105105
// MARK: - Scrap Logic (Optimistic)
106106
case .onTapScrap:
107107
guard var pose = state.currentPose else { return .none }
108+
let originalPose = pose
108109
pose.isScrapped.toggle()
109110
state.currentPose = pose
110111

111-
let trackingEffect: Effect<Action> = .run { _ in analytics.logEvent(event: PoseAnalyticsEvent.poseBookmark) }
112-
let scrapEffect: Effect<Action> = .run { [id = pose.id, pose] send in
113-
await send(.delegate(.poseUpdated(pose)))
114-
await send(.scrapResponse(id, Result { try await poseClient.scrapPose(poseID: id) }))
112+
let scrapEffect: Effect<Action> = .run { [originalPose, updatedPose = pose] send in
113+
await send(.delegate(.poseUpdated(updatedPose)))
114+
await send(.scrapResponse(originalPose, Result { try await poseClient.scrapPose(poseID: originalPose.id) }))
115115
}
116+
.cancellable(id: CancelID.scrap(pose.id), cancelInFlight: true)
116117

117-
return .merge(trackingEffect, scrapEffect)
118+
return scrapEffect
118119

119120
// MARK: - Internal Actions
120121
case let .poseResponse(.success(pose)):
@@ -127,18 +128,18 @@ struct RandomPoseCarouselFeature {
127128
Logger.presentation.error("Random Pose Fetching Failed: \(error)")
128129
return .none
129130

130-
case let .scrapResponse(id, .failure(error)):
131+
case .scrapResponse(_, .success):
132+
return .run { _ in analytics.logEvent(PoseAnalyticsEvent.poseBookmark) }
133+
134+
case let .scrapResponse(originalPose, .failure(error)):
131135
if error is CancellationError { return .none }
136+
Logger.presentation.error("Error occured while scrapping pose: ID-\(id) / Error: \(error)")
132137

133-
if var pose = state.currentPose, pose.id == id {
134-
pose.isScrapped.toggle()
135-
state.currentPose = pose
136-
return .send(.delegate(.poseUpdated(pose)))
138+
if state.currentPose?.id == originalPose.id {
139+
state.currentPose = originalPose
140+
return .send(.delegate(.poseUpdated(originalPose)))
137141
}
138-
Logger.presentation.error("Error occured while scrapping pose: ID-\(id) / Error: \(error)")
139-
return .none
140142

141-
case .scrapResponse:
142143
return .none
143144

144145
// MARK: - Navigation

0 commit comments

Comments
 (0)