Conversation
…formation-View' into style/#47-Splash,-Onboarding,-Information-View
📝 WalkthroughWalkthrough스플래시·온보딩 관련 이미지 에셋을 추가하고, 단일 OnboardingView를 다중 페이지 탭 기반의 OnboardingContainerView로 교체했으며, 정보 입력(InformationView)과 숫자 입력 지원을 위한 텍스트 필드 확장을 도입했습니다. Changes
Sequence DiagramsequenceDiagram
participant User as 사용자
participant App as 앱
participant OCV as rgba(30,144,255,0.5) OnboardingContainerView
participant Page as rgba(34,139,34,0.5) OnboardingPage(1/2)
participant Coord as rgba(255,140,0,0.5) OnboardingCoordinator
participant IV as rgba(147,112,219,0.5) InformationView
participant AppCoord as rgba(220,20,60,0.5) AppCoordinator
participant Tabbar as rgba(70,130,180,0.5) TabBar
App->>OCV: 온보딩 화면 표시
OCV->>Page: TabView로 Page1 렌더링
loop 페이지 전환
User->>OCV: 스와이프(페이지 이동)
OCV->>Page: 다음 페이지 렌더링
Page->>OCV: currentPage 업데이트
end
User->>OCV: 마지막 페이지에서 "다음" 클릭
OCV->>Coord: navigationToInformation()
Coord->>IV: InformationView 표시
User->>IV: 이름/나이 입력
IV->>IV: 입력 검증(이름≤7, 나이≤100)
User->>IV: "Next" 클릭
IV->>AppCoord: navigationToTabbar()
AppCoord->>Tabbar: TabBar로 전환
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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: 8
🤖 Fix all issues with AI agents
In
`@Cherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/illustration_onboarding_cal.imageset/Contents.json`:
- Around line 1-23: The asset catalog directory name contains a typo
("onboadrding") and must be renamed to "onboarding" across the repo; update the
directory name and any references accordingly (verify other onboarding asset
dirs and rename them too). In Contents.json (the file shown) standardize the
image filenames so 1x uses "illustration_onboarding_cal.png" instead of
"illustration_onboarding_cal-1.png" and keep the 2x/3x entries as
"illustration_onboarding_cal@2x.png" and "illustration_onboarding_cal@3x.png";
ensure the actual PNG files in the same folder are renamed to match and update
any references to these asset filenames.
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/InformationView.swift`:
- Around line 57-58: updateButtonState currently enables the button if name or
age are non-empty strings without trimming or validating age; change it to trim
whitespace from name and age and only set buttonState = .active when trimmed
name is not empty and trimmed age contains only digits (e.g., Int(trimmedAge)
succeeds or use CharacterSet decimal checks) otherwise set .normal; reference
updateButtonState, name, age, and buttonState/.active/.normal when making the
change.
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingContainerView.swift`:
- Around line 31-41: The "시작하기" CherrishButton remains tappable when hidden
because only opacity is used; modify the button view in OnboardingContainerView
to disable hits when not visible by adding a hit-testing or disabled guard —
e.g., use allowsHitTesting(currentPage == 1) or disabled(currentPage != 1) on
the CherrishButton so that when currentPage != 1 the button does not receive
touches (ensure this is applied alongside the existing .opacity and .animation
and keeps the onboardingCoordinator.push(.information) action intact).
- Around line 60-61: The preview is missing the required EnvironmentObject
(OnboardingCoordinator) causing runtime crashes; update the preview block that
instantiates OnboardingContainerView in OnboardingContainerView's `#Preview` to
create and inject a test/dummy OnboardingCoordinator via .environmentObject(...)
so the view's EnvironmentObject dependency is satisfied when Preview runs
(locate the preview that constructs OnboardingContainerView and add the
OnboardingCoordinator instance as an environment object).
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage1.swift`:
- Around line 51-69: The VStack is already expanded by the .frame(maxWidth:
.infinity) call so the trailing Spacer() is redundant; remove the Spacer() after
the VStack (or alternatively remove .frame(maxWidth: .infinity) and keep Spacer
if you prefer) to eliminate duplicate layout intent—update the view in
OnboardingPage1.swift around the VStack block (look for VStack(alignment:
.leading, spacing: 0) and its .frame(maxWidth: .infinity)) accordingly.
- Around line 13-22: The close icon Image(.close) in OnboardingPage1.swift has
no tap action; add a handler by using a Button or adding an onTapGesture that
calls a dismissal/close function (e.g., inject `@Environment`(\.dismiss) var
dismiss or a closeAction callback) and replace the bare Image with a tappable
control (keep the existing modifiers like renderingMode, resizable, frame, and
foregroundStyle). Ensure accessibility by providing a label for the
button/image.
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage2.swift`:
- Around line 10-27: The close icon Image(.close) in OnboardingPage2 has no tap
action; wrap it in a Button (or add an onTapGesture) and wire it to dismiss the
onboarding by adding `@Environment`(\.dismiss) private var dismiss to the
OnboardingPage2 view (or call the provided navigation/close handler if one
exists) and invoke dismiss() in the Button action, keeping the existing
modifiers (renderingMode, resizable, frame, foregroundStyle) applied to the
label so the icon appearance is unchanged.
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/PageIndicator.swift`:
- Around line 10-26: Add VoiceOver support to the PageIndicator by making the
HStack a single accessibility element and exposing the current page info: in
PageIndicator's body, mark the HStack as an accessibility element (ignore
children) and set an accessibilityLabel/value that uses currentPage and
totalPages (e.g., "Page X of Y") so users hear the current position; ensure
individual Circle views generated by ForEach are not announced separately (hide
their accessibility) so only the combined label is spoken.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (14)
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/Splash/logo.imageset/logo.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/LV.0.imageset/LV.0.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/LV.1.imageset/LV.1.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/LV.2.imageset/LV.2.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/LV.3.imageset/LV.3.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/LV.4.imageset/LV.4.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/comment.imageset/comment.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/illustration_onboarding_cal.imageset/illustration_onboarding_cal-1.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/illustration_onboarding_cal.imageset/illustration_onboarding_cal@2x.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/illustration_onboarding_cal.imageset/illustration_onboarding_cal@3x.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/illustration_onboarding_ch.imageset/illustration_onboarding_ch.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/onboarding2.imageset/onboarding2 1.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/onboarding2.imageset/onboarding2 2.pngis excluded by!**/*.pngCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/onboarding2.imageset/onboarding2.pngis excluded by!**/*.png
📒 Files selected for processing (24)
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/Splash/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/Splash/logo.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/LV.0.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/LV.1.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/LV.2.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/LV.3.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/LV.4.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/comment.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/illustration_onboarding_cal.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/illustration_onboarding_ch.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/onboarding2.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/Coordinator/OnboardingCoordinator.swiftCherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/Coordinator/OnboardingCoordinatorView.swiftCherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/InformationView.swiftCherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingContainerView.swiftCherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage1.swiftCherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage2.swiftCherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingView.swiftCherrish-iOS/Cherrish-iOS/Presentation/Feature/Splash/SplashView.swiftCherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishTextBox/CherrishTextBox.swiftCherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishTextField/CherrishTextField.swiftCherrish-iOS/Cherrish-iOS/Presentation/Global/Components/PageIndicator.swiftCherrish-iOS/Cherrish-iOS/Presentation/ViewFactory.swift
💤 Files with no reviewable changes (1)
- Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingView.swift
🧰 Additional context used
🧬 Code graph analysis (8)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Splash/SplashView.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 (1)
gray600(33-35)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingContainerView.swift (2)
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/View+Shadow.swift (1)
body(9-17)Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/Coordinator/OnboardingCoordinator.swift (1)
push(20-22)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage2.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 (2)
gray600(33-35)gray1000(49-51)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/InformationView.swift (1)
Cherrish-iOS/Cherrish-iOS/Presentation/Coordinator/AppCoordinator.swift (1)
navigationToTabbar(20-23)
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/PageIndicator.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 (2)
gray800(41-43)gray500(29-31)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage1.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)
gray600(33-35)gray1000(49-51)red700(89-91)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/Coordinator/OnboardingCoordinatorView.swift (1)
Cherrish-iOS/Cherrish-iOS/Presentation/ViewFactory.swift (2)
makeOnboardingContainerView(28-33)makeInformationView(35-40)
Cherrish-iOS/Cherrish-iOS/Presentation/ViewFactory.swift (1)
Cherrish-iOS/Cherrish-iOS/Core/DIContainer.swift (1)
resolve(27-34)
🔇 Additional comments (13)
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/Splash/Contents.json (1)
1-6: 에셋 카탈로그 메타데이터 정의가 적절합니다.Cherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/LV.0.imageset/Contents.json (1)
1-12: 이미지셋 정의가 정상입니다.Cherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/onboarding2.imageset/Contents.json (1)
1-23: 1x/2x/3x 스케일 리소스 구성이 적절합니다.Cherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/Contents.json (1)
1-6: 카탈로그 메타데이터 형식이 올바릅니다.Cherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/LV.3.imageset/Contents.json (1)
1-12: 이미지셋 정의가 정상입니다.Cherrish-iOS/Cherrish-iOS/Assets.xcassets/onboadrding/LV.1.imageset/Contents.json (1)
1-12: LGTM!SVG 벡터 에셋에 적합한 구조입니다. 디렉토리 이름 오타는 이전 파일에서 이미 언급되었습니다.
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Splash/SplashView.swift (1)
10-32: LGTM!스플래시 뷰의 레이아웃 구조가 깔끔합니다.
TypographyText를 활용한 일관된 타이포그래피 적용이 좋습니다.선택적으로, 패딩 값들(24, 157, 8)을 상수로 추출하면 디자인 시스템 변경 시 유지보수가 용이해질 수 있습니다.
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishTextField/CherrishTextField.swift (1)
10-34: 숫자 입력 스타일 확장과 키보드 타입 분기 좋습니다
.number스타일 추가와 키보드 타입 분기 적용이 일관적으로 적용되어 좋아요.Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishTextBox/CherrishTextBox.swift (1)
14-27: 숫자 입력 분기 적용 깔끔합니다
isNumberField로 스타일을 분기하는 방식이 명확하고 재사용성도 좋아요.Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/Coordinator/OnboardingCoordinator.swift (1)
10-13: 라우트 순서 변경 OK열거형 순서 변경은 동작에 영향이 없어 보입니다.
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/InformationView.swift (1)
47-49: 불필요한 제안 - CherrishButton이 이미 state로 탭을 방어합니다
CherrishButton은 line 41의.disabled(type.isDisabled(for: state))모디파이어를 통해 버튼 상태에 따라 탭을 제어합니다..next타입의 경우, state가.normal이면 버튼이 비활성화되고, SwiftUI에서 비활성화된 버튼은 탭해도 액션 클로저가 실행되지 않습니다. 따라서 액션 클로저 내부에 추가 guard 절은 필요하지 않습니다.Likely an incorrect or invalid review comment.
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/Coordinator/OnboardingCoordinatorView.swift (1)
16-26: 온보딩 진입점을 컨테이너로 정렬한 변경은 적절합니다.라우팅 매핑이 명확하고 일관됩니다.
Cherrish-iOS/Cherrish-iOS/Presentation/ViewFactory.swift (1)
10-12: 팩토리 진입점 변경이 흐름에 맞게 적용되었습니다.새 컨테이너 기반 온보딩 구조와 잘 정렬됩니다.
Also applies to: 28-33
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
.../Cherrish-iOS/Assets.xcassets/onboadrding/illustration_onboarding_cal.imageset/Contents.json
Show resolved
Hide resolved
| private func updateButtonState() { | ||
| buttonState = (!name.isEmpty && !age.isEmpty) ? .active : .normal |
There was a problem hiding this comment.
나이 입력이 숫자/공백 검증 없이 활성화됨
현재는 공백이나 숫자가 아닌 값도 입력만 있으면 활성화됩니다. 숫자 검증 및 공백 제거 후 활성화되도록 보완이 필요합니다.
🛠️ 검증 로직 보완 제안
private func updateButtonState() {
- buttonState = (!name.isEmpty && !age.isEmpty) ? .active : .normal
+ let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines)
+ let trimmedAge = age.trimmingCharacters(in: .whitespacesAndNewlines)
+ let ageIsValid = Int(trimmedAge) != nil
+ buttonState = (!trimmedName.isEmpty && ageIsValid) ? .active : .normal
}📝 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 updateButtonState() { | |
| buttonState = (!name.isEmpty && !age.isEmpty) ? .active : .normal | |
| private func updateButtonState() { | |
| let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines) | |
| let trimmedAge = age.trimmingCharacters(in: .whitespacesAndNewlines) | |
| let ageIsValid = Int(trimmedAge) != nil | |
| buttonState = (!trimmedName.isEmpty && ageIsValid) ? .active : .normal | |
| } |
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/InformationView.swift`
around lines 57 - 58, updateButtonState currently enables the button if name or
age are non-empty strings without trimming or validating age; change it to trim
whitespace from name and age and only set buttonState = .active when trimmed
name is not empty and trimmed age contains only digits (e.g., Int(trimmedAge)
succeeds or use CharacterSet decimal checks) otherwise set .normal; reference
updateButtonState, name, age, and buttonState/.active/.normal when making the
change.
| CherrishButton( | ||
| title: "시작하기", | ||
| type: .confirm, | ||
| state: $buttonState | ||
| ) { | ||
| onboardingCoordinator.push(.information) | ||
| } | ||
| .padding(.horizontal, 25.adjustedW) | ||
| .padding(.bottom, 57.adjustedH) | ||
| .opacity(currentPage == 1 ? 1 : 0) | ||
| .animation(.easeInOut(duration: 0.3), value: currentPage) |
There was a problem hiding this comment.
숨김 상태에서도 버튼이 탭되는 문제를 막아주세요.
opacity만으로는 터치가 비활성화되지 않아 0페이지에서도 “시작하기”가 동작할 수 있습니다.
🛠️ 수정 제안
.padding(.horizontal, 25.adjustedW)
.padding(.bottom, 57.adjustedH)
.opacity(currentPage == 1 ? 1 : 0)
+ .allowsHitTesting(currentPage == 1)
+ .accessibilityHidden(currentPage != 1)
.animation(.easeInOut(duration: 0.3), value: currentPage)🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingContainerView.swift`
around lines 31 - 41, The "시작하기" CherrishButton remains tappable when hidden
because only opacity is used; modify the button view in OnboardingContainerView
to disable hits when not visible by adding a hit-testing or disabled guard —
e.g., use allowsHitTesting(currentPage == 1) or disabled(currentPage != 1) on
the CherrishButton so that when currentPage != 1 the button does not receive
touches (ensure this is applied alongside the existing .opacity and .animation
and keeps the onboardingCoordinator.push(.information) action intact).
| #Preview { | ||
| OnboardingContainerView(viewModel: OnboardingViewModel()) |
There was a problem hiding this comment.
프리뷰에 EnvironmentObject가 빠져 크래시 가능성이 있습니다.
OnboardingCoordinator가 주입되지 않아 프리뷰 실행 시 런타임 오류가 발생합니다.
🛠️ 수정 제안
`#Preview` {
- OnboardingContainerView(viewModel: OnboardingViewModel())
+ OnboardingContainerView(viewModel: OnboardingViewModel())
+ .environmentObject(OnboardingCoordinator())
}🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingContainerView.swift`
around lines 60 - 61, The preview is missing the required EnvironmentObject
(OnboardingCoordinator) causing runtime crashes; update the preview block that
instantiates OnboardingContainerView in OnboardingContainerView's `#Preview` to
create and inject a test/dummy OnboardingCoordinator via .environmentObject(...)
so the view's EnvironmentObject dependency is satisfied when Preview runs
(locate the preview that constructs OnboardingContainerView and add the
OnboardingCoordinator instance as an environment object).
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage1.swift
Outdated
Show resolved
Hide resolved
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage1.swift
Show resolved
Hide resolved
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage2.swift
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage1.swift`:
- Around line 82-84: Preview crashes because OnboardingPage1 expects an
`@EnvironmentObject` of type OnboardingCoordinator but the Preview does not
provide one; fix by updating the preview to inject a coordinator instance (e.g.,
call .environmentObject(OnboardingCoordinator() or a lightweight mock) on the
OnboardingPage1 view in the `#Preview` block) so the `@EnvironmentObject` used by
OnboardingPage1 (and its onboardingCoordinator.push() call) is available during
previews.
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage2.swift`:
- Around line 65-141: The carousel currently always animates and only
initializes sizes on onAppear, so it doesn't respect Reduce Motion accessibility
settings or respond to width/rotation changes; update InfiniteLevelCarousel to
read the SwiftUI accessibilityReduceMotion environment value and skip
startAnimation when reduce motion is enabled (use
`@Environment`(\\.accessibilityReduceMotion) var reduceMotion), and add an
onChange handler for geometry.size.width (or onChange of screenWidth) to
recompute singleSetWidth, reset offsetX to initialOffset, and restart the
animation by calling stopAnimation() then startAnimation(); ensure
startAnimation, stopAnimation, animateCarousel, offsetX, initialOffset,
singleSetWidth, and screenWidth are used accordingly so the animation is
disabled for reduce motion and correctly recalculated/restarted on size changes.
- Around line 144-146: OnboardingPage2 crashes in preview because its
`@EnvironmentObject` onboardingCoordinator isn't provided; update the Preview
provider for OnboardingPage2 to inject a suitable onboardingCoordinator instance
(e.g., a real or mock OnboardingCoordinator) via .environmentObject(...) so the
preview supplies the required EnvironmentObject and no longer crashes.
♻️ Duplicate comments (3)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage1.swift (2)
15-26: 닫기 아이콘 접근성/터치 타깃 보강 필요
Image + onTapGesture는 버튼 시맨틱/VoiceOver 힌트가 부족하고 탭 영역도 24pt로 작습니다. 버튼으로 감싸고 접근성 레이블 및 최소 터치 타깃을 확보해 주세요.🔧 제안 수정
- Image(.close) - .renderingMode(.template) - .resizable() - .scaledToFit() - .frame(width: 24.adjustedW, height: 24.adjustedW) - .foregroundStyle(.gray600) - .onTapGesture { - onboardingCoordinator.push(.information) - } + Button { + onboardingCoordinator.push(.information) + } label: { + Image(.close) + .renderingMode(.template) + .resizable() + .scaledToFit() + .frame(width: 24.adjustedW, height: 24.adjustedW) + .foregroundStyle(.gray600) + } + .buttonStyle(.plain) + .frame(width: 44.adjustedW, height: 44.adjustedW, alignment: .center) + .accessibilityLabel("닫기")
56-74: 중복 레이아웃 제거 권장
frame(maxWidth: .infinity)와Spacer()가 동일 목적이라 중복됩니다. 한 쪽만 유지해도 됩니다.♻️ 간소화 예시
VStack(alignment: .leading, spacing: 0) { TypographyText("시술 후 불편감이 남을 수 있는 기간을 계산해", style: .title1_sb_18, color: .gray1000) @@ TypographyText("일정을 한 눈에 정리해드려요", style: .title1_sb_18, color: .gray1000) .padding(.top, 6.adjustedH) } .padding(.top, 7.adjustedH) - .frame(maxWidth: .infinity) - Spacer() + .frame(maxWidth: .infinity, alignment: .leading)Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage2.swift (1)
21-32: 닫기 아이콘 접근성/터치 타깃 보강 필요
Image + onTapGesture는 버튼 시맨틱/VoiceOver 힌트가 부족하고 탭 영역도 24pt로 작습니다. 버튼으로 감싸고 접근성 레이블 및 최소 터치 타깃을 확보해 주세요.🔧 제안 수정
- Image(.close) - .renderingMode(.template) - .resizable() - .scaledToFit() - .frame(width: 24.adjustedW, height: 24.adjustedW) - .foregroundStyle(.gray600) - .onTapGesture { - onboardingCoordinator.push(.information) - } + Button { + onboardingCoordinator.push(.information) + } label: { + Image(.close) + .renderingMode(.template) + .resizable() + .scaledToFit() + .frame(width: 24.adjustedW, height: 24.adjustedW) + .foregroundStyle(.gray600) + } + .buttonStyle(.plain) + .frame(width: 44.adjustedW, height: 44.adjustedW, alignment: .center) + .accessibilityLabel("닫기")
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (2)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage1.swiftCherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage2.swift
🧰 Additional context used
🧬 Code graph analysis (1)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage1.swift (3)
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)
gray600(33-35)gray1000(49-51)red700(89-91)Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/Coordinator/OnboardingCoordinator.swift (1)
push(20-22)
🔇 Additional comments (2)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage1.swift (1)
31-54: 이미지 오버레이/코멘트 배치가 깔끔합니다.Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage2.swift (1)
39-59: 본문 레이아웃 구성이 명확합니다.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage1.swift
Outdated
Show resolved
Hide resolved
| struct InfiniteLevelCarousel: View { | ||
| let levelImages: [ImageResource] | ||
| let animationDuration: Double | ||
|
|
||
| private var imageSize: CGFloat { 89.adjustedW } | ||
|
|
||
| @State private var offsetX: CGFloat = 0 | ||
| @State private var singleSetWidth: CGFloat = 0 | ||
| @State private var isAnimating: Bool = false | ||
| @State private var animationID: UUID = UUID() | ||
| @State private var screenWidth: CGFloat = 0 | ||
|
|
||
| private var initialOffset: CGFloat { | ||
| return -(singleSetWidth - (screenWidth - singleSetWidth) / 2) | ||
| } | ||
|
|
||
| var body: some View { | ||
| GeometryReader { geometry in | ||
| HStack(spacing: 0) { | ||
| ForEach(0..<3, id: \.self) { _ in | ||
| HStack(spacing: 0) { | ||
| ForEach(levelImages.indices, id: \.self) { index in | ||
| Image(levelImages[index]) | ||
| .resizable() | ||
| .scaledToFit() | ||
| .frame(width: imageSize, height: imageSize) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| .offset(x: offsetX) | ||
| .onAppear { | ||
| screenWidth = geometry.size.width | ||
| singleSetWidth = imageSize * CGFloat(levelImages.count) | ||
| startAnimation() | ||
| } | ||
| .onDisappear { | ||
| stopAnimation() | ||
| } | ||
| } | ||
| .clipped() | ||
| } | ||
|
|
||
| private func startAnimation() { | ||
| guard !isAnimating else { return } | ||
|
|
||
| isAnimating = true | ||
| let currentAnimationID = UUID() | ||
| animationID = currentAnimationID | ||
|
|
||
| offsetX = initialOffset | ||
| animateCarousel(animationID: currentAnimationID) | ||
| } | ||
|
|
||
| private func stopAnimation() { | ||
| isAnimating = false | ||
| animationID = UUID() | ||
|
|
||
| offsetX = initialOffset | ||
| } | ||
|
|
||
| private func animateCarousel(animationID: UUID) { | ||
| guard self.animationID == animationID, isAnimating else { return } | ||
|
|
||
| withAnimation(.linear(duration: animationDuration)) { | ||
| offsetX = initialOffset - singleSetWidth | ||
| } | ||
|
|
||
| DispatchQueue.main.asyncAfter(deadline: .now() + animationDuration) { | ||
|
|
||
| guard self.animationID == animationID, isAnimating else { return } | ||
|
|
||
| offsetX = initialOffset | ||
|
|
||
| animateCarousel(animationID: animationID) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Reduce Motion/사이즈 변경 대응 권장
무한 애니메이션은 Reduce Motion 사용자를 고려해 비활성화하는 편이 좋고, 화면 회전 등으로 폭이 변경될 때 재계산/재시작이 필요할 수 있습니다.
♿️ 모션/사이즈 대응 예시
struct InfiniteLevelCarousel: View {
+ `@Environment`(\.accessibilityReduceMotion) private var reduceMotion
let levelImages: [ImageResource]
let animationDuration: Double
@@
GeometryReader { geometry in
HStack(spacing: 0) {
@@
.offset(x: offsetX)
.onAppear {
screenWidth = geometry.size.width
singleSetWidth = imageSize * CGFloat(levelImages.count)
- startAnimation()
+ if reduceMotion {
+ offsetX = initialOffset
+ } else {
+ startAnimation()
+ }
}
+ .onChange(of: geometry.size.width) { newWidth in
+ screenWidth = newWidth
+ singleSetWidth = imageSize * CGFloat(levelImages.count)
+ if !reduceMotion {
+ stopAnimation()
+ startAnimation()
+ } else {
+ offsetX = initialOffset
+ }
+ }
.onDisappear {
stopAnimation()
}
}🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage2.swift`
around lines 65 - 141, The carousel currently always animates and only
initializes sizes on onAppear, so it doesn't respect Reduce Motion accessibility
settings or respond to width/rotation changes; update InfiniteLevelCarousel to
read the SwiftUI accessibilityReduceMotion environment value and skip
startAnimation when reduce motion is enabled (use
`@Environment`(\\.accessibilityReduceMotion) var reduceMotion), and add an
onChange handler for geometry.size.width (or onChange of screenWidth) to
recompute singleSetWidth, reset offsetX to initialOffset, and restart the
animation by calling stopAnimation() then startAnimation(); ensure
startAnimation, stopAnimation, animateCarousel, offsetX, initialOffset,
singleSetWidth, and screenWidth are used accordingly so the animation is
disabled for reduce motion and correctly recalculated/restarted on size changes.
| #Preview { | ||
| OnboardingPage2() | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "OnboardingPage2.swift" -type fRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 151
🏁 Script executed:
cat -n ./Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage2.swiftRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 5525
프리뷰에서 필수 EnvironmentObject 미주입으로 인한 크래시
OnboardingPage2는 @EnvironmentObject로 onboardingCoordinator를 주입받아야 하는데, 현재 프리뷰에서 이를 제공하지 않아 크래시가 발생합니다. 프리뷰에 필수 environment object를 주입해야 합니다.
💡 프리뷰 주입 예시
`#Preview` {
- OnboardingPage2()
+ OnboardingPage2()
+ .environmentObject(OnboardingCoordinator())
}📝 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.
| #Preview { | |
| OnboardingPage2() | |
| } | |
| `#Preview` { | |
| OnboardingPage2() | |
| .environmentObject(OnboardingCoordinator()) | |
| } |
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage2.swift`
around lines 144 - 146, OnboardingPage2 crashes in preview because its
`@EnvironmentObject` onboardingCoordinator isn't provided; update the Preview
provider for OnboardingPage2 to inject a suitable onboardingCoordinator instance
(e.g., a real or mock OnboardingCoordinator) via .environmentObject(...) so the
preview supplies the required EnvironmentObject and no longer crashes.
| @State var name = "" | ||
| @State var age = "" |
| .onChange(of: name) { _ in updateButtonState() } | ||
| .onChange(of: age) { _ in updateButtonState() } |
| #Preview { | ||
| InformationView(viewModel: OnboardingViewModel()) | ||
| } |
| startPoint: .top, | ||
| endPoint: .bottom | ||
| ) | ||
| .frame(height: 310) |
| .alignmentGuide(.leading) { _ in -152.adjustedW } | ||
| .alignmentGuide(.top) { _ in -209.adjustedH } |
There was a problem hiding this comment.
.alignmentGuide(.leading) { _ in -152.adjustedW }
leading(왼쪽) 기준점을 -152pt로 설정
-> 체리 캐릭터가 오른쪽으로 152pt 이동이라는 뜻인데
앞에서 계속 offset으로 조정해서 offset으로 바꾸겠습니다
| private var initialOffset: CGFloat { | ||
| return -(singleSetWidth - (screenWidth - singleSetWidth) / 2) | ||
| } |
| stopAnimation() | ||
| } | ||
| } | ||
| .clipped() |
There was a problem hiding this comment.
캐러셀 이미지가 화면 밖으러 넘쳐보이는거 자르는겁니다!! 탭뷰로 전환하다보니 잘라줘야해요
| #Preview { | ||
| OnboardingPage2() | ||
| } |
|
|
||
| import SwiftUI | ||
|
|
||
| struct SplashView: View { |
| #Preview { | ||
| VStack(spacing: 20) { | ||
| PageIndicator(currentPage: 0, totalPages: 2) | ||
| PageIndicator(currentPage: 1, totalPages: 2) | ||
| } | ||
| } |
wotjs020708
left a comment
There was a problem hiding this comment.
지금 보면 화면 흐름이
OnbodingPage1 -> OnbodingPage2 로 이동하는데 위에 내비게이션 바도 같이 이동하는 흐름이 자연스럽지 않은 거 같아서 NavgiationBar는 고정이고 아래 컵포넌트만 움직이는건 어떤가요?
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/InformationView.swift`:
- Around line 100-102: The Preview for InformationView is missing the required
EnvironmentObject injections (onboardingCoordinator and appCoordinator) which
can cause a crash; update the preview instantiation that currently uses
InformationView(viewModel: OnboardingViewModel()) to attach .environmentObject
instances for the onboardingCoordinator and appCoordinator (use the concrete
coordinator types used in your app) so InformationView and its viewModel can
access those objects during SwiftUI previews.
- Around line 84-90: When the user corrects their age the showAgeError flag
isn’t cleared; update the age-change handler so it recalculates the error state
whenever age changes. In InformationView.swift modify the .onChange(of: age)
block (and keep .onChange(of: isAgeFocused) behavior) to set showAgeError =
isAgeOverLimit (or false when valid) and then call updateButtonState(), e.g.
update showAgeError based on isAgeOverLimit inside the age change handler so the
error clears as soon as the value becomes valid.
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingContainerView.swift`:
- Around line 45-55: Replace the tappable Image in OnboardingContainerView with
a Button to improve accessibility: change the Image(.close) + .onTapGesture
block into a Button that calls onboardingCoordinator.push(.information), ensure
the close image is used as the Button's label (keeping renderingMode, resizable,
scaledToFit, frame, foregroundStyle and paddings), and add an accessibilityLabel
(e.g., "닫기" or localized equivalent) and accessibilityTraits/role for a button
so screen readers can focus and announce it properly.
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage1.swift`:
- Around line 41-46: The two Rectangle views use Color.red700 which doesn't
exist; either replace Color.red700 with Color("red_700") in OnboardingPage1 (the
two Rectangle().fill(...) calls) to match the existing asset name, or add a
Color extension (e.g., in View+Color) that defines static let red700 =
Color("red_700") so Color.red700 resolves; update the Rectangle().fill usages or
add the extension accordingly.
♻️ Duplicate comments (4)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage1.swift (1)
52-55: 레이아웃 의도가 중복됩니다(Spacer 제거).
.frame(maxWidth: .infinity)로 이미 확장되어Spacer()가 중복됩니다.♻️ 제안 변경
- .frame(maxWidth: .infinity) - Spacer() + .frame(maxWidth: .infinity, alignment: .leading)Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/InformationView.swift (1)
93-96: 나이 입력이 숫자 검증 없이 활성화됩니다.비숫자 입력도 버튼이 활성화될 수 있습니다. 공백 트림 + 숫자 검증을 반영해 주세요.
🛠️ 수정 제안
- private func updateButtonState() { - let isNameValid = !name.isEmpty && !isNameOverLimit - let isAgeValid = !age.isEmpty && !isAgeOverLimit - buttonState = (isNameValid && isAgeValid) ? .active : .normal - } + private func updateButtonState() { + let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines) + let trimmedAge = age.trimmingCharacters(in: .whitespacesAndNewlines) + let ageValue = Int(trimmedAge) + + let isNameValid = !trimmedName.isEmpty && trimmedName.count <= 7 + let isAgeValid = (ageValue != nil) && (ageValue! <= 100) + + buttonState = (isNameValid && isAgeValid) ? .active : .normal + }Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingContainerView.swift (1)
39-42: 숨김 상태에서도 버튼이 탭됩니다.
opacity만으로는 터치가 막히지 않습니다.🛠️ 수정 제안
.padding(.horizontal, 25.adjustedW) .padding(.bottom, 57.adjustedH) .opacity(currentPage == 1 ? 1 : 0) + .allowsHitTesting(currentPage == 1) + .accessibilityHidden(currentPage != 1) .animation(.easeInOut(duration: 0.3), value: currentPage)Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage2.swift (1)
61-84: Reduce Motion 및 화면 크기 변경 대응이 필요합니다.무한 애니메이션은 Reduce Motion 설정을 존중하고, 회전/크기 변경 시 재시작이 필요합니다.
♻️ 대응 예시
struct InfiniteLevelCarousel: View { + `@Environment`(\.accessibilityReduceMotion) private var reduceMotion let levelImages: [ImageResource] let animationDuration: Double @@ .onAppear { screenWidth = geometry.size.width singleSetWidth = imageSize * CGFloat(levelImages.count) - startAnimation() + if reduceMotion { + offsetX = initialOffset + } else { + startAnimation() + } } + .onChange(of: geometry.size.width) { newWidth in + screenWidth = newWidth + singleSetWidth = imageSize * CGFloat(levelImages.count) + if reduceMotion { + offsetX = initialOffset + } else { + stopAnimation() + startAnimation() + } + } .onDisappear { stopAnimation() }
| .onChange(of: name) { _ in updateButtonState() } | ||
| .onChange(of: age) { _ in updateButtonState() } | ||
| .onChange(of: isAgeFocused) { focused in | ||
| if !focused { | ||
| showAgeError = isAgeOverLimit | ||
| } | ||
| } |
There was a problem hiding this comment.
나이 오류 메시지가 수정 후에도 남을 수 있습니다.
오류가 뜬 뒤 값이 정상으로 바뀌어도 showAgeError가 유지됩니다. 값이 정상화되면 에러를 해제하도록 처리해 주세요.
💡 개선 예시
- .onChange(of: age) { _ in updateButtonState() }
+ .onChange(of: age) { _ in
+ updateButtonState()
+ if !isAgeOverLimit { showAgeError = false }
+ }
.onChange(of: isAgeFocused) { focused in
- if !focused {
- showAgeError = isAgeOverLimit
- }
+ if focused {
+ showAgeError = false
+ } else {
+ showAgeError = isAgeOverLimit
+ }
}🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/InformationView.swift`
around lines 84 - 90, When the user corrects their age the showAgeError flag
isn’t cleared; update the age-change handler so it recalculates the error state
whenever age changes. In InformationView.swift modify the .onChange(of: age)
block (and keep .onChange(of: isAgeFocused) behavior) to set showAgeError =
isAgeOverLimit (or false when valid) and then call updateButtonState(), e.g.
update showAgeError based on isAgeOverLimit inside the age change handler so the
error clears as soon as the value becomes valid.
| #Preview { | ||
| InformationView(viewModel: OnboardingViewModel()) | ||
| } |
There was a problem hiding this comment.
Preview에 EnvironmentObject 주입이 없습니다.
onboardingCoordinator, appCoordinator가 필요하므로 프리뷰에서 크래시가 날 수 있습니다.
🧪 프리뷰 보완
`#Preview` {
InformationView(viewModel: OnboardingViewModel())
+ .environmentObject(OnboardingCoordinator())
+ .environmentObject(AppCoordinator())
}🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/InformationView.swift`
around lines 100 - 102, The Preview for InformationView is missing the required
EnvironmentObject injections (onboardingCoordinator and appCoordinator) which
can cause a crash; update the preview instantiation that currently uses
InformationView(viewModel: OnboardingViewModel()) to attach .environmentObject
instances for the onboardingCoordinator and appCoordinator (use the concrete
coordinator types used in your app) so InformationView and its viewModel can
access those objects during SwiftUI previews.
| Image(.close) | ||
| .renderingMode(.template) | ||
| .resizable() | ||
| .scaledToFit() | ||
| .frame(width: 24.adjustedW, height: 24.adjustedW) | ||
| .foregroundStyle(.gray600) | ||
| .padding(.trailing, 29.adjustedW) | ||
| .padding(.top, 74.adjustedH) | ||
| .onTapGesture { | ||
| onboardingCoordinator.push(.information) | ||
| } |
There was a problem hiding this comment.
닫기 아이콘은 Button으로 처리해 접근성을 확보하세요.
Image + onTapGesture는 접근성 라벨/포커싱이 취약합니다. 버튼으로 감싸고 라벨을 추가해주세요.
♿️ 개선 예시
- Image(.close)
- .renderingMode(.template)
- .resizable()
- .scaledToFit()
- .frame(width: 24.adjustedW, height: 24.adjustedW)
- .foregroundStyle(.gray600)
- .padding(.trailing, 29.adjustedW)
- .padding(.top, 74.adjustedH)
- .onTapGesture {
- onboardingCoordinator.push(.information)
- }
+ Button {
+ onboardingCoordinator.push(.information)
+ } label: {
+ Image(.close)
+ .renderingMode(.template)
+ .resizable()
+ .scaledToFit()
+ .frame(width: 24.adjustedW, height: 24.adjustedW)
+ .foregroundStyle(.gray600)
+ }
+ .accessibilityLabel("닫기")
+ .padding(.trailing, 29.adjustedW)
+ .padding(.top, 74.adjustedH)📝 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.
| Image(.close) | |
| .renderingMode(.template) | |
| .resizable() | |
| .scaledToFit() | |
| .frame(width: 24.adjustedW, height: 24.adjustedW) | |
| .foregroundStyle(.gray600) | |
| .padding(.trailing, 29.adjustedW) | |
| .padding(.top, 74.adjustedH) | |
| .onTapGesture { | |
| onboardingCoordinator.push(.information) | |
| } | |
| Button { | |
| onboardingCoordinator.push(.information) | |
| } label: { | |
| Image(.close) | |
| .renderingMode(.template) | |
| .resizable() | |
| .scaledToFit() | |
| .frame(width: 24.adjustedW, height: 24.adjustedW) | |
| .foregroundStyle(.gray600) | |
| } | |
| .accessibilityLabel("닫기") | |
| .padding(.trailing, 29.adjustedW) | |
| .padding(.top, 74.adjustedH) |
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingContainerView.swift`
around lines 45 - 55, Replace the tappable Image in OnboardingContainerView with
a Button to improve accessibility: change the Image(.close) + .onTapGesture
block into a Button that calls onboardingCoordinator.push(.information), ensure
the close image is used as the Button's label (keeping renderingMode, resizable,
scaledToFit, frame, foregroundStyle and paddings), and add an accessibilityLabel
(e.g., "닫기" or localized equivalent) and accessibilityTraits/role for a button
so screen readers can focus and announce it properly.
| Rectangle() | ||
| .fill(Color.red700) | ||
| .frame(width: 252.adjustedW, height: 1.4) | ||
| Rectangle() | ||
| .fill(Color.red700) | ||
| .frame(width: 260.adjustedW, height: 1.4) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Color 확장 및 red700 정의 확인
echo "=== Color 확장 검색 ==="
rg -n --type=swift "extension\s+Color" | head -20
echo -e "\n=== red700 검색 ==="
rg -n --type=swift "red700" | head -20
echo -e "\n=== Color 관련 파일 확인 ==="
find . -type f -name "*.swift" | xargs rg -l "Color" | grep -i color | head -10Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 1043
🏁 Script executed:
#!/bin/bash
# Color 확장 찾기
echo "=== 전체 Color 확장 정의 확인 ==="
rg -n --type=swift "extension Color" -A 5
echo -e "\n=== Color 생성자 사용 패턴 확인 ==="
rg -n --type=swift 'Color\("' | head -20
echo -e "\n=== View+Color.swift 파일 내용 (red700 부분) ==="
cat -n "Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/View+Color.swift" | sed -n '85,95p'
echo -e "\n=== ColorAsset 또는 색상 정의 파일 확인 ==="
find . -type f \( -name "*Color*" -o -name "*color*" \) -name "*.swift" | head -20Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 3092
Color.red700 확장을 정의하거나 색상 이름으로 교체 필요
Color extension이 없어 Color.red700은 컴파일 오류가 발생합니다. 기존 코드베이스 패턴(View+Color.swift에서 Color("red_700") 사용)과 일치시키려면 Color("red_700")으로 교체하거나, Color extension을 추가해야 합니다.
해결 방법 1: Color 생성자 사용
Rectangle()
- .fill(Color.red700)
+ .fill(Color("red_700"))
.frame(width: 252.adjustedW, height: 1.4)
Rectangle()
- .fill(Color.red700)
+ .fill(Color("red_700"))
.frame(width: 260.adjustedW, height: 1.4)해결 방법 2: Color 확장 추가
View+Color.swift에 아래를 추가:
extension Color {
static let red700 = Color("red_700")
}📝 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.
| Rectangle() | |
| .fill(Color.red700) | |
| .frame(width: 252.adjustedW, height: 1.4) | |
| Rectangle() | |
| .fill(Color.red700) | |
| .frame(width: 260.adjustedW, height: 1.4) | |
| Rectangle() | |
| .fill(Color("red_700")) | |
| .frame(width: 252.adjustedW, height: 1.4) | |
| Rectangle() | |
| .fill(Color("red_700")) | |
| .frame(width: 260.adjustedW, height: 1.4) |
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingPage1.swift`
around lines 41 - 46, The two Rectangle views use Color.red700 which doesn't
exist; either replace Color.red700 with Color("red_700") in OnboardingPage1 (the
two Rectangle().fill(...) calls) to match the existing asset name, or add a
Color extension (e.g., in View+Color) that defines static let red700 =
Color("red_700") so Color.red700 resolves; update the Rectangle().fill usages or
add the extension accordingly.
| .focused($isNameFocused) | ||
| .overlay(alignment: .bottomLeading) { | ||
| if isNameOverLimit { | ||
| TypographyText("이름은 최대 7자까지 입력 가능합니다.", style: .body1_r_14, color: Color("red_700")) |
| #Preview { | ||
| VStack(spacing: 20) { | ||
| PageIndicator(currentPage: 0, totalPages: 2) | ||
| PageIndicator(currentPage: 1, totalPages: 2) | ||
| } | ||
| } |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/PageIndicator.swift`:
- Around line 10-26: The Color literals .gray800 and .gray500 used in
PageIndicator’s body are not defined and cause compilation errors; update the
Circle fill to use Color(.gray800) and Color(.gray500) (i.e., replace
.fill(index == currentPage ? .gray800 : .gray500) with .fill(index ==
currentPage ? Color(.gray800) : Color(.gray500))) so PageIndicator (properties
currentPage, totalPages, dotSize, dotSpacing) matches the project's Color usage
as in CheckBox/ProgressBar.
| struct PageIndicator: View { | ||
| let currentPage: Int | ||
| let totalPages: Int | ||
|
|
||
| private let dotSize: CGFloat = 8 | ||
| private let dotSpacing: CGFloat = 8 | ||
|
|
||
| var body: some View { | ||
| HStack(spacing: dotSpacing) { | ||
| ForEach(0..<totalPages, id: \.self) { index in | ||
| Circle() | ||
| .fill(index == currentPage ? .gray800 : .gray500) | ||
| .frame(width: dotSize, height: dotSize) | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Color extension 및 색상 에셋 검색
echo "=== Color extension gray800, gray500 정의 검색 ==="
rg -n --type swift 'gray(800|500)' -B 2 -A 2
echo ""
echo "=== View+Color.swift 파일 찾기 ==="
fd -e swift | xargs grep -l "View.*Color" | head -5
echo ""
echo "=== 색상 관련 asset catalog 검색 ==="
fd -e colorset . -o fd -e xcassets .
echo ""
echo "=== 전체 Color extension 정의 검색 ==="
rg -n 'extension\s+Color' --type swift -A 5Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 11494
🏁 Script executed:
#!/bin/bash
# Color extension 정적 속성 정의 검색
echo "=== Color extension 파일 찾기 ==="
find . -name "*.swift" -type f | xargs grep -l "extension Color" | head -10
echo ""
echo "=== Color extension 내에서 static var/let 정의 검색 ==="
rg -n "extension Color" -A 20 --type swift
echo ""
echo "=== gray800, gray500이 정의된 모든 위치 ==="
rg -n "gray800|gray500" --type swift | grep -E "(static|var|let|func)" | head -20Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 912
🏁 Script executed:
#!/bin/bash
# Color enum 또는 전체 컬러 정의 확인
echo "=== Color enum 정의 ==="
rg -n "^enum Color|^struct Color" --type swift -A 30
echo ""
echo "=== 모든 swift 파일에서 'gray800\|gray500' 정의 패턴 ==="
rg -n "var\s+gray(800|500)|let\s+gray(800|500)" --type swift
echo ""
echo "=== PageIndicator.swift 전체 내용 ==="
cat Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/PageIndicator.swiftRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 747
Color 속성이 정의되지 않아 컴파일 오류 발생
.gray800과 .gray500은 정의된 Color 속성이 아니므로 컴파일 오류가 발생합니다. 코드베이스의 다른 파일들(CheckBox.swift, ProgressBar.swift)에서는 Color(.gray800)과 Color(.gray500) 문법을 사용합니다.
.fill(index == currentPage ? .gray800 : .gray500) → .fill(index == currentPage ? Color(.gray800) : Color(.gray500))로 수정이 필요합니다.
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/PageIndicator.swift`
around lines 10 - 26, The Color literals .gray800 and .gray500 used in
PageIndicator’s body are not defined and cause compilation errors; update the
Circle fill to use Color(.gray800) and Color(.gray500) (i.e., replace
.fill(index == currentPage ? .gray800 : .gray500) with .fill(index ==
currentPage ? Color(.gray800) : Color(.gray500))) so PageIndicator (properties
currentPage, totalPages, dotSize, dotSpacing) matches the project's Color usage
as in CheckBox/ProgressBar.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishTextField/CherrishTextField.swift (1)
102-109: 오타 수정 필요:textAlinement→textAlignment프로퍼티 이름에 오타가 있습니다.
Alinement가 아닌Alignment가 올바른 철자입니다.✏️ 제안하는 수정 사항
- var textAlinement: TextAlignment { + var textAlignment: TextAlignment {그리고 사용하는 곳도 함께 수정해야 합니다:
- .multilineTextAlignment(style.textAlinement) + .multilineTextAlignment(style.textAlignment)
🤖 Fix all issues with AI agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishTextField/CherrishTextField.swift`:
- Around line 77-84: horizontalPadding 프로퍼티가 정의됐지만 CherrishTextField의 body에서
사용되지 않아 가로 여백이 적용되지 않습니다; CherrishTextField의 body 안에서 TextField(또는 관련 컨테이너)에 현재
적용된 .padding(.vertical, ...) 옆에 .padding(.horizontal, horizontalPadding) 또는 개별
.padding(.leading/.trailing, horizontalPadding) 호출을 추가해 가로 패딩을 적용하거나, 만약 사용 의도가
없다면 horizontalPadding 프로퍼티를 제거하세요; 참조 대상: horizontalPadding, CherrishTextField의
body, TextField/컨테이너 뷰(또는 동일한 파일의 다른 유사한 구현 블록 언급된 161-167 범위).
♻️ Duplicate comments (4)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/OnboardingContainerView.swift (2)
32-42: 숨김 상태에서도 버튼이 탭됩니다.
opacity만으로는 터치가 막히지 않습니다. 터치/접근성 숨김을 같이 처리해주세요.✅ 수정 제안
.padding(.horizontal, 25.adjustedW) .padding(.bottom, 57.adjustedH) .opacity(currentPage == 1 ? 1 : 0) + .allowsHitTesting(currentPage == 1) + .accessibilityHidden(currentPage != 1) .animation(.easeInOut(duration: 0.3), value: currentPage)
45-55: 닫기 아이콘은 Button으로 처리해 접근성을 확보해주세요.
Image + onTapGesture는 포커싱/라벨링이 취약합니다.✅ 수정 제안
- Image(.close) - .renderingMode(.template) - .resizable() - .scaledToFit() - .frame(width: 24.adjustedW, height: 24.adjustedW) - .foregroundStyle(.gray600) - .padding(.trailing, 29.adjustedW) - .padding(.top, 74.adjustedH) - .onTapGesture { - onboardingCoordinator.push(.information) - } + Button { + onboardingCoordinator.push(.information) + } label: { + Image(.close) + .renderingMode(.template) + .resizable() + .scaledToFit() + .frame(width: 24.adjustedW, height: 24.adjustedW) + .foregroundStyle(.gray600) + } + .accessibilityLabel("닫기") + .padding(.trailing, 29.adjustedW) + .padding(.top, 74.adjustedH)Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Onboarding/InformationView.swift (2)
93-96: 나이 입력이 숫자/공백 검증 없이 활성화됩니다.
공백 제거와 숫자 검증 후 활성화해주세요.✅ 수정 제안
private func updateButtonState() { - let isNameValid = !name.isEmpty && !isNameOverLimit - let isAgeValid = !age.isEmpty && !isAgeOverLimit + let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines) + let trimmedAge = age.trimmingCharacters(in: .whitespacesAndNewlines) + let ageIsValid = Int(trimmedAge) != nil && !isAgeOverLimit + let isNameValid = !trimmedName.isEmpty && !isNameOverLimit + let isAgeValid = !trimmedAge.isEmpty && ageIsValid buttonState = (isNameValid && isAgeValid) ? .active : .normal }
84-89: 나이 오류 메시지가 수정 후에도 남을 수 있습니다.
값이 정상화되면 에러 플래그를 해제하세요.✅ 수정 제안
- .onChange(of: age) { _ in updateButtonState() } + .onChange(of: age) { _ in + updateButtonState() + if !isAgeOverLimit { showAgeError = false } + } .onChange(of: isAgeFocused) { focused in if !focused { showAgeError = isAgeOverLimit } }
| var horizontalPadding: CGFloat { | ||
| switch self { | ||
| case .plain, .number: | ||
| return 16.adjustedW | ||
| case .date: | ||
| return 18.5.adjustedW | ||
| } | ||
| } |
There was a problem hiding this comment.
horizontalPadding 프로퍼티가 정의되었지만 사용되지 않습니다.
horizontalPadding 프로퍼티가 추가되었지만, 뷰의 body에서 실제로 적용되지 않았습니다. 현재 .padding(.vertical, ...) 만 적용되어 있어 TextField 콘텐츠에 가로 여백이 누락된 것으로 보입니다.
🐛 제안하는 수정 사항
.padding(.vertical, style.verticalPadding)
+ .padding(.horizontal, style.horizontalPadding)
.background {Also applies to: 161-167
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishTextField/CherrishTextField.swift`
around lines 77 - 84, horizontalPadding 프로퍼티가 정의됐지만 CherrishTextField의 body에서
사용되지 않아 가로 여백이 적용되지 않습니다; CherrishTextField의 body 안에서 TextField(또는 관련 컨테이너)에 현재
적용된 .padding(.vertical, ...) 옆에 .padding(.horizontal, horizontalPadding) 또는 개별
.padding(.leading/.trailing, horizontalPadding) 호출을 추가해 가로 패딩을 적용하거나, 만약 사용 의도가
없다면 horizontalPadding 프로퍼티를 제거하세요; 참조 대상: horizontalPadding, CherrishTextField의
body, TextField/컨테이너 뷰(또는 동일한 파일의 다른 유사한 구현 블록 언급된 161-167 범위).
…g,-Information-View Style/#47 스플래쉬, 온보딩, 인포메이션 뷰 제작

🔗 연결된 이슈
📄 작업 내용
💻 주요 코드 설명
OnboardingContainerView에서 온보딩 뷰 2개를 탭바로 관리합니다PageIndicator를 컨테이너에서 뷰에 띄우기에 버튼을 가장 맨 아래에 두려면 컨테이너에서 관리해야한다고 생각해서 컨테이너에서 에니메이션으로 자연스럽게 나타나게끔했습니다.
무한 캐러셀 에니메이션
📚 참고자료
👀 기타 더 이야기해볼 점