Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,17 @@
// Created by opfic on 6/6/26.
//

import Foundation
import Combine
import DevLogDomain

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

func publish(_ event: TodoMutationEvent) async {
continuations.values.forEach { $0.yield(event) }
func publish(_ event: TodoMutationEvent) {
subject.send(event)
}

func events() -> AsyncStream<TodoMutationEvent> {
let id = UUID()
let (stream, continuation) = AsyncStream.makeStream(of: TodoMutationEvent.self)

continuations[id] = continuation
continuation.onTermination = { [weak self] _ in
Task {
await self?.removeContinuation(id: id)
}
}

return stream
}
}

private extension TodoMutationEventBusImpl {
func removeContinuation(id: UUID) {
continuations[id] = nil
func observe() -> AnyPublisher<TodoMutationEvent, Never> {
subject.eraseToAnyPublisher()
}
}
Comment thread
opficdev marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ final class TodoRepositoryImpl: TodoRepository {
func upsertTodo(_ todo: Todo) async throws {
let todoRequest = TodoRequest.fromDomain(todo)
try await upsertTodo(todoRequest)
await todoMutationEventBus.publish(.updated(todo.id))
todoMutationEventBus.publish(.updated(todo.id))
}

func upsertTodo(_ todoDraft: TodoDraft) async throws {
Expand All @@ -131,7 +131,7 @@ final class TodoRepositoryImpl: TodoRepository {
do {
try await todoService.deleteTodo(todoId: todoId)
widgetSyncEventBus.publish(.syncRequested)
await todoMutationEventBus.publish(.deleted(todoId))
todoMutationEventBus.publish(.deleted(todoId))
} catch {
throw error.toDomain()
}
Expand All @@ -141,7 +141,7 @@ final class TodoRepositoryImpl: TodoRepository {
do {
try await todoService.undoDeleteTodo(todoId: todoId)
widgetSyncEventBus.publish(.syncRequested)
await todoMutationEventBus.publish(.restored(todoId))
todoMutationEventBus.publish(.restored(todoId))
} catch {
throw error.toDomain()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,24 @@
// Created by opfic on 6/6/26.
//

import Combine
import Testing
import DevLogDomain
@testable import DevLogData

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

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

let event = await task.value
#expect(event == .updated("todo-id"))
bus.publish(.updated("todo-id"))

#expect(events == [.updated("todo-id")])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct TodoRepositoryImplTests {
let events = fixture.widgetSyncEventBus.events
#expect(events == [.syncRequested, .syncRequested, .syncRequested])

let mutationEvents = await fixture.todoMutationEventBus.publishedEvents()
let mutationEvents = fixture.todoMutationEventBus.publishedEvents()
#expect(mutationEvents == [.updated(todo.id), .deleted(todo.id), .restored(todo.id)])
}

Expand Down Expand Up @@ -58,7 +58,7 @@ struct TodoRepositoryImplTests {
let syncEvents = fixture.widgetSyncEventBus.events
#expect(syncEvents.isEmpty)

let mutationEvents = await fixture.todoMutationEventBus.publishedEvents()
let mutationEvents = fixture.todoMutationEventBus.publishedEvents()
#expect(mutationEvents.isEmpty)
}

Expand Down Expand Up @@ -169,21 +169,19 @@ private final class WidgetSyncEventBusSpy: WidgetSyncEventBus {
}
}

private actor TodoMutationEventBusSpy: TodoMutationEventBus {
private final class TodoMutationEventBusSpy: TodoMutationEventBus {
private var capturedEvents = [TodoMutationEvent]()

func publish(_ event: TodoMutationEvent) async {
func publish(_ event: TodoMutationEvent) {
capturedEvents.append(event)
}

func publishedEvents() -> [TodoMutationEvent] {
capturedEvents
}

func events() async -> AsyncStream<TodoMutationEvent> {
AsyncStream { continuation in
continuation.finish()
}
func observe() -> AnyPublisher<TodoMutationEvent, Never> {
Empty().eraseToAnyPublisher()
}
}

Expand Down
2 changes: 1 addition & 1 deletion Application/DevLogDomain/Sources/Entity/AuthProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

public enum AuthProvider: String, CaseIterable, Sendable {
public enum AuthProvider: String, CaseIterable {
Comment thread
opficdev marked this conversation as resolved.
case apple = "apple.com"
case google = "google.com"
case github = "github.com"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// Created by opfic on 6/6/26.
//

public enum TodoMutationEvent: Equatable, Sendable {
public enum TodoMutationEvent: Equatable {
Comment thread
opficdev marked this conversation as resolved.
case updated(String)
case deleted(String)
case restored(String)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
// Created by opfic on 6/6/26.
//

public protocol TodoMutationEventBus: Sendable {
func publish(_ event: TodoMutationEvent) async
func events() async -> AsyncStream<TodoMutationEvent>
import Combine

public protocol TodoMutationEventBus {
func publish(_ event: TodoMutationEvent)
func observe() -> AnyPublisher<TodoMutationEvent, Never>
}
Comment thread
opficdev marked this conversation as resolved.
47 changes: 0 additions & 47 deletions Application/DevLogInfra/Sources/Common/FirebaseDependency.swift

This file was deleted.

83 changes: 22 additions & 61 deletions Application/DevLogInfra/Sources/Service/AuthServiceImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import DevLogCore
import DevLogData

final class AuthServiceImpl: AuthService {
private let store = FirebaseDependency(value: Firestore.firestore())
private let messaging = FirebaseDependency(value: Messaging.messaging())
private let store = Firestore.firestore()
private let messaging = Messaging.messaging()
private let logger = Logger(category: "AuthServiceImpl")
private let authStatePublisher: AuthStatePublisher
private let subject = CurrentValueSubject<Bool, Never>(Auth.auth().currentUser != nil)
private var handler: AuthStateDidChangeListenerHandle?
private var isCompletingSignIn = false
Comment thread
opficdev marked this conversation as resolved.

var uid: String? {
Auth.auth().currentUser?.uid
Expand All @@ -35,26 +37,36 @@ final class AuthServiceImpl: AuthService {
}

init() {
authStatePublisher = AuthStatePublisher(logger: logger)
handler = Auth.auth().addStateDidChangeListener { [weak self] _, user in
self?.handleAuthStateChange(user)
}
}

deinit {
guard let handler else { return }
Auth.auth().removeStateDidChangeListener(handler)
}

func observeSignedIn() -> AnyPublisher<Bool, Never> {
authStatePublisher.observeSignedIn()
subject.eraseToAnyPublisher()
}

func beginSignIn() {
logger.info("Beginning sign-in bootstrap")
authStatePublisher.beginSignIn()
isCompletingSignIn = true
subject.send(false)
}

func completeSignIn() {
logger.info("Completing sign-in bootstrap")
authStatePublisher.completeSignIn()
isCompletingSignIn = false
subject.send(Auth.auth().currentUser != nil)
}

func cancelSignIn() {
logger.info("Cancelling sign-in bootstrap")
authStatePublisher.cancelSignIn()
isCompletingSignIn = false
subject.send(Auth.auth().currentUser != nil)
}

func getProviderID() async throws -> String? {
Expand Down Expand Up @@ -114,59 +126,8 @@ final class AuthServiceImpl: AuthService {

}

private final class AuthStatePublisher {
private let logger: Logger
private let subject: CurrentValueSubject<Bool, Never>
private let lock = NSLock()
private var handler: FirebaseDependency<AuthStateDidChangeListenerHandle>?
private var isCompletingSignIn = false

init(logger: Logger) {
self.logger = logger
self.subject = CurrentValueSubject<Bool, Never>(Auth.auth().currentUser != nil)
self.handler = FirebaseDependency(
value: Auth.auth().addStateDidChangeListener { [weak self] _, user in
self?.handleAuthStateChange(user)
}
)
}

deinit {
guard let handler else { return }
handler.removeAuthStateDidChangeListener()
}

func observeSignedIn() -> AnyPublisher<Bool, Never> {
lock.lock()
defer { lock.unlock() }
return subject.eraseToAnyPublisher()
}

func beginSignIn() {
lock.lock()
isCompletingSignIn = true
subject.send(false)
lock.unlock()
}

func completeSignIn() {
lock.lock()
isCompletingSignIn = false
subject.send(Auth.auth().currentUser != nil)
lock.unlock()
}

func cancelSignIn() {
lock.lock()
isCompletingSignIn = false
subject.send(Auth.auth().currentUser != nil)
lock.unlock()
}

private func handleAuthStateChange(_ user: User?) {
lock.lock()
defer { lock.unlock() }

private extension AuthServiceImpl {
func handleAuthStateChange(_ user: User?) {
let signedIn = user != nil
logger.info("Firebase auth state changed. signedIn: \(signedIn)")

Expand Down
Loading