Skip to content

Conversation

@gnksbm
Copy link
Contributor

@gnksbm gnksbm commented Jul 28, 2025

작업내용

리뷰요청

관련 이슈

close #334

Summary by CodeRabbit

  • 신규 기능

    • 스플래시 화면 및 강제 업데이트 알림 로직이 추가되었습니다.
    • 홈, 알람, 근처지도 등 주요 기능의 코디네이터와 빌더 프로토콜이 도입되었습니다.
    • Info.plist 값 접근을 위한 프로퍼티 래퍼가 추가되고, 주요 설정 및 버전 정보가 동적으로 반영됩니다.
    • CoreData 기반 즐겨찾기, 버스 도착정보 등 비동기 저장소 및 스트림 기능이 추가되었습니다.
  • 기능 개선 및 리팩터링

    • RxSwift 기반 로직이 Swift Concurrency(Async/Await)로 대체되었습니다.
    • 코디네이터 구조가 빌더 패턴으로 분리되어 확장성과 테스트 용이성이 향상되었습니다.
    • 강제 업데이트, 버전 체크 등 도메인 모델 및 유즈케이스가 enum 및 구조체 기반으로 단순화되었습니다.
    • Info.plist, UserDefaults 접근 방식이 래퍼로 일원화되었습니다.
  • 버그 수정 및 개선

    • 중복 키 발생 시 사전 생성 오류 방지 등 일부 데이터 처리 방식 개선.
    • URL 생성 및 네트워크 요청시 명확한 에러 핸들링 도입.
  • 문서 및 스타일

    • 각종 파일 및 프로토콜에 헤더 주석, 불필요한 공백 제거 등 코드 스타일 정비가 이루어졌습니다.
  • 기타

    • 기존 사용되지 않는 프로토콜, 구현체, enum 등 다수의 불필요한 코드가 제거되었습니다.
    • 테스트 및 샘플 앱 구성이 리팩터링되어 유지보수성이 향상되었습니다.

@gnksbm gnksbm requested review from MUKER-WON and isakatty July 28, 2025 14:38
@gnksbm gnksbm self-assigned this Jul 28, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 28, 2025

Warning

Rate limit exceeded

@gnksbm has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 8 minutes and 35 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 800fc63 and 823451f.

📒 Files selected for processing (3)
  • Projects/App/Sources/Coordinator/Splash/SplashCoordinator.swift (1 hunks)
  • Projects/App/Sources/Coordinator/Splash/SplashViewController.swift (1 hunks)
  • Projects/App/Sources/DefaultCoordinatorProvider.swift (2 hunks)

Walkthrough

이번 변경은 프로젝트 전반에 걸쳐 대규모 구조 리팩터링 및 비동기/동기화 API 전환, 의존성 주입 패턴 개선, Feature/Coordinator/Repository/UseCase 계층의 분리 및 테스트 지원 강화, Info.plist 접근 방식 개선, RxSwift 기반 코드의 점진적 제거, 그리고 여러 Feature 모듈의 인터페이스/구현/테스팅 분리 등 다양한 영역을 포함합니다. 일부 레거시 코드와 프로토콜, 타입이 제거되었으며, 새로운 비동기 및 타입 안전 API가 도입되었습니다.

Changes

Cohort / File(s) Change Summary
CoreDataService 신규 도입 및 리팩터링
Projects/CoreDataService/Sources/CoreDataContainerBuilder.swift, .../CoreDataModel.swift, .../CoreDataStorage.swift, .../CoreDataStorageImpl.swift, .../Legacy/CoreDataService.swift
CoreData 스토리지 추상화 및 구현체, 모델 프로토콜 추가. 기존 CoreDataService 프로토콜 deprecated 처리.
Feature/Coordinator 구조 분리 및 빌더 패턴 도입
Projects/FeatureDependency/Sources/Coordinator/Coordinator.swift, .../CoordinatorProvider.swift, .../CoordinatorType.swift, .../AddRegularAlarmCoordinator.swift, .../HomeCoordinator.swift, .../NearMapCoordinator.swift, .../SearchCoordinator.swift, .../BusStopCoordinator.swift, .../Mock/MockCoordinator.swift, .../Mock/MockCoordinatorProvider.swift, Projects/Feature/AlarmFeature/Interface/Sources/AddRegularAlarmCoordinator.swift, .../HomeFeature/Interface/Sources/HomeCoordinator.swift, .../NearMapFeature/Interface/Sources/NearMapCoordinator.swift
CoordinatorType enum 및 일부 Coordinator 프로토콜 제거, 각 Feature 별 Coordinator/Builder 프로토콜 신설, delegate 및 openURL 도입, 빌더 패턴으로 생성 책임 분리.
Feature 모듈 분할 및 의존성 명확화
Projects/Feature/AlarmFeature/Project.swift, .../HomeFeature/Project.swift, .../BusStopFeature/Project.swift, .../NearMapFeature/Project.swift, .../SearchFeature/Project.swift
Feature별로 Interface/Implement/Testing(Target) 분리, 샘플앱/테스트앱 의존성 명확화, Project.swift 구조 리팩터링.
비동기/동기화 API 도입 및 RxSwift 점진적 제거
Projects/Domain/Sources/RepositoryInterface/BusStopArrivalInfoRepository.swift, .../FavoritesRepository.swift, .../VersionCheckRepository.swift, .../RegularAlarmRepository.swift, Projects/Data/Sources/Repository/DefaultBusStopArrivalInfoRepository.swift, .../DefaultFavoritesRepository.swift, .../DefaultVersionCheckRepository.swift, Projects/Domain/Sources/UseCase/DefaultVersionCheckUseCase.swift, .../Protocol/VersionCheckUseCase.swift, Projects/NetworkService/Sources/NetworkService/NetworkService.swift, .../DefaultNetworkService.swift
주요 Repository/UseCase/Service에 async/await API 추가, 기존 Rx 기반 API deprecated 및 제거, 테스트용 Mock에도 비동기 메서드 추가.
Info.plist 타입 안전 접근 개선
Projects/Core/Sources/PropertyWrapper/InfoPlistWrapper.swift, .../Extension/String+.swift, Projects/Domain/Sources/Entity/Response/AppVersionInfoResponse.swift, Projects/Feature/SettingsFeature/Sources/ViewModel/SettingsViewModel.swift, Projects/SceneDelegate.swift
InfoPlistWrapper 프로퍼티 래퍼 및 InfoPlistLoadable 도입, 기존 번들 직접 접근 코드 제거, Info.plist 값 타입 안전하게 주입.
Coordinator/DI/앱 진입점 리팩터링
Projects/App/Sources/Coordinator/AppCoordinator.swift, .../Splash/SplashCoordinator.swift, .../Splash/SplashViewController.swift, .../Splash/SplashViewModel.swift, .../DefaultCoordinatorProvider.swift, .../SceneDelegate.swift, .../AppDelegate.swift, .../AppDelegate+Register.swift
AppCoordinator 및 SplashCoordinator 신설, DI 패턴 개선, 앱 진입점에서 DI 등록/버전체크 흐름 개선, AppDelegate+Register 제거.
도메인/엔티티/유틸리티 확장
Projects/Domain/Sources/Entity/Alert.swift, .../ForceUpdate.swift, .../Response/AppVersionInfoResponse.swift, .../VersionCheckInfo.swift, .../Response/FavoritesBusResponse.swift, .../Response/BusStopArrivalInfoResponse.swift, .../DTO/BusStopArrivalInfoDTO.swift, Projects/Core/Sources/TypeBuilder.swift, .../Extension/TimeInterval+.swift
Alert, ForceUpdate 등 도메인 엔티티 신설/확장, 버전 정보 파싱 및 ForceUpdate Enum화, 타입 빌더, TimeInterval 유틸 추가.
Feature 구현/테스트 지원 파일 추가
Projects/Feature/AlarmFeature/Implement/Sources/Dependencies.swift, .../Testing/Sources/Dependencies.swift, .../Testing/Sources/MockAddRegularAlarmCoordinatorBuilder.swift, Projects/Feature/HomeFeature/Implement/Sources/Dependencies.swift, Projects/Feature/NearMapFeature/Testing/Sources/Dependencies.swift, .../Testing/Sources/MockNearMapCoordinatorBuilder.swift
Feature별 구현/테스트용 Dependencies 및 Mock 빌더 클래스 추가, 인터페이스 모듈 재export.
기타 리팩터링/포맷팅/코드 정리
Projects/DesignSystem/Project.swift, Projects/FeatureDependency/Project.swift, Projects/ThirdPartyLibs/Project.swift, Projects/Feature/SettingsFeature/Project.swift, Projects/Feature/SettingsFeature/Sources/Coordinator/DefaultSettingsCoordinator.swift, .../View/SettingButtonView.swift, .../ViewModel/SettingsViewModel.swift, Projects/FirebaseModule/Sources/FirebaseLoggerImpl.swift, Projects/Feature/AlarmFeature/Implement/Sources/Coordinator/DefaultRegularAlarmCoordinator.swift, .../ViewModel/RegularAlarmViewModel.swift, Projects/Feature/HomeFeature/Implement/Sources/ViewModel/FavoritesViewModel.swift, Projects/Feature/NearMapFeature/Implement/Sources/ViewController/NearMapViewController.swift, Projects/Feature/NearMapFeature/Implement/Sources/ViewModel/NearMapViewModel.swift, .swiftlint.yml
코드 포맷팅, 불필요한 프로퍼티/임포트/공백 제거, SwiftLint 룰 조정, 일부 변수명/프로퍼티 선언 방식 변경 등.

Sequence Diagram(s)

sequenceDiagram
actor User
participant SceneDelegate
participant AppCoordinator
participant SplashCoordinator
participant SplashViewModel
participant DIContainer
participant VersionCheckUseCase

User->>SceneDelegate: 앱 실행/포그라운드 진입
SceneDelegate->>AppCoordinator: start()
AppCoordinator->>SplashCoordinator: start()
SplashCoordinator->>SplashViewModel: viewDidLoad()
SplashViewModel->>DIContainer: registerDependency()
SplashViewModel->>VersionCheckUseCase: checkForceUpdateNeeded()
alt 업데이트 필요 없음
    SplashViewModel->>SplashCoordinator: startTabFlow()
else 강제 업데이트 필요
    SplashViewModel->>SplashViewController: alert(업데이트 안내)
end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90+ minutes

Possibly related PRs

Suggested reviewers

  • MUKER-WON
  • isakatty

Poem

🐇
새벽 풀밭 위를 깡총깡총,
코드 토끼가 구조를 쪼물딱!
DI도, 플로우도, 빌더도 새로,
비동기로 바뀐 흐름 따라
버전 체크도 한결 똑똑!
테스트 토끼도 신나서 춤추네,
WhereMyBus, 더 튼튼해진다네!
🚌🌱

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/#334

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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 generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @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.

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: 19

🔭 Outside diff range comments (4)
Projects/Feature/AlarmFeature/Interface/Sources/RegularAlarmCoordinator.swift (1)

9-14: Domain 의존성 제거로 인한 컴파일 오류 발생 가능성

RegularAlarmResponse 타입은 기존에 Domain 모듈에서 정의되어 있던 것으로 파악됩니다.
그러나 import Domain 구문이 삭제되고 Foundation만 남아 있어, 현재 파일은 RegularAlarmResponse 를 식별하지 못해 빌드가 실패합니다. 또한 Coordinator 프로토콜 역시 외부 모듈에 존재한다면 동일한 문제가 발생합니다.

빠른 해결책:

-import Foundation
+import Domain     // RegularAlarmResponse 정의 위치
+import Foundation // 필요 시 유지

만약 Interface 타깃에서 Domain 의존성을 제거하려는 의도라면,

  1. RegularAlarmResponse 를 Interface 계층으로 옮기거나
  2. 별도 프로토콜/DTO로 추상화하여 의존성 역전을 적용하는 리팩터링이 필요합니다.

현 상태 그대로는 빌드가 불가하니 반드시 조치 부탁드립니다.

Projects/Domain/Sources/RepositoryInterface/RegularAlarmRepository.swift (2)

14-31: Deprecation 메시지를 더 구체적으로 개선해주세요.

현재 모든 deprecated 항목에 동일한 일반적인 메시지가 사용되고 있습니다. 개발자들의 마이그레이션을 돕기 위해 더 구체적인 정보를 제공하는 것이 좋겠습니다.

다음과 같이 개선하는 것을 권장합니다:

-@available(*, deprecated, message: "이 변수는 제거될 예정입니다.")
+@available(*, deprecated, message: "RxSwift 기반 API에서 Swift Concurrency로 마이그레이션 예정입니다. 새로운 async 기반 API를 사용해주세요.")
 var currentRegularAlarm: BehaviorSubject<[RegularAlarmResponse]> { get }

