Skip to content

Commit d61f063

Browse files
committed
refactor: AsyncStream 기반 스트림을 Combine으로 수정
1 parent 18cc91c commit d61f063

6 files changed

Lines changed: 46 additions & 64 deletions

File tree

Application/DevLogData/Sources/Repository/TodoMutationEventBusImpl.swift

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,17 @@
55
// Created by opfic on 6/6/26.
66
//
77

8-
import Foundation
8+
import Combine
99
import DevLogDomain
1010

11-
actor TodoMutationEventBusImpl: TodoMutationEventBus {
12-
private var continuations = [UUID: AsyncStream<TodoMutationEvent>.Continuation]()
11+
final class TodoMutationEventBusImpl: TodoMutationEventBus {
12+
private let subject = PassthroughSubject<TodoMutationEvent, Never>()
1313

14-
func publish(_ event: TodoMutationEvent) async {
15-
continuations.values.forEach { $0.yield(event) }
14+
func publish(_ event: TodoMutationEvent) {
15+
subject.send(event)
1616
}
1717

18-
func events() -> AsyncStream<TodoMutationEvent> {
19-
let id = UUID()
20-
let (stream, continuation) = AsyncStream.makeStream(of: TodoMutationEvent.self)
21-
22-
continuations[id] = continuation
23-
continuation.onTermination = { [weak self] _ in
24-
Task {
25-
await self?.removeContinuation(id: id)
26-
}
27-
}
28-
29-
return stream
30-
}
31-
}
32-
33-
private extension TodoMutationEventBusImpl {
34-
func removeContinuation(id: UUID) {
35-
continuations[id] = nil
18+
func observe() -> AnyPublisher<TodoMutationEvent, Never> {
19+
subject.eraseToAnyPublisher()
3620
}
3721
}

Application/DevLogData/Sources/Repository/TodoRepositoryImpl.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ final class TodoRepositoryImpl: TodoRepository {
110110
func upsertTodo(_ todo: Todo) async throws {
111111
let todoRequest = TodoRequest.fromDomain(todo)
112112
try await upsertTodo(todoRequest)
113-
await todoMutationEventBus.publish(.updated(todo.id))
113+
todoMutationEventBus.publish(.updated(todo.id))
114114
}
115115

116116
func upsertTodo(_ todoDraft: TodoDraft) async throws {
@@ -131,7 +131,7 @@ final class TodoRepositoryImpl: TodoRepository {
131131
do {
132132
try await todoService.deleteTodo(todoId: todoId)
133133
widgetSyncEventBus.publish(.syncRequested)
134-
await todoMutationEventBus.publish(.deleted(todoId))
134+
todoMutationEventBus.publish(.deleted(todoId))
135135
} catch {
136136
throw error.toDomain()
137137
}
@@ -141,7 +141,7 @@ final class TodoRepositoryImpl: TodoRepository {
141141
do {
142142
try await todoService.undoDeleteTodo(todoId: todoId)
143143
widgetSyncEventBus.publish(.syncRequested)
144-
await todoMutationEventBus.publish(.restored(todoId))
144+
todoMutationEventBus.publish(.restored(todoId))
145145
} catch {
146146
throw error.toDomain()
147147
}

Application/DevLogData/Tests/Repository/TodoMutationEventBusImplTests.swift

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,24 @@
55
// Created by opfic on 6/6/26.
66
//
77

8+
import Combine
89
import Testing
910
import DevLogDomain
1011
@testable import DevLogData
1112

1213
struct TodoMutationEventBusImplTests {
1314
@Test("TodoMutationEventBus는 발행된 이벤트를 관찰자에게 전달한다")
14-
func todoMutationEventBus는_발행된_이벤트를_관찰자에게_전달한다() async {
15+
func todoMutationEventBus는_발행된_이벤트를_관찰자에게_전달한다() {
1516
let bus = TodoMutationEventBusImpl()
16-
let events = await bus.events()
17-
let task = Task {
18-
var iterator = events.makeAsyncIterator()
19-
return await iterator.next()
20-
}
17+
var events = [TodoMutationEvent]()
18+
var cancellables = Set<AnyCancellable>()
2119

22-
await bus.publish(.updated("todo-id"))
20+
bus.observe()
21+
.sink { events.append($0) }
22+
.store(in: &cancellables)
2323

24-
let event = await task.value
25-
#expect(event == .updated("todo-id"))
24+
bus.publish(.updated("todo-id"))
25+
26+
#expect(events == [.updated("todo-id")])
2627
}
2728
}

Application/DevLogData/Tests/Repository/TodoRepositoryImplTests.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct TodoRepositoryImplTests {
2525
let events = fixture.widgetSyncEventBus.events
2626
#expect(events == [.syncRequested, .syncRequested, .syncRequested])
2727

28-
let mutationEvents = await fixture.todoMutationEventBus.publishedEvents()
28+
let mutationEvents = fixture.todoMutationEventBus.publishedEvents()
2929
#expect(mutationEvents == [.updated(todo.id), .deleted(todo.id), .restored(todo.id)])
3030
}
3131

@@ -58,7 +58,7 @@ struct TodoRepositoryImplTests {
5858
let syncEvents = fixture.widgetSyncEventBus.events
5959
#expect(syncEvents.isEmpty)
6060

61-
let mutationEvents = await fixture.todoMutationEventBus.publishedEvents()
61+
let mutationEvents = fixture.todoMutationEventBus.publishedEvents()
6262
#expect(mutationEvents.isEmpty)
6363
}
6464

@@ -169,21 +169,19 @@ private final class WidgetSyncEventBusSpy: WidgetSyncEventBus {
169169
}
170170
}
171171

172-
private actor TodoMutationEventBusSpy: TodoMutationEventBus {
172+
private final class TodoMutationEventBusSpy: TodoMutationEventBus {
173173
private var capturedEvents = [TodoMutationEvent]()
174174

175-
func publish(_ event: TodoMutationEvent) async {
175+
func publish(_ event: TodoMutationEvent) {
176176
capturedEvents.append(event)
177177
}
178178

179179
func publishedEvents() -> [TodoMutationEvent] {
180180
capturedEvents
181181
}
182182

183-
func events() async -> AsyncStream<TodoMutationEvent> {
184-
AsyncStream { continuation in
185-
continuation.finish()
186-
}
183+
func observe() -> AnyPublisher<TodoMutationEvent, Never> {
184+
Empty().eraseToAnyPublisher()
187185
}
188186
}
189187

Application/DevLogDomain/Sources/Protocol/TodoMutationEventBus.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
// Created by opfic on 6/6/26.
66
//
77

8-
public protocol TodoMutationEventBus: Sendable {
9-
func publish(_ event: TodoMutationEvent) async
10-
func events() async -> AsyncStream<TodoMutationEvent>
8+
import Combine
9+
10+
public protocol TodoMutationEventBus {
11+
func publish(_ event: TodoMutationEvent)
12+
func observe() -> AnyPublisher<TodoMutationEvent, Never>
1113
}

Application/DevLogPresentation/Sources/Home/Home/HomeViewCoordinator.swift

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,15 @@ import DevLogDomain
1313
@MainActor
1414
@Observable
1515
final class HomeViewCoordinator {
16-
private enum AsyncStreamTaskID {
17-
case todoMutationEvent
18-
}
19-
2016
let viewModel: HomeViewModel
2117
let router = NavigationRouter<HomeRoute>()
2218
private let container: DIContainer
2319
@ObservationIgnored
24-
private var cancellable: AnyCancellable?
20+
private var cancellables = Set<AnyCancellable>()
21+
@ObservationIgnored
22+
private var isTodoMutationEventBound = false
2523
@ObservationIgnored
26-
private var streamTasks = [AsyncStreamTaskID: Task<Void, Never>]()
24+
private var isWindowEventBound = false
2725

2826
init(container: DIContainer) {
2927
self.container = container
@@ -40,10 +38,6 @@ final class HomeViewCoordinator {
4038
)
4139
}
4240

43-
deinit {
44-
streamTasks.values.forEach { $0.cancel() }
45-
}
46-
4741
func fetchData() {
4842
viewModel.send(.fetchData)
4943
}
@@ -53,30 +47,33 @@ final class HomeViewCoordinator {
5347
}
5448

5549
func bindTodoMutationEvent() {
56-
guard streamTasks[.todoMutationEvent] == nil else { return }
50+
guard isTodoMutationEventBound == false else { return }
51+
isTodoMutationEventBound = true
5752

5853
let bus = container.resolve(TodoMutationEventBus.self)
59-
streamTasks[.todoMutationEvent] = Task { [weak self] in
60-
let events = await bus.events()
61-
for await event in events {
62-
guard let self else { break }
54+
bus.observe()
55+
.receive(on: DispatchQueue.main)
56+
.sink { [weak self] event in
57+
guard let self else { return }
6358
switch event {
6459
case .updated, .deleted, .restored:
6560
self.refreshRecentTodos()
6661
}
6762
}
68-
}
63+
.store(in: &cancellables)
6964
}
7065

7166
func bindWindowEvent(_ windowEvent: TodoEditorWindowEvent) {
72-
guard cancellable == nil else { return }
67+
guard isWindowEventBound == false else { return }
68+
isWindowEventBound = true
7369

74-
cancellable = windowEvent.submits
70+
windowEvent.submits
7571
.sink { [weak self] submit in
7672
guard case .create(let value) = submit,
7773
value.matchesCreate(source: .home) else { return }
7874
self?.viewModel.send(.fetchData)
7975
}
76+
.store(in: &cancellables)
8077
}
8178

8279
func makeTodoManageViewModel() -> TodoManageViewModel {

0 commit comments

Comments
 (0)