Conversation
|
Warning Rate limit exceeded@choijungp has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 5 minutes and 18 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the 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. ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (10)
Walkthrough이번 변경에서는 앱 버전 정보를 원격(App Store)에서 조회하는 기능이 추가되었습니다. 이를 위해 새로운 DTO, 레포지토리 구현, 프로토콜이 도입되었고, DI 컨테이너에 등록되었습니다. SettingView 및 ViewModel에서는 의존성 주입과 버전 조회, 인증 상태 변경 시 루트 뷰 전환 등이 반영되었습니다. 알림 관련 UI는 주석 처리되어 비활성화되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant SettingView
participant SettingViewModel
participant AppConfigRepository
participant App Store API
SettingView->>SettingViewModel: configure(authRepo, appConfigRepo)
SettingView->>SettingViewModel: action(.fetchVersion)
SettingViewModel->>AppConfigRepository: fetchAppVersion()
AppConfigRepository->>App Store API: GET /lookup?bundleId=...&country=KR
App Store API-->>AppConfigRepository: AppVersionDTO (JSON)
AppConfigRepository-->>SettingViewModel: 버전 정보 반환
SettingViewModel-->>SettingView: 버전 상태 발행 (.latest / .needUpdate)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (1)
Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift (1)
119-128: 회원 탈퇴도 로그아웃과 동일한 에러 처리 개선이 필요합니다.
🧹 Nitpick comments (4)
Projects/DataSource/Sources/DTO/AppVersionDTO.swift (1)
8-15: 접근 제어자를 명시적으로 추가하는 것을 권장합니다.DTO가 DataSource 모듈 내부에서만 사용된다면
internal또는private을 명시적으로 선언하는 것이 좋습니다.-struct AppVersionDTO: Codable { +internal struct AppVersionDTO: Codable { let resultCount: Int let results: [AppStoreResultDTO] } -struct AppStoreResultDTO: Codable { +internal struct AppStoreResultDTO: Codable { let version: String }Projects/DataSource/Sources/Repository/AppConfigRepository.swift (1)
13-21: URLSession 설정에 타임아웃을 추가하는 것을 권장합니다.기본 URLSession은 매우 긴 타임아웃을 가지고 있어, 네트워크 문제 발생 시 사용자가 오래 기다릴 수 있습니다.
+private lazy var session: URLSession = { + let configuration = URLSessionConfiguration.default + configuration.timeoutIntervalForRequest = 10.0 + return URLSession(configuration: configuration) +}() + func fetchAppVersion() async throws -> String? { guard let bundleId = Bundle.main.bundleIdentifier else { return nil } let urlString = "https://itunes.apple.com/lookup?bundleId=\(bundleId)&country=KR" guard let url = URL(string: urlString) else { return nil } - let (data, _) = try await URLSession.shared.data(from: url) + let (data, _) = try await session.data(from: url)Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift (1)
114-114: TODO 주석을 이슈로 관리하는 것을 권장합니다.에러 처리를 위한 토스트 팝업 구현이 필요하다고 명시되어 있습니다. 이를 GitHub 이슈로 등록하여 추적하는 것이 좋습니다.
에러 처리를 위한 토스트 팝업 구현에 대한 이슈를 생성할까요?
Also applies to: 125-125
Projects/Presentation/Sources/Setting/View/SettingView.swift (1)
201-201: 미사용 클로저 매개변수 수정SwiftLint 경고에 따라
isAuthenticated매개변수가 사용되지 않으므로_로 대체해야 합니다.- .sink(receiveValue: { isAuthenticated in + .sink(receiveValue: { _ in
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift(1 hunks)Projects/DataSource/Sources/DTO/AppVersionDTO.swift(1 hunks)Projects/DataSource/Sources/Repository/AppConfigRepository.swift(1 hunks)Projects/Domain/Sources/Protocol/Repository/AppConfigRepositoryProtocol.swift(1 hunks)Projects/Presentation/Sources/Setting/View/SettingView.swift(12 hunks)Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift(5 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
Learnt from: taipaise
PR: YAPP-Github/Bitnagil-iOS#33
File: Projects/DataSource/Sources/Repository/RoutineRepository.swift:45-46
Timestamp: 2025-08-04T23:43:28.527Z
Learning: taipaise는 디버깅을 위해 명시적인 에러 처리를 선호함. nil 체크 시 조용히 실패하는 것보다 에러를 던지는 방식을 추후 디버깅에 도움이 된다고 생각함.
📚 Learning: baseviewcontroller의 viewdidload() 메서드에서 이미 configureattribute(), configurelayout(), bind()를 호출하므로, 하...
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Login/View/TermsAgreementView.swift:44-46
Timestamp: 2025-07-16T09:09:13.869Z
Learning: BaseViewController의 viewDidLoad() 메서드에서 이미 configureAttribute(), configureLayout(), bind()를 호출하므로, 하위 클래스에서 super.viewDidLoad()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.
Applied to files:
Projects/Presentation/Sources/Setting/View/SettingView.swift
📚 Learning: onboardingrecommendedroutineview에서 viewwillappear에 registeronboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 ...
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Onboarding/View/OnboardingRecommendedRoutineView.swift:57-59
Timestamp: 2025-07-16T09:21:15.038Z
Learning: OnboardingRecommendedRoutineView에서 viewWillAppear에 registerOnboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 온보딩 선택지를 변경한 후 돌아올 때 새로운 선택지로 다시 등록해야 하기 때문. 홈 뷰에서는 이 뷰로 돌아올 수 없어서 중복 호출 문제가 발생하지 않음.
Applied to files:
Projects/Presentation/Sources/Setting/View/SettingView.swift
🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/Setting/View/SettingView.swift
[Warning] 201-201: Unused parameter in a closure should be replaced with _
(unused_closure_parameter)
Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift
[Warning] 114-114: TODOs should be resolved (- 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 ...)
(todo)
[Warning] 125-125: TODOs should be resolved (- 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 ...)
(todo)
🔇 Additional comments (9)
Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift (1)
39-42: LGTM!AppConfigRepositoryProtocol의 DI 등록이 기존 패턴과 일치하게 올바르게 구현되었습니다.
Projects/Domain/Sources/Protocol/Repository/AppConfigRepositoryProtocol.swift (1)
8-10: LGTM!프로토콜이 깔끔하게 정의되었습니다. 비동기 처리와 에러 핸들링을 위한 async throws 패턴이 적절합니다.
Projects/Presentation/Sources/Setting/View/SettingView.swift (7)
9-9: 새로운 모듈 import 추가Domain과 Shared 모듈을 import하여 의존성 주입에 필요한 프로토콜과 유틸리티에 접근할 수 있도록 했습니다.
Also applies to: 11-11
30-30: 알림 관련 UI 비활성화알림 섹션을 주석 처리하여 현재 개발 단계에서는 비활성화했습니다. PR 목표에 언급된 대로 추후 기능 추가 시 활성화할 예정인 것으로 보입니다.
Also applies to: 36-37
119-133: 의존성 주입 구현 및 ViewModel 구성AuthRepositoryProtocol과 AppConfigRepositoryProtocol을 DI 컨테이너에서 해결하고, ViewModel에 전달하여 구성하는 로직이 잘 구현되었습니다. 또한 앱 버전 정보를 가져오기 위해
.fetchVersion액션을 트리거하는 것도 적절합니다.
145-169: 알림 관련 바인딩 코드 비활성화알림 토글 상태 처리를 위한 Combine 바인딩 코드가 주석 처리되었습니다. 알림 UI와 일관성 있게 비활성화되었습니다.
192-192: 버전 표시 포맷 개선버전 문자열에 공백을 추가하여 가독성을 향상시켰습니다.
Also applies to: 194-194
199-213: 인증 상태 변경 시 루트 뷰 전환 로직로그아웃 시 IntroView로 루트 뷰 컨트롤러를 교체하는 로직이 적절하게 구현되었습니다. WindowScene을 통해 안전하게 루트 뷰를 변경하는 방식이 좋습니다.
222-223: 테이블뷰 관련 알림 섹션 비활성화UITableView의 delegate/datasource 메서드에서 알림 관련 처리가 모두 주석 처리되어 일관성 있게 비활성화되었습니다. 추후 알림 기능 추가 시 주석을 해제하면 됩니다.
Also applies to: 271-272, 290-292, 327-330, 345-346, 386-393
| let urlString = "https://itunes.apple.com/lookup?bundleId=\(bundleId)&country=KR" | ||
| guard let url = URL(string: urlString) else { return nil } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
국가 코드를 하드코딩하지 않고 유연하게 처리하는 것을 권장합니다.
현재 "KR"로 하드코딩된 국가 코드는 향후 다른 지역 지원 시 문제가 될 수 있습니다. Bundle의 locale 정보를 사용하거나 설정 가능하도록 개선하는 것이 좋습니다.
func fetchAppVersion() async throws -> String? {
guard let bundleId = Bundle.main.bundleIdentifier else { return nil }
- let urlString = "https://itunes.apple.com/lookup?bundleId=\(bundleId)&country=KR"
+ let countryCode = Locale.current.regionCode ?? "KR"
+ let urlString = "https://itunes.apple.com/lookup?bundleId=\(bundleId)&country=\(countryCode)"
guard let url = URL(string: urlString) else { return nil }🤖 Prompt for AI Agents
In Projects/DataSource/Sources/Repository/AppConfigRepository.swift around lines
15 to 16, the country code "KR" is hardcoded in the URL string, which reduces
flexibility for supporting other regions. Modify the code to dynamically obtain
the country code from the device's locale settings or allow it to be passed as a
configurable parameter, then use that value in the URL string instead of the
fixed "KR".
| let (data, _) = try await URLSession.shared.data(from: url) | ||
| let decoded = try JSONDecoder().decode(AppVersionDTO.self, from: data) | ||
| return decoded.results.first?.version | ||
| } |
There was a problem hiding this comment.
HTTP 응답 상태 코드 검증이 필요합니다.
네트워크 요청 후 HTTP 상태 코드를 확인하지 않고 있어, 서버 오류나 잘못된 응답을 처리하지 못할 수 있습니다.
-let (data, _) = try await URLSession.shared.data(from: url)
+let (data, response) = try await URLSession.shared.data(from: url)
+guard let httpResponse = response as? HTTPURLResponse,
+ (200...299).contains(httpResponse.statusCode) else {
+ throw NSError(domain: "AppConfigRepository",
+ code: (response as? HTTPURLResponse)?.statusCode ?? -1,
+ userInfo: [NSLocalizedDescriptionKey: "Invalid response from App Store"])
+}
let decoded = try JSONDecoder().decode(AppVersionDTO.self, from: data)📝 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.
| let (data, _) = try await URLSession.shared.data(from: url) | |
| let decoded = try JSONDecoder().decode(AppVersionDTO.self, from: data) | |
| return decoded.results.first?.version | |
| } | |
| let (data, response) = try await URLSession.shared.data(from: url) | |
| guard let httpResponse = response as? HTTPURLResponse, | |
| (200...299).contains(httpResponse.statusCode) else { | |
| throw NSError( | |
| domain: "AppConfigRepository", | |
| code: (response as? HTTPURLResponse)?.statusCode ?? -1, | |
| userInfo: [NSLocalizedDescriptionKey: "Invalid response from App Store"] | |
| ) | |
| } | |
| let decoded = try JSONDecoder().decode(AppVersionDTO.self, from: data) | |
| return decoded.results.first?.version |
🤖 Prompt for AI Agents
In Projects/DataSource/Sources/Repository/AppConfigRepository.swift around lines
18 to 21, the code does not check the HTTP response status code after the
network request, which can cause issues if the server returns an error or
invalid response. Modify the code to capture the URLResponse from the data task,
cast it to HTTPURLResponse, and verify that the status code indicates success
(e.g., 200-299). If the status code is outside this range, throw an appropriate
error or handle the failure before attempting to decode the data.
| private func logout() { | ||
| // TODO: - 로그아웃 api | ||
| Task { | ||
| do { | ||
| try await authRepository?.logout() | ||
| authenticatedSubject.send(false) | ||
| } catch { | ||
| // TODO: - 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 불안정 알림 | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
에러 처리를 개선해야 합니다.
현재 에러를 무시하고 있는데, 사용자에게 피드백이 필요합니다. 또한 authRepository가 nil인 경우 아무 일도 일어나지 않아 혼란을 줄 수 있습니다.
private func logout() {
+ guard let authRepository = authRepository else {
+ print("AuthRepository is not configured")
+ return
+ }
Task {
do {
- try await authRepository?.logout()
+ try await authRepository.logout()
authenticatedSubject.send(false)
} catch {
// TODO: - 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 불안정 알림
+ print("Logout failed: \(error)")
}
}
}📝 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.
| private func logout() { | |
| // TODO: - 로그아웃 api | |
| Task { | |
| do { | |
| try await authRepository?.logout() | |
| authenticatedSubject.send(false) | |
| } catch { | |
| // TODO: - 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 불안정 알림 | |
| } | |
| } | |
| } | |
| private func logout() { | |
| guard let authRepository = authRepository else { | |
| print("AuthRepository is not configured") | |
| return | |
| } | |
| Task { | |
| do { | |
| try await authRepository.logout() | |
| authenticatedSubject.send(false) | |
| } catch { | |
| // TODO: - 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 불안정 알림 | |
| print("Logout failed: \(error)") | |
| } | |
| } | |
| } |
🧰 Tools
🪛 SwiftLint (0.57.0)
[Warning] 114-114: TODOs should be resolved (- 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 ...)
(todo)
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift around
lines 108 to 117, the logout function currently ignores errors and does nothing
if authRepository is nil, which can confuse users. Update the code to handle the
case when authRepository is nil by providing appropriate user feedback, and
improve error handling by showing a toast or alert to inform the user of network
issues or logout failures.
| private func fetchVersion() { | ||
| let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String | ||
|
|
||
| Task { | ||
| do { | ||
| let appStoreAppVersion = try await appConfigRepository?.fetchAppVersion() | ||
|
|
||
| if currentVersion != appStoreAppVersion { | ||
| versionSubject.send(.needUpdate(version: currentVersion ?? "")) | ||
| } else { | ||
| versionSubject.send(.latest(version: currentVersion ?? "")) | ||
| } | ||
| } catch { | ||
|
|
||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
fetchVersion 메서드에서 에러 처리가 비어있습니다.
네트워크 오류나 파싱 오류 발생 시 사용자에게 아무런 피드백이 없습니다. 최소한 현재 버전이라도 표시해주는 것이 좋습니다.
private func fetchVersion() {
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
Task {
do {
let appStoreAppVersion = try await appConfigRepository?.fetchAppVersion()
if currentVersion != appStoreAppVersion {
versionSubject.send(.needUpdate(version: currentVersion ?? ""))
} else {
versionSubject.send(.latest(version: currentVersion ?? ""))
}
} catch {
-
+ // 에러 발생 시에도 현재 버전은 표시
+ versionSubject.send(.latest(version: currentVersion ?? ""))
+ print("Failed to fetch app version: \(error)")
}
}
}🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift around
lines 130 to 146, the fetchVersion method's catch block is empty, providing no
feedback on errors like network or parsing failures. Update the catch block to
send a versionSubject event indicating the current version as the latest,
ensuring the user sees at least the current version even if fetching the app
store version fails.
| if currentVersion != appStoreAppVersion { | ||
| versionSubject.send(.needUpdate(version: currentVersion ?? "")) | ||
| } else { | ||
| versionSubject.send(.latest(version: currentVersion ?? "")) | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
버전 비교 로직이 단순한 문자열 비교를 사용하고 있습니다.
현재 버전 비교가 단순 문자열 비교로 되어 있어 "1.0.10"과 "1.0.9" 같은 경우 잘못된 결과를 낼 수 있습니다. 또한 appStoreAppVersion이 nil일 때의 처리가 명확하지 않습니다.
🏁 Script executed:
#!/bin/bash
# 버전 비교를 위한 유틸리티 함수나 라이브러리 사용 여부 확인
rg -A 5 "version.*compar|compar.*version" --type swift
ast-grep --pattern 'func $_($$$ version $$$) { $$$ }'Length of output: 112
버전 비교 로직 재확인을 위해 전체 코드베이스에서 Version 관련 타입 정의 및 Comparable 준수 여부를 검색합니다.
🏁 Script executed:
#!/bin/bash
# Repository 전체에서 Version 관련 타입 정의 및 Comparable 준수 검사
rg -n "struct .*Version" --type swift
rg -n "enum .*Version" --type swift
rg -n "class .*Version" --type swift
rg -n "extension .*Version.*:.*Comparable" --type swiftLength of output: 357
AppVersionDTO와 SettingViewModel의 VersionType 정의를 확인하여 버전 필드 타입과 비교 로직 의존 여부를 검증해야 합니다.
🏁 Script executed:
#!/bin/bash
# AppVersionDTO에서 버전 필드 타입 확인
rg -n "struct AppVersionDTO" -A 20 Projects/DataSource/Sources/DTO/AppVersionDTO.swift
# SettingViewModel 내부 VersionType 확인
rg -n "enum VersionType" -A 20 Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swiftLength of output: 1097
버전 비교 로직을 문자열 비교에서 SemVer 비교로 교체 및 nil 처리 명확화 필요
현재 currentVersion과 appStoreAppVersion을 단순 문자열(!=)로 비교하면
- “1.0.10” vs “1.0.9” 같은 케이스에서 잘못된 결과 발생
appStoreAppVersion이nil일 때 업데이트 여부가 불명확
수정 제안:
- Foundation의
Version타입(macOS 10.15+) 또는 서드파티SemanticVersion라이브러리 등으로 버전 문자열을 파싱해Comparable로 비교 appStoreAppVersion == nil인 경우 로직(에러 표시, 재시도 등)을 명확히 정의- 수정 위치:
- Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift Lines 137–141
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift around
lines 137 to 141, replace the current string inequality comparison between
currentVersion and appStoreAppVersion with a semantic version comparison using
Foundation's Version type or a similar semantic versioning library. Parse both
version strings into Version instances and compare them using their Comparable
conformance. Additionally, explicitly handle the case where appStoreAppVersion
is nil by defining clear behavior such as showing an error or retrying, instead
of proceeding with the comparison. Update the logic to send the appropriate
versionSubject event based on this semantic comparison and nil handling.
|
@choijungp 업데이트 버튼을 눌렀을때, 앱스토어로 이동해야하는데 관련 로직은 조금 고민해봐야할 것 같아서 우선은 PR을 닫도록 하겠습니다. 죄송합니다. |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (3)
Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift (3)
117-126: 에러 처리를 개선해야 합니다현재 에러를 무시하고 있는데, 사용자에게 피드백이 필요합니다. 또한 authRepository가 nil인 경우 아무 일도 일어나지 않아 혼란을 줄 수 있습니다.
private func logout() { + guard let authRepository = authRepository else { + print("AuthRepository is not configured") + return + } Task { do { - try await authRepository?.logout() + try await authRepository.logout() authenticatedSubject.send(false) } catch { // TODO: - 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 불안정 알림 + print("Logout failed: \(error)") } } }
139-155: fetchVersion 메서드에서 에러 처리가 비어있습니다네트워크 오류나 파싱 오류 발생 시 사용자에게 아무런 피드백이 없습니다. 최소한 현재 버전이라도 표시해주는 것이 좋습니다.
private func fetchVersion() { let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String Task { do { let appStoreAppVersion = try await appConfigRepository?.fetchAppVersion() if currentVersion != appStoreAppVersion { versionSubject.send(.needUpdate(version: currentVersion ?? "")) } else { versionSubject.send(.latest(version: currentVersion ?? "")) } } catch { - + // 에러 발생 시에도 현재 버전은 표시 + versionSubject.send(.latest(version: currentVersion ?? "")) + print("Failed to fetch app version: \(error)") } } }
146-150: 버전 비교 로직이 단순한 문자열 비교를 사용하고 있습니다현재 버전 비교가 단순 문자열 비교로 되어 있어 "1.0.10"과 "1.0.9" 같은 경우 잘못된 결과를 낼 수 있습니다. 또한 appStoreAppVersion이 nil일 때의 처리가 명확하지 않습니다.
시맨틱 버전 비교를 위해 다음과 같은 방법을 고려하세요:
- Foundation의
Version타입 사용 (iOS 14+)- 버전 문자열을 컴포넌트로 분리하여 숫자 비교
- 서드파티 라이브러리 활용
private func fetchVersion() { let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String + guard let currentVersion = currentVersion else { + versionSubject.send(.latest(version: "Unknown")) + return + } Task { do { - let appStoreAppVersion = try await appConfigRepository?.fetchAppVersion() + guard let appStoreAppVersion = try await appConfigRepository?.fetchAppVersion() else { + versionSubject.send(.latest(version: currentVersion)) + return + } - if currentVersion != appStoreAppVersion { + // 버전 비교 로직 개선 필요 + if compareVersions(current: currentVersion, appStore: appStoreAppVersion) { versionSubject.send(.needUpdate(version: currentVersion)) } else { versionSubject.send(.latest(version: currentVersion)) } } catch { versionSubject.send(.latest(version: currentVersion)) } } } + +private func compareVersions(current: String, appStore: String) -> Bool { + // TODO: 시맨틱 버전 비교 구현 + return current != appStore +}
🧹 Nitpick comments (2)
Projects/Presentation/Sources/Setting/View/SettingView.swift (1)
132-133: fetchVersion 호출 시점을 viewWillAppear로 이동 고려현재
configureAttribute에서 즉시fetchVersion을 호출하고 있습니다. 뷰 라이프사이클을 고려하면viewWillAppear에서 호출하는 것이 더 적절할 수 있습니다. 이렇게 하면 사용자가 설정 화면으로 돌아올 때마다 최신 버전을 확인할 수 있습니다.override func configureAttribute() { // ... existing code ... viewModel.configure(authRepository: authRepository, appConfigRepository: appConfigRepository) - viewModel.action(input: .fetchVersion) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) configureNavigationBar(navigationStyle: .withBackButton(title: "설정")) + viewModel.action(input: .fetchVersion) }Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift (1)
20-21: App Store URL에 플레이스홀더가 포함되어 있습니다URL에
{빛나길 id}플레이스홀더가 있습니다. 앱 출시 전에 실제 App Store ID로 교체해야 합니다.실제 App Store ID로 교체하는 작업이 필요합니다. 이를 추적하기 위한 이슈를 생성할까요?
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
Projects/Presentation/Sources/Setting/View/SettingView.swift(12 hunks)Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift(5 hunks)SupportingFiles/Info.plist(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- SupportingFiles/Info.plist
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
Learnt from: taipaise
PR: YAPP-Github/Bitnagil-iOS#33
File: Projects/DataSource/Sources/Repository/RoutineRepository.swift:45-46
Timestamp: 2025-08-04T23:43:28.527Z
Learning: taipaise는 디버깅을 위해 명시적인 에러 처리를 선호함. nil 체크 시 조용히 실패하는 것보다 에러를 던지는 방식을 추후 디버깅에 도움이 된다고 생각함.
📚 Learning: baseviewcontroller의 viewdidload() 메서드에서 이미 configureattribute(), configurelayout(), bind()를 호출하므로, 하...
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Login/View/TermsAgreementView.swift:44-46
Timestamp: 2025-07-16T09:09:13.869Z
Learning: BaseViewController의 viewDidLoad() 메서드에서 이미 configureAttribute(), configureLayout(), bind()를 호출하므로, 하위 클래스에서 super.viewDidLoad()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.
Applied to files:
Projects/Presentation/Sources/Setting/View/SettingView.swift
📚 Learning: onboardingrecommendedroutineview에서 viewwillappear에 registeronboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 ...
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Onboarding/View/OnboardingRecommendedRoutineView.swift:57-59
Timestamp: 2025-07-16T09:21:15.038Z
Learning: OnboardingRecommendedRoutineView에서 viewWillAppear에 registerOnboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 온보딩 선택지를 변경한 후 돌아올 때 새로운 선택지로 다시 등록해야 하기 때문. 홈 뷰에서는 이 뷰로 돌아올 수 없어서 중복 호출 문제가 발생하지 않음.
Applied to files:
Projects/Presentation/Sources/Setting/View/SettingView.swift
🧬 Code Graph Analysis (1)
Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift (1)
Projects/DataSource/Sources/Repository/AppConfigRepository.swift (1)
fetchAppVersion(13-21)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/Setting/View/SettingView.swift
[Warning] 204-204: Unused parameter in a closure should be replaced with _
(unused_closure_parameter)
Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift
[Warning] 123-123: TODOs should be resolved (- 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 ...)
(todo)
[Warning] 134-134: TODOs should be resolved (- 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 ...)
(todo)
⏰ 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). (2)
- GitHub Check: build
- GitHub Check: build
|
|
||
| viewModel.output.isAuthenticatedPublisher | ||
| .receive(on: DispatchQueue.main) | ||
| .sink(receiveValue: { isAuthenticated in |
There was a problem hiding this comment.
사용하지 않는 클로저 파라미터를 _로 대체
isAuthenticated 파라미터를 사용하지 않으므로 _로 대체해야 합니다.
- .sink(receiveValue: { isAuthenticated in
+ .sink(receiveValue: { _ in📝 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.
| .sink(receiveValue: { isAuthenticated in | |
| .sink(receiveValue: { _ in |
🧰 Tools
🪛 SwiftLint (0.57.0)
[Warning] 204-204: Unused parameter in a closure should be replaced with _
(unused_closure_parameter)
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Setting/View/SettingView.swift at line 204, the
closure parameter named isAuthenticated is not used inside the closure body.
Replace the parameter name isAuthenticated with an underscore (_) to indicate
that the parameter is intentionally unused.
There was a problem hiding this comment.
아하 ! 아니면 logout, withdraw 시에 false를 보내니까 false 받았을 때에만 IntroView로 이동하는 것은 어떠신가요 ??
There was a problem hiding this comment.
@choijungp 좋은 제안입니다! 맞습니다. 로그아웃이나 탈퇴 시에만 IntroView로 이동해야 하므로, isAuthenticated가 false일 때에만 화면 전환을 하는 것이 더 정확한 로직이네요.
viewModel.output.isAuthenticatedPublisher
.receive(on: DispatchQueue.main)
.sink(receiveValue: { isAuthenticated in
guard !isAuthenticated else { return }
guard
let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let sceneDelegate = windowScene.delegate as? UIWindowSceneDelegate,
let window = sceneDelegate.window
else { return }
let introView = IntroView()
let navigationController = UINavigationController(rootViewController: introView)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
})
.store(in: &cancellables)이렇게 하면 SwiftLint 경고도 해결되고, 로직도 더 명확해집니다!
🧠 Learnings used
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Onboarding/View/OnboardingRecommendedRoutineView.swift:57-59
Timestamp: 2025-07-16T09:21:15.038Z
Learning: OnboardingRecommendedRoutineView에서 viewWillAppear에 registerOnboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 온보딩 선택지를 변경한 후 돌아올 때 새로운 선택지로 다시 등록해야 하기 때문. 홈 뷰에서는 이 뷰로 돌아올 수 없어서 중복 호출 문제가 발생하지 않음.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Domain/Sources/UseCase/Onboarding/OnboardingUseCase.swift:0-0
Timestamp: 2025-07-16T10:58:30.858Z
Learning: choijungp는 OnboardingUseCase의 convertToDictionary 함수에서 에러 처리 개선을 추후 고민하고 반영할 계획을 가지고 있음.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#30
File: Projects/DataSource/Sources/NetworkService/Plugin/RefreshTokenPlugin.swift:42-43
Timestamp: 2025-07-30T03:56:18.617Z
Learning: choijungp는 현재 테스트 단계에서 RefreshTokenPlugin의 토큰 갱신 로직 디버깅을 위해 액세스 토큰과 리프레시 토큰의 실제 값을 로그에 기록하는 것을 선호함.
| private func withdrawal() { | ||
| // TODO: - 회원탈퇴 api | ||
| Task { | ||
| do { | ||
| try await authRepository?.withdraw() | ||
| authenticatedSubject.send(false) | ||
| } catch { | ||
| // TODO: - 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 불안정 알림 | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
withdrawal 메서드도 동일한 에러 처리 개선이 필요합니다
logout과 마찬가지로 에러 처리와 nil 체크가 필요합니다.
private func withdrawal() {
+ guard let authRepository = authRepository else {
+ print("AuthRepository is not configured")
+ return
+ }
Task {
do {
- try await authRepository?.withdraw()
+ try await authRepository.withdraw()
authenticatedSubject.send(false)
} catch {
// TODO: - 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 불안정 알림
+ print("Withdrawal failed: \(error)")
}
}
}📝 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.
| private func withdrawal() { | |
| // TODO: - 회원탈퇴 api | |
| Task { | |
| do { | |
| try await authRepository?.withdraw() | |
| authenticatedSubject.send(false) | |
| } catch { | |
| // TODO: - 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 불안정 알림 | |
| } | |
| } | |
| } | |
| private func withdrawal() { | |
| guard let authRepository = authRepository else { | |
| print("AuthRepository is not configured") | |
| return | |
| } | |
| Task { | |
| do { | |
| try await authRepository.withdraw() | |
| authenticatedSubject.send(false) | |
| } catch { | |
| // TODO: - 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 불안정 알림 | |
| print("Withdrawal failed: \(error)") | |
| } | |
| } | |
| } |
🧰 Tools
🪛 SwiftLint (0.57.0)
[Warning] 134-134: TODOs should be resolved (- 토스트 팝업 구현 시 + 디자인 추가 시 네트워크 ...)
(todo)
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift around
lines 128 to 137, the withdrawal() method lacks proper error handling and nil
checking for authRepository similar to logout. Add a nil check for
authRepository before calling withdraw(), and improve the catch block to handle
errors appropriately, such as showing a network instability toast notification
or logging the error.
choijungp
left a comment
There was a problem hiding this comment.
띵동두 고생 많으셨씀니다 ㅜㅜ !!!!!!!! 조금만 더 힘내붑시다 !!!! 아자아자아자 !!!
🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
|
|
||
| viewModel.output.isAuthenticatedPublisher | ||
| .receive(on: DispatchQueue.main) | ||
| .sink(receiveValue: { isAuthenticated in |
There was a problem hiding this comment.
아하 ! 아니면 logout, withdraw 시에 false를 보내니까 false 받았을 때에만 IntroView로 이동하는 것은 어떠신가요 ??
| func configure(authRepository: AuthRepositoryProtocol, appConfigRepository: AppConfigRepositoryProtocol) { | ||
| self.authRepository = authRepository | ||
| self.appConfigRepository = appConfigRepository | ||
| } |
There was a problem hiding this comment.
SettingView에서는 생성자 주입이 아닌 setter로 의존성을 주입하고 있는지 여쭤봐도 될까용 ??
| case .update: | ||
| return URL(string: "itms-apps://itunes.apple.com/app/{빛나길 id}") |
There was a problem hiding this comment.
요기서 {빛나길 id}는 App bundle id랑은 다른 건가용 ?? 순수 궁금증 !!!
There was a problem hiding this comment.
지금은 무조건 버전 옆에 [업데이트] 버튼이 뜨는 건가욤 ???
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
Projects/Presentation/Sources/MyPage/View/MypageView.swift (1)
16-16: 사용하지 않는 상수를 제거하세요.
profileImageViewCornerRadius상수가 더 이상 사용되지 않으므로 제거하는 것이 좋겠습니다.- static let profileImageViewCornerRadius: CGFloat = profileImageViewSize / 2
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
Projects/Presentation/Resources/Images.xcassets/profile_graphic.imageset/profile_graphic.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/profile_graphic.imageset/profile_graphic@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/profile_graphic.imageset/profile_graphic@3x.pngis excluded by!**/*.png
📒 Files selected for processing (3)
Projects/Presentation/Resources/Images.xcassets/profile_graphic.imageset/Contents.json(1 hunks)Projects/Presentation/Sources/Common/DesignSystem/BitnagilGraphic.swift(1 hunks)Projects/Presentation/Sources/MyPage/View/MypageView.swift(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- Projects/Presentation/Sources/Common/DesignSystem/BitnagilGraphic.swift
- Projects/Presentation/Resources/Images.xcassets/profile_graphic.imageset/Contents.json
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
Learnt from: taipaise
PR: YAPP-Github/Bitnagil-iOS#33
File: Projects/DataSource/Sources/Repository/RoutineRepository.swift:45-46
Timestamp: 2025-08-04T23:43:28.527Z
Learning: taipaise는 디버깅을 위해 명시적인 에러 처리를 선호함. nil 체크 시 조용히 실패하는 것보다 에러를 던지는 방식을 추후 디버깅에 도움이 된다고 생각함.
🔇 Additional comments (1)
Projects/Presentation/Sources/MyPage/View/MypageView.swift (1)
54-54: 이미지 설정 간소화가 적절합니다.
BitnagilGraphic.profileGraphic을 사용하여 프로필 이미지 설정을 간소화한 것은 좋은 변경입니다. 수동 스타일링 대신 미리 디자인된 그래픽 에셋을 사용하는 것이 더 일관성 있고 유지보수하기 쉽습니다.
🌁 Background
👩💻 Contents
날먹연결 시키기📝 Review Note
이미 너무 잘 구현해주셔서, 로그아웃과 회원 탈퇴 로직은 5분 컷 했습니다!! 로그아웃 시 IntroView로 돌아갑니다.
주석 처리에 관하여..
AppConfigRepository
버전 가져오기
원래는, 이미 잘 구현해주신 NetworkService를 사용하고자 했습니다. AppStore에서 내려주는 body도 나름 레퍼런스가 많아서 DTO 자체를 구현하는 것은 어렵지 않았거든요.. 하지만 제가 간과 한 것이 있었으니,, Bitnagil에서는모든 응답이 BaseDTO 형태로 내려온다는 것!!! 이걸 파악하지 못해서 진짜 금방 끝나야 하는 로직인데 삽질 하느라 마무리가 늦었습니다 (죄송,,,)
아쉽게도, 애플에서는 저희의 BaseDTO 형식을 따르지 않기 때문에, Repository내부에서 URLSession으로 직접 버전 정보를 가져오는 로직을 구현했습니다.
지금은 아직 배포가되지 않은 상태이기 때문에, 아래 처럼 body를 내려줍니다.
하지만, 나중에는 저 results 배열 안에 version 값이 내려올거에요!!
Summary by CodeRabbit
신규 기능
기능 개선
UI 변경