Skip to content

Conversation

@JinUng41
Copy link
Collaborator

@JinUng41 JinUng41 commented Mar 29, 2025

👻 PULL REQUEST

📄 작업 내용

  • DIContainer의 클로저 저장 방식을 수정하였습니다.
  • 소식 탭의 세그먼트 변화에 따른 앰플리튜드 태그를 추가하였습니다.

💻 주요 코드 설명

DIContainer + Injected 사용 방법

  • 앱이 시작 시, 의존성을 컨테이너에 미리 등록해 놓고 사용하는 곳에서 Injected 프로퍼티 래퍼를 통해 간편하게 의존성을 주입할 수 있습니다.
  • 필요한 의존성은 반드시 앱 시작 시에 모두 등록해야 합니다.
  • 미리 등록하지 않은 타입에 대하여, resolve()를 호출하게 되면 fatalError가 발생하여 앱이 터지기 때문에 주의해야 합니다.
  • App 폴더에 파일(AppDelegate+InjectDependency.swift)를 추가하여 의존성 등록을 할 수 있도록 하였습니다.
AppDelegate+InjectDependency.swift
import Foundation

extension AppDelegate {
    var diContainer: AppDIContainer { AppDIContainer.shared }
    
    func injectDependency() {
        
        // TODO: 객체를 주입
        
    }
}

// AppDelegate에서는..
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        injectDependency() // 앱이 시작할 때, 의존성 등록
        
        KakaoSDK.initSDK(appKey: Bundle.kakaoAppKey)
        
        return true
    }
// 생략..
}
  • "Repository -> UseCase -> ViewModel -> ViewController"의 의존 관계에서 의존성 등록을 어디까지 할 것인가에 대한 논의가 필요해 보입니다.
  • '객체의 생명이 긴가', '테스트를 위한 Mock 객체로 대체할 수 있어야 하는가' 등의 기준을 가지고 의견을 나눠보면 좋을 것 같습니다.
  • 아래는 DIContainer와 Injected 사용 예시입니다.
DIContainer+Injected 사용 예시
import Foundation

// MARK: - DependencyContainer

protocol DependencyRegistable {
    func register<T>(for type: T.Type, object: T)
    func register<T>(for type: T.Type, _ resolver: @escaping (DependencyResolvable) -> T)
    func unregister<T>(for type: T.Type)
}

protocol DependencyResolvable {
    func resolve<T>(for type: T.Type) -> T
}

typealias DependencyContainer = DependencyRegistable & DependencyResolvable

// MARK: - AppDIContainer

final class AppDIContainer {
    static let shared = AppDIContainer()
    
    private var dependencies = [String: Any]()
    
    private init() {}
}

extension AppDIContainer: DependencyContainer {
    func register<T>(for type: T.Type, object: T) {
        dependencies[key(type)] = object
    }
    
    func register<T>(for type: T.Type, _ resolver: @escaping (any DependencyResolvable) -> T) {
        dependencies[key(type)] = resolver
    }
    
    
    func unregister<T>(for type: T.Type) {
        dependencies.removeValue(forKey: key(type))
    }
    
    func resolve<T>(for type: T.Type) -> T {
        let key = key(type)
        
        if let resolver = dependencies[key] as? (any DependencyResolvable) -> T {
            return resolver(self)
        } else if let object = dependencies[key] as? T {
            return object
        } else {
            fatalError("No dependency registered for \(key)")
        }
    }
    
    private func key<T>(_ type: T.Type) -> String {
        return String(describing: type)
    }
}

@propertyWrapper
struct Injected<T> {
    private var dependency: T
    
    init() {
        self.dependency = AppDIContainer.shared.resolve(for: T.self)
    }
    
    var wrappedValue: T {
        get { dependency }
    }
}

struct NetworkService {
    private let session: URLSession
    
    init(session: URLSession = .shared) {
        self.session = session
    }
}

struct Repository {
    private let network: NetworkService
    
    init(network: NetworkService) {
        self.network = network
    }
}

struct UseCase {
    @Injected var repository: Repository // Injected 프로퍼티 래퍼를 사용하여 간단하게 의존성을 주입
    
    init() { // 생성자에서 repository를 주입해야 할 필요 X
        print("객체 생성 완")
    }
}

// 미리 의존성을 등록
func register() {
    AppDIContainer.shared.register(for: NetworkService.self, object: NetworkService())
    
    AppDIContainer.shared.register(for: Repository.self) { resolver in
        let network = resolver.resolve(for: NetworkService.self)
        return Repository(network: network)
    }
}

