Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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 @@ -7,13 +7,13 @@

import Foundation

public struct TodoCategoryPreferenceResponse: Equatable {
public enum Category: Equatable {
public struct TodoCategoryPreferenceResponse: Equatable, Codable {
public enum Category: Equatable, Codable {
case system(String)
case user(UserCategory)
}

public struct UserCategory: Equatable {
public struct UserCategory: Equatable, Codable {
public let id: String
public let name: String
public let colorHex: String
Expand Down
16 changes: 10 additions & 6 deletions Application/DevLogData/Sources/DataAssembler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,26 +40,29 @@ public final class DataAssembler: Assembler {
TodoRepositoryImpl(
todoService: container.resolve(TodoService.self),
todoCategoryService: container.resolve(TodoCategoryService.self),
store: container.resolve(MemoryCacheStore.self),
widgetSyncEventBus: container.resolve(WidgetSyncEventBus.self),
todoMutationEventBus: container.resolve(TodoMutationEventBus.self)
)
}

container.register(WidgetTodoSnapshotRepository.self) {
WidgetTodoSnapshotRepositoryImpl(
repository: container.resolve(TodoRepository.self)
)
WidgetTodoSnapshotRepositoryImpl(todoService: container.resolve(TodoService.self))
}

container.register(TodoCategoryRepository.self) {
TodoCategoryRepositoryImpl(
todoCategoryService: container.resolve(TodoCategoryService.self)
todoCategoryService: container.resolve(TodoCategoryService.self),
store: container.resolve(MemoryCacheStore.self)
)
}

container.register(AuthSessionRepository.self) {
AuthSessionRepositoryImpl(
authService: container.resolve(AuthService.self)
authService: container.resolve(AuthService.self),
todoCategoryService: container.resolve(TodoCategoryService.self),
store: container.resolve(MemoryCacheStore.self),
provider: container.resolve(AuthSessionStateProvider.self)
)
}

Expand Down Expand Up @@ -100,7 +103,8 @@ public final class DataAssembler: Assembler {
container.register(PushNotificationRepository.self) {
PushNotificationRepositoryImpl(
pushNotificationService: container.resolve(PushNotificationService.self),
todoCategoryService: container.resolve(TodoCategoryService.self)
todoCategoryService: container.resolve(TodoCategoryService.self),
store: container.resolve(MemoryCacheStore.self)
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// AuthSessionStateProvider.swift
// DevLogData
//
// Created by opfic on 6/9/26.
//

import Combine

public protocol AuthSessionStateProvider {
func publish(_ isSignedIn: Bool)
func observeSignedIn() -> AnyPublisher<Bool, Never>
}
13 changes: 13 additions & 0 deletions Application/DevLogData/Sources/Protocol/MemoryCacheStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// MemoryCacheStore.swift
// DevLogData
//
// Created by opfic on 6/9/26.
//

import Foundation

public protocol MemoryCacheStore {
func value<T: Codable>(forKey key: String) -> T?
func setValue<T: Codable>(_ value: T?, forKey key: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
import Foundation

public protocol TodoCategoryService {
func fetchPreferences() async throws -> [TodoCategoryPreferenceResponse]
func updatePreferences(_ preferences: [TodoCategoryPreferenceResponse]) async throws
func fetchCategoryPreferences() async throws -> [TodoCategoryPreferenceResponse]
func updateCategoryPreferences(_ preferences: [TodoCategoryPreferenceResponse]) async throws
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import Foundation

public protocol UserDefaultsStore {
func value<T: Codable>(forKey key: String) -> T?
func setValue<T: Codable>(_ value: T?, forKey key: String)
func removeValues(withPrefix prefix: String)
func string(forKey key: String) -> String?
func setString(_ value: String?, forKey key: String)
func stringArray(forKey key: String) -> [String]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,62 @@ import Combine
import DevLogDomain

final class AuthSessionRepositoryImpl: AuthSessionRepository {
private enum Key {
static let preferences = "TodoCategory.preferences"
}

private let authService: AuthService
private let todoCategoryService: TodoCategoryService
private let store: MemoryCacheStore
private let provider: AuthSessionStateProvider

init(authService: AuthService) {
init(
authService: AuthService,
todoCategoryService: TodoCategoryService,
store: MemoryCacheStore,
provider: AuthSessionStateProvider
) {
self.authService = authService
self.todoCategoryService = todoCategoryService
self.store = store
self.provider = provider
}

func observeSignedIn() -> AnyPublisher<Bool, Never> {
authService.observeSignedIn()
.removeDuplicates()
.map { [self] isSignedIn in
Future { promise in
Task {
if isSignedIn {
await self.cachePreferencesIfNeeded()
} else {
self.clearPreferencesCache()
}
self.provider.publish(isSignedIn)
promise(.success(isSignedIn))
}
}
}
.switchToLatest()
.eraseToAnyPublisher()
}
Comment thread
opficdev marked this conversation as resolved.
}
Comment thread
opficdev marked this conversation as resolved.
Outdated

private extension AuthSessionRepositoryImpl {
func cachePreferencesIfNeeded() async {
if store.value(forKey: Key.preferences) as [TodoCategoryPreferenceResponse]? != nil {
return
}

guard let preferences = try? await todoCategoryService.fetchCategoryPreferences() else {
return
}

store.setValue(preferences, forKey: Key.preferences)
}

func clearPreferencesCache() {
store.setValue(Optional<[TodoCategoryPreferenceResponse]>.none, forKey: Key.preferences)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,22 @@ import DevLogCore
import DevLogDomain

final class PushNotificationRepositoryImpl: PushNotificationRepository {
private enum Key {
static let preferences = "TodoCategory.preferences"
}

private let pushNotificationService: PushNotificationService
private let todoCategoryService: TodoCategoryService
private let store: MemoryCacheStore

init(
pushNotificationService: PushNotificationService,
todoCategoryService: TodoCategoryService
todoCategoryService: TodoCategoryService,
store: MemoryCacheStore
) {
self.pushNotificationService = pushNotificationService
self.todoCategoryService = todoCategoryService
self.store = store
}

/// 푸시 알림 On/Off 설정
Expand Down Expand Up @@ -59,7 +66,7 @@ final class PushNotificationRepositoryImpl: PushNotificationRepository {
do {
let cursorDTO = cursor.map { PushNotificationCursorDTO.fromDomain($0) }
async let responseTask = pushNotificationService.requestNotifications(query, cursor: cursorDTO)
async let preferencesTask = todoCategoryService.fetchPreferences()
async let preferencesTask = todoCategoryPreferenceResponses()

let (response, preferenceResponses) = try await (responseTask, preferencesTask)
return try resolvePage(from: response, with: preferenceResponses.toDomain())
Expand Down Expand Up @@ -91,7 +98,8 @@ final class PushNotificationRepositoryImpl: PushNotificationRepository {

Task {
do {
let preferences = try await self.todoCategoryService.fetchPreferences().toDomain()
let preferences = try await self.todoCategoryPreferenceResponses()
.toDomain()
let page = try self.resolvePage(from: response, with: preferences)
subject.send(page)
} catch {
Expand Down Expand Up @@ -147,6 +155,16 @@ final class PushNotificationRepositoryImpl: PushNotificationRepository {
}

private extension PushNotificationRepositoryImpl {
func todoCategoryPreferenceResponses() async throws -> [TodoCategoryPreferenceResponse] {
if let preferences: [TodoCategoryPreferenceResponse] = store.value(forKey: Key.preferences) {
return preferences
}

let preferences = try await todoCategoryService.fetchCategoryPreferences()
store.setValue(preferences, forKey: Key.preferences)
return preferences
}

func resolvePage(
from response: PushNotificationPageResponse,
with preferences: [TodoCategoryPreference]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,40 @@
import DevLogDomain

final class TodoCategoryRepositoryImpl: TodoCategoryRepository {
private enum Key {
static let preferences = "TodoCategory.preferences"
}

private let todoCategoryService: TodoCategoryService
private let store: MemoryCacheStore

init(todoCategoryService: TodoCategoryService) {
init(
todoCategoryService: TodoCategoryService,
store: MemoryCacheStore
) {
self.todoCategoryService = todoCategoryService
self.store = store
}

func fetchPreferences() async throws -> [TodoCategoryPreference] {
func fetchCategoryPreferences() async throws -> [TodoCategoryPreference] {
do {
return try await todoCategoryService.fetchPreferences().toDomain()
if let preferences: [TodoCategoryPreferenceResponse] = store.value(forKey: Key.preferences) {
return preferences.toDomain()
}

let responses = try await todoCategoryService.fetchCategoryPreferences()
store.setValue(responses, forKey: Key.preferences)
return responses.toDomain()
} catch {
throw error.toDomain()
}
}

func updatePreferences(_ preferences: [TodoCategoryPreference]) async throws {
func updateCategoryPreferences(_ preferences: [TodoCategoryPreference]) async throws {
do {
try await todoCategoryService.updatePreferences(
preferences.map(TodoCategoryPreferenceResponse.fromDomain)
)
let responses = preferences.map(TodoCategoryPreferenceResponse.fromDomain)
try await todoCategoryService.updateCategoryPreferences(responses)
store.setValue(responses, forKey: Key.preferences)
} catch {
throw error.toDomain()
}
Expand Down
58 changes: 45 additions & 13 deletions Application/DevLogData/Sources/Repository/TodoRepositoryImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,26 @@ import DevLogCore
import DevLogDomain

final class TodoRepositoryImpl: TodoRepository {
private enum Key {
static let preferences = "TodoCategory.preferences"
}

private let todoService: TodoService
private let todoCategoryService: TodoCategoryService
private let store: MemoryCacheStore
private let widgetSyncEventBus: WidgetSyncEventBus
private let todoMutationEventBus: TodoMutationEventBus

init(
todoService: TodoService,
todoCategoryService: TodoCategoryService,
store: MemoryCacheStore,
widgetSyncEventBus: WidgetSyncEventBus,
todoMutationEventBus: TodoMutationEventBus
) {
self.todoService = todoService
self.todoCategoryService = todoCategoryService
self.store = store
self.widgetSyncEventBus = widgetSyncEventBus
self.todoMutationEventBus = todoMutationEventBus
}
Expand All @@ -31,11 +38,16 @@ final class TodoRepositoryImpl: TodoRepository {
let responseCursor = cursor.map { TodoCursorDTO.fromDomain($0) }

do {
async let response = todoService.fetchTodos(query, cursor: responseCursor)
async let preferences = todoCategoryService.fetchPreferences()

let (todoResponse, todoPreferenceResponses) = try await (response, preferences)
let userTodoCategories: [UserTodoCategory] = todoPreferenceResponses.toDomain().compactMap { preference in
async let todos = todoService.fetchTodos(query, cursor: responseCursor)
async let preferences = todoCategoryPreferenceResponses()

let (todoResponse, todoPreferenceResponses) = try await (
todos,
preferences
)
let userTodoCategories: [UserTodoCategory] = todoPreferenceResponses
.toDomain()
.compactMap { preference in
guard case .user(let category) = preference.category else {
return nil
}
Expand All @@ -59,10 +71,15 @@ final class TodoRepositoryImpl: TodoRepository {
func fetchTodo(_ todoId: String) async throws -> Todo {
do {
async let response = todoService.fetchTodo(todoId: todoId)
async let preferences = todoCategoryService.fetchPreferences()

let (todoResponse, todoPreferenceResponses) = try await (response, preferences)
let userTodoCategories: [UserTodoCategory] = todoPreferenceResponses.toDomain().compactMap { preference in
async let preferences = todoCategoryPreferenceResponses()

let (todoResponse, todoPreferenceResponses) = try await (
response,
preferences
)
let userTodoCategories: [UserTodoCategory] = todoPreferenceResponses
.toDomain()
.compactMap { preference in
guard case .user(let category) = preference.category else {
return nil
}
Expand All @@ -79,10 +96,15 @@ final class TodoRepositoryImpl: TodoRepository {
func fetchReferences(_ numbers: [Int]) async throws -> [Int: TodoReference] {
do {
async let responseTask = todoService.fetchReferences(numbers)
async let preferencesTask = todoCategoryService.fetchPreferences()

let (responses, preferenceResponses) = try await (responseTask, preferencesTask)
let userTodoCategories: [UserTodoCategory] = preferenceResponses.toDomain().compactMap { preference in
async let preferencesTask = todoCategoryPreferenceResponses()

let (responses, preferenceResponses) = try await (
responseTask,
preferencesTask
)
let userTodoCategories: [UserTodoCategory] = preferenceResponses
.toDomain()
.compactMap { preference in
guard case .user(let category) = preference.category else {
return nil
}
Expand Down Expand Up @@ -149,6 +171,16 @@ final class TodoRepositoryImpl: TodoRepository {
}

private extension TodoRepositoryImpl {
func todoCategoryPreferenceResponses() async throws -> [TodoCategoryPreferenceResponse] {
if let preferences: [TodoCategoryPreferenceResponse] = store.value(forKey: Key.preferences) {
return preferences
}

let preferences = try await todoCategoryService.fetchCategoryPreferences()
store.setValue(preferences, forKey: Key.preferences)
return preferences
}
Comment thread
opficdev marked this conversation as resolved.

func resolve(
_ response: TodoResponse,
userTodoCategories: [UserTodoCategory]
Expand Down
Loading