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
8 changes: 7 additions & 1 deletion Cherrish-iOS/Cherrish-iOS/App/Cherrish_iOSApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ import SwiftUI

@main
struct Cherrish_iOSApp: App {

init() {
// TODO: ์ฝ”๋””๋„ค์ดํ„ฐ์—์„œ ์‹คํ–‰ํ•˜๊ธฐ
DIContainer.shared.dependencyInjection()
}

var body: some Scene {
WindowGroup {
ContentView()
ViewFactory.makeTestView()
}
}
}
18 changes: 18 additions & 0 deletions Cherrish-iOS/Cherrish-iOS/App/DIContainer+.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// AppDIContainer+.swift
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue | ๐ŸŸก Minor

File name mismatch in header comment.

The header comment references "AppDIContainer+.swift" but the actual filename is "DIContainer+.swift".

๐Ÿ”Ž Proposed fix
-//  AppDIContainer+.swift
+//  DIContainer+.swift
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// AppDIContainer+.swift
// DIContainer+.swift
๐Ÿค– Prompt for AI Agents
In @Cherrish-iOS/Cherrish-iOS/App/DIContainer+.swift around line 2, The file
header comment currently says "AppDIContainer+.swift" but the actual file is
named "DIContainer+.swift"; update the top-of-file header comment to match the
real filename by replacing "AppDIContainer+.swift" with "DIContainer+.swift" (or
conversely rename the file if the header is authoritative), ensuring the header
and actual filename are consistent.

// Cherrish-iOS
//
// Created by ์ด๋‚˜์—ฐ on 1/5/26.
//

import Foundation

extension DIContainer {
func dependencyInjection() {
let dataDependencyAssembler = DataDependencyAssembler()
let domainDependencyAssembler = DomainDependencyAssembler(preAssembler: dataDependencyAssembler)
let presentationDependencyAssembler = PresentationDependencyAssembler(preAssembler: domainDependencyAssembler)

presentationDependencyAssembler.assemble()
}
}
30 changes: 29 additions & 1 deletion Cherrish-iOS/Cherrish-iOS/Core/DIContainer.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,35 @@
//
// DIContainer.swift
// AppDIContainer.swift
// Cherrish-iOS
//
// Created by ์ด๋‚˜์—ฐ on 12/31/25.
//

import Foundation

protocol DependencyAssembler {
func assemble()
}

final class DIContainer {

static let shared = DIContainer()

private init() { }

private var dependencies: [String: () -> Any] = [:]

func register<T>(type: T.Type, closure: @escaping () -> T) {
let key = String(describing: T.self)
dependencies[key] = closure
}
Comment on lines +20 to +25
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue | ๐Ÿ”ด Critical

Add thread-safety to prevent data races.

The dependencies dictionary is mutated in register and accessed in resolve without any synchronization. If multiple threads call these methods concurrently (e.g., during app initialization with async tasks, or from background threads), this can lead to data races and crashes.

๐Ÿ”Ž Proposed fix using a serial queue or lock
 final class DIContainer {
     
     static let shared = DIContainer()
     
     private init() { }
     
+    private let queue = DispatchQueue(label: "com.cherrish.dicontainer", attributes: .concurrent)
     private var dependencies: [String: () -> Any] = [:]
     
     func register<T>(type: T.Type, closure: @escaping () -> T) {
         let key = String(describing: T.self)
-        dependencies[key] = closure
+        queue.async(flags: .barrier) { [weak self] in
+            self?.dependencies[key] = closure
+        }
     }
     
     func resolve<T>(type: T.Type) -> T? {
         let key = String(describing: T.self)
-        guard let closure = dependencies[key] else {
-            return nil
-        }
-        
-        return closure() as? T
+        return queue.sync {
+            guard let closure = dependencies[key] else {
+                return nil
+            }
+            return closure() as? T
+        }
     }
 }

Committable suggestion skipped: line range outside the PR's diff.

๐Ÿค– Prompt for AI Agents
In @Cherrish-iOS/Cherrish-iOS/Core/DIContainer.swift around lines 20-25, The
dependencies dictionary is not thread-safe: mutations in register(type:closure:)
and reads in resolve can race; protect access to the private dependencies by
adding a synchronization primitive (e.g., a private serial DispatchQueue or an
NSLock) and wrap all reads and writes to dependencies with that lock/queue (use
sync for reads and async/sync or barrier for writes if using a concurrent
queue). Update register(type:closure:) and resolve to use this synchronization
so all accesses to dependencies are serialized and data races are prevented.


func resolve<T>(type: T.Type) -> T? {
let key = String(describing: T.self)
guard let closure = dependencies[key] else {
return nil
}

return closure() as? T
}
}
16 changes: 16 additions & 0 deletions Cherrish-iOS/Cherrish-iOS/Data/DataDependencyAssembler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// DataDependencyAssembler.swift
// Cherrish-iOS
//
// Created by ์ด๋‚˜์—ฐ on 1/5/26.
//

