Conversation
📝 WalkthroughWalkthrough홈 화면의 대시보드 데이터를 가져오고 표시하는 기능을 구현합니다. 도메인 계층에 HomeInterface 프로토콜, DashboardEntity 모델, FetchDashboardData 유스케이스를 추가하고, 데이터 계층에 DefaultHomeRepository와 MockHomeRepository를 구현합니다. 프레젠테이션 계층에서 HomeView와 HomeViewModel을 새로 작성하여 상태 기반 UI 렌더링을 지원하며, 의존성 주입을 통해 연결합니다. 관련 컴포넌트와 에셋도 함께 업데이트합니다. Changes
Sequence DiagramsequenceDiagram
participant View as HomeView
participant ViewModel as HomeViewModel
participant UseCase as FetchDashboardData
participant Repository as HomeRepository
participant NetworkService as NetworkService
View->>ViewModel: loadDashboard() [.task]
activate ViewModel
ViewModel->>ViewModel: isLoading = true
ViewModel->>UseCase: execute()
activate UseCase
UseCase->>Repository: fetchDashboard()
activate Repository
Repository->>NetworkService: GET /dashboard
activate NetworkService
NetworkService-->>Repository: DashboardEntity
deactivate NetworkService
Repository-->>UseCase: DashboardEntity
deactivate Repository
UseCase-->>ViewModel: DashboardEntity
deactivate UseCase
ViewModel->>ViewModel: dashboardData = result
ViewModel->>ViewModel: isLoading = false
deactivate ViewModel
View->>View: Render with dashboardData
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45분 Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
🤖 Fix all issues with AI agents
In
`@Cherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_chellenge_bar0.imageset/Contents.json`:
- Line 4: 파일명 및 폴더에 있는 오타 "chellenge"를 "challenge"로 고치세요: 폴더명
home_chellenge_bar0.imageset → home_challenge_bar0.imageset, 내부 asset 파일
home_chellenge_bar0.svg → home_challenge_bar0.svg, 그리고 Assets.xcassets의
Contents.json의 "filename" 항목을 변경하세요; 변경 후 Xcode에서 해당 에셋을 참조하는 코드(예:
UIImage(named: "home_chellenge_bar0") 또는 Asset Catalog 이름)들을 찾아 모두
"home_challenge_bar0"로 업데이트하고 빌드/캐시 문제 방지를 위해 Clean Build Folder 후 재빌드하세요.
In `@Cherrish-iOS/Cherrish-iOS/Data/DataDependencyAssembler.swift`:
- Around line 22-24: DI registration currently binds HomeInterface to
MockHomeRepository unconditionally; change it so MockHomeRepository is
registered only in debug builds and DefaultHomeRepository is used for
release/production. In the block where DIContainer.shared.register(type:
HomeInterface.self) is called, wrap the MockHomeRepository registration with a
DEBUG conditional compilation check and add an else branch that registers
DefaultHomeRepository(networkService: self.networkService) so production builds
get the real implementation; keep the same constructor args (networkService)
when registering DefaultHomeRepository.
In `@Cherrish-iOS/Cherrish-iOS/Domain/DomainDependencyAssembler.swift`:
- Around line 28-34: The guard silently returning when
DIContainer.shared.resolve(type: HomeInterface.self) fails hides configuration
errors; update the block that registers FetchDashboardData to surface failures
by adding a fail-fast check and diagnostic logging/assertion: when
DIContainer.shared.resolve(type: HomeInterface.self) returns nil, call
assertionFailure or fatalError in debug (and log an error via your logger in
release) so the missing HomeInterface is immediately obvious; keep the rest of
the registration (DIContainer.shared.register(type: FetchDashboardData.self) {
DefaultFetchDashboardData(repository: homeRepository) }) unchanged, only replace
the silent return with an explicit assert/log/fatal tied to the resolve call.
In `@Cherrish-iOS/Cherrish-iOS/Domain/Model/DashboardEntity.swift`:
- Around line 10-18: DashboardEntity currently stores date and dayOfWeek as
String; change date's type to Date (replace let date: String with let date:
Date) and remove or convert the stored dayOfWeek to a computed property or a
typed enum (e.g., var dayOfWeek: String { /* format from date */ } or DayOfWeek
enum) so domain consumers can perform date operations and comparisons; update
any initializers or mappers that construct DashboardEntity to parse the API
string into a Date and derive dayOfWeek from that Date (ensure
RecentProcedureEntity and UpcomingProcedureEntity usage is unaffected).
- Around line 38-47: Remove the UI presentation property displayText from the
domain model (DashboardEntity/ProcedurePhase) so the Domain layer only contains
business logic, then add a Presentation-layer extension for ProcedurePhase that
reintroduces the displayText computed property (mapping .sensitive, .caution,
.recovery to the Korean strings) in a new file (e.g.,
Presentation/Extensions/ProcedurePhase+Display.swift); update any call sites to
import/use the Presentation module or extension where UI strings are needed.
In `@Cherrish-iOS/Cherrish-iOS/Domain/UseCase/FetchDashboardData.swift`:
- Around line 21-23: The execute() method in FetchDashboardData.swift declares a
return type DashboardEntity but currently does not return a value; update
execute() to await the repository.fetchDashboard() result and return it (e.g.,
capture the result of repository.fetchDashboard() and return that
DashboardEntity) so the function satisfies its declared return type.
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/Coordinator/HomeCoordinatorView.swift`:
- Around line 16-21: The switch inside navigationDestination(for:
HomeRoute.self) contains a dead-case for .root because HomeRoute currently
defines only .root and nothing ever calls homeCoordinator.push(.root); either
remove the .root case from the navigationDestination block to eliminate dead
code, or instead expand HomeRoute with real cases (e.g., .detail, .search) and
implement corresponding branches that call ViewFactory.shared.makeHomeView() (or
other view factories) and ensure callers use homeCoordinator.push(...); if you
intend to navigate back to root document that behavior and prefer programmatic
popping, update callers to use homeCoordinator.path.removeLast() (or equivalent)
and add a comment in HomeRoute/navigationDestination explaining intended future
routes.
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeView.swift`:
- Around line 140-151: Replace the hardcoded TypographyText literals in
ChallengeCardView with the view's data properties: use the challengeName
(instead of "웰니스 • 마음챙김") and a formatted challengeRate (instead of "80.9%")
passed into the view; keep the same modifiers (.frame, .padding, .background
with RoundedRectangle and .strokeBorder) on the dynamic TypographyText so
styling/layout remains identical and ensure challengeRate is formatted as a
percentage string (e.g., using String(format:) or NumberFormatter) before
passing to TypographyText.
- Around line 244-257: The background modifier is using .gray0 which is a View
extension (foregroundStyle) defined in View+Color.swift and not a
ShapeStyle/Color, causing a compile error; in HomeView.swift replace the
.background(.gray0) call on the HStack with a proper Color shape style such as
Color("gray_0") (i.e., use Color("gray_0") for background) and keep the existing
overlay/clipShape logic unchanged so the view uses the color resource instead of
the View extension.
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/OnComingCard.swift`:
- Line 15: The property declaration onTap: (() -> Void)? = nil in OnComingCard
contains a redundant optional nil initializer; remove the "= nil" so the
optional is implicitly nil (i.e., change the declaration of onTap in the
OnComingCard type to just var onTap: (() -> Void)?). This will satisfy SwiftLint
and keep behavior unchanged.
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swift`:
- Around line 33-39: Currently assemble() silently returns if
DIContainer.shared.resolve(type: FetchDashboardData.self) fails and it captures
fetchDashboardData once into the registration closure; instead, stop the silent
failure and switch to lazy resolution inside the registration closure: in
DIContainer.shared.register(type: HomeViewModel.self) { ... } resolve
FetchDashboardData again (guard let fetch = DIContainer.shared.resolve(type:
FetchDashboardData.self) else { fatalError("Missing FetchDashboardData for
HomeViewModel") }) and then return HomeViewModel(fetchDashboardData: fetch) so
you avoid capturing a single instance and provide an explicit error message that
makes ViewFactory.makeHomeView failures diagnosable; reference
FetchDashboardData, DIContainer.shared.resolve, DIContainer.shared.register,
HomeViewModel, assemble, and ViewFactory.makeHomeView.
In `@Cherrish-iOS/Cherrish-iOS/Presentation/ViewFactory.swift`:
- Around line 42-47: The guard in makeHomeView() calls fatalError() without
context; update it to provide a descriptive message (e.g., "Failed to resolve
HomeViewModel from DIContainer") when DIContainer.shared.resolve(type:
HomeViewModel.self) returns nil, and apply the same change to the other related
factory methods (makeOnboardingView(), makeInformationView()) so each fatalError
includes the specific type that failed to resolve.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (21)
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/cherrish_character.imageset/체리.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/cherrish_logo.imageset/cherrish_logo.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_chellenge_bar0.imageset/home_chellenge_bar0.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.0.imageset/home_lv.0.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.0.imageset/home_lv.0@2x.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.0.imageset/home_lv.0@3x.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.1.imageset/home_lv.1.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.1.imageset/home_lv.1@2x.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.1.imageset/home_lv.1@3x.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.2.imageset/home_lv.2.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.2.imageset/home_lv.2@2x.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.2.imageset/home_lv.2@3x.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.3.imageset/home_lv.3.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.3.imageset/home_lv.3@2x.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.3.imageset/home_lv.3@3x.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.4.imageset/home_lv.4.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.4.imageset/home_lv.4@2x.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.4.imageset/home_lv.4@3x.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/illustration_noschedule.imageset/illustration_noschedule.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/illustration_noschedule.imageset/illustration_noschedule@2x.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/illustration_noschedule.imageset/illustration_noschedule@3x.pngis excluded by!**/*.png
📒 Files selected for processing (22)
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/cherrish_character.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_chellenge_bar0.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.0.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.1.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.2.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.3.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.4.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/illustration_noschedule.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Data/DataDependencyAssembler.swiftCherrish-iOS/Cherrish-iOS/Data/Repository/HomeRepository.swiftCherrish-iOS/Cherrish-iOS/Domain/DomainDependencyAssembler.swiftCherrish-iOS/Cherrish-iOS/Domain/Interface/HomeInterface.swiftCherrish-iOS/Cherrish-iOS/Domain/Model/DashboardEntity.swiftCherrish-iOS/Cherrish-iOS/Domain/UseCase/FetchDashboardData.swiftCherrish-iOS/Cherrish-iOS/Presentation/Feature/ChallengeView/Coordinator/ChallengeCoordinatorView.swiftCherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/Coordinator/HomeCoordinatorView.swiftCherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeView.swiftCherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeViewModel.swiftCherrish-iOS/Cherrish-iOS/Presentation/Global/Components/MonthPlan.swiftCherrish-iOS/Cherrish-iOS/Presentation/Global/Components/OnComingCard.swiftCherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swiftCherrish-iOS/Cherrish-iOS/Presentation/ViewFactory.swift
💤 Files with no reviewable changes (1)
- Cherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/cherrish_character.imageset/Contents.json
🧰 Additional context used
🧬 Code graph analysis (10)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/Coordinator/HomeCoordinatorView.swift (1)
Cherrish-iOS/Cherrish-iOS/Presentation/ViewFactory.swift (1)
makeHomeView(42-47)
Cherrish-iOS/Cherrish-iOS/Domain/UseCase/FetchDashboardData.swift (1)
Cherrish-iOS/Cherrish-iOS/Data/Repository/HomeRepository.swift (2)
fetchDashboard(17-19)fetchDashboard(78-80)
Cherrish-iOS/Cherrish-iOS/Domain/DomainDependencyAssembler.swift (1)
Cherrish-iOS/Cherrish-iOS/Core/DIContainer.swift (2)
resolve(27-34)register(22-25)
Cherrish-iOS/Cherrish-iOS/Domain/Interface/HomeInterface.swift (1)
Cherrish-iOS/Cherrish-iOS/Data/Repository/HomeRepository.swift (2)
fetchDashboard(17-19)fetchDashboard(78-80)
Cherrish-iOS/Cherrish-iOS/Data/DataDependencyAssembler.swift (1)
Cherrish-iOS/Cherrish-iOS/Core/DIContainer.swift (1)
register(22-25)
Cherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swift (1)
Cherrish-iOS/Cherrish-iOS/Core/DIContainer.swift (2)
resolve(27-34)register(22-25)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeViewModel.swift (1)
Cherrish-iOS/Cherrish-iOS/Domain/UseCase/FetchDashboardData.swift (1)
execute(21-24)
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/OnComingCard.swift (2)
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/View+Shadow.swift (1)
body(9-17)Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/View+Color.swift (3)
gray700(37-39)gray900(45-47)gray600(33-35)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeView.swift (4)
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/View+Shadow.swift (2)
body(9-17)cherrishShadow(21-23)Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeViewModel.swift (1)
loadDashboard(21-33)Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/View+Color.swift (6)
gray700(37-39)gray0(9-11)red700(89-91)red600(85-87)gray600(33-35)red500(81-83)Cherrish-iOS/Cherrish-iOS/Core/DIContainer.swift (1)
resolve(27-34)
Cherrish-iOS/Cherrish-iOS/Presentation/ViewFactory.swift (1)
Cherrish-iOS/Cherrish-iOS/Core/DIContainer.swift (1)
resolve(27-34)
🪛 SwiftLint (0.57.0)
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/OnComingCard.swift
[Warning] 15-15: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
🔇 Additional comments (13)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/ChallengeView/Coordinator/ChallengeCoordinatorView.swift (1)
28-30:.selectRoutine에서 다른 화면으로 이동 시 탭 바 깜빡임 가능성 검토 필요
.selectRoutine에서.loading또는.selectMission으로 네비게이션할 경우,onDisappear가 먼저 실행되어 탭 바가 표시된 후, 목적지의onAppear에서 다시 숨기게 됩니다. 이로 인해 탭 바가 순간적으로 깜빡일 수 있습니다.또한
.loading과.selectMission라우트에는onDisappear핸들러가 없어서 해당 화면에서 뒤로 가기 시 탭 바 복원 로직이 일관되지 않습니다.다음 시나리오들을 테스트하여 탭 바 상태 전환이 부드럽게 이루어지는지 확인해 주세요:
.selectRoutine→.loading네비게이션 시 깜빡임 여부.loading또는.selectMission에서 뒤로 가기 시 탭 바 복원 여부필요시 탭 바 상태 관리를 중앙화하거나, 모든 관련 라우트에 일관된
onDisappear핸들러를 추가하는 것을 고려해 보세요.Cherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_chellenge_bar0.imageset/Contents.json (1)
8-15: 2x, 3x 스케일 에셋 파일이 누락되었습니다.2x와 3x 스케일 항목에 파일명이 지정되지 않았습니다. SVG는 벡터 포맷이므로 Xcode의 "Preserve Vector Data" 옵션을 활성화하면 자동 스케일링이 가능하지만, 해당 설정이 올바르게 되어 있는지 확인이 필요합니다.
다음 사항을 확인해 주세요:
- Xcode에서 해당 이미지셋의 "Preserve Vector Data" 옵션이 활성화되어 있는지
- 또는 의도적으로 2x/3x 에셋을 생략한 것인지
- 고해상도 디바이스(iPhone Pro 등)에서 이미지가 정상적으로 렌더링되는지
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/illustration_noschedule.imageset/Contents.json (1)
1-23: 에셋 카탈로그 구성이 올바릅니다.1x/2x/3x 스케일과 universal idiom, info 메타데이터 구성이 표준 형식에 맞습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.0.imageset/Contents.json (1)
1-23: 에셋 카탈로그 구성이 올바릅니다.1x/2x/3x 스케일과 universal idiom, info 메타데이터 구성이 표준 형식에 맞습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.4.imageset/Contents.json (1)
1-23: 에셋 카탈로그 구성이 올바릅니다.1x/2x/3x 스케일과 universal idiom, info 메타데이터 구성이 표준 형식에 맞습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.2.imageset/Contents.json (1)
1-23: 에셋 카탈로그 구성이 올바릅니다.1x/2x/3x 스케일과 universal idiom, info 메타데이터 구성이 표준 형식에 맞습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.1.imageset/Contents.json (1)
1-23: 에셋 카탈로그 구성이 올바릅니다.1x/2x/3x 스케일과 universal idiom, info 메타데이터 구성이 표준 형식에 맞습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_lv.3.imageset/Contents.json (1)
1-22: 에셋 카탈로그 설정 확인 완료.
스케일/idiom 구성이 정상적입니다.Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/OnComingCard.swift (1)
13-23: D-Day 표기 개선과 패딩 적용 좋습니다.
가독성 및 UI 일관성 측면에서 변경이 명확합니다.Also applies to: 31-38, 48-48
Cherrish-iOS/Cherrish-iOS/Domain/Interface/HomeInterface.swift (1)
1-12: HomeInterface 정의 명확합니다.
새 use-case/DI 흐름과 잘 맞습니다.Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/MonthPlan.swift (1)
25-32: LGTM - 태그 너비 동적 처리 개선고정 너비(width: 32.adjustedW) 제거로 태그 텍스트 길이에 따라 자연스럽게 크기가 조정됩니다. 다양한 태그 텍스트 길이를 지원하기 위한 적절한 변경입니다.
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeViewModel.swift (1)
21-33: 로딩 상태 업데이트 흐름이 명확합니다MainActor에서 로딩/에러 상태를 관리하는 흐름이 잘 정리되어 있습니다.
Cherrish-iOS/Cherrish-iOS/Data/Repository/HomeRepository.swift (1)
17-19: fetchDashboard() 구현이 인터페이스 계약을 잘 지킵니다두 구현 모두 DashboardEntity를 반환하도록 되어 있어 사용처와 잘 맞습니다.
Also applies to: 78-80
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/home_icon/home_chellenge_bar0.imageset/Contents.json
Show resolved
Hide resolved
| struct DashboardEntity { | ||
| let date: String | ||
| let dayOfWeek: String | ||
| let challengeName: String | ||
| let cherryLevel: Int | ||
| let challengeRate: Double | ||
| let recentProcedures: [RecentProcedureEntity] | ||
| let upcomingProcedures: [UpcomingProcedureEntity] | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
date와 dayOfWeek의 타입 검토
Domain 모델에서 날짜를 String으로 저장하면 날짜 연산이나 비교가 어려워집니다. API 응답을 그대로 전달하는 DTO가 아닌 Domain Entity라면, Date 타입 사용을 권장합니다.
만약 현재 구조가 의도된 것이라면 무시해주세요.
🔧 제안하는 수정
struct DashboardEntity {
- let date: String
- let dayOfWeek: String
+ let date: Date
let challengeName: String
let cherryLevel: Int
let challengeRate: Double
let recentProcedures: [RecentProcedureEntity]
let upcomingProcedures: [UpcomingProcedureEntity]
+
+ var formattedDate: String {
+ // Presentation layer에서 포맷팅
+ }
+
+ var formattedDayOfWeek: String {
+ // Date에서 요일 계산
+ }
}🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Domain/Model/DashboardEntity.swift` around lines 10
- 18, DashboardEntity currently stores date and dayOfWeek as String; change
date's type to Date (replace let date: String with let date: Date) and remove or
convert the stored dayOfWeek to a computed property or a typed enum (e.g., var
dayOfWeek: String { /* format from date */ } or DayOfWeek enum) so domain
consumers can perform date operations and comparisons; update any initializers
or mappers that construct DashboardEntity to parse the API string into a Date
and derive dayOfWeek from that Date (ensure RecentProcedureEntity and
UpcomingProcedureEntity usage is unaffected).
| var displayText: String { | ||
| switch self { | ||
| case .sensitive: | ||
| return "민감기" | ||
| case .caution: | ||
| return "주의기" | ||
| case .recovery: | ||
| return "회복기" | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
displayText는 Presentation 레이어에 적합
displayText computed property는 한국어 UI 문자열을 반환하므로 Presentation 로직입니다. Domain 레이어는 비즈니스 로직만 포함하고, UI 표시 문자열은 View 또는 ViewModel에서 처리하는 것이 Clean Architecture 원칙에 부합합니다.
현재 구조가 팀의 컨벤션이라면 무시해도 됩니다.
🔧 제안하는 수정 (Presentation 레이어로 이동)
Domain 모델에서 displayText 제거:
enum ProcedurePhase {
case sensitive
case caution
case recovery
-
- var displayText: String {
- switch self {
- case .sensitive:
- return "민감기"
- case .caution:
- return "주의기"
- case .recovery:
- return "회복기"
- }
- }
}Presentation 레이어에 extension 추가:
// Presentation/Extensions/ProcedurePhase+Display.swift
extension ProcedurePhase {
var displayText: String {
switch self {
case .sensitive: return "민감기"
case .caution: return "주의기"
case .recovery: return "회복기"
}
}
}🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Domain/Model/DashboardEntity.swift` around lines 38
- 47, Remove the UI presentation property displayText from the domain model
(DashboardEntity/ProcedurePhase) so the Domain layer only contains business
logic, then add a Presentation-layer extension for ProcedurePhase that
reintroduces the displayText computed property (mapping .sensitive, .caution,
.recovery to the Korean strings) in a new file (e.g.,
Presentation/Extensions/ProcedurePhase+Display.swift); update any call sites to
import/use the Presentation module or extension where UI strings are needed.
| HStack(spacing: 0) { | ||
| TypographyText("웰니스 • 마음챙김", style: .title2_m_16, color: .gray900) | ||
|
|
||
| TypographyText("80.9%", style: .body3_m_12, color: .red700) | ||
| .frame(height: 19.adjustedH) | ||
| .padding(.horizontal, 7.adjustedW) | ||
| .background( | ||
| RoundedRectangle(cornerRadius: 30.adjustedW) | ||
| .strokeBorder(.red600, lineWidth: 1.adjustedW) | ||
| ) | ||
| .padding(.leading, 6.adjustedW) | ||
|
|
There was a problem hiding this comment.
ChallengeCardView가 전달받은 데이터 대신 하드코딩된 값을 표시함
challengeName/challengeRate가 실제 UI에 반영되지 않아 데이터 표시가 깨집니다.
🛠️ 수정 제안
- HStack(spacing: 0) {
- TypographyText("웰니스 • 마음챙김", style: .title2_m_16, color: .gray900)
-
- TypographyText("80.9%", style: .body3_m_12, color: .red700)
+ HStack(spacing: 0) {
+ TypographyText(challengeName, style: .title2_m_16, color: .gray900)
+
+ TypographyText(challengeRate, style: .body3_m_12, color: .red700)
.frame(height: 19.adjustedH)
.padding(.horizontal, 7.adjustedW)
.background(
RoundedRectangle(cornerRadius: 30.adjustedW)
.strokeBorder(.red600, lineWidth: 1.adjustedW)
)
.padding(.leading, 6.adjustedW)📝 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.
| HStack(spacing: 0) { | |
| TypographyText("웰니스 • 마음챙김", style: .title2_m_16, color: .gray900) | |
| TypographyText("80.9%", style: .body3_m_12, color: .red700) | |
| .frame(height: 19.adjustedH) | |
| .padding(.horizontal, 7.adjustedW) | |
| .background( | |
| RoundedRectangle(cornerRadius: 30.adjustedW) | |
| .strokeBorder(.red600, lineWidth: 1.adjustedW) | |
| ) | |
| .padding(.leading, 6.adjustedW) | |
| HStack(spacing: 0) { | |
| TypographyText(challengeName, style: .title2_m_16, color: .gray900) | |
| TypographyText(challengeRate, style: .body3_m_12, color: .red700) | |
| .frame(height: 19.adjustedH) | |
| .padding(.horizontal, 7.adjustedW) | |
| .background( | |
| RoundedRectangle(cornerRadius: 30.adjustedW) | |
| .strokeBorder(.red600, lineWidth: 1.adjustedW) | |
| ) | |
| .padding(.leading, 6.adjustedW) | |
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeView.swift` around
lines 140 - 151, Replace the hardcoded TypographyText literals in
ChallengeCardView with the view's data properties: use the challengeName
(instead of "웰니스 • 마음챙김") and a formatted challengeRate (instead of "80.9%")
passed into the view; keep the same modifiers (.frame, .padding, .background
with RoundedRectangle and .strokeBorder) on the dynamic TypographyText so
styling/layout remains identical and ensure challengeRate is formatted as a
percentage string (e.g., using String(format:) or NumberFormatter) before
passing to TypographyText.
There was a problem hiding this comment.
api 연결할때 연결하겠습니다 ~
| let name: String | ||
| let count: Int | ||
| let dDay: Int | ||
| var onTap: (() -> Void)? = nil |
There was a problem hiding this comment.
옵셔널 초기값 nil은 중복입니다.
SwiftLint 경고를 피하려면 초기값을 제거하세요.
✅ 수정 제안
- var onTap: (() -> Void)? = nil
+ var onTap: (() -> Void)?🧰 Tools
🪛 SwiftLint (0.57.0)
[Warning] 15-15: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/OnComingCard.swift`
at line 15, The property declaration onTap: (() -> Void)? = nil in OnComingCard
contains a redundant optional nil initializer; remove the "= nil" so the
optional is implicitly nil (i.e., change the declaration of onTap in the
OnComingCard type to just var onTap: (() -> Void)?). This will satisfy SwiftLint
and keep behavior unchanged.
| guard let fetchDashboardData = DIContainer.shared.resolve(type: FetchDashboardData.self) else { | ||
| return | ||
| } | ||
|
|
||
| DIContainer.shared.register(type: HomeViewModel.self) { | ||
| return HomeViewModel(fetchDashboardData: fetchDashboardData) | ||
| } |
There was a problem hiding this comment.
Silent failure와 클로저 캡처 이슈 검토 필요
두 가지 잠재적 문제가 있습니다:
-
Silent failure:
FetchDashboardDataresolve 실패 시 조용히 return하여HomeViewModel이 등록되지 않습니다. 이후ViewFactory.makeHomeView()에서fatalError가 발생하면 근본 원인 파악이 어렵습니다. -
클로저 캡처:
fetchDashboardData가assemble()시점에 한 번 resolve되어 클로저에 캡처됩니다. 만약 use-case가 상태를 가지거나, DI 컨테이너가 재구성되는 경우 예상치 못한 동작이 발생할 수 있습니다.
🔧 제안하는 수정
- guard let fetchDashboardData = DIContainer.shared.resolve(type: FetchDashboardData.self) else {
- return
+ guard let fetchDashboardData = DIContainer.shared.resolve(type: FetchDashboardData.self) else {
+ assertionFailure("FetchDashboardData를 resolve할 수 없습니다. DomainDependencyAssembler 등록 여부를 확인하세요.")
+ return
}또는 lazy resolution 방식으로 변경:
DIContainer.shared.register(type: HomeViewModel.self) {
- return HomeViewModel(fetchDashboardData: fetchDashboardData)
+ guard let fetchDashboardData = DIContainer.shared.resolve(type: FetchDashboardData.self) else {
+ fatalError("FetchDashboardData를 resolve할 수 없습니다.")
+ }
+ return HomeViewModel(fetchDashboardData: fetchDashboardData)
}📝 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.
| guard let fetchDashboardData = DIContainer.shared.resolve(type: FetchDashboardData.self) else { | |
| return | |
| } | |
| DIContainer.shared.register(type: HomeViewModel.self) { | |
| return HomeViewModel(fetchDashboardData: fetchDashboardData) | |
| } | |
| guard let fetchDashboardData = DIContainer.shared.resolve(type: FetchDashboardData.self) else { | |
| assertionFailure("FetchDashboardData를 resolve할 수 없습니다. DomainDependencyAssembler 등록 여부를 확인하세요.") | |
| return | |
| } | |
| DIContainer.shared.register(type: HomeViewModel.self) { | |
| return HomeViewModel(fetchDashboardData: fetchDashboardData) | |
| } |
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swift`
around lines 33 - 39, Currently assemble() silently returns if
DIContainer.shared.resolve(type: FetchDashboardData.self) fails and it captures
fetchDashboardData once into the registration closure; instead, stop the silent
failure and switch to lazy resolution inside the registration closure: in
DIContainer.shared.register(type: HomeViewModel.self) { ... } resolve
FetchDashboardData again (guard let fetch = DIContainer.shared.resolve(type:
FetchDashboardData.self) else { fatalError("Missing FetchDashboardData for
HomeViewModel") }) and then return HomeViewModel(fetchDashboardData: fetch) so
you avoid capturing a single instance and provide an explicit error message that
makes ViewFactory.makeHomeView failures diagnosable; reference
FetchDashboardData, DIContainer.shared.resolve, DIContainer.shared.register,
HomeViewModel, assemble, and ViewFactory.makeHomeView.
| func fetchDashboard() async throws -> DashboardEntity { | ||
| return createMockDashboard() | ||
| } | ||
|
|
||
| private func createMockDashboard() -> DashboardEntity { |
| enum ProcedurePhase { | ||
| case sensitive | ||
| case caution | ||
| case recovery | ||
|
|
||
| var displayText: String { | ||
| switch self { | ||
| case .sensitive: | ||
| return "민감기" | ||
| case .caution: | ||
| return "주의기" | ||
| case .recovery: | ||
| return "회복기" | ||
| } | ||
| } |
There was a problem hiding this comment.
이것은 도메인에 있을게 아닌것 같아요~~ 프레젠테이션으로 옮겨주세요 !!
|
|
||
| init(viewModel: HomeViewModel) { | ||
| _viewModel = StateObject(wrappedValue: viewModel) | ||
| } |
There was a problem hiding this comment.
뷰모델 여기서 init 하지 않아도 DIContainer 프레젠테이션 레이어에서 주입해주고 있어서 괜찮슴
| ) | ||
| .frame(height: 270.adjustedH) | ||
|
|
||
| Color("home_gradient2") |
|
|
||
| } | ||
|
|
||
| private var contentView: some View { |
| VStack(spacing: 0) { | ||
| Circle() | ||
| .fill(circleColor) | ||
| .frame(width: circleSize, height: circleSize) | ||
|
|
||
| Rectangle() | ||
| .fill( | ||
| LinearGradient( | ||
| colors: [lineTopColor, lineBottomColor], | ||
| startPoint: .top, | ||
| endPoint: .bottom | ||
| ) | ||
| ) | ||
| .frame(width: lineWidth, height: lineLength) | ||
| } |
| @Published var isLoading: Bool = false | ||
| @Published var errorMessage: String? | ||
|
|
||
| private let fetchDashboardData: FetchDashboardData |
| var challengeRateText: String { | ||
| guard let rate = dashboardData?.challengeRate else { return "0%" } | ||
| return String(format: "%.0f%%", rate) | ||
| } |
| struct MonthPlanItem: Identifiable { | ||
| let id: String | ||
| let name: String | ||
| let dayCount: Int | ||
| let tag: String | ||
| let isRecent: Bool | ||
| } | ||
|
|
||
| struct UpcomingItem: Identifiable { | ||
| let id: String | ||
| let date: String | ||
| let name: String | ||
| let count: Int | ||
| let dDay: Int | ||
| } |
There was a problem hiding this comment.
프레젠테이션에 있는 것보단 도메인으로 옮기는게 좋을 것 같습니다 ~
| name: procedure.name, | ||
| dayCount: procedure.daysSince, | ||
| tag: procedure.currentPhase.displayText, | ||
| isRecent: true |
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Fix all issues with AI agents
In `@Cherrish-iOS/Cherrish-iOS/Data/Repository/HomeRepository.swift`:
- Around line 23-28: MockHomeRepository currently stores a private
networkService in its initializer but never uses it in fetchDashboard(), so
either remove the unused dependency or explicitly document/forward it to
maintain API parity with DefaultHomeRepository; update MockHomeRepository by
deleting the networkService property and removing it from the init signature if
the mock doesn't need networking, or if you want parity keep the property but
mark it unused with a comment and/or use it in a no-op way in fetchDashboard()
to make intent clear (referencing MockHomeRepository, networkService, and
fetchDashboard to locate the change).
- Around line 62-79: The RecentProcedureEntity entries in HomeRepository.swift
have stray trailing commas on their own lines; tidy the mock array by moving
each comma to the end of the previous element line (or removing duplicate extra
commas) so each RecentProcedureEntity initializer is followed directly by a
comma on the same line, ensuring consistent array element formatting for the
RecentProcedureEntity(...) items.
In `@Cherrish-iOS/Cherrish-iOS/Domain/Model/DashboardEntity.swift`:
- Around line 20-24: RecentProcedureEntity in the Domain layer depends on
Presentation's ProcedurePhase enum; move the core ProcedurePhase enum definition
into the Domain module (keep only domain values and logic, remove any
UI/displayText properties), update RecentProcedureEntity to reference the new
Domain ProcedurePhase type, and then in the Presentation layer add an extension
on ProcedurePhase to provide UI strings (displayText) and any presentation-only
helpers; finally update imports/usages across the codebase so Presentation
consumes the Domain enum via the extension and Domain has no Presentation
dependencies.
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeView.swift`:
- Around line 363-387: PinView is declared as a public struct while other
subviews are private; change its declaration to private struct PinView: View to
match the file's access control and limit scope, unless PinView is intentionally
used from other files—if it is used externally, keep it non-private and instead
make the other subviews public as needed; update the declaration of PinView
accordingly and verify all usages within the file still compile.
- Line 11: Replace the force-unwrap in HomeView by resolving the HomeViewModel
safely: in HomeView's initializer call DIContainer.shared.resolve(type:
HomeViewModel.self) and handle the optional result (e.g., guard let vm = ...
else { log an explicit error including context about
PresentationDependencyAssembler and either create a sensible fallback
HomeViewModel or call fatalError with a clear message }). Then assign the
resolved vm to the StateObject via _viewModel = StateObject(wrappedValue: vm)
instead of using DIContainer.shared.resolve(...)! directly; reference HomeView,
DIContainer.shared.resolve(type:), and HomeViewModel to locate the change.
- Around line 261-281: The pinStyle(for:totalCount:) function references
non-existent static colors (Color.gray0, Color.red600, Color.red500,
Color.red300) causing a compile error; fix by replacing those static references
with asset-based initializers (e.g. let gray0 = Color("gray_0"), let red600 =
Color("red600"), let red500 = Color("red500"), let red300 = Color("red300"))
inside pinStyle or alternatively create a Color extension (e.g. extension Color
{ static let gray0 = Color("gray_0") ... }) in View+Color.swift and use those
static properties from pinStyle.
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeViewModel.swift`:
- Around line 26-30: The catch block only sets errorMessage with
error.localizedDescription but doesn't log the error; update the catch to call
CherrishLogger.error(...) with a clear context (e.g., "Failed to fetch dashboard
data") and include the caught error (and stack if available) alongside keeping
errorMessage = error.localizedDescription; target the catch after
fetchDashboardDataUseCase.execute() in HomeViewModel where dashboardData and
errorMessage are set.
- Around line 35-44: The computed property formattedDate recreates DateFormatter
on every access; cache formatters to avoid the allocation cost by adding
reusable formatters (e.g., private static let inputDateFormatter with dateFormat
"yyyy-MM-dd" and private static let outputDateFormatter with locale
Locale(identifier: "ko_KR") and dateFormat "yyyy년 M월 d일 (E)") in HomeViewModel,
then replace the local formatter usage in formattedDate to parse with
inputDateFormatter and format with outputDateFormatter; keep names
formattedDate, inputDateFormatter, and outputDateFormatter to locate the
changes.
♻️ Duplicate comments (6)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeViewModel.swift (1)
46-49: 소수점 포맷팅 확인 필요
"%.0f%%"는 소수점을 버립니다. 예:80.9→"81%". 기획 의도에 따라"%.1f%%"(80.9%) 사용을 검토해주세요.Cherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swift (1)
33-39: Silent failure 및 클로저 캡처 이슈이전 리뷰에서 지적된 문제가 아직 해결되지 않았습니다:
FetchDashboardDataresolve 실패 시assertionFailure없이 조용히 return하여 디버깅이 어렵습니다.fetchDashboardData가assemble()시점에 한 번 resolve되어 클로저에 캡처됩니다.권장 수정: lazy resolution 방식
- guard let fetchDashboardData = DIContainer.shared.resolve(type: FetchDashboardData.self) else { - return - } - DIContainer.shared.register(type: HomeViewModel.self) { - return HomeViewModel(fetchDashboardDataUseCase: fetchDashboardData) + guard let fetchDashboardData = DIContainer.shared.resolve(type: FetchDashboardData.self) else { + fatalError("FetchDashboardData를 resolve할 수 없습니다.") + } + return HomeViewModel(fetchDashboardDataUseCase: fetchDashboardData) }Cherrish-iOS/Cherrish-iOS/Domain/Model/DashboardEntity.swift (1)
10-18:date와dayOfWeek의 타입 검토이전 리뷰에서 지적된 사항입니다. Domain Entity에서 날짜를
String으로 저장하면 날짜 연산이나 비교가 어렵습니다.Date타입 사용을 권장합니다.Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeView.swift (2)
136-147: ChallengeCardView가 전달받은 데이터 대신 하드코딩된 값을 표시
challengeName과challengeRate가 파라미터로 전달되지만 실제 UI에서는 하드코딩된"웰니스 • 마음챙김"과"80.9%"를 표시합니다.수정 제안
HStack(spacing: 0) { - TypographyText("웰니스 • 마음챙김", style: .title2_m_16, color: .gray900) + TypographyText(challengeName, style: .title2_m_16, color: .gray900) - TypographyText("80.9%", style: .body3_m_12, color: .red700) + TypographyText(challengeRate, style: .body3_m_12, color: .red700)
246-246:.background(.gray0)컴파일 오류 가능성
View+Color.swift에서gray0()는foregroundStyle을 설정하는 View extension 함수입니다.backgroundmodifier에 ShapeStyle로 사용할 수 없습니다.수정 제안
- .background(.gray0) + .background(Color("gray_0"))Cherrish-iOS/Cherrish-iOS/Data/Repository/HomeRepository.swift (1)
17-19:fatalError로 인한 프로덕션 크래시 위험
DefaultHomeRepository.fetchDashboard()가fatalError("Not implemented")를 던집니다. DI 설정 오류로MockHomeRepository대신DefaultHomeRepository가 주입되면 앱이 크래시됩니다.API 구현 전까지 임시로 mock 데이터를 반환하거나, 커스텀 에러를 throw하는 것이 안전합니다.
권장 수정
func fetchDashboard() async throws -> DashboardEntity { - fatalError("Not implemented") + throw HomeRepositoryError.notImplemented } +} + +enum HomeRepositoryError: Error { + case notImplemented }
| struct MockHomeRepository: HomeInterface { | ||
| private let networkService: NetworkService | ||
|
|
||
| init(networkService: NetworkService) { | ||
| self.networkService = networkService | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
MockHomeRepository의 미사용 의존성
networkService가 주입되지만 fetchDashboard()에서 사용되지 않습니다. Mock 구현에서는 네트워크가 필요 없으므로 제거하거나, DefaultHomeRepository와의 API 일관성을 위해 유지하는 것인지 명확히 해주세요.
제거 시 수정 제안
struct MockHomeRepository: HomeInterface {
- private let networkService: NetworkService
-
- init(networkService: NetworkService) {
- self.networkService = networkService
- }
+ init() {}📝 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.
| struct MockHomeRepository: HomeInterface { | |
| private let networkService: NetworkService | |
| init(networkService: NetworkService) { | |
| self.networkService = networkService | |
| } | |
| struct MockHomeRepository: HomeInterface { | |
| init() {} | |
| } |
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Data/Repository/HomeRepository.swift` around lines
23 - 28, MockHomeRepository currently stores a private networkService in its
initializer but never uses it in fetchDashboard(), so either remove the unused
dependency or explicitly document/forward it to maintain API parity with
DefaultHomeRepository; update MockHomeRepository by deleting the networkService
property and removing it from the init signature if the mock doesn't need
networking, or if you want parity keep the property but mark it unused with a
comment and/or use it in a no-op way in fetchDashboard() to make intent clear
(referencing MockHomeRepository, networkService, and fetchDashboard to locate
the change).
| , | ||
| RecentProcedureEntity( | ||
| name: "필러", | ||
| daysSince: 8, | ||
| currentPhase: .recovery | ||
| ) | ||
| , | ||
| RecentProcedureEntity( | ||
| name: "필러", | ||
| daysSince: 8, | ||
| currentPhase: .recovery | ||
| ) | ||
| , | ||
| RecentProcedureEntity( | ||
| name: "필러", | ||
| daysSince: 8, | ||
| currentPhase: .recovery | ||
| ) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Mock 데이터 포맷팅 정리
trailing comma가 라인 끝에 별도로 위치해 있어 코드가 깔끔하지 않습니다.
포맷팅 수정 제안
RecentProcedureEntity(
name: "필러",
daysSince: 8,
currentPhase: .recovery
- )
- ,
+ ),
RecentProcedureEntity(📝 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.
| , | |
| RecentProcedureEntity( | |
| name: "필러", | |
| daysSince: 8, | |
| currentPhase: .recovery | |
| ) | |
| , | |
| RecentProcedureEntity( | |
| name: "필러", | |
| daysSince: 8, | |
| currentPhase: .recovery | |
| ) | |
| , | |
| RecentProcedureEntity( | |
| name: "필러", | |
| daysSince: 8, | |
| currentPhase: .recovery | |
| ) | |
| , | |
| RecentProcedureEntity( | |
| name: "필러", | |
| daysSince: 8, | |
| currentPhase: .recovery | |
| ), | |
| RecentProcedureEntity( | |
| name: "필러", | |
| daysSince: 8, | |
| currentPhase: .recovery | |
| ), | |
| RecentProcedureEntity( | |
| name: "필러", | |
| daysSince: 8, | |
| currentPhase: .recovery | |
| ) |
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Data/Repository/HomeRepository.swift` around lines
62 - 79, The RecentProcedureEntity entries in HomeRepository.swift have stray
trailing commas on their own lines; tidy the mock array by moving each comma to
the end of the previous element line (or removing duplicate extra commas) so
each RecentProcedureEntity initializer is followed directly by a comma on the
same line, ensuring consistent array element formatting for the
RecentProcedureEntity(...) items.
| struct RecentProcedureEntity { | ||
| let name: String | ||
| let daysSince: Int | ||
| let currentPhase: ProcedurePhase | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find ProcedurePhase file locations
echo "=== ProcedurePhase 파일 위치 ==="
find . -name "*ProcedurePhase*" -type f 2>/dev/null | grep -v ".git" | head -20
echo -e "\n=== Domain 모델에서 ProcedurePhase 사용 확인 ==="
rg -n "ProcedurePhase" --type=swift | head -30Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 514
🏁 Script executed:
#!/bin/bash
# Examine DashboardEntity.swift content
echo "=== DashboardEntity.swift 전체 내용 ==="
fd "DashboardEntity.swift" --type f -x cat -n {} 2>/dev/null
echo -e "\n=== Domain 디렉토리 구조 ==="
find . -path "*/Domain/*" -type f -name "*.swift" 2>/dev/null | head -20Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 1448
🏁 Script executed:
#!/bin/bash
# Check import statements in DashboardEntity.swift
echo "=== DashboardEntity.swift imports 확인 ==="
fd "DashboardEntity.swift" --type f -x head -20 {} 2>/dev/null
echo -e "\n=== RecentProcedureEntity 정의 확인 ==="
rg -A 5 "struct RecentProcedureEntity" --type=swiftRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 1026
🏁 Script executed:
#!/bin/bash
# Check for all imports in DashboardEntity.swift
echo "=== DashboardEntity.swift 전체 import 확인 ==="
rg "^import" --type=swift Cherrish-iOS/Cherrish-iOS/Domain/Model/DashboardEntity.swift
echo -e "\n=== ProcedurePhase 전체 정의 ==="
cat Cherrish-iOS/Cherrish-iOS/Presentation/Global/Model/ProcedurePhase.swift
echo -e "\n=== 다른 Domain 모델들에서 Presentation 타입 사용 여부 ==="
rg "currentPhase\|ProcedurePhase" --type=swift Cherrish-iOS/Cherrish-iOS/Domain/Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 603
ProcedurePhase의 Domain 레이어 이동 필요
RecentProcedureEntity가 ProcedurePhase 타입을 사용하지만, 현재 ProcedurePhase는 Presentation/Global/Model/에 정의되어 있습니다. Domain 레이어가 Presentation 레이어의 타입에 의존하게 되어 Clean Architecture 원칙을 위반합니다.
권장 해결책:
ProcedurePhaseenum을 Domain 레이어로 이동 (displayText 제외)- Presentation 레이어에서 extension으로 UI 문자열 추가
이를 통해 Domain이 순수하게 유지되고, Presentation에서만 UI 관련 로직을 갖게 됩니다.
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Domain/Model/DashboardEntity.swift` around lines 20
- 24, RecentProcedureEntity in the Domain layer depends on Presentation's
ProcedurePhase enum; move the core ProcedurePhase enum definition into the
Domain module (keep only domain values and logic, remove any UI/displayText
properties), update RecentProcedureEntity to reference the new Domain
ProcedurePhase type, and then in the Presentation layer add an extension on
ProcedurePhase to provide UI strings (displayText) and any presentation-only
helpers; finally update imports/usages across the codebase so Presentation
consumes the Domain enum via the extension and Domain has no Presentation
dependencies.
| import SwiftUI | ||
|
|
||
| struct HomeView: View { | ||
| @StateObject private var viewModel = DIContainer.shared.resolve(type: HomeViewModel.self)! |
There was a problem hiding this comment.
Force unwrap으로 인한 런타임 크래시 위험
DIContainer.shared.resolve(type: HomeViewModel.self)!에서 force unwrap 사용 시, resolve 실패 시 앱이 크래시됩니다. PresentationDependencyAssembler에서 silent return 문제가 해결되지 않으면 이 크래시의 원인 파악이 어렵습니다.
권장 수정: 명시적 에러 처리
- `@StateObject` private var viewModel = DIContainer.shared.resolve(type: HomeViewModel.self)!
+ `@StateObject` private var viewModel: HomeViewModel = {
+ guard let vm = DIContainer.shared.resolve(type: HomeViewModel.self) else {
+ fatalError("HomeViewModel을 resolve할 수 없습니다. DI 등록을 확인하세요.")
+ }
+ return vm
+ }()🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeView.swift` at line
11, Replace the force-unwrap in HomeView by resolving the HomeViewModel safely:
in HomeView's initializer call DIContainer.shared.resolve(type:
HomeViewModel.self) and handle the optional result (e.g., guard let vm = ...
else { log an explicit error including context about
PresentationDependencyAssembler and either create a sensible fallback
HomeViewModel or call fatalError with a clear message }). Then assign the
resolved vm to the StateObject via _viewModel = StateObject(wrappedValue: vm)
instead of using DIContainer.shared.resolve(...)! directly; reference HomeView,
DIContainer.shared.resolve(type:), and HomeViewModel to locate the change.
| private func pinStyle(for index: Int, totalCount: Int) -> (circleColor: Color, lineTopColor: Color, lineBottomColor: Color) { | ||
| let red600 = Color.red600 | ||
| let red500 = Color.red500 | ||
| let red300 = Color.red300 | ||
| let gray0 = Color.gray0 | ||
|
|
||
| let result: (circleColor: Color, lineTopColor: Color, lineBottomColor: Color) | ||
|
|
||
| switch index { | ||
| case 0: | ||
| let lineBottomColor = totalCount >= 2 ? red500 : gray0 | ||
| result = (red600, red600, lineBottomColor) | ||
| case 1: | ||
| let lineBottomColor = totalCount >= 3 ? red300 : gray0 | ||
| result = (red500, red500, lineBottomColor) | ||
| default: | ||
| result = (red300, red300, gray0) | ||
| } | ||
|
|
||
| return result | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Color.gray0 static 정의 확인
echo "=== Color extension에서 gray0 관련 정의 확인 ==="
rg -n "gray0" --type=swift -B 2 -A 2
echo -e "\n=== Color extension에서 static var 정의 확인 ==="
rg -n "static\s+var\s+gray0" --type=swift
echo -e "\n=== Color("gray_0") 사용 확인 ==="
rg -n 'Color\("gray_0"\)' --type=swift | head -20Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 7545
🏁 Script executed:
#!/bin/bash
# Color extension 전체 파일 찾기
echo "=== Color extension 파일 찾기 ==="
find . -type f -name "*.swift" | xargs grep -l "extension Color" | head -10
echo -e "\n=== Color extension에서 red600, red500, red300, gray0 정의 확인 ==="
rg -n "extension Color" --type=swift -A 100 | grep -E "(static\s+var|var\s+)(red600|red500|red300|gray0)" | head -20Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 162
🏁 Script executed:
#!/bin/bash
# red600, red500 등의 정의 찾기
echo "=== red600 정의 찾기 ==="
rg -n "red600" --type=swift -B 5 -A 2 | head -40
echo -e "\n=== Color extension 파일 전체 목록 ==="
rg -l "extension Color" --type=swift
echo -e "\n=== Assets.xcassets 구조 확인 ==="
find . -type d -name "*.xcassets" | head -5
echo -e "\n=== ColorSet 파일 확인 ==="
find . -name "*.colorset" | head -20Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 5731
🏁 Script executed:
#!/bin/bash
# View+Color.swift 전체 내용 확인
echo "=== View+Color.swift 전체 내용 ==="
cat -n Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/View+Color.swift
echo -e "\n=== red_300, red_500, red_600 colorset 확인 ==="
find . -name "red_*.colorset" -type dRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 3452
🏁 Script executed:
#!/bin/bash
# Color를 확장하는 모든 파일 찾기
echo "=== Color 확장 정의 찾기 ==="
rg -n "extension Color" --type=swift -A 50 2>/dev/null
echo -e "\n=== static var 패턴으로 Color 속성 찾기 ==="
rg -n "static.*var.*(red|gray)" --type=swift | head -20
echo -e "\n=== Color 직접 사용 패턴 (Color.red600 등) 찾기 ==="
rg -n "Color\.(red|gray)[0-9]" --type=swift | head -30Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 1015
🏁 Script executed:
#!/bin/bash
# 모든 Swift 파일에서 extension Color 검색
echo "=== 전체 extension Color 검색 ==="
find . -name "*.swift" -type f | xargs grep -n "extension Color" 2>/dev/null
echo -e "\n=== Color 관련 파일 모두 확인 ==="
find . -name "*Color*.swift" -type f | head -20
echo -e "\n=== Xcode Build Phase에서 생성된 Color 파일 확인 ==="
find . -name "*.swift" -type f | xargs grep -l "red600\|red500\|red300\|gray0" | head -10Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 944
🏁 Script executed:
#!/bin/bash
# Derived Data나 빌드 생성 파일 확인
echo "=== 프로젝트 구조에서 Color 정의 파일 찾기 ==="
find . -name "*.swift" -type f -exec grep -l "^extension Color" {} \;
echo -e "\n=== HomeView.swift 주변 코드 확인 ==="
sed -n '260,280p' Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeView.swift
echo -e "\n=== Color 속성 정의 방식 검색 (shorthand syntax) ==="
rg -n "@color|ColorResource|Color\(" --type=swift Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/ -A 2 -B 2 | head -50
echo -e "\n=== 프로젝트 설정 파일 확인 ==="
find . -name "*.pbxproj" -o -name "*.xcconfig" | head -5Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 5936
🏁 Script executed:
#!/bin/bash
# Swift Package 확인
echo "=== Package.swift 확인 ==="
find . -name "Package.swift" | xargs cat 2>/dev/null
echo -e "\n=== build 폴더 확인 ==="
find . -path "*/build/*" -name "*.swift" 2>/dev/null | head -20
echo -e "\n=== DerivedData 확인 ==="
find . -path "*DerivedData*" -name "*.swift" 2>/dev/null | head -20
echo -e "\n=== 프로젝트 내 모든 Swift 파일 수 ==="
find . -name "*.swift" -type f | wc -l
echo -e "\n=== Color 관련 모든 Swift 파일 목록 ==="
find . -name "*.swift" -type f | xargs grep -l "Color\." | sortRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 835
🏁 Script executed:
#!/bin/bash
# 모든 Swift 파일 전체 검색 - Color static var 정의
echo "=== 모든 Swift 파일에서 static 정의 검색 ==="
find . -name "*.swift" -type f -exec grep -l "static" {} \; | while read f; do
echo "File: $f"
grep -n "static.*var\|static.*let" "$f" | grep -i "color\|red\|gray" | head -5
done
echo -e "\n=== 특정 Color 확장 파일 내용 전체 ==="
find . -path "*Global*" -name "*.swift" | grep -i color
echo -e "\n=== HomeView import 확인 ==="
head -20 Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeView.swiftRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 1447
🏁 Script executed:
#!/bin/bash
# 전체 Swift 파일에서 extension Color 검색
echo "=== 전체 Swift 파일 목록 ==="
find . -name "*.swift" -type f
echo -e "\n=== 각 파일에서 Color 관련 static 정의 검색 ==="
find . -name "*.swift" -type f -exec bash -c '
if grep -q "extension Color\|Color\." "$1"; then
echo "File: $1"
grep -n "extension Color\|static.*var.*red\|static.*var.*gray" "$1" | head -5
fi
' _ {} \;
echo -e "\n=== HomeView.swift에서 Color 사용 부분 ==="
grep -n "let red\|let gray" Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeView.swiftRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 7106
🏁 Script executed:
#!/bin/bash
# 모든 Swift 파일 내용에서 Color 정의 찾기
echo "=== Color 직접 정의 있는지 검색 ==="
find . -name "*.swift" -type f -exec bash -c '
content=$(cat "$1")
if echo "$content" | grep -q "\.red600\|\.red500\|\.red300\|\.gray0"; then
echo "=== Found Color usage in: $1 ==="
echo "$content" | grep -n "extension Color\|static.*var\|var.*red600\|var.*gray0" | head -10
fi
' _ {} \;
echo -e "\n=== Color extension을 정의하는 파일 찾기 ==="
find . -name "*.swift" -type f | xargs grep -l "extension Color"
echo -e "\n=== 모든 .swift 파일에서 Color extension 패턴 찾기 ==="
find . -name "*.swift" -exec grep -H "extension Color" {} \;
echo -e "\n=== 혹시 generated 파일이나 다른 폴더에 Color 정의가 있는지 확인 ==="
find . -type f \( -name "*Color*.swift" -o -name "*color*.swift" \) 2>/dev/nullRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 1168
🏁 Script executed:
#!/bin/bash
# Macro 관련 파일이나 특수한 설정이 있는지 확인
echo "=== Macro 폴더 확인 ==="
find . -type d -name "*Macro*" -o -name "*Generated*"
echo -e "\n=== Build Phases 확인 ==="
cat ./Cherrish-iOS/Cherrish-iOS.xcodeproj/project.pbxproj | grep -A 5 "Color\|red600\|gray0" | head -30
echo -e "\n=== 혹시 Color extension이 조건부로 정의되었는지 확인 ==="
find . -name "*.swift" -exec grep -B 5 -A 20 "Color.red600\|Color.gray0" {} \; | head -50
echo -e "\n=== XCCodeGen이나 다른 생성 도구 확인 ==="
find . -name "*.yml" -o -name "*.yaml" | head -10Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 2218
Color.gray0 사용 수정 필요
Color.gray0, Color.red600, Color.red500, Color.red300 등의 static 속성이 Color extension에 정의되어 있지 않습니다. 현재 코드는 컴파일되지 않습니다.
Assets.xcassets에 ColorSet 파일이 존재하므로 다음과 같이 수정하세요:
let gray0 = Color("gray_0")
let red600 = Color("red600")
let red500 = Color("red500")
let red300 = Color("red300")또는 View+Color.swift에 Color extension을 추가하여 static 속성을 정의하는 방식도 가능합니다.
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeView.swift` around
lines 261 - 281, The pinStyle(for:totalCount:) function references non-existent
static colors (Color.gray0, Color.red600, Color.red500, Color.red300) causing a
compile error; fix by replacing those static references with asset-based
initializers (e.g. let gray0 = Color("gray_0"), let red600 = Color("red600"),
let red500 = Color("red500"), let red300 = Color("red300")) inside pinStyle or
alternatively create a Color extension (e.g. extension Color { static let gray0
= Color("gray_0") ... }) in View+Color.swift and use those static properties
from pinStyle.
| struct PinView: View { | ||
| var circleColor: Color | ||
| var lineTopColor: Color | ||
| var lineBottomColor: Color | ||
| var circleSize: CGFloat = 10.adjustedW | ||
| var lineLength: CGFloat = 65.adjustedH | ||
| var lineWidth: CGFloat = 2.adjustedW | ||
|
|
||
| var body: some View { | ||
| VStack(spacing: 0) { | ||
| Circle() | ||
| .fill(circleColor) | ||
| .frame(width: circleSize, height: circleSize) | ||
|
|
||
| Rectangle() | ||
| .fill( | ||
| LinearGradient( | ||
| colors: [lineTopColor, lineBottomColor], | ||
| startPoint: .top, | ||
| endPoint: .bottom | ||
| ) | ||
| ) | ||
| .frame(width: lineWidth, height: lineLength) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
PinView 접근 제어자 확인 필요
다른 subview들은 private struct로 선언되어 있는데, PinView만 struct로 선언되어 있습니다. 다른 화면에서 재사용할 계획이 아니라면 private으로 통일하는 것이 좋습니다.
수정 제안
-struct PinView: View {
+private struct PinView: View {🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeView.swift` around
lines 363 - 387, PinView is declared as a public struct while other subviews are
private; change its declaration to private struct PinView: View to match the
file's access control and limit scope, unless PinView is intentionally used from
other files—if it is used externally, keep it non-private and instead make the
other subviews public as needed; update the declaration of PinView accordingly
and verify all usages within the file still compile.
| do { | ||
| dashboardData = try await fetchDashboardDataUseCase.execute() | ||
| } catch { | ||
| errorMessage = error.localizedDescription | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
에러 로깅 추가 권장
error.localizedDescription만 저장하고 있어 디버깅 시 상세 정보가 부족할 수 있습니다. CherrishLogger.error()를 활용하여 에러를 기록하면 좋겠습니다.
권장 수정
do {
dashboardData = try await fetchDashboardDataUseCase.execute()
} catch {
+ CherrishLogger.error(error)
errorMessage = error.localizedDescription
}📝 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.
| do { | |
| dashboardData = try await fetchDashboardDataUseCase.execute() | |
| } catch { | |
| errorMessage = error.localizedDescription | |
| } | |
| do { | |
| dashboardData = try await fetchDashboardDataUseCase.execute() | |
| } catch { | |
| CherrishLogger.error(error) | |
| errorMessage = error.localizedDescription | |
| } |
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeViewModel.swift`
around lines 26 - 30, The catch block only sets errorMessage with
error.localizedDescription but doesn't log the error; update the catch to call
CherrishLogger.error(...) with a clear context (e.g., "Failed to fetch dashboard
data") and include the caught error (and stack if available) alongside keeping
errorMessage = error.localizedDescription; target the catch after
fetchDashboardDataUseCase.execute() in HomeViewModel where dashboardData and
errorMessage are set.
| var formattedDate: String { | ||
| guard let date = dashboardData?.date else { return "" } | ||
| let formatter = DateFormatter() | ||
| formatter.dateFormat = "yyyy-MM-dd" | ||
| guard let parsedDate = formatter.date(from: date) else { return date } | ||
|
|
||
| formatter.locale = Locale(identifier: "ko_KR") | ||
| formatter.dateFormat = "yyyy년 M월 d일 (E)" | ||
| return formatter.string(from: parsedDate) | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
DateFormatter 생성 최적화
formattedDate가 호출될 때마다 DateFormatter가 새로 생성됩니다. DateFormatter는 생성 비용이 높으므로 캐싱하는 것이 좋습니다.
권장 수정
+ private static let inputFormatter: DateFormatter = {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "yyyy-MM-dd"
+ return formatter
+ }()
+
+ private static let outputFormatter: DateFormatter = {
+ let formatter = DateFormatter()
+ formatter.locale = Locale(identifier: "ko_KR")
+ formatter.dateFormat = "yyyy년 M월 d일 (E)"
+ return formatter
+ }()
+
var formattedDate: String {
guard let date = dashboardData?.date else { return "" }
- let formatter = DateFormatter()
- formatter.dateFormat = "yyyy-MM-dd"
- guard let parsedDate = formatter.date(from: date) else { return date }
-
- formatter.locale = Locale(identifier: "ko_KR")
- formatter.dateFormat = "yyyy년 M월 d일 (E)"
- return formatter.string(from: parsedDate)
+ guard let parsedDate = Self.inputFormatter.date(from: date) else { return date }
+ return Self.outputFormatter.string(from: parsedDate)
}📝 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.
| var formattedDate: String { | |
| guard let date = dashboardData?.date else { return "" } | |
| let formatter = DateFormatter() | |
| formatter.dateFormat = "yyyy-MM-dd" | |
| guard let parsedDate = formatter.date(from: date) else { return date } | |
| formatter.locale = Locale(identifier: "ko_KR") | |
| formatter.dateFormat = "yyyy년 M월 d일 (E)" | |
| return formatter.string(from: parsedDate) | |
| } | |
| private static let inputFormatter: DateFormatter = { | |
| let formatter = DateFormatter() | |
| formatter.dateFormat = "yyyy-MM-dd" | |
| return formatter | |
| }() | |
| private static let outputFormatter: DateFormatter = { | |
| let formatter = DateFormatter() | |
| formatter.locale = Locale(identifier: "ko_KR") | |
| formatter.dateFormat = "yyyy년 M월 d일 (E)" | |
| return formatter | |
| }() | |
| var formattedDate: String { | |
| guard let date = dashboardData?.date else { return "" } | |
| guard let parsedDate = Self.inputFormatter.date(from: date) else { return date } | |
| return Self.outputFormatter.string(from: parsedDate) | |
| } |
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Home/HomeViewModel.swift`
around lines 35 - 44, The computed property formattedDate recreates
DateFormatter on every access; cache formatters to avoid the allocation cost by
adding reusable formatters (e.g., private static let inputDateFormatter with
dateFormat "yyyy-MM-dd" and private static let outputDateFormatter with locale
Locale(identifier: "ko_KR") and dateFormat "yyyy년 M월 d일 (E)") in HomeViewModel,
then replace the local formatter usage in formattedDate to parse with
inputDateFormatter and format with outputDateFormatter; keep names
formattedDate, inputDateFormatter, and outputDateFormatter to locate the
changes.
sum130
left a comment
There was a problem hiding this comment.
이미 많이 수정하셨군뇨! 수고했어요~~ 굿!!
| import SwiftUI | ||
|
|
||
| struct HomeView: View { | ||
| @StateObject private var viewModel = DIContainer.shared.resolve(type: HomeViewModel.self)! |


엠티뷰 추가 (챌린지 레벨이 0일때)
작업 내용
설명
offset를 챌린지카드뷰 기준으로 위치조정하여 ZStack으로 올려서 기기대응했습니다.
3인 maxVisibleCount를 기준으로 더보기 버튼을 관리
아닐때는 3개만
카드수의 따른 그라데이션 변화