-@available(*, deprecated, message: "이 메서드는 제거될 예정입니다.")
+@available(*, deprecated, message: "비동기 메서드로 교체 예정입니다. async/await 기반의 새로운 API를 사용해주세요.")
 func createRegularAlarm(

또한 제거 예정 버전이나 시기를 명시하면 더욱 도움이 될 것입니다.


13-32: 새로운 async 기반 API 미구현 확인 – 구현이 필요합니다

현재 모든 기존 API를 deprecated 처리했으나, 대응되는 async/await 기반 메서드가 코드베이스에 존재하지 않습니다. 아래 위치들에 새 async API를 정의·구현하고, 기존 호출부를 async/await 방식으로 전환해주세요.

  • 인터페이스 (Projects/Domain/Sources/RepositoryInterface/RegularAlarmRepository.swift)
    func createRegularAlarm(response:) async
    func updateRegularAlarm(response:) async
    func deleteRegularAlarm(response:) async
    var currentRegularAlarm 대체용 AsyncSequence 또는 async 스트림 추가
  • UseCase
    • Projects/Domain/Sources/UseCase/DefaultRegularAlarmUseCase.swift
    • Projects/Domain/Sources/UseCase/DefaultAddRegularAlarmUseCase.swift
    – 기존 completion 클로저 호출부를 async/await 호출로 전환
  • 데이터 레포지토리 (Projects/Data/Sources/Repository/DefaultRegularAlarmRepository.swift)
    • 위 async 메서드에 대응하는 실제 구현 추가 (throws 여부 검토)

새 API가 안정화되면 deprecated 주석 처리된 메서드는 제거하세요.

Projects/Data/Sources/DTO/CoreDataModelObject/FavoritesBusResponseMO+CoreDataClass.swift (1)

18-32: 중복된 매핑 로직 리팩터링 필요

toDomain 속성과 toDataModel 메서드에서 동일한 guard 로직이 중복됩니다.

공통 매핑 로직을 추출하여 중복을 제거하는 것을 제안합니다:

+    private static func extractProperties(from object: FavoritesBusResponseMO) -> (String, String, String, String, String)? {
+        guard let busStopId = object.busStopId,
+              let busStopName = object.busStopName,
+              let busId = object.busId,
+              let busName = object.busName,
+              let adirection = object.adirection
+        else { return nil }
+        return (busStopId, busStopName, busId, busName, adirection)
+    }
🧹 Nitpick comments (17)
.swiftlint.yml (1)

35-37: 라인 길이 경고 상한 120자 상승: 장·단점 점검 권장

가독성 확보를 위해 100~110자 선을 권장하는 팀도 많습니다. 120자로 늘리면 긴 체이닝·closure 작성은 편해지지만, 13″ 화면이나 side-by-side diff 뷰에서 한눈에 읽기 어려울 수 있습니다.
PR 전반에 걸친 코드 스타일 변화가 예상되므로 팀 내 합의 여부를 다시 확인해주세요.

Projects/Core/Sources/TypeBuilder.swift (1)

11-16: 제네릭 Result Builder 구현 승인

TypeBuilder<T>는 잘 설계된 제네릭 result builder입니다. 간단하고 재사용 가능한 구현으로 DSL 스타일 문법을 지원합니다. Alert 구조체에서 사용되어 선언적 구문을 가능하게 합니다.

문서화 개선을 위해 다음과 같은 주석 추가를 고려해보세요:

 @resultBuilder
+/// A generic result builder that aggregates multiple values of type T into an array.
+/// Enables DSL-like syntax for constructing collections of elements.
 public enum TypeBuilder<T> {
+    /// Builds an array from variadic components of type T.
     public static func buildBlock(_ components: T...) -> [T] {
         components
     }
 }
Projects/FirebaseModule/Sources/FirebaseLoggerImpl.swift (1)

18-19: parameter 파라미터 네이밍을 parameters 로 변경 고려

Firebase Analytics.logEvent(_:parameters:) 시그니처와 통일성을 맞추면 가독성이 조금 더 좋아집니다. 기능엔 영향 없으므로 단순 스타일 제안입니다.

-    public func log(name: String, parameter: [String: String]) {
-        Analytics.logEvent(name, parameters: parameter)
+    public func log(name: String, parameters: [String: String]) {
+        Analytics.logEvent(name, parameters: parameters)
     }
Projects/Feature/HomeFeature/Implement/Sources/ViewModel/FavoritesViewModel.swift (1)

167-175: 튜플 생성-해제 과정을 한 단계 줄여 간결화 제안

withLatestFrom 블록에서 타이머 값을 더 이상 사용하지 않으므로, 굳이 튜플을 만들었다가 다시 버릴 필요가 없습니다. 다음처럼 스트림을 단순화하면 가독성이 좋아집니다.

-        timer.distanceFromStart
-            .withLatestFrom(fetchedResponse) { timerValue, responses in
-                (timerValue, responses)
-            }
-            .map { tuple in
-                let (_, responses) = tuple
-                return responses.map {
-                    return $0.replaceTime()
-                }
-            }
+        timer.distanceFromStart
+            .withLatestFrom(fetchedResponse) { _, responses in
+                responses.map { $0.replaceTime() }
+            }
Projects/Core/Sources/Extension/TimeInterval+.swift (1)

11-15: 유용한 TimeInterval 확장이 잘 구현되었습니다.

시간 단위를 TimeInterval로 변환하는 편의 메서드가 깔끔하게 구현되었습니다. 계산 로직(Self(value) * 60 * 60)도 정확합니다.

일관성을 위해 다른 시간 단위도 추가하는 것을 고려해보세요:

 public extension TimeInterval {
     static func hour(_ value: Int) -> Self {
         Self(value) * 60 * 60
     }
+    
+    static func minute(_ value: Int) -> Self {
+        Self(value) * 60
+    }
+    
+    static func second(_ value: Int) -> Self {
+        Self(value)
+    }
 }
Projects/Feature/SettingsFeature/Sources/ViewModel/SettingsViewModel.swift (1)

81-83: URL 유효성 검증 추가를 고려해보세요.

defaultValue: ""를 사용하여 빈 문자열로 기본값을 설정했지만, 빈 URL로 인한 잠재적 문제를 방지하기 위해 URL 유효성 검증을 추가하는 것이 좋겠습니다.

 @InfoPlistWrapper(key: "INQURY_URL", defaultValue: "")
 var inquryURL: String
-viewModel.coordinator.presentPrivacy(url: inquryURL)
+if !inquryURL.isEmpty {
+    viewModel.coordinator.presentPrivacy(url: inquryURL)
+}
Projects/FeatureDependency/Sources/Coordinator/Coordinator.swift (1)

37-39: 부모 코디네이터로의 위임 패턴이 적절합니다.

기본 구현에서 부모 코디네이터로 URL 처리를 위임하는 것이 책임 체인 패턴을 잘 구현했습니다. 하지만 URL 처리 실패에 대한 오류 처리를 고려해볼 수 있습니다.

 func openURL(_ url: URL) {
-    parent?.openURL(url)
+    if let parent = parent {
+        parent.openURL(url)
+    } else {
+        // 최상위 코디네이터에서 처리할 수 없는 경우 로깅 또는 오류 처리
+        print("Warning: No parent coordinator to handle URL: \(url)")
+    }
 }
Projects/Data/Sources/DTO/BusStopArrivalInfoDTO.swift (1)

23-35: 비동기 환경을 위한 throwing 프로퍼티 추가가 적절합니다.

기존 toDomain 프로퍼티와 병행하여 에러를 던지는 _toDomain 프로퍼티를 추가하여 Swift concurrency 환경에서 더 나은 에러 전파가 가능합니다.

네이밍 일관성을 위해 _toDomain 대신 toDomainThrowing 같은 더 명확한 이름을 고려해보세요:

-    var _toDomain: BusStopArrivalInfoResponse {
+    var toDomainThrowing: BusStopArrivalInfoResponse {
Projects/CoreDataService/Sources/CoreDataStorage.swift (1)

9-14: Core Data 추상화 프로토콜 설계가 우수합니다.

비동기/에러 던지기 패턴을 사용한 CRUD 연산과 CoreDataModel 제네릭 제약을 통해 타입 안전성을 보장하면서도 유연한 인터페이스를 제공합니다. Swift concurrency를 활용한 현대적인 설계입니다.

문서화를 추가하여 각 메서드의 용도와 에러 조건을 명확히 해주세요:

+/// Core Data storage operations with async/await support
 public protocol CoreDataStorage {
+    /// Creates a new entity in Core Data
+    /// - Parameter data: The data model to create
+    /// - Throws: CoreDataStorageError if creation fails
     func create<T: CoreDataModel>(data: T) async throws
+    /// Reads all entities of the specified type
+    /// - Parameter type: The type of entities to fetch
+    /// - Returns: Array of data models
+    /// - Throws: CoreDataStorageError if fetch fails
     func read<T: CoreDataModel>(type: T.Type) async throws -> [T]
Projects/Feature/AlarmFeature/Project.swift (1)

8-23: 모듈화 아키텍처 설계가 우수함

Feature를 Interface, Implement, Testing 세 개의 타겟으로 분리한 것은 의존성 역전 원칙을 잘 적용한 훌륭한 아키텍처입니다. 이를 통해 테스트 가능성과 모듈 간 결합도를 낮출 수 있습니다.

Plugins/EnvironmentPlugin/ProjectDescriptionHelpers/Buildable/TargetBuildable/Target/Feature/FeatureImplement.swift (1)

8-8: TODO 주석에 대한 구체적인 계획을 명시해주세요.

TODO 주석에서 "Feature 객체가 필요하지 않을 때"라는 조건이 모호합니다. 리팩토링 계획에서 언제 이 변경이 예상되는지 구체적으로 명시하거나 이슈 번호를 참조하는 것이 좋겠습니다.

Projects/Data/Sources/Repository/DefaultFavoritesRepository.swift (1)

36-54: AsyncStream에서의 에러 처리 개선을 고려해보세요.

fetchFavoritesaddFavorites, removeFavorites 메서드에서 발생할 수 있는 에러가 AsyncStream으로 전파되지 않습니다.

다음과 같이 에러를 포함하는 Result 타입 사용을 고려해보세요:

public nonisolated let favoritesStream: AsyncStream<Result<[FavoritesBusResponse], Error>>
private let favoritesContinuation: AsyncStream<Result<[FavoritesBusResponse], Error>>.Continuation

public func fetchFavorites() async throws -> [FavoritesBusResponse] {
    do {
        let favorites = try await coreDataStorage.read(type: FavoritesBusResponse.self)
        currentFavorites = favorites
        favoritesContinuation.yield(.success(favorites))
        return favorites
    } catch {
        favoritesContinuation.yield(.failure(error))
        throw error
    }
}

이렇게 하면 스트림 구독자들이 에러 상황도 처리할 수 있습니다.

Projects/App/Sources/Coordinator/Splash/SplashViewModel.swift (1)

40-71: 비동기 처리 구현이 적절합니다

RxSwift와 async/await를 혼용하는 것은 점진적 마이그레이션 과정에서 이해할 수 있습니다. 에러 처리도 적절히 구현되어 있습니다.

향후 RxSwift 의존성을 완전히 제거하는 것을 고려해보세요.

Projects/Core/Sources/PropertyWrapper/InfoPlistWrapper.swift (1)

102-134: 컬렉션 파싱의 엄격한 실패 처리 재고려 필요

현재 구현은 하나의 요소라도 파싱에 실패하면 전체 컬렉션 초기화가 실패합니다. Info.plist의 데이터가 예상과 다를 경우 전체 기능이 동작하지 않을 수 있습니다.

부분적 실패를 허용하는 유연한 접근 방식을 고려해보세요:

 extension Array: InfoPlistLoadable where Element: InfoPlistLoadable {
     public init?(rawValue: Any?) {
         guard let rawArray = rawValue as? [Any] else {
             return nil
         }
-        var result: [Element] = []
-        for element in rawArray {
-            if let value = Element(rawValue: element) {
-                result.append(value)
-            } else {
-                return nil
-            }
-        }
-        self = result
+        self = rawArray.compactMap { Element(rawValue: $0) }
     }
 }

Dictionary도 유사하게 개선할 수 있습니다:

 extension Dictionary: InfoPlistLoadable where Key == String, Value: InfoPlistLoadable {
     public init?(rawValue: Any?) {
         guard let rawDict = rawValue as? [String: Any] else {
             return nil
         }
-        var result: [String: Value] = [:]
-        for (key, value) in rawDict {
-            if let v = Value(rawValue: value) {
-                result[key] = v
-            } else {
-                return nil
-            }
-        }
-        self = result
+        self = rawDict.compactMapValues { Value(rawValue: $0) }
     }
 }
Projects/Data/Sources/Repository/DefaultVersionCheckRepository.swift (1)

18-19: 캐싱 전략 검토 필요

UserDefaults를 통한 캐싱이 간단하지만, 버전 체크 정보의 특성상 메모리 캐시나 다른 전략도 고려해볼 수 있습니다.

버전 정보의 사용 패턴과 업데이트 빈도를 고려하여 최적의 캐싱 전략을 선택하세요.

Also applies to: 29-31, 41-43

Projects/CoreDataService/Sources/CoreDataStorageImpl.swift (1)

74-83: read 메서드의 fetchLimit 설정 검토

현재 fetchLimit = 0으로 설정되어 있어 모든 레코드를 가져옵니다. 대량의 데이터가 있을 경우 성능 문제가 발생할 수 있습니다.

페이징이나 제한적 조회를 위한 파라미터 추가를 고려하세요:

-public func read<T: CoreDataModel>(type: T.Type) async throws -> [T] {
+public func read<T: CoreDataModel>(
+    type: T.Type,
+    predicate: NSPredicate? = nil,
+    limit: Int? = nil,
+    offset: Int? = nil
+) async throws -> [T] {
     let managedObjects = try await context.perform { [self] in
         let request = NSFetchRequest<T.ManagedObject>(entityName: String(describing: T.ManagedObject.self))
-        request.fetchLimit = 0
+        request.fetchLimit = limit ?? 0
         request.fetchBatchSize = batchSize
+        if let predicate = predicate {
+            request.predicate = predicate
+        }
+        if let offset = offset {
+            request.fetchOffset = offset
+        }
         
         return try context.fetch(request)
     }
     return managedObjects.map { T.toDataModel($0) }
 }
Projects/FeatureDependency/Sources/Mock/MockCoordinator.swift (1)

17-18: delegate 프로퍼티 네이밍 일관성 개선 필요

busStopCoordinatorDelegate라는 이름은 다른 coordinator들의 delegate 네이밍 패턴과 일치하지 않습니다. 단순히 delegate로 명명하는 것이 더 일관성 있을 것 같습니다.

-public var busStopCoordinatorDelegate: BusStopCoordinatorDelegate?
+public var delegate: BusStopCoordinatorDelegate?

그리고 91번 라인의 computed property도 다음과 같이 수정:

-public var delegate: BusStopCoordinatorDelegate? { busStopCoordinatorDelegate }
+public var delegate: BusStopCoordinatorDelegate? { delegate }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1f08e87 and 800fc63.

📒 Files selected for processing (94)
  • .swiftlint.yml (2 hunks)
  • Plugins/EnvironmentPlugin/ProjectDescriptionHelpers/Buildable/TargetBuildable/Target/Feature/FeatureImplement.swift (1 hunks)
  • Plugins/EnvironmentPlugin/ProjectDescriptionHelpers/Buildable/TargetBuildable/Target/Feature/FeatureInterface.swift (1 hunks)
  • Plugins/EnvironmentPlugin/ProjectDescriptionHelpers/Buildable/TargetBuildable/Target/Feature/FeatureTesting.swift (1 hunks)
  • Projects/App/Sources/AppDelegate+Register.swift (0 hunks)
  • Projects/App/Sources/AppDelegate.swift (0 hunks)
  • Projects/App/Sources/Coordinator/AppCoordinator.swift (2 hunks)
  • Projects/App/Sources/Coordinator/Splash/SplashCoordinator.swift (1 hunks)
  • Projects/App/Sources/Coordinator/Splash/SplashViewController.swift (1 hunks)
  • Projects/App/Sources/Coordinator/Splash/SplashViewModel.swift (1 hunks)
  • Projects/App/Sources/DefaultCoordinatorProvider.swift (2 hunks)
  • Projects/App/Sources/SceneDelegate.swift (3 hunks)
  • Projects/Core/Sources/Extension/String+.swift (1 hunks)
  • Projects/Core/Sources/Extension/TimeInterval+.swift (1 hunks)
  • Projects/Core/Sources/PropertyWrapper/InfoPlistWrapper.swift (1 hunks)
  • Projects/Core/Sources/PropertyWrapper/UserDefaultsWrapper.swift (1 hunks)
  • Projects/Core/Sources/TypeBuilder.swift (1 hunks)
  • Projects/CoreDataService/Sources/CoreDataContainerBuilder.swift (1 hunks)
  • Projects/CoreDataService/Sources/CoreDataModel.swift (1 hunks)
  • Projects/CoreDataService/Sources/CoreDataStorage.swift (1 hunks)
  • Projects/CoreDataService/Sources/CoreDataStorageImpl.swift (1 hunks)
  • Projects/CoreDataService/Sources/Legacy/CoreDataService.swift (1 hunks)
  • Projects/Data/Sources/DTO/BusStopArrivalInfoDTO.swift (1 hunks)
  • Projects/Data/Sources/DTO/CoreDataModelObject/FavoritesBusResponseMO+CoreDataClass.swift (2 hunks)
  • Projects/Data/Sources/Repository/DefaultBusStopArrivalInfoRepository.swift (1 hunks)
  • Projects/Data/Sources/Repository/DefaultFavoritesRepository.swift (1 hunks)
  • Projects/Data/Sources/Repository/DefaultVersionCheckRepository.swift (1 hunks)
  • Projects/Data/Sources/Service/ForceUpdateService/DefaultForceUpdateService.swift (0 hunks)
  • Projects/DesignSystem/Project.swift (1 hunks)
  • Projects/Domain/Sources/Entity/Alert.swift (1 hunks)
  • Projects/Domain/Sources/Entity/ForceUpdate.swift (1 hunks)
  • Projects/Domain/Sources/Entity/Response/AppVersionInfoResponse.swift (3 hunks)
  • Projects/Domain/Sources/Entity/Response/BusStopArrivalInfoResponse.swift (1 hunks)
  • Projects/Domain/Sources/Entity/Response/FavoritesBusResponse.swift (1 hunks)
  • Projects/Domain/Sources/Entity/VersionCheckInfo.swift (1 hunks)
  • Projects/Domain/Sources/RepositoryInterface/BusStopArrivalInfoRepository.swift (1 hunks)
  • Projects/Domain/Sources/RepositoryInterface/FavoritesRepository.swift (1 hunks)
  • Projects/Domain/Sources/RepositoryInterface/RegularAlarmRepository.swift (1 hunks)
  • Projects/Domain/Sources/RepositoryInterface/VersionCheckRepository.swift (1 hunks)
  • Projects/Domain/Sources/Service/ForceUpdateService/ForceUpdateService.swift (0 hunks)
  • Projects/Domain/Sources/UseCase/DefaultVersionCheckUseCase.swift (1 hunks)
  • Projects/Domain/Sources/UseCase/Protocol/VersionCheckUseCase.swift (1 hunks)
  • Projects/Feature/AlarmFeature/Implement/Sources/Coordinator/DefaultAddRegularAlarmCoordinator.swift (4 hunks)
  • Projects/Feature/AlarmFeature/Implement/Sources/Coordinator/DefaultRegularAlarmCoordinator.swift (0 hunks)
  • Projects/Feature/AlarmFeature/Implement/Sources/Dependencies.swift (1 hunks)
  • Projects/Feature/AlarmFeature/Implement/Sources/ViewModel/AddRegularAlarmViewModel.swift (1 hunks)
  • Projects/Feature/AlarmFeature/Implement/Sources/ViewModel/RegularAlarmViewModel.swift (1 hunks)
  • Projects/Feature/AlarmFeature/Interface/Sources/AddRegularAlarmCoordinator.swift (1 hunks)
  • Projects/Feature/AlarmFeature/Interface/Sources/Dependencies.swift (1 hunks)
  • Projects/Feature/AlarmFeature/Interface/Sources/RegularAlarmCoordinator.swift (1 hunks)
  • Projects/Feature/AlarmFeature/Project.swift (1 hunks)
  • Projects/Feature/AlarmFeature/Testing/Sources/Dependencies.swift (1 hunks)
  • Projects/Feature/AlarmFeature/Testing/Sources/MockAddRegularAlarmCoordinatorBuilder.swift (1 hunks)
  • Projects/Feature/BusStopFeature/Demo/SceneDelegate.swift (2 hunks)
  • Projects/Feature/BusStopFeature/Project.swift (1 hunks)
  • Projects/Feature/BusStopFeature/Sources/Coordinator/DafaultBusStopCoordinator.swift (3 hunks)
  • Projects/Feature/BusStopFeature/Sources/ViewModel/BusStopViewModel.swift (1 hunks)
  • Projects/Feature/HomeFeature/Implement/Sources/Coordinator/DefaultHomeCoordinator.swift (2 hunks)
  • Projects/Feature/HomeFeature/Implement/Sources/Dependencies.swift (1 hunks)
  • Projects/Feature/HomeFeature/Implement/Sources/ViewModel/FavoritesViewModel.swift (1 hunks)
  • Projects/Feature/HomeFeature/Interface/Sources/HomeCoordinator.swift (1 hunks)
  • Projects/Feature/HomeFeature/Project.swift (1 hunks)
  • Projects/Feature/NearMapFeature/Implement/Sources/Coordinator/DafaultNearMapCoordinator.swift (2 hunks)
  • Projects/Feature/NearMapFeature/Implement/Sources/ViewController/NearMapViewController.swift (1 hunks)
  • Projects/Feature/NearMapFeature/Implement/Sources/ViewModel/NearMapViewModel.swift (1 hunks)
  • Projects/Feature/NearMapFeature/Interface/Sources/NearMapCoordinator.swift (1 hunks)
  • Projects/Feature/NearMapFeature/Project.swift (1 hunks)
  • Projects/Feature/NearMapFeature/Testing/Sources/Dependencies.swift (1 hunks)
  • Projects/Feature/NearMapFeature/Testing/Sources/MockNearMapCoordinatorBuilder.swift (1 hunks)
  • Projects/Feature/SearchFeature/Project.swift (2 hunks)
  • Projects/Feature/SearchFeature/Sources/Coordinator/DafaultSearchCoordinator.swift (3 hunks)
  • Projects/Feature/SearchFeature/Sources/ViewModel/SearchViewModel.swift (2 hunks)
  • Projects/Feature/SettingsFeature/Project.swift (1 hunks)
  • Projects/Feature/SettingsFeature/Sources/Coordinator/DefaultSettingsCoordinator.swift (0 hunks)
  • Projects/Feature/SettingsFeature/Sources/View/SettingButtonView.swift (3 hunks)
  • Projects/Feature/SettingsFeature/Sources/ViewModel/SettingsViewModel.swift (4 hunks)
  • Projects/FeatureDependency/Project.swift (1 hunks)
  • Projects/FeatureDependency/Sources/Coordinator/AddRegularAlarmCoordinator.swift (0 hunks)
  • Projects/FeatureDependency/Sources/Coordinator/BusStopCoordinator.swift (1 hunks)
  • Projects/FeatureDependency/Sources/Coordinator/Coordinator.swift (2 hunks)
  • Projects/FeatureDependency/Sources/Coordinator/CoordinatorProvider.swift (1 hunks)
  • Projects/FeatureDependency/Sources/Coordinator/CoordinatorType.swift (0 hunks)
  • Projects/FeatureDependency/Sources/Coordinator/HomeCoordinator.swift (0 hunks)
  • Projects/FeatureDependency/Sources/Coordinator/NearMapCoordinator.swift (0 hunks)
  • Projects/FeatureDependency/Sources/Coordinator/SearchCoordinator.swift (1 hunks)
  • Projects/FeatureDependency/Sources/Mock/MockBusStopArrivalInfoRepository.swift (1 hunks)
  • Projects/FeatureDependency/Sources/Mock/MockCoordinator.swift (4 hunks)
  • Projects/FeatureDependency/Sources/Mock/MockCoordinatorProvider.swift (2 hunks)
  • Projects/FirebaseModule/Sources/FirebaseLoggerImpl.swift (1 hunks)
  • Projects/MainFeature/Sources/TabBarCoordinator.swift (3 hunks)
  • Projects/NetworkService/Sources/EndPoint/EndPoint.swift (1 hunks)
  • Projects/NetworkService/Sources/NetworkService/DefaultNetworkService.swift (1 hunks)
  • Projects/NetworkService/Sources/NetworkService/NetworkService.swift (1 hunks)
  • Projects/ThirdPartyLibs/Project.swift (1 hunks)
💤 Files with no reviewable changes (10)
  • Projects/Feature/AlarmFeature/Implement/Sources/Coordinator/DefaultRegularAlarmCoordinator.swift
  • Projects/FeatureDependency/Sources/Coordinator/NearMapCoordinator.swift
  • Projects/App/Sources/AppDelegate+Register.swift
  • Projects/FeatureDependency/Sources/Coordinator/HomeCoordinator.swift
  • Projects/Data/Sources/Service/ForceUpdateService/DefaultForceUpdateService.swift
  • Projects/Domain/Sources/Service/ForceUpdateService/ForceUpdateService.swift
  • Projects/App/Sources/AppDelegate.swift
  • Projects/FeatureDependency/Sources/Coordinator/CoordinatorType.swift
  • Projects/Feature/SettingsFeature/Sources/Coordinator/DefaultSettingsCoordinator.swift
  • Projects/FeatureDependency/Sources/Coordinator/AddRegularAlarmCoordinator.swift
🧰 Additional context used
🧬 Code Graph Analysis (24)
Projects/Domain/Sources/RepositoryInterface/RegularAlarmRepository.swift (1)
Projects/Data/Sources/Repository/DefaultRegularAlarmRepository.swift (2)
  • createRegularAlarm (31-75)
  • updateRegularAlarm (77-89)
Projects/NetworkService/Sources/NetworkService/DefaultNetworkService.swift (1)
Projects/NetworkService/Sources/EndPoint/EndPoint.swift (1)
  • toURLRequest (36-63)
Projects/Feature/BusStopFeature/Sources/ViewModel/BusStopViewModel.swift (1)
Projects/Feature/AlarmFeature/Implement/Sources/ViewModel/AddRegularAlarmViewModel.swift (1)
  • didSelect (144-153)
Projects/CoreDataService/Sources/CoreDataStorage.swift (1)
Projects/CoreDataService/Sources/CoreDataStorageImpl.swift (4)
  • create (58-72)
  • read (74-83)
  • update (85-91)
  • delete (93-99)
Projects/FeatureDependency/Sources/Mock/MockBusStopArrivalInfoRepository.swift (1)
Projects/Data/Sources/Repository/DefaultBusStopArrivalInfoRepository.swift (2)
  • fetchArrivalList (22-26)
  • fetchArrivalList (28-40)
Projects/App/Sources/DefaultCoordinatorProvider.swift (3)
Projects/FeatureDependency/Sources/Mock/MockCoordinatorProvider.swift (2)
  • makeBusStopCoordinator (29-40)
  • makeSearchCoordinator (17-27)
Projects/Feature/AlarmFeature/Testing/Sources/MockAddRegularAlarmCoordinatorBuilder.swift (1)
  • build (13-24)
Projects/Feature/NearMapFeature/Testing/Sources/MockNearMapCoordinatorBuilder.swift (1)
  • build (14-27)
Projects/Domain/Sources/UseCase/DefaultVersionCheckUseCase.swift (2)
Projects/Data/Sources/Repository/DefaultVersionCheckRepository.swift (4)
  • getCachedVersionCheckInfo (29-31)
  • getAppStoreURL (45-47)
  • fetchRequiredVersion (33-39)
  • saveVersionCheckInfoCache (41-43)
Projects/Core/Sources/Extension/TimeInterval+.swift (1)
  • hour (12-14)
Projects/Data/Sources/Repository/DefaultBusStopArrivalInfoRepository.swift (3)
Projects/FeatureDependency/Sources/Mock/MockBusStopArrivalInfoRepository.swift (2)
  • fetchArrivalList (18-202)
  • fetchArrivalList (206-397)
Projects/NetworkService/Sources/NetworkService/DefaultNetworkService.swift (3)
  • request (16-26)
  • request (28-72)
  • request (74-127)
Projects/Core/Sources/Extension/Data+.swift (1)
  • decode (18-20)
Projects/CoreDataService/Sources/CoreDataModel.swift (1)
Projects/Data/Sources/DTO/CoreDataModelObject/FavoritesBusResponseMO+CoreDataClass.swift (2)
  • toDataModel (38-52)
  • sync (54-61)
Projects/FeatureDependency/Sources/Coordinator/SearchCoordinator.swift (5)
Projects/App/Sources/Coordinator/AppCoordinator.swift (1)
  • startBusStopFlow (50-63)
Projects/Feature/NearMapFeature/Implement/Sources/Coordinator/DafaultNearMapCoordinator.swift (1)
  • startBusStopFlow (46-56)
Projects/Feature/HomeFeature/Implement/Sources/Coordinator/DefaultHomeCoordinator.swift (1)
  • startBusStopFlow (62-73)
Projects/Feature/SearchFeature/Sources/Coordinator/DafaultSearchCoordinator.swift (3)
  • startBusStopFlow (45-55)
  • startNearMapFlow (57-66)
  • startNearMapFlow (68-77)
Projects/FeatureDependency/Sources/Mock/MockCoordinator.swift (3)
  • startBusStopFlow (74-81)
  • startNearMapFlow (56-63)
  • startNearMapFlow (65-72)
Projects/Feature/AlarmFeature/Testing/Sources/MockAddRegularAlarmCoordinatorBuilder.swift (2)
Projects/App/Sources/DefaultCoordinatorProvider.swift (3)
  • build (73-80)
  • build (84-90)
  • build (94-107)
Projects/Feature/NearMapFeature/Testing/Sources/MockNearMapCoordinatorBuilder.swift (1)
  • build (14-27)
Projects/App/Sources/Coordinator/Splash/SplashCoordinator.swift (3)
Projects/App/Sources/Coordinator/AppCoordinator.swift (2)
  • openURL (65-69)
  • start (38-48)
Projects/FeatureDependency/Sources/Coordinator/Coordinator.swift (1)
  • openURL (37-39)
Projects/MainFeature/Sources/TabBarCoordinator.swift (1)
  • start (35-37)
Projects/App/Sources/Coordinator/Splash/SplashViewController.swift (1)
Projects/App/Sources/Coordinator/Splash/SplashViewModel.swift (1)
  • transform (40-71)
Projects/FeatureDependency/Sources/Coordinator/Coordinator.swift (1)
Projects/App/Sources/Coordinator/AppCoordinator.swift (1)
  • openURL (65-69)
Projects/FeatureDependency/Sources/Coordinator/BusStopCoordinator.swift (2)
Projects/Feature/BusStopFeature/Sources/Coordinator/DafaultBusStopCoordinator.swift (2)
  • busStopMapLocation (58-67)
  • moveToRegualrAlarm (69-77)
Projects/Feature/AlarmFeature/Implement/Sources/ViewModel/AddRegularAlarmViewModel.swift (1)
  • didSelect (144-153)
Projects/Domain/Sources/UseCase/Protocol/VersionCheckUseCase.swift (1)
Projects/Domain/Sources/UseCase/DefaultVersionCheckUseCase.swift (1)
  • checkForceUpdateNeeded (23-40)
Projects/Feature/SettingsFeature/Sources/ViewModel/SettingsViewModel.swift (1)
Projects/Feature/SettingsFeature/Sources/Coordinator/DefaultSettingsCoordinator.swift (1)
  • presentPrivacy (33-41)
Projects/Feature/AlarmFeature/Implement/Sources/ViewModel/AddRegularAlarmViewModel.swift (1)
Projects/Feature/AlarmFeature/Implement/Sources/Coordinator/DefaultAddRegularAlarmCoordinator.swift (1)
  • removeChildViewController (69-75)
Projects/NetworkService/Sources/NetworkService/NetworkService.swift (1)
Projects/NetworkService/Sources/NetworkService/DefaultNetworkService.swift (3)
  • request (16-26)
  • request (28-72)
  • request (74-127)
Projects/App/Sources/Coordinator/AppCoordinator.swift (3)
Projects/App/Sources/SceneDelegate.swift (1)
  • sceneWillEnterForeground (80-82)
Projects/App/Sources/Coordinator/Splash/SplashCoordinator.swift (1)
  • start (43-48)
Projects/FeatureDependency/Sources/Coordinator/Coordinator.swift (1)
  • openURL (37-39)
Projects/FeatureDependency/Sources/Coordinator/CoordinatorProvider.swift (2)
Projects/App/Sources/DefaultCoordinatorProvider.swift (1)
  • makeBusStopCoordinator (23-40)
Projects/FeatureDependency/Sources/Mock/MockCoordinatorProvider.swift (1)
  • makeBusStopCoordinator (29-40)
Projects/Domain/Sources/RepositoryInterface/FavoritesRepository.swift (3)
Projects/Data/Sources/Repository/DefaultFavoritesRepository.swift (6)
  • fetchFavorites (36-41)
  • fetchFavorites (101-115)
  • addFavorites (43-47)
  • addFavorites (68-75)
  • removeFavorites (49-53)
  • removeFavorites (77-86)
Projects/FeatureDependency/Sources/Mock/MockFavoritesRepository.swift (4)
  • fetchFavorites (25-27)
  • fetchFavorites (28-31)
  • addFavorites (32-34)
  • removeFavorites (35-37)
Projects/CoreDataService/Sources/CoreDataStorageImpl.swift (1)
  • create (58-72)
Projects/App/Sources/Coordinator/Splash/SplashViewModel.swift (7)
Projects/App/Sources/Coordinator/Splash/SplashViewController.swift (1)
  • viewDidLoad (29-60)
Projects/Domain/Sources/UseCase/DefaultVersionCheckUseCase.swift (1)
  • checkForceUpdateNeeded (23-40)
Projects/App/Sources/Coordinator/Splash/SplashCoordinator.swift (1)
  • startTabFlow (50-58)
Projects/App/Sources/Coordinator/AppCoordinator.swift (1)
  • openURL (65-69)
Projects/FeatureDependency/Sources/Coordinator/Coordinator.swift (1)
  • openURL (37-39)
Projects/CoreDataService/Sources/CoreDataContainerBuilder.swift (1)
  • buildContainer (22-47)
Projects/Core/Sources/DIContainer/DIContainer.swift (1)
  • setLogger (17-19)
Projects/Domain/Sources/RepositoryInterface/VersionCheckRepository.swift (1)
Projects/Data/Sources/Repository/DefaultVersionCheckRepository.swift (4)
  • getCachedVersionCheckInfo (29-31)
  • saveVersionCheckInfoCache (41-43)
  • getAppStoreURL (45-47)
  • fetchRequiredVersion (33-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_test
🔇 Additional comments (118)
.swiftlint.yml (1)

23-25: id·v 식별자 허용 범위 확대에 대한 재검토 필요

id는 Obj-C 브리징 타입과 동일한 이름이고, v는 단일 문자라 가독성이 급격히 저하될 수 있습니다. 짧은 식별자 허용은 코딩 컨벤션을 무력화할 위험이 있으므로 실제 사용 빈도·의도와 팀 합의를 다시 한번 확인하는 편이 안전합니다.
필요한 경우 면책 범위를 최소화하거나 특정 컨텍스트(예: for-loop 인덱스)로 제한하는 옵션도 고려해보세요.

Projects/Domain/Sources/Entity/Response/FavoritesBusResponse.swift (1)

11-11: CoreData import 추가 승인

FavoritesBusResponseCoreDataStorable 프로토콜을 구현하고 있으므로 CoreData import 추가가 적절합니다. 이는 즐겨찾기 데이터의 Core Data 통합을 지원하며 기존 기능에 영향을 주지 않습니다.

Projects/ThirdPartyLibs/Project.swift (1)

5-5: DSL 문법 개선 승인

ThirdPartyLibs() 에서 ThirdPartyLibs {로 변경하여 불필요한 괄호를 제거한 것은 좋은 개선입니다. 다른 프로젝트 파일들과의 일관성을 유지하며 더 깔끔한 DSL 문법을 제공합니다.

Projects/Feature/SettingsFeature/Project.swift (1)

17-21: 코드 포맷팅 개선 승인

AppInfoPlist 초기화를 멀티라인 형식으로 변경한 것은 가독성을 향상시킵니다. 다른 Feature 프로젝트들과의 일관성을 유지하며 설정 매개변수들의 유지보수성을 개선합니다.

Projects/Feature/AlarmFeature/Implement/Sources/ViewModel/RegularAlarmViewModel.swift (1)

6-6: 모듈화 개선을 위한 import 추가 승인

AlarmFeatureInterface import 추가는 Feature 모듈의 인터페이스/구현 분리 리팩터링과 일치합니다. RegularAlarmCoordinator와 같은 인터페이스 프로토콜들을 사용하기 위해 필요한 의존성이며, 의존성 역전 원칙을 지원합니다.

Projects/Feature/NearMapFeature/Implement/Sources/ViewModel/NearMapViewModel.swift (1)

6-6: FeatureDependency 제거가 실제로 무관한지 한 번 더 확인 필요

NearMapFeatureInterfaceFeatureDependency를 re-export 하고 있다면 문제없지만,
@Injected, NearMapUseCase 등 DI 매크로나 타입들이 여전히 FeatureDependency
직접 의존하고 있을 가능성이 있습니다.
다른 구현 파일(Implement/Dependencies.swift 등)을 통해 암묵적으로 가져오는 구조라면
컴파일 단계에서 잡히지 않을 수 있으니, 빌드 전후로 의존성 누락 경고가 없는지
확인해 주세요.

Projects/FeatureDependency/Project.swift (1)

5-9: Tuist DSL 문법 변경 검증 필요

FeatureDependency { … } 형태로 괄호를 제거하면서 trailing-closure
만을 전달하도록 수정하셨는데,
해당 매크로/함수가 파라미터 없이 클로저만 받도록 선언돼 있어야 합니다.
기존에 FeatureDependency()FeatureDependency { … }
변경한 다른 모듈들과 일관되게 빌드가 통과되는지 확인해 주세요.

Projects/Feature/NearMapFeature/Implement/Sources/ViewController/NearMapViewController.swift (1)

183-186: let 사용으로 불변성 확보 👍

response, distancelet으로 선언해
불필요한 가변성을 제거한 점 좋습니다.
별도 추가 의견 없습니다.

Projects/Feature/AlarmFeature/Implement/Sources/Dependencies.swift (1)

9-9: 재-export 파일 추가 확인

@_exported import AlarmFeatureInterface 만으로 구현 타겟 전역 노출이
충분히 해결됩니다.
다만 동일 모듈을 중복 re-export 하는 다른 파일이 있는지,
그리고 실수로 순환 의존성이 생기지 않는지 한 번 더 체크해 주세요.

Projects/Feature/AlarmFeature/Interface/Sources/Dependencies.swift (1)

9-11: Interface 레이어의 최소 의존성 선언 👍

Interface 레이어에서 FeatureDependency, Domain만 노출하도록
제한한 점 모듈 경계를 명확히 해 줍니다.
추가 의존성이 생길 경우 이 파일만 수정하면 되므로 유지보수성이 좋아집니다.

Projects/Feature/HomeFeature/Implement/Sources/Dependencies.swift (1)

1-10: 모듈 재-export 방식 확인 완료

@_exported import HomeFeatureInterface 로 구현 모듈에서 인터페이스를 재노출하는 전략은 테스트 및 상위 모듈에서 import 간소화에 유용합니다. 중복 재-export 가 다른 타겟들과 충돌하지 않는지만 한 번 더 확인 부탁드립니다.

Projects/Feature/SearchFeature/Project.swift (2)

9-10: NearMapFeature 인터페이스 추가 이후 의존성 그래프 빌드 확인 필요

새 인터페이스 타겟이 실제로 정의되어 있고, 동일 이름의 Implement/Testing 타겟도 함께 포함되어 있는지 빌드 확인 부탁드립니다. 누락 시 프로젝트 그래프 생성 단계에서 오류가 발생합니다.


18-22: InfoPlist 다중 줄 포매팅 변경 사항 문제 없음

형식 변경만 이루어졌으며 기능적 차이는 없습니다.

Projects/Feature/AlarmFeature/Testing/Sources/Dependencies.swift (1)

1-11: 테스팅 모듈의 의존성 재-export 확인 완료

테스트 코드에서 AlarmFeature 구현과 인터페이스를 동시에 import 없이 사용할 수 있어 편리합니다. 다른 테스트 타겟도 동일 패턴이면 통일성 확보됩니다.

Projects/Feature/NearMapFeature/Implement/Sources/Coordinator/DafaultNearMapCoordinator.swift (1)

3-3: 모듈화에 따른 import 변경이 적절합니다.

FeatureDependency에서 NearMapFeatureInterface로 변경하여 Feature 인터페이스와 구현을 분리하는 아키텍처 개선이 잘 반영되었습니다.

Projects/Feature/NearMapFeature/Testing/Sources/Dependencies.swift (1)

9-10: 테스팅 모듈 의존성 재내보내기 일관성 확인 및 승인

확인 결과, AlarmFeatureTestingNearMapFeatureTesting 모듈 모두 @_exported import {Feature}@_exported import {Feature}Interface 패턴을 동일하게 따르고 있어 다른 Feature 모듈들과 일관성이 유지되고 있습니다. 해당 변경을 승인합니다.

Projects/Feature/AlarmFeature/Implement/Sources/ViewModel/AddRegularAlarmViewModel.swift (1)

143-154: 델리게이트 패턴 구현이 적절합니다.

BusStopCoordinatorDelegate 프로토콜 구현이 올바르게 되어 있고, 버스 정류장 선택 시 필요한 모든 정보를 regularAlarmEditingService에 업데이트한 후 자식 뷰 컨트롤러를 제거하는 플로우가 적절합니다.

Projects/Feature/BusStopFeature/Sources/ViewModel/BusStopViewModel.swift (1)

155-155: 델리게이트 패턴으로의 리팩터링이 적절합니다.

셀 선택 이벤트 처리를 coordinator의 delegate로 위임하는 방식으로 변경한 것이 아키텍처 개선에 도움이 됩니다. 옵셔널 delegate를 사용하여 하위 호환성도 유지하고 있습니다.

Projects/FeatureDependency/Sources/Coordinator/SearchCoordinator.swift (1)

13-15: 매개변수 명명 일관성 개선이 좋습니다.

stationIdbusStopId를 모두 busStopID로 통일한 것이 코드베이스 전반의 일관성을 향상시킵니다. 관련 구현체들도 함께 업데이트된 것을 확인했습니다.

Projects/Data/Sources/Repository/DefaultBusStopArrivalInfoRepository.swift (1)

22-26: toDomain 네이밍 의도 확인 완료

  • BusStopArrivalInfoDTO.swift 에서 오류를 던지는 매핑 로직은 var _toDomain: BusStopArrivalInfoResponse (throws) 로 정의되어 있습니다.
  • 다른 DTO들은 오류를 던지지 않기 때문에 toDomain (non-throws) 으로만 제공됩니다.
  • 따라서 async 메서드의 ._toDomain 호출은 의도된 네이밍이며, 일관성 있게 사용된 것이 맞습니다.
Projects/Core/Sources/PropertyWrapper/UserDefaultsWrapper.swift (1)

49-57: 옵셔널 값 지원을 위한 편의 이니셜라이저가 잘 구현되었습니다.

제네릭 제약조건을 활용하여 옵셔널 타입에 대한 편의 이니셜라이저를 제공하는 것이 좋습니다. 기존 이니셜라이저를 재사용하여 코드 중복을 피하고, Swift의 프로퍼티 래퍼 패턴을 적절히 따르고 있습니다.

Projects/Feature/SearchFeature/Sources/ViewModel/SearchViewModel.swift (1)

68-68: 매개변수 명명 일관성 개선이 적절합니다.

stationId/busStopId에서 busStopID로의 변경이 코디네이터 프로토콜 시그니처와 일치하며, 코드베이스 전반의 명명 일관성을 향상시킵니다.

Also applies to: 93-93

Projects/Feature/SettingsFeature/Sources/ViewModel/SettingsViewModel.swift (3)

3-3: Core 모듈 import가 적절합니다.

새로운 @InfoPlistWrapper 사용을 위해 필요한 의존성 추가가 올바릅니다.


35-39: Info.plist 접근 방식 개선이 우수합니다.

@InfoPlistWrapper 프로퍼티 래퍼를 사용하여 타입 안전성과 코드 일관성을 향상시켰습니다. 기존의 수동 Bundle.main 접근 방식보다 훨씬 깔끔합니다.

Also applies to: 46-50


70-70: 앱 버전 접근 API 업데이트가 적절합니다.

String.getCurrentVersion()에서 String.currentVersion 프로퍼티로의 변경이 Core 모듈의 API 개선사항과 일치합니다.

Projects/Domain/Sources/UseCase/Protocol/VersionCheckUseCase.swift (1)

14-14: VersionCheckUseCase 프로토콜 시그니처 업데이트 확인 완료

모든 구현체(DefaultVersionCheckUseCase)와 호출 지점(SplashViewModel)에서
checkForceUpdateNeeded() async throws -> ForceUpdate로 일관되게 반영되었으며,
더 이상 fetchAppStoreURL 사용 흔적이 없습니다.
비동기 패턴으로의 현대화와 타입 안전성 개선을 승인합니다.

Projects/FeatureDependency/Sources/Coordinator/Coordinator.swift (1)

20-20: 코디네이터 프로토콜에 URL 처리 표준화가 훌륭합니다.

모든 코디네이터에서 일관된 URL 처리를 제공하는 새로운 메서드 요구사항이 아키텍처를 개선합니다.

Projects/NetworkService/Sources/NetworkService/DefaultNetworkService.swift (1)

16-26: 비동기 네트워크 메서드 구현이 우수합니다.

새로운 async throws 메서드가 다음과 같은 장점을 제공합니다:

  • Swift 동시성 패턴에 맞는 현대적 구현
  • 적절한 오류 처리 (invalidResponse, invalidStatusCode)
  • HTTP 상태 코드 유효성 검증 (200-299 범위)
  • 기존 RxSwift 메서드와의 역호환성 유지
Projects/FeatureDependency/Sources/Coordinator/BusStopCoordinator.swift (3)

9-9: 도메인 모듈로의 import 변경이 적절합니다.

Foundation에서 Domain으로 import를 변경한 것은 도메인 타입들(BusStopArrivalInfoResponse, BusArrivalInfoResponse)을 사용하기 위한 올바른 의존성 변경입니다.


12-16: 델리게이트 패턴 도입으로 결합도가 개선되었습니다.

BusStopCoordinatordelegate 프로퍼티를 추가하여 버스 정류장 선택 이벤트를 위임할 수 있게 되었습니다. 이는 코디네이터 간의 결합도를 낮추고 재사용성을 높이는 좋은 아키텍처 개선입니다.


18-20: 델리게이트 프로토콜 정의가 명확합니다.

BusStopCoordinatorDelegate 프로토콜의 didSelect 메서드는 버스 정류장과 버스 정보를 명확하게 분리하여 전달받습니다. 도메인 타입을 직접 사용하여 타입 안정성을 확보한 점이 좋습니다.

Projects/Feature/HomeFeature/Project.swift (2)

8-18: Feature 모듈의 인터페이스/구현 분리가 잘 적용되었습니다.

FeatureInterfaceFeatureImplement 타겟으로 분리하여 모듈화를 개선했습니다:

  • FeatureInterface: 프로토콜과 인터페이스 정의
  • FeatureImplement: 구체적인 구현체

이러한 분리는 빌드 의존성을 줄이고 테스트 가능성을 높이는 좋은 아키텍처 패턴입니다.


22-26: AppInfoPlist 포맷팅 개선

다중 라인 스타일로 변경하여 가독성이 향상되었습니다.

Projects/NetworkService/Sources/EndPoint/EndPoint.swift (1)

65-82: URL 생성의 에러 핸들링이 개선되었습니다.

옵셔널 문자열을 반환하던 방식에서 throwing 프로퍼티로 변경하여:

  • 명시적인 에러 핸들링 제공
  • Swift concurrency 패턴과 일치
  • URLError(.badURL)로 구체적인 에러 타입 제공

이는 네트워크 서비스의 async/await 마이그레이션과 잘 어울리는 개선사항입니다.

Projects/Feature/BusStopFeature/Demo/SceneDelegate.swift (2)

6-7: 새로운 Feature 모듈 import 추가

NearMapFeatureAlarmFeatureTesting 모듈을 추가하여 데모 앱에서 필요한 의존성을 확보했습니다.


28-31: 코디네이터 빌더 패턴 적용이 적절합니다.

DefaultBusStopCoordinator 생성 시 새로운 매개변수들을 추가했습니다:

  • nearMapCoordinatorBuilder: 지도 기능을 위한 빌더
  • addRegularAlarmCoordinatorBuilder: 알람 추가 기능을 위한 빌더
  • delegate: 델리게이트 패턴 지원

데모 환경에서 Mock 구현체를 사용하는 것이 적절합니다.

Projects/Domain/Sources/Entity/VersionCheckInfo.swift (1)

11-19: 버전 체크 정보를 위한 엔티티 설계가 우수합니다.

VersionCheckInfo 구조체는 다음과 같은 좋은 설계 특징을 가지고 있습니다:

  • Codable 프로토콜 채택으로 직렬화 지원
  • 필수 앱 버전 정보와 업데이트 시간을 명확히 분리
  • public 초기화자 제공으로 모듈 외부에서 사용 가능
  • 불변 프로퍼티로 데이터 무결성 보장

이는 async/await 기반의 버전 체크 시스템과 잘 통합될 것으로 보입니다.

Projects/Domain/Sources/Entity/ForceUpdate.swift (1)

11-14: 도메인 모델링 개선이 잘 되었습니다!

구조체에서 열거형으로의 변경이 비즈니스 로직을 더 명확하게 표현합니다. 강제 업데이트 상태를 notNeededneeded(appStoreURL:)로 명시적으로 구분하여 타입 안전성을 높이고, 외부에서 버전 비교 로직을 처리할 필요가 없어졌습니다.

Projects/Feature/BusStopFeature/Project.swift (3)

18-19: 샘플 앱 구성이 적절합니다.

NearMapFeature를 Feature로, AlarmFeature를 FeatureTesting으로 추가한 것이 테스트 환경 구성에 적합합니다.


21-25: 앱 정보 설정이 명확해졌습니다.

AppInfoPlist에 구체적인 displayName과 버전 정보를 명시하여 샘플 앱 구성이 더 명확해졌습니다.


9-10: 새로운 피처 인터페이스 구현 확인 필요

NearMapFeatureAlarmFeature 인터페이스가 Project.swift에 추가되었으나, 자동 탐색(fd) 결과 해당 모듈이나 파일이 존재하지 않는 것으로 보입니다. 아래 항목을 확인해주세요:

  • NearMapFeature 모듈/디렉터리 및 관련 Swift 파일 존재 여부
  • AlarmFeature 모듈/디렉터리 및 관련 Swift 파일 존재 여부

구현이 완료되었음을 확인하거나, 누락된 경우 해당 모듈을 추가한 후 다시 알려주세요.

Projects/Data/Sources/DTO/BusStopArrivalInfoDTO.swift (1)

19-21: 에러 처리 개선이 좋습니다.

API 응답 에러를 명시적으로 처리할 수 있도록 전용 에러 타입을 추가한 것이 적절합니다. invalidResponse에 메시지를 포함하여 디버깅에도 도움이 됩니다.

Projects/Domain/Sources/Entity/Response/BusStopArrivalInfoResponse.swift (1)

126-129: 딕셔너리 생성 방식 개선이 좋습니다.

uniqueKeysWithValues 대신 uniquingKeysWith 클로저를 사용하여 중복 키가 있을 때 런타임 크래시를 방지하고 첫 번째 값을 유지하도록 개선했습니다. 더 안전하고 견고한 구현입니다.

Projects/Feature/HomeFeature/Implement/Sources/Coordinator/DefaultHomeCoordinator.swift (1)

68-69: 일관된 매개변수 패턴 적용

startBusStopFlow에서도 동일하게 flow: .fromHomedelegate: nil 매개변수가 추가되어 일관된 패턴을 유지하고 있습니다.

Projects/Domain/Sources/RepositoryInterface/BusStopArrivalInfoRepository.swift (2)

14-14: Swift 동시성 API 추가 - 우수한 접근법

새로운 async throws 메서드가 추가되어 최신 Swift 동시성 패턴을 지원합니다. 메서드 시그니처가 기존 RxSwift 버전과 일치하여 마이그레이션이 용이합니다.


16-19: 적절한 마이그레이션 전략

기존 RxSwift 메서드를 즉시 제거하지 않고 @available(*, deprecated) 어트리뷰트로 단계적 마이그레이션을 지원하는 것이 좋은 접근법입니다. 한국어로 된 명확한 메시지도 개발자에게 도움이 됩니다.

Projects/Feature/AlarmFeature/Implement/Sources/Coordinator/DefaultAddRegularAlarmCoordinator.swift (4)

22-22: 적절한 약한 참조 사용

AddRegularAlarmViewModel에 대한 약한 참조를 추가하여 retain cycle을 방지하면서 델리게이트 패턴을 지원합니다. 메모리 관리 측면에서 올바른 접근법입니다.


37-37: 뷰 모델 생성 코드 단순화

start() 메서드에서 뷰 모델 생성을 인라인으로 처리하여 코드가 더 간결해졌습니다.


48-50: 명시적 뷰 모델 관리

start(with:) 메서드에서 뷰 모델을 명시적으로 생성하고 저장한 후 뷰 컨트롤러에 전달하는 패턴이 명확합니다. 이후 델리게이트로 사용할 수 있도록 참조를 유지하는 것이 좋은 설계입니다.


62-63: 델리게이트 패턴을 통한 결합도 감소

검색 코디네이터 생성 시 viewModelbusStopCoordinatorDelegate로 전달하여 느슨한 결합도를 달성했습니다. 이는 더 나은 아키텍처 구조를 제공합니다.

Projects/Domain/Sources/Entity/Response/AppVersionInfoResponse.swift (3)

11-11: Core 모듈 임포트 추가

InfoPlistLoadable 프로토콜 사용을 위해 Core 모듈이 적절히 임포트되었습니다.


14-14: 기본 버전 상수 추가

defaultVersion을 1.0.0으로 설정한 것이 합리적입니다. 버전 파싱 실패 시 폴백으로 사용될 수 있습니다.


40-54: 견고한 버전 문자열 파싱 구현

InfoPlistLoadable 프로토콜 구현이 잘 되어 있습니다:

  • 문자열 타입 검증
  • "." 구분자로 분할 후 정수 변환
  • 정확히 3개 컴포넌트 검증
  • 실패 시 nil 반환

버전 문자열 파싱 로직이 안전하고 명확합니다.

Projects/CoreDataService/Sources/CoreDataModel.swift (1)

11-16: 잘 설계된 Core Data 매핑 프로토콜

CoreDataModel 프로토콜이 매우 잘 설계되었습니다:

  • Identifiable 제약 조건과 CVarArg를 준수하는 ID 요구사항이 Core Data predicate 생성에 필수적
  • associatedtype ManagedObject로 타입 안전성 보장
  • toDataModel(_:) 정적 메서드로 managed object → 데이터 모델 변환
  • sync(for:) 인스턴스 메서드로 데이터 모델 → managed object 동기화

제공된 코드 스니펫에서 FavoritesBusResponseMO+CoreDataClass.swift의 구현과 완벽히 일치하며, 양방향 매핑을 지원하는 견고한 아키텍처입니다.

Projects/Feature/NearMapFeature/Testing/Sources/MockNearMapCoordinatorBuilder.swift (2)

11-12: 테스트용 mock builder 구현이 적절합니다.

MockNearMapCoordinatorBuilderNearMapCoordinatorBuilder 프로토콜을 올바르게 구현하고 있으며, public 접근 제어자와 빈 초기화 메서드가 테스트 환경에서의 사용에 적합합니다.


14-27: Mock coordinator 생성 로직이 일관성 있게 구현되었습니다.

build 메서드가 DefaultNearMapCoordinatorMockCoordinatorProvider와 함께 생성하여, 테스트 환경에서 필요한 의존성 주입을 제공하고 있습니다. 메서드 시그니처와 반환 타입이 프로토콜 요구사항과 정확히 일치합니다.

Projects/Feature/NearMapFeature/Interface/Sources/NearMapCoordinator.swift (3)

9-11: 모듈 의존성이 적절하게 구성되었습니다.

UIKit import와 @_exported import FeatureDependency 사용이 인터페이스 모듈의 목적에 부합합니다. @_exported를 통해 FeatureDependency의 타입들을 재내보내어 클라이언트 코드에서 편리하게 사용할 수 있도록 했습니다.


13-15: Coordinator 프로토콜이 명확하게 정의되었습니다.

NearMapCoordinator가 기본 Coordinator를 상속하고 startBusStopFlow(busStopId:) 메서드를 선언하여, 근처 지도 기능의 navigation flow를 명확하게 정의하고 있습니다.


17-24: Builder 프로토콜이 의존성 주입 패턴을 잘 지원합니다.

NearMapCoordinatorBuilderbuild 메서드가 필요한 모든 의존성(parent coordinator, navigation controller, flow state, busStopId)을 매개변수로 받아 coordinator 생성을 위한 완전한 팩토리 인터페이스를 제공합니다.

Projects/NetworkService/Sources/NetworkService/NetworkService.swift (3)

14-14: Swift concurrency 지원이 추가되어 좋습니다.

새로운 async throws 메서드가 추가되어 최신 Swift concurrency 패턴을 지원합니다. 이는 RxSwift에서 async/await로의 점진적 마이그레이션을 가능하게 합니다.


16-21: Deprecation 메시지가 명확하게 작성되었습니다.

한국어로 된 deprecation 메시지가 개발자들에게 마이그레이션 경로를 명확하게 안내하고 있습니다. renamed 매개변수를 통해 IDE에서 자동 수정 제안을 받을 수 있어 좋습니다.


23-28: 일관된 deprecation 패턴이 적용되었습니다.

두 번째 RxSwift 메서드에도 동일한 deprecation 패턴이 적용되어 일관성을 유지하고 있습니다. 개발자들이 두 메서드 모두 동일한 async 메서드로 마이그레이션해야 함을 명확히 알 수 있습니다.

Projects/Feature/HomeFeature/Interface/Sources/HomeCoordinator.swift (3)

9-11: 모듈 의존성이 올바르게 설정되었습니다.

UIKit과 FeatureDependency import가 HomeCoordinator 인터페이스 정의에 필요한 타입들을 적절히 가져오고 있습니다.


13-16: Home feature navigation 인터페이스가 명확합니다.

HomeCoordinatorstartSearchFlow()startBusStopFlow(stationId:) 메서드를 통해 홈 화면에서 가능한 주요 navigation flow들을 명확하게 정의하고 있습니다.


18-23: Builder 패턴이 의존성 주입을 잘 지원합니다.

HomeCoordinatorBuilderbuild 메서드가 필요한 핵심 의존성들(parent coordinator, navigation controller)을 매개변수로 받아 간결하면서도 완전한 팩토리 인터페이스를 제공합니다.

Projects/FeatureDependency/Sources/Mock/MockBusStopArrivalInfoRepository.swift (1)

18-18: Async/await 지원이 추가되어 좋습니다.

fetchArrivalList(busStopId:) async throws -> Domain.BusStopArrivalInfoResponse 메서드가 추가되어 새로운 Swift concurrency 패턴을 지원합니다. 이는 실제 repository 구현과 일치하는 인터페이스를 제공합니다.

Projects/Feature/AlarmFeature/Testing/Sources/MockAddRegularAlarmCoordinatorBuilder.swift (1)

11-25: 테스트 목적에 맞는 깔끔한 mock 구현입니다.

Mock builder 패턴을 잘 적용했으며, MockCoordinatorProvider를 통한 의존성 격리도 적절합니다. 코드 구조와 네이밍이 일관성 있게 작성되었습니다.

Projects/App/Sources/Coordinator/Splash/SplashViewController.swift (1)

34-38: viewDidLoad 시점의 즉시 실행을 검토해주세요.

.just(())로 viewDidLoad와 동시에 즉시 로직을 실행하는데, 화면 전환이나 애니메이션을 고려하면 약간의 딜레이가 필요할 수 있습니다.

스플래시 화면의 사용자 경험을 고려하여 즉시 실행이 적절한지 확인해주세요. 필요시 다음과 같은 접근을 고려할 수 있습니다:

input: .init(
    viewDidLoad: .just(()).delay(.milliseconds(500), scheduler: MainScheduler.instance)
)
Projects/Domain/Sources/Entity/Alert.swift (1)

13-33: 깔끔한 도메인 엔티티 설계입니다.

AlertAlertAction 구조체가 잘 설계되었습니다:

  • 불변성을 보장하는 let 프로퍼티들
  • @TypeBuilder 결과 빌더를 통한 선언적 API
  • 도메인 레이어에 적절히 위치

현재 요구사항에는 충분하며, 향후 필요시 AlertAction에 스타일 정보(.destructive, .cancel 등)를 추가할 수 있을 것입니다.

Projects/Feature/NearMapFeature/Project.swift (2)

8-24: Feature 모듈의 3계층 분리가 잘 설계되었습니다.

Interface, Implement, Testing으로의 분리가 클린 아키텍처 원칙에 잘 부합합니다:

  • FeatureInterface: 외부 의존성을 최소화한 인터페이스 레이어
  • FeatureImplement: 구체적 구현과 시크릿 설정 포함
  • FeatureTesting: 테스트 전용 환경 제공

각 타겟의 의존성 설정도 적절하며, 향후 다른 Feature 모듈들도 동일한 패턴을 따르면 일관성 있는 아키텍처를 유지할 수 있을 것입니다.


28-32: AppInfoPlist 포맷팅 개선이 좋습니다.

멀티라인 포맷으로 변경하여 가독성이 향상되었습니다. 파라미터별로 명확히 구분되어 유지보수가 용이해졌습니다.

Projects/Feature/AlarmFeature/Interface/Sources/AddRegularAlarmCoordinator.swift (1)

17-23: Builder 패턴 구현이 적절함

Builder 프로토콜을 통한 의존성 주입 패턴이 잘 설계되었습니다. 테스트 가능성과 모듈화를 향상시키는 좋은 접근입니다.

Projects/Data/Sources/DTO/CoreDataModelObject/FavoritesBusResponseMO+CoreDataClass.swift (1)

14-14: CoreDataService 모듈 통합이 적절함

새로운 CoreData 추상화 레이어와의 통합이 잘 구현되었습니다.

Projects/Feature/AlarmFeature/Project.swift (1)

27-31: 코드 가독성 개선이 적절함

AppInfoPlist 초기화를 멀티라인으로 포맷팅한 것이 가독성을 향상시켰습니다.

Projects/Feature/SettingsFeature/Sources/View/SettingButtonView.swift (2)

22-30: 불변성 및 InfoPlist 접근 방식 개선이 우수함

lazy varlet으로 변경하여 불변성을 향상시키고, 새로운 @InfoPlistWrapper를 통한 타입 안전한 버전 정보 접근이 잘 구현되었습니다.


32-60: 일관된 코드 스타일 개선

모든 버튼 속성들을 let으로 통일하여 일관성과 불변성을 확보한 것이 좋습니다.

Projects/CoreDataService/Sources/CoreDataContainerBuilder.swift (1)

50-57: CloudKit 상태 확인 로직이 적절함

CloudKit 가용성을 비동기적으로 확인하는 방식이 잘 구현되었습니다. 에러가 발생할 경우 CloudKit을 사용하지 않도록 안전하게 처리하는 것도 좋습니다.

Plugins/EnvironmentPlugin/ProjectDescriptionHelpers/Buildable/TargetBuildable/Target/Feature/FeatureImplement.swift (1)

9-36: 모듈화된 Feature 아키텍처 구조가 잘 설계되었습니다.

FeatureImplement 구조체가 다음과 같은 좋은 설계 패턴을 보여줍니다:

  • @TargetComponentBuilder를 통한 일관된 빌더 패턴
  • hasResource 플래그를 통한 조건부 리소스 처리
  • 상대 경로를 통한 명확한 타겟 의존성 관리

Interface/Implement/Testing으로 분리된 모듈 구조가 의존성 관리와 테스트 가능성을 크게 개선할 것으로 보입니다.

Projects/FeatureDependency/Sources/Mock/MockCoordinatorProvider.swift (2)

17-27: MockCoordinator에서 delegate 매개변수 처리가 적절합니다.

테스트용 Mock에서 busStopCoordinatorDelegate 매개변수를 추가했지만 실제로는 사용하지 않는 것이 올바른 접근입니다. Mock 구현체는 실제 비즈니스 로직을 수행하지 않고 테스트 메시지만 반환하므로, delegate를 사용하지 않는 것이 적절합니다.


29-40: BusStopCoordinator delegate 패턴 지원이 잘 구현되었습니다.

makeBusStopCoordinator 메서드에 delegate 매개변수가 추가되어 새로운 delegation 패턴을 지원합니다. Mock 구현에서는 delegate를 사용하지 않고 테스트용 MockCoordinator를 반환하는 것이 테스트 환경에 적합합니다.

Plugins/EnvironmentPlugin/ProjectDescriptionHelpers/Buildable/TargetBuildable/Target/Feature/FeatureInterface.swift (1)

8-31: Feature Interface 아키텍처가 일관성 있게 구현되었습니다.

FeatureInterface 구조체가 다음과 같은 좋은 설계를 보여줍니다:

  • BundleIDSuffixTargetLocalDependency 프로토콜 준수로 명확한 역할 정의
  • projectName + "Interface" 네이밍 규칙으로 일관된 타겟 이름 생성
  • .framework 제품 타입으로 인터페이스 레이어에 적합한 설정
  • FeatureImplement와 동일한 빌더 패턴으로 일관성 유지

Interface/Implement 분리를 통해 의존성 역전과 모듈 간 결합도 감소를 잘 달성했습니다.

Projects/Data/Sources/Repository/DefaultFavoritesRepository.swift (1)

18-35: AsyncStream을 활용한 비동기 Repository 구현이 잘 설계되었습니다.

AsyncFavoritesRepositoryImpl actor의 설계가 훌륭합니다:

  • Actor를 사용한 스레드 안전성 보장
  • nonisolated AsyncStream으로 외부 접근 허용 (AsyncStream은 Sendable이므로 안전)
  • .bufferingNewest(1) 정책으로 최신 상태 유지
  • 점진적 마이그레이션을 위해 기존 RxSwift 구현과 공존
Projects/MainFeature/Sources/TabBarCoordinator.swift (3)

14-14: Interface 모듈 import로 의존성 분리가 잘 적용되었습니다.

HomeFeature 대신 HomeFeatureInterface를 import하여 인터페이스와 구현체를 분리한 아키텍처가 잘 반영되었습니다. 이는 모듈 간 결합도를 낮추고 테스트 가능성을 높이는 좋은 설계입니다.


23-33: HomeCoordinatorBuilder를 통한 의존성 주입이 잘 구현되었습니다.

Builder 패턴을 도입하여 coordinator 생성을 모듈화한 것이 좋은 개선입니다:

  • 의존성 주입을 통한 테스트 가능성 향상
  • 생성 로직의 캡슐화
  • 점진적 마이그레이션 지원 (Home만 Builder 적용, 나머지는 직접 생성)

74-77: Builder 패턴을 통한 Home Coordinator 생성이 잘 구현되었습니다.

homeCoordinatorBuilder.build 메서드를 사용하여 coordinator를 생성하는 방식이 다음과 같은 이점을 제공합니다:

  • 생성 로직의 캡슐화
  • 의존성 주입 패턴 지원
  • Mock을 사용한 테스트 가능성 향상

다른 coordinator들도 점진적으로 Builder 패턴으로 마이그레이션되면 더욱 일관된 아키텍처가 될 것입니다.

Plugins/EnvironmentPlugin/ProjectDescriptionHelpers/Buildable/TargetBuildable/Target/Feature/FeatureTesting.swift (1)

10-33: 깔끔한 테스팅 타겟 구현입니다!

Feature 모듈의 테스팅 타겟을 위한 구조체가 잘 구현되었습니다. BundleIDSuffixTarget과 LocalDependency 프로토콜을 적절히 구현하고, 빌더 패턴을 통한 유연한 의존성 구성이 좋습니다.

Projects/Core/Sources/Extension/String+.swift (1)

29-38: InfoPlistWrapper 적용이 훌륭합니다

새로운 property wrapper를 사용하여 Info.plist 접근을 타입 안전하게 만들었습니다. 기본값 설정도 적절합니다.

Projects/App/Sources/SceneDelegate.swift (2)

27-30: AsyncStream 구현이 적절합니다

Scene foreground 이벤트를 위한 AsyncStream이 올바르게 구현되었습니다. 버퍼링 정책도 적절합니다.

Also applies to: 81-81


18-42: 의존성 프로토콜 구현이 깔끔합니다

AppCoordinatorDependency 프로토콜 준수가 잘 구현되었고, @InfoPlistWrapper를 통한 설정 값 접근이 일관성 있게 적용되었습니다.

Projects/App/Sources/Coordinator/AppCoordinator.swift (3)

15-20: 의존성 주입 패턴 적용 👍

AppCoordinatorDependency 프로토콜 도입으로 의존성이 명확하게 정의되었습니다. AsyncStream<UIScene>을 사용한 scene 이벤트 처리 방식도 적절합니다.


39-47: Splash 화면 플로우 추가

앱 시작 시 SplashCoordinatorImpl을 통해 초기화 작업을 처리하는 방식으로 변경되었습니다. 이는 앱 초기화 로직을 중앙화하는 좋은 접근입니다.


65-69: URL 열기 전 검증 로직이 이미 포함됨

canOpenURL 검증 후 open을 호출하는 안전한 구현입니다.

Projects/Domain/Sources/RepositoryInterface/FavoritesRepository.swift (2)

14-22: 단계적 마이그레이션 전략 👍

기존 RxSwift 기반 API를 deprecated로 표시하여 점진적인 마이그레이션을 지원하는 좋은 접근입니다.


25-31: Swift Concurrency 기반 새로운 인터페이스

AsyncStream과 async/await 패턴을 활용한 현대적인 API 설계입니다.

Projects/FeatureDependency/Sources/Coordinator/CoordinatorProvider.swift (2)

14-19: Delegate 패턴 추가로 coordinator 간 통신 개선

busStopCoordinatorDelegate 매개변수 추가로 coordinator 간 통신이 더 명확해졌습니다.


21-27: 일관된 delegate 패턴 적용

BusStopCoordinator에도 delegate 패턴이 적용되어 전체적인 일관성이 향상되었습니다.

Projects/Domain/Sources/UseCase/DefaultVersionCheckUseCase.swift (2)

15-21: 의존성 주입 개선

currentVersion을 생성자를 통해 주입받도록 변경하여 테스트 가능성과 유연성이 향상되었습니다.


23-40: 효율적인 캐싱 전략과 async/await 마이그레이션

4시간 캐시 타임아웃과 함께 구현된 버전 체크 로직이 효율적입니다. RxSwift에서 Swift Concurrency로의 마이그레이션도 깔끔하게 처리되었습니다.

Projects/Domain/Sources/RepositoryInterface/VersionCheckRepository.swift (1)

11-17: 깔끔한 async/await 인터페이스로 리팩토링

RxSwift 의존성을 제거하고 Swift Concurrency 기반으로 전환하여 더 간결하고 명확한 인터페이스가 되었습니다. 캐싱 관련 메서드들도 명확하게 분리되어 있습니다.

Projects/Core/Sources/PropertyWrapper/InfoPlistWrapper.swift (3)

11-27: LGTM!

InfoPlistWrapper의 구현이 깔끔하고 안전합니다. defaultValue를 통한 fallback 처리가 적절합니다.


29-43: Optional 지원 구현이 우아합니다!

Optional 타입에 대한 InfoPlistLoadable 구현과 convenience initializer가 잘 설계되었습니다. nil을 defaultValue로 사용할 수 있어 편리합니다.


49-100: 기본 타입 구현이 견고합니다!

각 타입별로 Info.plist의 다양한 값 형태를 적절히 처리하고 있습니다. 특히 Bool 타입의 문자열 파싱 처리가 세심합니다.

Projects/Feature/SearchFeature/Sources/Coordinator/DafaultSearchCoordinator.swift (3)

11-14: 프로퍼티 변경이 적절합니다!

delegate에 weak 참조 사용과 coordinatorProvider의 접근 제한이 메모리 관리와 캡슐화 측면에서 개선되었습니다.


45-45: 파라미터 명명 일관성 개선!

busStopID로 통일된 네이밍이 코드의 일관성을 향상시킵니다.


57-77: 빌더 패턴 적용이 훌륭합니다!

NearMapCoordinatorBuilder를 통한 coordinator 생성이 모듈성과 테스트 용이성을 개선합니다.

Projects/Data/Sources/Repository/DefaultVersionCheckRepository.swift (2)

21-27: 의존성 주입 개선!

설정 값들을 생성자 주입으로 변경한 것이 테스트 용이성과 명시적 의존성 관리 측면에서 개선되었습니다.


33-39: async/await 마이그레이션이 깔끔합니다!

RxSwift에서 Swift Concurrency로의 전환이 잘 이루어졌고, 에러 처리도 적절합니다.

Projects/CoreDataService/Sources/CoreDataStorageImpl.swift (3)

19-27: Core Data 컨텍스트 설정이 우수합니다!

백그라운드 컨텍스트 사용, undo 매니저 비활성화, 그리고 충돌 해결 정책 설정이 성능과 안정성을 고려한 좋은 선택입니다.


43-54: 에러 처리가 견고합니다!

변경사항 확인, 에러 시 롤백, 그리고 에러 재전파가 적절히 구현되었습니다.


57-100: 전반적인 CRUD 구현이 훌륭합니다!

context.perform을 통한 스레드 안전성, 타입 체크, 그리고 일관된 에러 처리가 잘 구현되었습니다.

Projects/Feature/BusStopFeature/Sources/Coordinator/DafaultBusStopCoordinator.swift (2)

12-18: 빌더 패턴과 delegate 적용이 일관성 있습니다!

다른 Coordinator들과 동일한 패턴을 적용하여 일관성과 모듈성이 향상되었습니다. weak delegate 사용도 적절합니다.

Also applies to: 26-28, 35-37


69-77: moveToRegualrAlarm의 flow 타입 확인 필요

flow가 .fromAlarm으로 하드코딩되어 있습니다. 이것이 의도된 것인지 확인이 필요합니다.

다른 flow 타입이 필요한 경우가 있는지, 또는 항상 .fromAlarm이 올바른지 확인해주세요.

Projects/FeatureDependency/Sources/Mock/MockCoordinator.swift (1)

55-87: SearchCoordinator 구현이 적절합니다

Mock coordinator의 메서드 구현이 테스트 목적에 적합하게 작성되었습니다. child coordinator 추적도 올바르게 처리되고 있습니다.

Projects/App/Sources/DefaultCoordinatorProvider.swift (5)

17-19: 인터페이스 모듈 import가 적절합니다

Feature 모듈 리팩토링의 일환으로 인터페이스 모듈을 분리한 것이 좋은 설계 결정입니다. 이는 모듈 간 의존성을 줄이고 테스트 가능성을 향상시킵니다.


23-40: Coordinator 생성 메서드가 Builder 패턴으로 개선되었습니다

delegate 파라미터 추가와 builder 참조 전달이 적절하게 구현되었습니다. 이는 의존성 주입과 모듈화를 개선하는 좋은 변경사항입니다.


42-56: SearchCoordinator 생성 메서드도 일관되게 업데이트되었습니다

Builder 패턴과 delegate 처리가 makeBusStopCoordinator와 일관되게 적용되었습니다.


83-91: HomeCoordinatorBuilder 구현이 적절합니다

Builder 패턴이 올바르게 구현되었습니다.


93-108: NearMapCoordinatorBuilder 구현이 적절합니다

모든 파라미터가 올바르게 전달되고 있으며, optional busStopId 처리도 적절합니다.

Comment on lines 20 to 59
final class SplashCoordinatorImpl: SplashCoordinator {
var parent: Coordinator?
var childs: [Coordinator] = []
var navigationController: UINavigationController

private let coordinatorProvider: CoordinatorProvider
private let homeCoordinatorBuilder: HomeCoordinatorBuilder
private let viewModelDependency: SplashViewModelDependency

init(
parent: Coordinator,
navigationController: UINavigationController,
coordinatorProvider: CoordinatorProvider,
homeCoordinatorBuilder: HomeCoordinatorBuilder,
viewModelDependency: SplashViewModelDependency
) {
self.parent = parent
self.navigationController = navigationController
self.coordinatorProvider = coordinatorProvider
self.homeCoordinatorBuilder = homeCoordinatorBuilder
self.viewModelDependency = viewModelDependency
}

func start() {
let splashViewController = SplashViewController(
viewModel: SplashViewModel(coordinator: self, dependency: viewModelDependency)
)
navigationController.setViewControllers([splashViewController], animated: false)
}

func startTabFlow() {
let tabBarCoordinator = TabBarCoordinator(
navigationController: navigationController,
coordinatorProvider: coordinatorProvider,
homeCoordinatorBuilder: homeCoordinatorBuilder
)
childs.append(tabBarCoordinator)
tabBarCoordinator.start()
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

openURL 메서드 구현 누락

SplashCoordinator 프로토콜에 선언된 openURL(_:) 메서드의 구현이 누락되었습니다. 컴파일 오류가 발생할 것입니다.

 func startTabFlow() {
     let tabBarCoordinator = TabBarCoordinator(
         navigationController: navigationController,
         coordinatorProvider: coordinatorProvider,
         homeCoordinatorBuilder: homeCoordinatorBuilder
     )
     childs.append(tabBarCoordinator)
     tabBarCoordinator.start()
 }
+
+func openURL(_ url: URL) {
+    parent?.openURL(url)
+}
📝 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
final class SplashCoordinatorImpl: SplashCoordinator {
var parent: Coordinator?
var childs: [Coordinator] = []
var navigationController: UINavigationController
private let coordinatorProvider: CoordinatorProvider
private let homeCoordinatorBuilder: HomeCoordinatorBuilder
private let viewModelDependency: SplashViewModelDependency
init(
parent: Coordinator,
navigationController: UINavigationController,
coordinatorProvider: CoordinatorProvider,
homeCoordinatorBuilder: HomeCoordinatorBuilder,
viewModelDependency: SplashViewModelDependency
) {
self.parent = parent
self.navigationController = navigationController
self.coordinatorProvider = coordinatorProvider
self.homeCoordinatorBuilder = homeCoordinatorBuilder
self.viewModelDependency = viewModelDependency
}
func start() {
let splashViewController = SplashViewController(
viewModel: SplashViewModel(coordinator: self, dependency: viewModelDependency)
)
navigationController.setViewControllers([splashViewController], animated: false)
}
func startTabFlow() {
let tabBarCoordinator = TabBarCoordinator(
navigationController: navigationController,
coordinatorProvider: coordinatorProvider,
homeCoordinatorBuilder: homeCoordinatorBuilder
)
childs.append(tabBarCoordinator)
tabBarCoordinator.start()
}
}
final class SplashCoordinatorImpl: SplashCoordinator {
var parent: Coordinator?
var childs: [Coordinator] = []
var navigationController: UINavigationController
private let coordinatorProvider: CoordinatorProvider
private let homeCoordinatorBuilder: HomeCoordinatorBuilder
private let viewModelDependency: SplashViewModelDependency
init(
parent: Coordinator,
navigationController: UINavigationController,
coordinatorProvider: CoordinatorProvider,
homeCoordinatorBuilder: HomeCoordinatorBuilder,
viewModelDependency: SplashViewModelDependency
) {
self.parent = parent
self.navigationController = navigationController
self.coordinatorProvider = coordinatorProvider
self.homeCoordinatorBuilder = homeCoordinatorBuilder
self.viewModelDependency = viewModelDependency
}
func start() {
let splashViewController = SplashViewController(
viewModel: SplashViewModel(coordinator: self, dependency: viewModelDependency)
)
navigationController.setViewControllers([splashViewController], animated: false)
}
func startTabFlow() {
let tabBarCoordinator = TabBarCoordinator(
navigationController: navigationController,
coordinatorProvider: coordinatorProvider,
homeCoordinatorBuilder: homeCoordinatorBuilder
)
childs.append(tabBarCoordinator)
tabBarCoordinator.start()
}
func openURL(_ url: URL) {
parent?.openURL(url)
}
}
🤖 Prompt for AI Agents
In Projects/App/Sources/Coordinator/Splash/SplashCoordinator.swift between lines
20 and 59, the SplashCoordinatorImpl class is missing the implementation of the
openURL(_:) method declared in the SplashCoordinator protocol, causing a compile
error. Add the openURL(_:) method to SplashCoordinatorImpl with the required
signature and provide an appropriate implementation or a placeholder to satisfy
the protocol conformance.

Comment on lines 40 to 59
output.alert
.observe(on: MainScheduler.instance)
.bind(with: self) { owner, alert in
let alertController = UIAlertController(
title: alert.title,
message: alert.message,
preferredStyle: .alert
)
alert.actions.forEach { alertAction in
let alertAction = UIAlertAction(
title: alertAction.title,
style: .default
) { _ in
alertAction.handler()
}
alertController.addAction(alertAction)
}
owner.present(alertController, animated: true)
}
.disposed(by: disposeBag)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Alert 바인딩에 에러 처리와 안전장치를 추가해주세요.

현재 alert 바인딩에 몇 가지 개선이 필요합니다:

  1. 에러 처리가 없어서 스트림에서 에러 발생 시 앱이 예상치 못하게 동작할 수 있습니다
  2. alert.actions가 빈 배열인 경우를 고려해야 합니다

다음과 같이 개선하는 것을 권장합니다:

 output.alert
     .observe(on: MainScheduler.instance)
     .bind(with: self) { owner, alert in
+        guard !alert.actions.isEmpty else { return }
         let alertController = UIAlertController(
             title: alert.title,
             message: alert.message,
             preferredStyle: .alert
         )
         alert.actions.forEach { alertAction in
             let alertAction = UIAlertAction(
                 title: alertAction.title,
                 style: .default
             ) { _ in
                 alertAction.handler()
             }
             alertController.addAction(alertAction)
         }
         owner.present(alertController, animated: true)
     }
+    .catch { error in
+        // 에러 로깅 및 기본 처리
+        return .empty()
+    }
     .disposed(by: disposeBag)

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

🤖 Prompt for AI Agents
In Projects/App/Sources/Coordinator/Splash/SplashViewController.swift around
lines 40 to 59, the alert binding lacks error handling and does not check if
alert.actions is empty. To fix this, add error handling to the observable chain
to gracefully handle any errors emitted by the alert stream, preventing
unexpected app behavior. Also, before iterating over alert.actions, check if the
array is empty and handle that case appropriately, such as by not presenting the
alert or providing a default action to avoid presenting an alert with no
actions.

Comment on lines 73 to 112
private func registerDependency() async {
let coreDataContainer = await CoreDataContainerBuilder().buildContainer()
let firebaseLogger = FirebaseLoggerImpl()

DIContainer.setLogger(firebaseLogger)

DIContainer.register(type: CoreDataStorage.self, CoreDataStorageImpl(container: coreDataContainer))
DIContainer.register(type: CoreDataService.self, DefaultCoreDataService())
DIContainer.register(type: NetworkService.self, DefaultNetworkService())
DIContainer.register(type: LocationService.self, DefaultLocationService())

DIContainer.register(type: AsyncFavoritesRepository.self, AsyncFavoritesRepositoryImpl())
DIContainer.register(type: FavoritesRepository.self, DefaultFavoritesRepository())
DIContainer.register(type: BusStopArrivalInfoRepository.self, DefaultBusStopArrivalInfoRepository())
DIContainer.register(type: StationListRepository.self, DefaultStationListRepository())
DIContainer.register(type: RegularAlarmRepository.self, DefaultRegularAlarmRepository())
DIContainer.register(type: LocalNotificationService.self, DefaultLocalNotificationService())
DIContainer.register(type: RegularAlarmEditingService.self, DefaultRegularAlarmEditingService())
DIContainer.register(
type: VersionCheckRepository.self,
VersionCheckRepositoryImpl(
appStoreID: dependency.appStoreID,
domainURL: dependency.domainURL
)
)

DIContainer.register(type: FavoritesUseCase.self, DefaultFavoritesUseCase())
DIContainer.register(type: RegularAlarmUseCase.self, DefaultRegularAlarmUseCase())
DIContainer.register(type: AddRegularAlarmUseCase.self, DefaultAddRegularAlarmUseCase())
DIContainer.register(type: SearchUseCase.self, DefaultSearchUseCase())
DIContainer.register(type: BusStopUseCase.self, DefaultBusStopUseCase())
DIContainer.register(type: NearMapUseCase.self, DefaultNearMapUseCase())
DIContainer.register(type: FirebaseLogger.self, firebaseLogger)
DIContainer.register(
type: VersionCheckUseCase.self,
VersionCheckUseCaseImpl(
currentVersion: dependency.appVersion
)
)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

의존성 등록 메서드 리팩토링 제안

의존성 등록 로직이 너무 길고 복잡합니다. 유지보수성을 위해 카테고리별로 분리하는 것을 권장합니다.

 private func registerDependency() async {
+    await registerCoreServices()
+    registerRepositories()
+    registerUseCases()
+}
+
+private func registerCoreServices() async {
     let coreDataContainer = await CoreDataContainerBuilder().buildContainer()
     let firebaseLogger = FirebaseLoggerImpl()
     
     DIContainer.setLogger(firebaseLogger)
     
     DIContainer.register(type: CoreDataStorage.self, CoreDataStorageImpl(container: coreDataContainer))
     DIContainer.register(type: CoreDataService.self, DefaultCoreDataService())
     DIContainer.register(type: NetworkService.self, DefaultNetworkService())
     DIContainer.register(type: LocationService.self, DefaultLocationService())
+    DIContainer.register(type: FirebaseLogger.self, firebaseLogger)
+}
+
+private func registerRepositories() {
     DIContainer.register(type: AsyncFavoritesRepository.self, AsyncFavoritesRepositoryImpl())
     // ... 나머지 repository 등록
+}
+
+private func registerUseCases() {
     DIContainer.register(type: FavoritesUseCase.self, DefaultFavoritesUseCase())
     // ... 나머지 use case 등록
-}

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

🤖 Prompt for AI Agents
In Projects/App/Sources/Coordinator/Splash/SplashViewModel.swift around lines 73
to 112, the registerDependency method is too long and complex. Refactor this
method by splitting the dependency registrations into smaller private helper
methods grouped by categories such as services, repositories, and use cases.
Then call these helper methods from registerDependency to improve readability
and maintainability.

Comment on lines 72 to 81
extension DefaultCoordinatorProvider: AddRegularAlarmCoordinatorBuilder {
func build(parent: any Coordinator, navigationController: UINavigationController, flow: FlowState) -> any AddRegularAlarmCoordinator {
DefaultAddRegularAlarmCoordinator(
parent: parent,
navigationController: navigationController,
coordinatorProvider: self,
flow: .fromAlarm
)
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

build 메서드에서 flow 파라미터가 무시되고 있습니다

build 메서드에서 flow 파라미터를 받고 있지만, 실제로는 하드코딩된 .fromAlarm을 사용하고 있습니다. 이는 버그로 보입니다.

 func build(parent: any Coordinator, navigationController: UINavigationController, flow: FlowState) -> any AddRegularAlarmCoordinator {
     DefaultAddRegularAlarmCoordinator(
         parent: parent,
         navigationController: navigationController,
         coordinatorProvider: self,
-        flow: .fromAlarm
+        flow: flow
     )
 }

동일한 문제가 58-69번 라인의 makeAddRegularAlarmCoordinator 메서드에도 있는 것으로 보입니다.

📝 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
extension DefaultCoordinatorProvider: AddRegularAlarmCoordinatorBuilder {
func build(parent: any Coordinator, navigationController: UINavigationController, flow: FlowState) -> any AddRegularAlarmCoordinator {
DefaultAddRegularAlarmCoordinator(
parent: parent,
navigationController: navigationController,
coordinatorProvider: self,
flow: .fromAlarm
)
}
}
extension DefaultCoordinatorProvider: AddRegularAlarmCoordinatorBuilder {
func build(parent: any Coordinator, navigationController: UINavigationController, flow: FlowState) -> any AddRegularAlarmCoordinator {
DefaultAddRegularAlarmCoordinator(
parent: parent,
navigationController: navigationController,
coordinatorProvider: self,
flow: flow
)
}
}
🤖 Prompt for AI Agents
In Projects/App/Sources/DefaultCoordinatorProvider.swift around lines 72 to 81,
the build method receives a flow parameter but ignores it by hardcoding
.fromAlarm. Update the method to use the passed flow parameter instead of the
hardcoded value. Also, check lines 58 to 69 for the
makeAddRegularAlarmCoordinator method and apply the same fix to ensure it uses
the flow parameter properly.

Comment on lines +11 to +15
public protocol AddRegularAlarmCoordinator: Coordinator {
func start(with: RegularAlarmResponse)
func startSearchFlow()
func removeChildViewController()
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

프로토콜 메서드에 문서화와 매개변수 레이블 개선 필요

현재 프로토콜 메서드들이 명확한 문서화 없이 정의되어 있습니다. 특히 start(with:) 메서드의 매개변수 레이블이 불명확합니다.

다음과 같이 개선을 제안합니다:

 public protocol AddRegularAlarmCoordinator: Coordinator {
-    func start(with: RegularAlarmResponse)
+    /// 정규 알람 응답을 사용하여 코디네이터를 시작합니다
+    func start(with response: RegularAlarmResponse)
+    /// 검색 플로우를 시작합니다
     func startSearchFlow()
+    /// 자식 뷰 컨트롤러를 제거합니다
     func removeChildViewController()
 }
📝 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
public protocol AddRegularAlarmCoordinator: Coordinator {
func start(with: RegularAlarmResponse)
func startSearchFlow()
func removeChildViewController()
}
public protocol AddRegularAlarmCoordinator: Coordinator {
/// 정규 알람 응답을 사용하여 코디네이터를 시작합니다
func start(with response: RegularAlarmResponse)
/// 검색 플로우를 시작합니다
func startSearchFlow()
/// 자식 뷰 컨트롤러를 제거합니다
func removeChildViewController()
}
🤖 Prompt for AI Agents
In
Projects/Feature/AlarmFeature/Interface/Sources/AddRegularAlarmCoordinator.swift
lines 11 to 15, the protocol methods lack documentation and the parameter label
for the start(with:) method is unclear. Add clear documentation comments above
each method explaining their purpose, and rename the parameter label in
start(with:) to a more descriptive name that clarifies what the
RegularAlarmResponse argument represents.

Comment on lines +55 to +56
flow: .fromHome,
busStopCoordinatorDelegate: nil
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

명시적 매개변수 전달로 개선됨

flow: .fromHomebusStopCoordinatorDelegate: nil 매개변수가 추가되어 코디네이터 생성이 더 명시적이 되었습니다. 델리게이트를 nil로 전달하는 것이 적절한지 확인이 필요합니다.

다음 스크립트로 다른 코디네이터에서 delegate가 어떻게 사용되는지 확인해보세요:


🏁 Script executed:

#!/bin/bash
# Description: 다른 코디네이터에서 busStopCoordinatorDelegate 사용 패턴 확인

# 델리게이트가 nil이 아닌 경우들을 찾아보기
rg -A 3 "busStopCoordinatorDelegate:" --type swift

Length of output: 4047


코디네이터 Delegate 전달 확인 및 수정 필요

다른 피처(AlarmFeature 등)에서는 busStopCoordinatorDelegate에 실제 뷰모델이나 self를 넘겨 콜백을 처리하고 있습니다.
HomeFeature에서도 nil이 전달되면 버스정류장 선택 후 이벤트가 누락될 수 있으므로, 아래 위치를 점검해 주세요.

  • 파일: Projects/Feature/HomeFeature/Implement/Sources/Coordinator/DefaultHomeCoordinator.swift
    • startSearchFlow() (55–56행)
      - busStopCoordinatorDelegate: nil
      + busStopCoordinatorDelegate: self // 혹은 실제 뷰모델
    • startBusStopFlow(stationId:) (68–69행)
      - delegate: nil
      + delegate: self // 혹은 실제 뷰모델

위와 같이 Delegate를 올바로 전달해서, 버스정류장 선택 후 콜백이 정상 작동하는지 확인 부탁드립니다.

🤖 Prompt for AI Agents
In
Projects/Feature/HomeFeature/Implement/Sources/Coordinator/DefaultHomeCoordinator.swift
around lines 55-56 and 68-69, the busStopCoordinatorDelegate is currently set to
nil, which can cause missing callbacks after bus stop selection. To fix this,
pass the appropriate delegate instance (such as self or the relevant view model)
instead of nil when initializing the bus stop coordinator in both
startSearchFlow() and startBusStopFlow(stationId:) methods to ensure callbacks
are properly handled.

Comment on lines +51 to +52
flow: flow,
delegate: nil
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

delegate 패턴 추가가 적절합니다.

makeBusStopCoordinator 호출에 delegate: nil 매개변수가 추가되어 coordinator 간 통신을 위한 delegate 패턴이 도입되었습니다.

delegate 매개변수가 다른 coordinator들에서도 일관되게 사용되고 있는지 확인해보겠습니다:


🏁 Script executed:

#!/bin/bash
# Description: BusStopCoordinator의 delegate 매개변수 사용 패턴 확인

# BusStopCoordinator 생성 부분에서 delegate 매개변수 사용 확인
rg -A 3 -B 1 "makeBusStopCoordinator"

# BusStopCoordinatorDelegate 프로토콜 정의 확인
rg -A 5 "BusStopCoordinatorDelegate"

Length of output: 12859


다른 Coordinator 호출부에서 delegate 매개변수가 실제로 포함됐는지 확인하기 위해 아래 스크립트를 실행하겠습니다:


🏁 Script executed:

#!/bin/bash
set -e

echo "=== NearMapFeature ==="
rg -A 10 -B 1 "makeBusStopCoordinator" Projects/Feature/NearMapFeature/Implement/Sources/Coordinator/DafaultNearMapCoordinator.swift

echo -e "\n=== SearchFeature ==="
rg -A 10 -B 1 "makeBusStopCoordinator" Projects/Feature/SearchFeature/Sources/Coordinator/DafaultSearchCoordinator.swift

echo -e "\n=== HomeFeature ==="
rg -A 10 -B 1 "makeBusStopCoordinator" Projects/Feature/HomeFeature/Implement/Sources/Coordinator/DefaultHomeCoordinator.swift

Length of output: 1745


delegate 전달 매개변수 사용 일관성 확인 필요

  • DefaultSearchCoordinator 에서는 delegate: busStopCoordinatorDelegate 로 실제 delegate를 전달하고 있습니다.
  • 반면 DefaultNearMapCoordinatorDefaultHomeCoordinator 에서는 delegate: nil 로 전달하여 delegate 패턴 도입 취지가 온전히 반영되지 않았습니다.
  • 만약 각 Coordinator에서 BusStopCoordinatorDelegate 호출이 필요 없다면, delegate 매개변수 자체 제거를 고려해주세요.
  • 그렇지 않다면 delegate: self 등으로 일관되게 delegate를 연결해 주시기 바랍니다.

점검 위치:

  • Projects/Feature/NearMapFeature/Implement/Sources/Coordinator/DafaultNearMapCoordinator.swift (startBusStopFlow 호출부)
  • Projects/Feature/HomeFeature/Implement/Sources/Coordinator/DefaultHomeCoordinator.swift (startBusStopFlow 호출부)
  • Projects/Feature/SearchFeature/Sources/Coordinator/DafaultSearchCoordinator.swift (delegate 사용 예시)
🤖 Prompt for AI Agents
In
Projects/Feature/NearMapFeature/Implement/Sources/Coordinator/DafaultNearMapCoordinator.swift
around lines 51 to 52, the delegate parameter is passed as nil when calling
startBusStopFlow, which is inconsistent with DefaultSearchCoordinator where an
actual delegate is passed. Review whether the delegate is needed in
DefaultNearMapCoordinator; if it is, pass the appropriate delegate instance
(e.g., self) instead of nil to maintain consistent delegate usage. If the
delegate is not required, remove the delegate parameter entirely from the
startBusStopFlow method and its calls to avoid unused parameters.

Comment on lines 19 to 202
BusStopArrivalInfoResponse(
busStopId: "23290",
busStopName: "강남구보건소",
direction: "강남구청역",
buses: [
BusArrivalInfoResponse(
busId: "124000038",
busName: "342",
busType: BusType.trunkLine.rawValue,
nextStation: "강남구청역",
firstArrivalState: ArrivalState
.arrivalTime(time: 62),
firstArrivalRemaining: "3번째 전",
secondArrivalState: ArrivalState
.arrivalTime(time: 913),
secondArrivalRemaining: "6번째 전",
adirection: "",
isFavorites: false,
isAlarmOn: false
),
BusArrivalInfoResponse(
busId: "100100075",
busName: "472",
busType: BusType.trunkLine.rawValue,
nextStation: "강남구청역",
firstArrivalState: ArrivalState
.arrivalTime(time: 198),
firstArrivalRemaining: "1번째 전",
secondArrivalState: ArrivalState
.arrivalTime(time: 566),
secondArrivalRemaining: "5번째 전",
adirection: "",
isFavorites: false,
isAlarmOn: false
),
BusArrivalInfoResponse(
busId: "100100226",
busName: "3414",
busType: BusType.branchLine.rawValue,
nextStation: "삼성동서광아파트",
firstArrivalState: ArrivalState.soon,
firstArrivalRemaining: "",
secondArrivalState: ArrivalState
.arrivalTime(time: 1086),
secondArrivalRemaining: "9번째 전",
adirection: "",
isFavorites: false,
isAlarmOn: false
),
BusArrivalInfoResponse(
busId: "100100612",
busName: "3426",
busType: BusType.branchLine.rawValue,
nextStation: "삼성동서광아파트",
firstArrivalState: ArrivalState.soon,
firstArrivalRemaining: "",
secondArrivalState: ArrivalState
.arrivalTime(time: 689),
secondArrivalRemaining: "6번째 전",
adirection: "",
isFavorites: false,
isAlarmOn: false
),
BusArrivalInfoResponse(
busId: "100100500",
busName: "4312",
busType: BusType.branchLine.rawValue,
nextStation: "강남구청역",
firstArrivalState: ArrivalState
.arrivalTime(time: 490),
firstArrivalRemaining: "4번째 전",
secondArrivalState: ArrivalState
.arrivalTime(time: 916),
secondArrivalRemaining: "9번째 전",
adirection: "",
isFavorites: false,
isAlarmOn: false
),
BusArrivalInfoResponse(
busId: "100100226",
busName: "3414",
busType: BusType.airport.rawValue,
nextStation: "삼성동서광아파트",
firstArrivalState: ArrivalState.soon,
firstArrivalRemaining: "",
secondArrivalState: ArrivalState
.arrivalTime(time: 1086),
secondArrivalRemaining: "9번째 전",
adirection: "",
isFavorites: false,
isAlarmOn: false
),
BusArrivalInfoResponse(
busId: "100100612",
busName: "3426",
busType: BusType.airport.rawValue,
nextStation: "삼성동서광아파트",
firstArrivalState: ArrivalState.soon,
firstArrivalRemaining: "",
secondArrivalState: ArrivalState
.arrivalTime(time: 689),
secondArrivalRemaining: "6번째 전",
adirection: "",
isFavorites: false,
isAlarmOn: false
),
BusArrivalInfoResponse(
busId: "100100500",
busName: "4312",
busType: BusType.airport.rawValue,
nextStation: "강남구청역",
firstArrivalState: ArrivalState
.arrivalTime(time: 490),
firstArrivalRemaining: "4번째 전",
secondArrivalState: ArrivalState
.arrivalTime(time: 916),
secondArrivalRemaining: "9번째 전",
adirection: "",
isFavorites: false,
isAlarmOn: false
),
BusArrivalInfoResponse(
busId: "1001005001",
busName: "4312",
busType: BusType.branchLine.rawValue,
nextStation: "강남구청역",
firstArrivalState: ArrivalState
.arrivalTime(time: 490),
firstArrivalRemaining: "4번째 전",
secondArrivalState: ArrivalState
.arrivalTime(time: 916),
secondArrivalRemaining: "9번째 전",
adirection: "",
isFavorites: false,
isAlarmOn: false
),
BusArrivalInfoResponse(
busId: "1001005002",
busName: "4312",
busType: BusType.branchLine.rawValue,
nextStation: "강남구청역",
firstArrivalState: ArrivalState
.arrivalTime(time: 490),
firstArrivalRemaining: "4번째 전",
secondArrivalState: ArrivalState
.arrivalTime(time: 916),
secondArrivalRemaining: "9번째 전",
adirection: "",
isFavorites: false,
isAlarmOn: false
),
BusArrivalInfoResponse(
busId: "1001005003",
busName: "4312",
busType: BusType.branchLine.rawValue,
nextStation: "강남구청역",
firstArrivalState: ArrivalState
.arrivalTime(time: 490),
firstArrivalRemaining: "4번째 전",
secondArrivalState: ArrivalState
.arrivalTime(time: 916),
secondArrivalRemaining: "9번째 전",
adirection: "",
isFavorites: false,
isAlarmOn: false
),
BusArrivalInfoResponse(
busId: "1001005004",
busName: "4312",
busType: BusType.branchLine.rawValue,
nextStation: "강남구청역",
firstArrivalState: ArrivalState
.arrivalTime(time: 490),
firstArrivalRemaining: "4번째 전",
secondArrivalState: ArrivalState
.arrivalTime(time: 916),
secondArrivalRemaining: "9번째 전",
adirection: "",
isFavorites: false,
isAlarmOn: false
)
]
)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Mock 데이터에 중복 및 불일치 문제가 있습니다.

Mock 데이터에서 다음과 같은 문제들이 발견됩니다:

  1. 중복된 busId: 100100226 (lines 99, 247), 100100612 (lines 112, 261), 100100500 (lines 126, 275)가 서로 다른 busType으로 중복됩니다.
  2. 동일한 버스의 다른 busType: 같은 버스 번호가 branchLineairport 타입으로 중복 정의되어 있습니다.
  3. 과도한 테스트 데이터: 12개의 버스 정보는 일반적인 단위 테스트에는 과도할 수 있습니다.

다음과 같이 개선하는 것을 권장합니다:

-                BusArrivalInfoResponse(
-                    busId: "100100226",
-                    busName: "3414",
-                    busType: BusType.airport.rawValue,  // 중복
-                    nextStation: "삼성동서광아파트",
-                    firstArrivalState: ArrivalState.soon,
-                    firstArrivalRemaining: "",
-                    secondArrivalState: ArrivalState
-                        .arrivalTime(time: 1086),
-                    secondArrivalRemaining: "9번째 전",
-                    adirection: "",
-                    isFavorites: false,
-                    isAlarmOn: false
-                ),

테스트 데이터를 3-4개의 고유한 버스로 줄이고, 각각 다른 busType과 고유한 busId를 사용하세요.

🤖 Prompt for AI Agents
In
Projects/FeatureDependency/Sources/Mock/MockBusStopArrivalInfoRepository.swift
between lines 19 and 202, the mock data contains duplicate busId values with
conflicting busType assignments and excessive entries for typical unit tests. To
fix this, reduce the test data to 3-4 unique bus entries, ensuring each has a
distinct busId and a consistent, unique busType. Remove duplicates and
conflicting definitions to maintain clarity and relevance for testing.

Comment on lines 40 to 50
Task { @MainActor in
let tabBarController = TabBarViewController()
navigationController.setViewControllers(
[tabBarController], animated: true
)
let viewControllers = MainTab.allCases.map {
makeNavigationController(tabKind: $0)
}
tabBarController.viewControllers = viewControllers
}
tabBarController.viewControllers = viewControllers
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

비동기 UI 설정에서 에러 처리를 추가하는 것을 고려해보세요.

Task { @MainActor in ... }로 UI 설정을 메인 스레드에서 수행하는 것은 좋지만, 현재 fire-and-forget 방식으로 실행되어 에러 처리가 되지 않습니다.

다음과 같이 에러 처리를 추가하는 것을 고려해보세요:

private func setupTabBarController() {
    Task { @MainActor in
        do {
            let tabBarController = TabBarViewController()
            navigationController.setViewControllers(
                [tabBarController], animated: true
            )
            let viewControllers = MainTab.allCases.map {
                makeNavigationController(tabKind: $0)
            }
            tabBarController.viewControllers = viewControllers
        } catch {
            // 에러 처리 로직 추가
            print("TabBar 설정 중 오류 발생: \(error)")
        }
    }
}

또는 메서드를 async로 변경하여 호출자에서 처리하도록 할 수 있습니다.

🤖 Prompt for AI Agents
In Projects/MainFeature/Sources/TabBarCoordinator.swift around lines 40 to 50,
the asynchronous UI setup inside the Task block lacks error handling because it
is fire-and-forget. To fix this, wrap the code inside the Task with a do-catch
block to catch and handle any errors that might occur during the setup.
Alternatively, refactor the method to be async and propagate errors to the
caller for handling.

@gnksbm gnksbm changed the base branch from dev to fix/#330 August 2, 2025 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Refact] Feature 모듈 분리

2 participants