🔗 연결된 이슈

Summary by CodeRabbit

  • New Features

    • Enabled automatic tracking of page navigation events to enhance analytics insights.
  • Refactor

    • Upgraded the app’s launch process with improved dependency setup for smoother initialization.
    • Simplified dependency registration logic to boost overall stability.

@JinUng41 JinUng41 added 🛠️ fix 기능적 버그나 오류 해결 시 사용 🍻 진웅 술 한잔 가온나~ labels Mar 29, 2025
@JinUng41 JinUng41 added this to the 리팩토링 마감 milestone Mar 29, 2025
@JinUng41 JinUng41 requested a review from youz2me March 29, 2025 04:57
@JinUng41 JinUng41 self-assigned this Mar 29, 2025
@coderabbitai
Copy link

coderabbitai bot commented Mar 29, 2025

Walkthrough

This pull request integrates a new dependency injection mechanism and analytics tracking into the iOS application. A new file is added to extend the AppDelegate with DI logic, and the project file is updated to include it. The DIContainer registration has been simplified to remove unnecessary nil-checks. Additionally, the app launch sequence now calls the injection method and the Overview page controller is updated to track page change events for analytics.

Changes

File(s) Change Summary
Wable-iOS.xcodeproj/project.pbxproj Added new build and file references for AppDelegate+InjectDependency.swift and updated group and build phase entries.
Wable-iOS/App/AppDelegate+InjectDependency.swift
Wable-iOS/App/AppDelegate.swift
Introduced a new Swift file extending AppDelegate with a diContainer property and injectDependency() method; updated AppDelegate launch sequence to invoke dependency injection.
Wable-iOS/Core/DI/DIContainer.swift Simplified the dependency registration logic by removing the unnecessary closure wrapping and nil-check of self.
Wable-iOS/Presentation/.../OverviewPageViewController.swift Added a property observer on currentIndex that triggers trackPageChangeEvent(for:) to track page change events via a switch statement.

Sequence Diagram(s)

sequenceDiagram
    participant AppDelegate
    participant Injector as "AppDelegate+InjectDependency"
    participant DIContainer
    participant App as "Application Lifecycle"

    App->>AppDelegate: Launch Application
    AppDelegate->>Injector: Call injectDependency()
    Injector->>DIContainer: Utilize dependency registration
    DIContainer-->>Injector: Return DI container
    Injector-->>AppDelegate: Dependencies injected
Loading
sequenceDiagram
    participant VC as "OverviewPageViewController"
    participant Tracker as "trackPageChangeEvent()"
    participant Analytics as "Analytics Module"

    VC->>Tracker: currentIndex updated (new value ≠ old value)
    Tracker->>Analytics: Switch-case triggers event tracking (indices 0-3)
    Analytics-->>Tracker: Event logged
Loading

Assessment against linked issues