import Foundation

final class DataDependencyAssembler: DependencyAssembler {
func assemble() {
DIContainer.shared.register(type: TestInterface.self) {
return DefaultTestRepository()
}
}
}
8 changes: 0 additions & 8 deletions Cherrish-iOS/Cherrish-iOS/Data/Repository/Repository.swift

This file was deleted.

14 changes: 14 additions & 0 deletions Cherrish-iOS/Cherrish-iOS/Data/Repository/TestRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// Repository.swift
// Cherrish-iOS
//
// Created by ์ด๋‚˜์—ฐ on 1/3/26.
//

import Foundation

struct DefaultTestRepository: TestInterface {
func test() {
print("Repository Test!")
}
}
28 changes: 28 additions & 0 deletions Cherrish-iOS/Cherrish-iOS/Domain/DomainDependencyAssembler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// DomainDependencyAssembler.swift
// Cherrish-iOS
//
// Created by ์ด๋‚˜์—ฐ on 1/5/26.
//

import Foundation

final class DomainDependencyAssembler: DependencyAssembler {
private let preAssembler: DependencyAssembler

init(preAssembler: DependencyAssembler) {
self.preAssembler = preAssembler
}

func assemble() {
preAssembler.assemble()

guard let testRepository = DIContainer.shared.resolve(type: TestInterface.self) else {
return
}

DIContainer.shared.register(type: TestUseCase.self) {
return DefaultTestUseCase(repository: testRepository)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@
//

import Foundation

protocol TestInterface {
func test()
}
25 changes: 25 additions & 0 deletions Cherrish-iOS/Cherrish-iOS/Domain/UseCase/TestUseCase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// Usecase.swift
// Cherrish-iOS
//
// Created by ์ด๋‚˜์—ฐ on 1/3/26.
//

import Foundation

protocol TestUseCase {
func execute()
}

struct DefaultTestUseCase: TestUseCase {
private let repository: TestInterface

init(repository: TestInterface) {
self.repository = repository
}

func execute() {
self.repository.test()
print("UseCase execute")
}
}
8 changes: 0 additions & 8 deletions Cherrish-iOS/Cherrish-iOS/Domain/UseCase/Usecase.swift

This file was deleted.

17 changes: 0 additions & 17 deletions Cherrish-iOS/Cherrish-iOS/Presentation/Feature/ContentView.swift

This file was deleted.

23 changes: 23 additions & 0 deletions Cherrish-iOS/Cherrish-iOS/Presentation/Feature/TestView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// ContentView.swift
Comment on lines +1 to +2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue | ๐ŸŸก Minor

Fix the file header comment mismatch.

The header comment references "ContentView.swift" but the actual file is "TestView.swift". This inconsistency can cause confusion during code navigation and maintenance.

๐Ÿ”Ž Proposed fix
 //
-//  ContentView.swift
+//  TestView.swift
 //  Cherrish-iOS
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//
// ContentView.swift
//
// TestView.swift
๐Ÿค– Prompt for AI Agents
In @Cherrish-iOS/Cherrish-iOS/Presentation/Feature/TestView.swift around lines
1-2, Update the file header comment to match the actual filename by replacing
the incorrect "ContentView.swift" text with "TestView.swift" in the top-of-file
comment; ensure any other header metadata (if present) also reflects
TestView.swift so the header is consistent with the file and class/struct names
in TestView.

// Cherrish-iOS
//
// Created by ์ด๋‚˜์—ฐ on 12/31/25.
//

import SwiftUI

struct TestView: View {
@ObservedObject var viewModel: TestViewModel

var body: some View {
VStack {
Button(action: {
viewModel.test()
}) {
Text("\(viewModel.text)")
}
.padding()
}
}
}
23 changes: 23 additions & 0 deletions Cherrish-iOS/Cherrish-iOS/Presentation/Feature/TestViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// TestViewModel.swift
// Cherrish-iOS
//
// Created by ์ด๋‚˜์—ฐ on 1/5/26.
//

import Foundation

final class TestViewModel: ObservableObject {
@Published var text: String = "ํ„ฐ์น˜ํ•ด๋ณด์„ธ์—ฌ"
private let testUseCase: TestUseCase

init(testUseCase: TestUseCase) {
self.testUseCase = testUseCase
}

func test() {
testUseCase.execute()
text = "๋ฒ„ํŠผ ํ„ฐ์น˜ํ–ˆ์Œ!"
print("view model execute")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// PresentationDependencyAssembler.swift
// Cherrish-iOS
//
// Created by ์ด๋‚˜์—ฐ on 1/5/26.
//

import Foundation

final class PresentationDependencyAssembler: DependencyAssembler {
private let preAssembler: DependencyAssembler

init(preAssembler: DependencyAssembler) {
self.preAssembler = preAssembler
}

func assemble() {
preAssembler.assemble()

guard let testUseCase = DIContainer.shared.resolve(type: TestUseCase.self) else {
return
}

DIContainer.shared.register(type: TestViewModel.self) {
print("๋ทฐ๋ชจ๋ธ ๋“ฑ๋ก")
return TestViewModel(testUseCase: testUseCase)
}
Comment on lines +17 to +27
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๐Ÿ› ๏ธ Refactor suggestion | ๐ŸŸ  Major

Remove debug print statement before production.

The print statement on line 25 should be removed or replaced with proper logging infrastructure. Debug prints are not suitable for production code.

๐Ÿ”Ž Proposed fix
 DIContainer.shared.register(type: TestViewModel.self) {
-    print("๋ทฐ๋ชจ๋ธ ๋“ฑ๋ก")
     return TestViewModel(testUseCase: testUseCase)
 }
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func assemble() {
preAssembler.assemble()
guard let testUseCase = DIContainer.shared.resolve(type: TestUseCase.self) else {
return
}
DIContainer.shared.register(type: TestViewModel.self) {
print("๋ทฐ๋ชจ๋ธ ๋“ฑ๋ก")
return TestViewModel(testUseCase: testUseCase)
}
func assemble() {
preAssembler.assemble()
guard let testUseCase = DIContainer.shared.resolve(type: TestUseCase.self) else {
return
}
DIContainer.shared.register(type: TestViewModel.self) {
return TestViewModel(testUseCase: testUseCase)
}
๐Ÿค– Prompt for AI Agents
In @Cherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swift
around lines 17-27, In assemble(), remove the debug print statement inside the
DIContainer.shared.register closure (the line that prints "๋ทฐ๋ชจ๋ธ ๋“ฑ๋ก"); instead
either delete it or replace it with a call to the app logging facility (e.g.,
Logger.log or similar) if persistent logs are required; ensure the closure still
returns TestViewModel(testUseCase: testUseCase) and keep the guard resolving
TestUseCase unchanged.

}


}
22 changes: 22 additions & 0 deletions Cherrish-iOS/Cherrish-iOS/Presentation/ViewFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// ViewFactory.swift
// Cherrish-iOS
//
// Created by ์ด๋‚˜์—ฐ on 1/5/26.
//

import Foundation

protocol ViewFactoryProtocol {
static func makeTestView() -> TestView
}

final class ViewFactory: ViewFactoryProtocol {
static func makeTestView() -> TestView {
guard let viewModel = DIContainer.shared.resolve(type: TestViewModel.self) else {
// TODO: DI ์‹คํŒจ ์‹œ ๊ธฐ๋ณธ์œผ๋กœ ๊ฐˆ ๊ณณ ์ง€์ •
fatalError()
}
return TestView(viewModel: viewModel)
}
Comment on lines +15 to +21
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue | ๐ŸŸ  Major

Replace fatalError with graceful degradation.

Using fatalError() when DI resolution fails will crash the app. Since this factory is called during app initialization, a crash here provides a poor user experience. Consider:

  1. Logging the error for debugging
  2. Returning a default/fallback view
  3. Throwing an error that can be caught at the app level

This aligns with the TODO comment about specifying a default destination.

Would you like me to generate an implementation that provides graceful degradation with proper error logging?

๐Ÿงฐ Tools
๐Ÿช› SwiftLint (0.57.0)

[Warning] 17-17: TODOs should be resolved (DI ์‹คํŒจ ์‹œ ๊ธฐ๋ณธ์œผ๋กœ ๊ฐˆ ๊ณณ ์ง€์ •)

(todo)

๐Ÿค– Prompt for AI Agents
In @Cherrish-iOS/Cherrish-iOS/Presentation/ViewFactory.swift around lines 15-21,
Replace the fatalError in makeTestView: when DIContainer.shared.resolve(type:
TestViewModel.self) returns nil, log the failure (using the app logger or
print), and return a safe fallback TestView instance instead of crashing;
implement this by either creating a lightweight DefaultTestViewModel (or a
TestViewModel.mock()/initWithDefaults()) and returning TestView(viewModel:
defaultVM), or add a TestView.init() convenience initializer that creates an
empty/placeholder UI and return that; update makeTestView to use the fallback
path and keep the method signature unchanged so callers arenโ€™t forced to handle
throws.

}