Objective Addressed Explanation
Modify DIContainer registration logic [#149]
Update amplitude tags for news tab segment [#149] Added event tracking logic in page change flow

Poem

I'm a rabbit in the code, so spry and fleet,
Hopping through files with each little beat.
DI flows smooth and pages now sing,
Analytics dance, oh what joy they bring!
With each hop, our app finds its groove—
A playful leap forward in every move!

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@JinUng41 JinUng41 moved this to In Review in Wable-iOS Mar 29, 2025
@JinUng41 JinUng41 moved this from In Review to Ready for Review in Wable-iOS Mar 29, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🔭 Outside diff range comments (1)
Wable-iOS.xcodeproj/project.pbxproj (1)

1071-1813: 💡 Verification agent

🧩 Analysis chain

Verify the implementation of the dependency injection mechanism.

The PR mentions that all dependencies must be registered at app launch to prevent crashes when resolving unregistered types. Since we can't see the actual implementation of AppDelegate+InjectDependency.swift, ensure it handles edge cases appropriately.


🏁 Script executed:

#!/bin/bash
# Description: Check implementation of the new AppDelegate+InjectDependency.swift file

# Check how DIContainer and injectDependency are implemented
echo "Checking AppDelegate+InjectDependency.swift implementation..."
cat "Wable-iOS/App/AppDelegate+InjectDependency.swift"

# Check how the injection is called in AppDelegate
echo -e "\n\nChecking AppDelegate.swift for injection method call..."
rg -A 3 -B 3 "injectDependency" "Wable-iOS/App/AppDelegate.swift"

# Check for Amplitude tags in news tab
echo -e "\n\nChecking for Amplitude tags implementation in news tab..."
rg -A 3 "Amplitude" "Wable-iOS/Presentation/Overview/News/View/NewsViewController.swift" || echo "No direct Amplitude references found in NewsViewController"
rg -A 3 "track" "Wable-iOS/Presentation/Overview/News/View/NewsViewController.swift" || echo "No tracking method calls found in NewsViewController"
rg "OverviewPageViewController" "Wable-iOS/Presentation/Overview/Page/View/OverviewPageViewController.swift" -A 10 | rg -e "Amplitude|track|segment" || echo "No tracking references found in OverviewPageViewController"

Length of output: 2009


Dependency Injection Implementation Requires Completion

  • The file Wable-iOS/App/AppDelegate+InjectDependency.swift still contains only a placeholder (// TODO: 객체를 주입) and does not register any dependencies.
  • While injectDependency() is correctly called in AppDelegate.swift, the lack of an actual implementation could lead to unresolved types at runtime.
  • Additionally, although no direct Amplitude references are found in NewsViewController.swift, tracking calls do exist in OverviewPageViewController.swift. Please ensure that the DI mechanism and event tracking integrations are consistent across the app.

Please implement the dependency registration logic to fully initialize the DI container and handle potential edge cases.

🧹 Nitpick comments (2)
Wable-iOS/App/AppDelegate.swift (1)

35-35: Empty implementation for didDiscardSceneSessions

The method has been simplified to an empty implementation. While this is valid since the method is optional, consider adding a comment explaining why no implementation is needed, or restore the previous comments if they provided useful context.

-   func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {}
+   func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
+       // This method is called when the user discards a scene session.
+       // No additional handling needed at this time.
+   }
Wable-iOS/Presentation/Overview/Page/View/OverviewPageViewController.swift (1)

204-217: Amplitude tracking implementation meets requirements

The implementation properly tracks different events based on the selected tab index. This fulfills the PR objective of adding Amplitude tags for tracking segment changes in the news tab.

Two suggestions to improve robustness:

func trackPageChangeEvent(for index: Int) {
+   // Map index to tab name for more readable tracking
+   let tabNames = ["gameschedule", "ranking", "news", "announcement"]
+   
+   guard index >= 0 && index < tabNames.count else { return }
+   
+   AmplitudeManager.shared.trackEvent(tag: "click_\(tabNames[index])")

-   switch index {
-   case 0:
-       AmplitudeManager.shared.trackEvent(tag: "click_gameschedule")
-   case 1:
-       AmplitudeManager.shared.trackEvent(tag: "click_ranking")
-   case 2:
-       AmplitudeManager.shared.trackEvent(tag: "click_news")
-   case 3:
-       AmplitudeManager.shared.trackEvent(tag: "click_announcement")
-   default:
-       break
-   }
}

This approach is more maintainable as it centralizes the tab names in one place and adds a safety check for index bounds.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 41f9c6e and 8e1adcf.

📒 Files selected for processing (5)
  • Wable-iOS.xcodeproj/project.pbxproj (4 hunks)
  • Wable-iOS/App/AppDelegate+InjectDependency.swift (1 hunks)
  • Wable-iOS/App/AppDelegate.swift (2 hunks)
  • Wable-iOS/Core/DI/DIContainer.swift (1 hunks)
  • Wable-iOS/Presentation/Overview/Page/View/OverviewPageViewController.swift (2 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
Wable-iOS/App/AppDelegate.swift (1)
Wable-iOS/App/AppDelegate+InjectDependency.swift (1)
  • injectDependency (13-17)
🪛 SwiftLint (0.57.0)
Wable-iOS/App/AppDelegate+InjectDependency.swift

[Warning] 15-15: TODOs should be resolved (객체를 주입)

(todo)

🔇 Additional comments (7)
Wable-iOS/App/AppDelegate+InjectDependency.swift (1)

10-12: Computation property diContainer looks good

The property correctly returns the shared instance of the AppDIContainer, making it easily accessible throughout the AppDelegate.

Wable-iOS/Core/DI/DIContainer.swift (1)

39-41: Simplified dependency registration looks good

The dependency registration has been simplified by directly assigning the resolver closure to the dependencies dictionary without unnecessary wrapping. This approach is cleaner and reduces complexity.

The change aligns with the PR's objective to revise how closures are stored in the DIContainer.

Wable-iOS/App/AppDelegate.swift (1)

18-19: Dependency injection at startup looks good

Adding the injectDependency() call before initializing the Kakao SDK ensures that all dependencies are properly registered at app launch.

Wable-iOS/Presentation/Overview/Page/View/OverviewPageViewController.swift (1)

16-21: Observer for tracking page changes looks good

The property observer effectively triggers the tracking function whenever the current index changes, with a guard clause to prevent unnecessary tracking when the index hasn't changed.

Wable-iOS.xcodeproj/project.pbxproj (3)

217-217: New file added to handle dependency injection.

This addition of AppDelegate+InjectDependency.swift matches the PR objective of modifying the DIContainer. Using an extension on AppDelegate is a clean approach to separate dependency injection concerns from the main AppDelegate implementation.


459-459: File reference added for AppDelegate+InjectDependency.

This file reference properly includes the new extension file in the project's file references section, ensuring it's properly tracked by Xcode.


1071-1071: Extension file appropriately added to App group.

The AppDelegate+InjectDependency.swift file is correctly placed in the App group alongside the main AppDelegate, maintaining good project organization. This follows best practices for organizing related files together.

Comment on lines +13 to +17
func injectDependency() {

// TODO: 객체를 주입

}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Implement the TODO for dependency injection

The method is currently empty with only a TODO comment. According to the PR description, this method should register all necessary dependencies at startup to avoid application crashes from unregistered dependencies.

func injectDependency() {
    
-   // TODO: 객체를 주입
+   // Register your dependencies here
+   // Example:
+   // diContainer.register(for: SomeService.self) { _ in
+   //     SomeServiceImplementation()
+   // }
    
}
📝 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 injectDependency() {
// TODO: 객체를 주입
}
func injectDependency() {
// Register your dependencies here
// Example:
// diContainer.register(for: SomeService.self) { _ in
// SomeServiceImplementation()
// }
}
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 15-15: TODOs should be resolved (객체를 주입)

(todo)

Copy link
Member

@youz2me youz2me left a comment

Choose a reason for hiding this comment

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

고생하셨습니닷. 저도 이제 앰플리튜드를 슬슬 붙여야 하는데 진웅님 코드가 많은 도움이 될 것 같습니닷 ㅎㅎ

"Repository -> UseCase -> ViewModel -> ViewController"의 의존 관계에서 의존성 등록을 어디까지 할 것인가에 대한 논의

-> 이 부분에 대해서는 DIContainer를 이용한 의존성 주입을 해본 경험이 없어서 부가적으로 설명을 듣고 싶은데요 ..!!! 내일 회의 시간에 같이 이야기해보면 좋을 것 같습니다.

고생하셨습니다 🙇‍♀️

@@ -0,0 +1,18 @@
//
// AppDelegate+InjectDependency.swift
Copy link
Member

Choose a reason for hiding this comment

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

오... 보통 네이밍을 이런식으로 많이 짓나요? 처음 보는 네이밍이어서 여쭤봅니다요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

어떻게 해야할 지 저도 몰라서 우선 저렇게 지어보었습니다. ㅎㅎ

Copy link
Member

Choose a reason for hiding this comment

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

앗 ㅎㅎ 확인했습니닷

Comment on lines +204 to +217
func trackPageChangeEvent(for index: Int) {
switch index {
case 0:
AmplitudeManager.shared.trackEvent(tag: "click_gameschedule")
case 1:
AmplitudeManager.shared.trackEvent(tag: "click_ranking")
case 2:
AmplitudeManager.shared.trackEvent(tag: "click_news")
case 3:
AmplitudeManager.shared.trackEvent(tag: "click_announcement")
default:
break
}
}
Copy link
Member

Choose a reason for hiding this comment

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

💯

@github-project-automation github-project-automation bot moved this from Ready for Review to In Review in Wable-iOS Mar 30, 2025
@JinUng41 JinUng41 merged commit 8200b2d into develop Mar 30, 2025
@github-project-automation github-project-automation bot moved this from In Review to Done in Wable-iOS Mar 30, 2025
@JinUng41 JinUng41 deleted the fix/#149-dicontainer-overview-tag branch March 30, 2025 08:34
@coderabbitai coderabbitai bot mentioned this pull request May 28, 2025
youz2me pushed a commit that referenced this pull request Oct 26, 2025
[Fix] DIContainer 수정 및 소식 탭 앰플리튜드 태그 추가
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍻 진웅 술 한잔 가온나~ 🛠️ fix 기능적 버그나 오류 해결 시 사용

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Fix] DIContainer 수정 및 소식 탭 앰플리튜드 태그 추가

3 participants