Conversation
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughiOS 자산 카탈로그에 Changes
Sequence Diagram(s)(생성 기준 미충족 — 생략) Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 분 Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro 📒 Files selected for processing (1)
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: 7
🤖 Fix all issues with AI agents
In
@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift:
- Around line 61-86: The extension on CherrishButtonType currently hardcodes
CGFloat sizes in the width, height, and cornerRadius properties which breaks
responsiveness; change these to compute sizes from the environment (e.g., add
width(for screenWidth: CGFloat) and height(for traitCollection:
UITraitCollection or screenScale) methods or return values that reference
DesignSystem.Layout tokens) and replace direct numeric literals for
.next/.save/.confirm/.addEvent with either screen-width based calculations
(percentages or inset-based like screenWidth - x) or constants from a
centralized DesignSystem (e.g.,
DesignSystem.Layout.buttonWidthLarge/Medium/Small and
DesignSystem.CornerRadius.button) so the button scales across devices and
respects Dynamic Type.
- Around line 10-13: The enum ButtonState is currently internal and should be
made explicitly public for reuse in Global Components; update the declaration
for ButtonState to include the public access modifier (e.g., public enum
ButtonState) and, if useful for callers, add protocol conformances such as
Equatable, Hashable, and/or CaseIterable (e.g., public enum ButtonState:
CaseIterable, Equatable { ... }) so the type is accessible and more usable
across modules.
- Around line 15-20: CherrishButtonType is missing explicit access control and
could benefit from CaseIterable for safer reuse; update the enum declaration for
CherrishButtonType to include the public access modifier (e.g., public enum
CherrishButtonType) and, if iteration over cases is needed, conform it to
CaseIterable (public enum CherrishButtonType: CaseIterable) while leaving the
existing cases (next, confirm, save, addEvent) unchanged.
- Line 22: Make the reusable component accessible by adding the public access
modifier to the CherrishButton declaration and its initializer: change struct
CherrishButton to public struct CherrishButton and mark the init(...)
initializer as public (the initializer that takes title: String, type:
CherrishButtonType, state: Binding<ButtonState>, action: @escaping () -> Void).
Ensure any stored properties or related types referenced by the public API
(e.g., CherrishButtonType, ButtonState) are at least internal/public as needed
to avoid access-level conflicts.
- Around line 29-37: Add VoiceOver support to CherrishButton by attaching
accessibility modifiers to the Button in CherrishButton.body: set an
accessibilityLabel (use title/localized), call
CherrishButtonType.accessibilityHint(for: state) for accessibilityHint, add
accessibilityAddTraits(.isButton) and conditionally add .isDisabled trait when
type.isDisabled(for: state) is true. Also implement the suggested extension
method on CherrishButtonType: func accessibilityHint(for state: ButtonState) ->
String that returns localized hints for each case (.next, .confirm, .save,
.addEvent) to be used by the button.
- Around line 41-58: Make CherrishButtonStyle public and add light haptic
feedback when the button is pressed: change the declaration to public struct
CherrishButtonStyle and inside makeBody observe configuration.isPressed (e.g.,
.onChange(of: configuration.isPressed) or equivalent) to call a
UIImpactFeedbackGenerator(style: .light).impactOccurred() when it becomes true;
also add import UIKit at the top so haptics compile. Reference:
CherrishButtonStyle, makeBody, configuration.isPressed.
- Around line 88-120: Add a Color extension that defines the missing static
color properties used by CherrishButtonType (e.g., .red700, .gray0, .gray200,
.gray400, .gray600, .gray700) so the calls in backgroundColor(for:) and
textColor(for:) compile; implement a new extension on Color with static lets
mapping each property to the corresponding asset name (e.g., "red_700",
"gray_0", etc.) and add that file to the project alongside other UI extensions.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (15)
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/check_circular.imageset/check_circular.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/checkbox_active.imageset/checkbox_active.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/checkbox_default.imageset/checkbox_default.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/chevron_left.imageset/chevron_left.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/chevron_right.imageset/chevron_right.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/clock.imageset/clock.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/close.imageset/close.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/deletebox.imageset/deletebox.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/info.imageset/info.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/plus.imageset/plus.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/radiobtn_default.imageset/radiobtn_default.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/radiobtn_selected.imageset/radiobtn_selected.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/return.imageset/return.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/search.imageset/search.svgis excluded by!**/*.svgCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/sun.imageset/sun.svgis excluded by!**/*.svg
📒 Files selected for processing (17)
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/check_circular.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/checkbox_active.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/checkbox_default.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/chevron_left.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/chevron_right.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/clock.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/close.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/deletebox.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/info.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/plus.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/radiobtn_default.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/radiobtn_selected.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/return.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/search.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/sun.imageset/Contents.jsonCherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift
🧰 Additional context used
🧬 Code graph analysis (1)
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift (1)
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/Font+.swift (1)
typography(52-56)
🔇 Additional comments (16)
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/sun.imageset/Contents.json (1)
1-12: 올바른 에셋 카탈로그 구조입니다.표준 형식을 준수하며 문제없습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/check_circular.imageset/Contents.json (1)
1-12: 올바른 에셋 카탈로그 구조입니다.표준 형식을 준수하며 문제없습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/chevron_left.imageset/Contents.json (1)
1-12: 올바른 에셋 카탈로그 구조입니다.표준 형식을 준수하며 문제없습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/deletebox.imageset/Contents.json (1)
1-12: 올바른 에셋 카탈로그 구조입니다.표준 형식을 준수하며 문제없습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/radiobtn_selected.imageset/Contents.json (1)
1-12: 에셋 카탈로그 구조가 올바릅니다.JSON 구조가 유효하며 Xcode 에셋 카탈로그의 표준 형식을 따릅니다. 참조된 SVG 파일(
radiobtn_selected.svg)이 디렉토리에 실제로 존재합니다.Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/return.imageset/Contents.json (1)
1-12: 애셋 카탈로그 구조가 정상입니다.return 아이콘에 대한 표준 애셋 카탈로그 정의가 올바르게 작성되었습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/Contents.json (1)
1-6: 루트 레벨 애셋 카탈로그 메타데이터가 올바릅니다.cherrishIcon 폴더의 루트 레벨 Contents.json 파일로, 표준 구조를 따르고 있습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/chevron_right.imageset/Contents.json (1)
1-12: 애셋 카탈로그 구조가 정상입니다.chevron_right 아이콘에 대한 표준 애셋 카탈로그 정의가 올바르게 작성되었습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/search.imageset/Contents.json (1)
1-12: 애셋 카탈로그 구조가 올바르게 구성되었습니다.JSON 구조와 메타데이터가 Xcode 규칙을 따르고 있으며, 참조된 search.svg 파일이 올바른 위치에 존재합니다. SVG 형식 사용은 해상도 독립적인 아이콘에 적합합니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/checkbox_active.imageset/Contents.json (1)
1-12: 애셋 카탈로그 구조가 올바르게 작성되었습니다.checkbox_active 이미지셋의 Contents.json이 표준 Xcode 애셋 카탈로그 형식을 따르고 있으며, 참조되는 checkbox_active.svg 파일이 실제로 존재합니다. 모든 15개의 cherrishIcon SVG 애셋이 올바르게 배치되어 있습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/close.imageset/Contents.json (1)
1-12: LGTM! 에셋 구조가 올바릅니다.close 아이콘에 대한 에셋 카탈로그가 표준 형식을 따르고 있습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/info.imageset/Contents.json (1)
1-12: LGTM! 표준 구조를 따릅니다.info 아이콘 에셋이 올바르게 정의되어 있습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/radiobtn_default.imageset/Contents.json (1)
1-12: LGTM! 라디오 버튼 에셋이 올바르게 구성되었습니다.radiobtn_default 아이콘의 에셋 카탈로그 정의가 표준을 따르고 있습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/checkbox_default.imageset/Contents.json (1)
1-12: LGTM! 체크박스 에셋이 올바르게 정의되었습니다.checkbox_default 아이콘의 에셋 카탈로그가 표준 형식을 따르고 있습니다.
Cherrish-iOS/Cherrish-iOS/Assets.xcassets/cherrishIcon/clock.imageset/Contents.json (1)
1-12: 구조 및 파일 모두 올바르게 구성되어 있습니다.clock 아이콘에 대한 에셋 카탈로그 정의가 표준 구조를 따르고 있으며, clock.svg 파일이 올바른 위치에 존재합니다. SVG 형식과 universal idiom 설정이 적절합니다.
현재 iOS 배포 타겟(16.6 이상)이 SVG asset catalog 지원 요구사항(iOS 16.0 이상)을 충족하므로 문제가 없습니다.
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift (1)
26-27: state Binding의 사용 의도를 명확히 하세요.
@Binding var state가 선언되어 있지만, 버튼 내부에서 이 상태를 변경하지 않습니다. 상태 변경이 외부에서만 이루어지는 것이 의도된 설계인지 확인이 필요합니다. 만약 버튼이 자체적으로 상태를 토글해야 한다면 로직 추가가 필요합니다.현재 설계가 의도된 것인지, 아니면 버튼이 탭될 때 상태를 자동으로 변경해야 하는지 확인해주세요. 만약 후자라면 다음과 같이 수정할 수 있습니다:
var body: some View { Button(action: { // 상태 토글 로직 추가 if type == .next && state == .active { state = .normal } action() }) { Text(title) .typography(.title2_sb_16) .foregroundStyle(type.textColor(for: state)) } .buttonStyle(CherrishButtonStyle(state: state, type: type)) .disabled(type.isDisabled(for: state)) }
| enum CherrishButtonType { | ||
| case next | ||
| case confirm | ||
| case save | ||
| case addEvent | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
접근 제어 수정자 추가를 고려하세요.
재사용 가능한 컴포넌트의 일부이므로 CherrishButtonType enum에도 명시적인 public 접근 제어 수정자를 추가하세요. 타입 안전성을 위해 CaseIterable 프로토콜 준수도 고려해볼 수 있습니다.
♻️ 제안하는 개선사항
-enum CherrishButtonType {
+public enum CherrishButtonType: CaseIterable {
case next
case confirm
case save
case addEvent
}🤖 Prompt for AI Agents
In
@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift
around lines 15 - 20, CherrishButtonType is missing explicit access control and
could benefit from CaseIterable for safer reuse; update the enum declaration for
CherrishButtonType to include the public access modifier (e.g., public enum
CherrishButtonType) and, if iteration over cases is needed, conform it to
CaseIterable (public enum CherrishButtonType: CaseIterable) while leaving the
existing cases (next, confirm, save, addEvent) unchanged.
| case addEvent | ||
| } | ||
|
|
||
| struct CherrishButton: View { |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
접근 제어 수정자를 추가하세요.
Global Components로 제공되는 재사용 가능한 컴포넌트이므로 CherrishButton struct에 public 접근 제어 수정자가 필요합니다.
♻️ 제안하는 개선사항
-struct CherrishButton: View {
+public struct CherrishButton: View {초기화 메서드도 public으로 명시해야 합니다:
public init(title: String, type: CherrishButtonType, state: Binding<ButtonState>, action: @escaping () -> Void) {
self.title = title
self.type = type
self._state = state
self.action = action
}🤖 Prompt for AI Agents
In
@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift
at line 22, Make the reusable component accessible by adding the public access
modifier to the CherrishButton declaration and its initializer: change struct
CherrishButton to public struct CherrishButton and mark the init(...)
initializer as public (the initializer that takes title: String, type:
CherrishButtonType, state: Binding<ButtonState>, action: @escaping () -> Void).
Ensure any stored properties or related types referenced by the public API
(e.g., CherrishButtonType, ButtonState) are at least internal/public as needed
to avoid access-level conflicts.
| struct CherrishButtonStyle: ButtonStyle { | ||
| let state: ButtonState | ||
| let type: CherrishButtonType | ||
|
|
||
| func makeBody(configuration: Configuration) -> some View { | ||
| configuration.label | ||
| .frame( | ||
| width: configuration.isPressed ? type.width * 0.95 : type.width, | ||
| height: configuration.isPressed ? type.height * 0.95 : type.height | ||
| ) | ||
| .background(type.backgroundColor(for: state)) | ||
| .cornerRadius( | ||
| configuration.isPressed | ||
| ? type.cornerRadius * 0.95 | ||
| : type.cornerRadius | ||
| ) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
접근 제어 수정자를 추가하고 햅틱 피드백을 고려하세요.
CherrishButtonStyle에 public 접근 제어 수정자가 필요합니다. 또한 더 나은 사용자 경험을 위해 버튼을 눌렀을 때 햅틱 피드백 추가를 고려해보세요.
♻️ 제안하는 개선사항
-struct CherrishButtonStyle: ButtonStyle {
+public struct CherrishButtonStyle: ButtonStyle {
let state: ButtonState
let type: CherrishButtonType
func makeBody(configuration: Configuration) -> some View {
configuration.label
.frame(
width: configuration.isPressed ? type.width * 0.95 : type.width,
height: configuration.isPressed ? type.height * 0.95 : type.height
)
.background(type.backgroundColor(for: state))
.cornerRadius(
configuration.isPressed
? type.cornerRadius * 0.95
: type.cornerRadius
)
+ .onChange(of: configuration.isPressed) { oldValue, newValue in
+ if newValue {
+ let generator = UIImpactFeedbackGenerator(style: .light)
+ generator.impactOccurred()
+ }
+ }
}
}🤖 Prompt for AI Agents
In
@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift
around lines 41 - 58, Make CherrishButtonStyle public and add light haptic
feedback when the button is pressed: change the declaration to public struct
CherrishButtonStyle and inside makeBody observe configuration.isPressed (e.g.,
.onChange(of: configuration.isPressed) or equivalent) to call a
UIImpactFeedbackGenerator(style: .light).impactOccurred() when it becomes true;
also add import UIKit at the top so haptics compile. Reference:
CherrishButtonStyle, makeBody, configuration.isPressed.
| extension CherrishButtonType { | ||
|
|
||
| var width: CGFloat { | ||
| switch self { | ||
| case .next: return 326 | ||
| case .confirm: return 126 | ||
| case .save: return 278 | ||
| case .addEvent: return 196 | ||
| } | ||
| } | ||
|
|
||
| var height: CGFloat { | ||
| switch self { | ||
| case .save: return 44 | ||
| default: | ||
| return 50 | ||
| } | ||
| } | ||
|
|
||
| var cornerRadius: CGFloat { | ||
| switch self { | ||
| case .save: return 10 | ||
| default: return 12 | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
하드코딩된 치수 값의 확장성을 고려하세요.
고정된 CGFloat 값들은 다양한 디바이스 크기와 Dynamic Type에 대응하지 못합니다. 디자인 시스템을 통한 상대적 크기 또는 화면 너비 기반 계산을 고려해보세요.
♻️ 더 확장 가능한 접근 방식 제안
화면 너비를 기준으로 상대적 크기를 사용하는 방법:
extension CherrishButtonType {
func width(for screenWidth: CGFloat) -> CGFloat {
switch self {
case .next: return screenWidth - 48 // 좌우 패딩 24
case .confirm: return 126
case .save: return screenWidth * 0.75
case .addEvent: return 196
}
}
// 또는 디자인 토큰 시스템 사용:
var width: CGFloat {
switch self {
case .next: return DesignSystem.Layout.buttonWidthLarge
case .confirm: return DesignSystem.Layout.buttonWidthSmall
case .save: return DesignSystem.Layout.buttonWidthMedium
case .addEvent: return DesignSystem.Layout.buttonWidthCompact
}
}
}이렇게 하면 다양한 디바이스와 화면 크기에 더 잘 대응할 수 있습니다.
🤖 Prompt for AI Agents
In
@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift
around lines 61 - 86, The extension on CherrishButtonType currently hardcodes
CGFloat sizes in the width, height, and cornerRadius properties which breaks
responsiveness; change these to compute sizes from the environment (e.g., add
width(for screenWidth: CGFloat) and height(for traitCollection:
UITraitCollection or screenScale) methods or return values that reference
DesignSystem.Layout tokens) and replace direct numeric literals for
.next/.save/.confirm/.addEvent with either screen-width based calculations
(percentages or inset-based like screenWidth - x) or constants from a
centralized DesignSystem (e.g.,
DesignSystem.Layout.buttonWidthLarge/Medium/Small and
DesignSystem.CornerRadius.button) so the button scales across devices and
respects Dynamic Type.
| extension CherrishButtonType { | ||
|
|
||
| func backgroundColor(for state: ButtonState) -> Color { | ||
| switch self { | ||
| case .next: | ||
| return state == .active ? .red700 : .gray200 | ||
| case .confirm, .save: | ||
| return .red700 | ||
| case .addEvent: | ||
| return .gray400 | ||
| } | ||
| } | ||
|
|
||
| func textColor(for state: ButtonState) -> Color { | ||
| switch self { | ||
| case .next: | ||
| return state == .active ? .gray0 : .gray600 | ||
| case .confirm, .save: | ||
| return .gray0 | ||
| case .addEvent: | ||
| return .gray700 | ||
| } | ||
| } | ||
|
|
||
| func isDisabled(for state: ButtonState) -> Bool { | ||
| switch self { | ||
| case .next: | ||
| return state == .normal | ||
| default: | ||
| return false | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for Color extension definitions
echo "=== Searching for Color extensions ==="
rg -n "extension Color" --type swift -A 15
echo -e "\n=== Searching for specific color definitions ==="
rg -n "(red700|gray200|gray0|gray600|gray400|gray700)" --type swift -B 2 -A 2
echo -e "\n=== Searching for ButtonState definition ==="
rg -n "enum ButtonState" --type swift -A 10
echo -e "\n=== Checking if CherrishButton.swift has documentation ==="
rg -n "///" cherrish-ios/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift || echo "No documentation comments found"Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 3383
🏁 Script executed:
#!/bin/bash
# More comprehensive search for color definitions
echo "=== Search for Color-related files ==="
fd "Color" --type f --extension swift | head -20
echo -e "\n=== Search for 'red700' definition anywhere ==="
rg -n "red700" --type swift -B 5 -A 1
echo -e "\n=== Search for Color assignments/definitions ==="
rg -n "\.red|\.gray" --type swift -B 2 -A 2 | head -50
echo -e "\n=== Check for DesignSystem or Token files ==="
fd -e swift | xargs grep -l "DesignSystem\|Token\|Color" | head -10
echo -e "\n=== List all files in Presentation/Global ==="
fd --type f --extension swift "Presentation/Global" | head -20Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 3300
🏁 Script executed:
#!/bin/bash
# Broader search for color definitions
echo "=== List repository structure ==="
find . -type f -name "*.swift" | grep -i color | head -20
echo -e "\n=== Search for Color definitions across all files ==="
rg -n "static.*red700|let.*red700|var.*red700" -A 2 -B 2
echo -e "\n=== Search for extension Color in all content ==="
rg -n "extension Color" -A 20
echo -e "\n=== Look for imports in CherrishButton.swift ==="
head -30 Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift
echo -e "\n=== Search for where Color comes from ==="
rg -n "import.*Color|from.*Color" --type swift
echo -e "\n=== Find all Color usage patterns ==="
rg -n "Color\." --type swift | head -30Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 810
🏁 Script executed:
#!/bin/bash
# Final comprehensive search
echo "=== List all Swift files to understand structure ==="
find . -name "*.swift" -type f 2>/dev/null | head -30
echo -e "\n=== Search in all Swift files for Color extension ==="
find . -name "*.swift" -type f -exec grep -l "extension Color" {} \;
echo -e "\n=== Grep all files for 'red700' ==="
grep -r "red700" . --include="*.swift" 2>/dev/null | head -20
echo -e "\n=== Check if Presentation/Global/Colors or similar exists ==="
find . -type d -name "*[Cc]olor*" 2>/dev/null
echo -e "\n=== Look for any xcassets or color assets ==="
find . -type d -name "*.xcassets" 2>/dev/null
echo -e "\n=== Search entire codebase for Color with + or static ==="
grep -r "Color.*+\|Color.*static" . --include="*.swift" 2>/dev/null | head -20Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 3329
🏁 Script executed:
#!/bin/bash
# Check Font+ pattern and confirm Color extension is missing
echo "=== Check Font+ extension pattern ==="
cat ./Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/Font+.swift
echo -e "\n=== Look for Color extension file ==="
find . -name "*Color*" -type f | grep -i swift
echo -e "\n=== Check Extension directory for all files ==="
ls -la ./Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/
echo -e "\n=== Search for Color+ or similar ==="
find . -name "*olor*" -type f 2>/dev/null | grep -v xcassetsRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 2635
Color 확장이 누락되어 컴파일 오류가 발생합니다.
Assets.xcassets에 색상 자산(red_700.colorset, gray_0.colorset 등)이 정의되어 있지만, 코드에서 사용하는 .red700, .gray0 등의 Color 확장이 정의되지 않았습니다. Font+ 확장과 같은 패턴으로 Color 확장을 추가해야 합니다:
extension Color {
static let red700 = Color("red_700")
static let gray0 = Color("gray_0")
static let gray200 = Color("gray_200")
static let gray400 = Color("gray_400")
static let gray600 = Color("gray_600")
static let gray700 = Color("gray_700")
}Presentation/Global/Extension/ 디렉토리에 Color+ 파일을 추가하여 구현하세요.
🤖 Prompt for AI Agents
In
@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift
around lines 88 - 120, Add a Color extension that defines the missing static
color properties used by CherrishButtonType (e.g., .red700, .gray0, .gray200,
.gray400, .gray600, .gray700) so the calls in backgroundColor(for:) and
textColor(for:) compile; implement a new extension on Color with static lets
mapping each property to the corresponding asset name (e.g., "red_700",
"gray_0", etc.) and add that file to the project alongside other UI extensions.
soseoyo12
left a comment
There was a problem hiding this comment.
수고하셨습니다 로직 분리 꼼꼼히 진짜 잘하셨네요!!
y-eonee
left a comment
There was a problem hiding this comment.
좋아요!! 훨씬 좋은 컴포넌트가 되었습니다 굿 ㅎㅎ
| } | ||
| } | ||
|
|
||
| extension CherrishButtonType { |
There was a problem hiding this comment.
이거 굳이 익스텐션 나눈 이유가 있나요 ?! 한번에 써도 될것 같아요
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In
@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift:
- Around line 63-70: The fixed-width switch in var width (inside
CherrishButton.swift) uses hardcoded values for cases .next, .confirm, .save,
.addEvent which won’t adapt to different screen sizes or Dynamic Type; change
the implementation to compute widths relative to available space (e.g., use
UIScreen.main.bounds.width or, preferably, pass a parent width via
GeometryReader/parent view and calculate widths as screenWidth minus desired
horizontal margins for cases like .next and .save while keeping small controls
like .confirm/.addEvent either a fixed min/max or a percentage); update the var
width logic to reference the calculated screenWidth and apply safe min/max
constraints so buttons remain accessible on all devices.
- Around line 10-13: Rename the enum ButtonState to CherrishButtonState to avoid
naming collisions; update all references/usages (e.g., the declaration "enum
ButtonState" and every place that types/constructs or switches on ButtonState)
to use CherrishButtonState and adjust any associated cases or imports
accordingly to maintain compilation and clarity.
- Around line 29-37: The Button in CherrishButton's body is missing VoiceOver
support; add accessibility metadata on the Button (the var body view) by
providing an accessibilityLabel derived from the title (or a localized label),
add an accessibilityHint when the button is disabled (use type.isDisabled(for:
state) to choose the hint text), and set appropriate traits (e.g.,
.accessibilityAddTraits(.isButton) and .accessibilityAddTraits(.isDisabled) when
disabled) so VoiceOver announces state; update the Button view (the same Button
wrapped with .buttonStyle(CherrishButtonStyle(...))) to include these
accessibility modifiers.
- Around line 45-57: In makeBody(configuration:) replace the deprecated
.cornerRadius(...) call with .clipShape(RoundedRectangle(cornerRadius: ...))
using the same corner radius expression (configuration.isPressed ?
type.cornerRadius * 0.95 : type.cornerRadius) and add an explicit animation on
the press state (e.g., .animation(.easeInOut(duration: 0.1), value:
configuration.isPressed)) so the size and corner radius transitions are
animated; update the chain on configuration.label inside the CherrishButton
style to use clipShape(RoundedRectangle(...)) and the animation modifier
referencing configuration.isPressed.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift
🧰 Additional context used
🧬 Code graph analysis (1)
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift (1)
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/Font+.swift (1)
typography(52-56)
🔇 Additional comments (2)
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift (2)
15-20: LGTM!버튼 타입 정의가 명확하고 PR 목표(다음, 확인, 등록하기, 일정 추가 버튼)와 잘 일치합니다.
87-96:confirm,save,addEvent타입에서state파라미터가 사용되지 않습니다.이 타입들의 배경색과 텍스트 색상은 상태와 무관하게 고정되어 있습니다. 의도된 동작이라면 괜찮지만, 향후 상태 기반 스타일링이 필요할 경우 이 점을 고려해 주세요.
| enum ButtonState { | ||
| case normal | ||
| case active | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
ButtonState 네이밍이 너무 일반적입니다.
다른 모듈과의 충돌을 방지하고 일관성을 위해 CherrishButtonState로 이름을 변경하는 것을 고려해 주세요.
♻️ 제안된 수정
-enum ButtonState {
+enum CherrishButtonState {
case normal
case active
}📝 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.
| enum ButtonState { | |
| case normal | |
| case active | |
| } | |
| enum CherrishButtonState { | |
| case normal | |
| case active | |
| } |
🤖 Prompt for AI Agents
In
@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift
around lines 10 - 13, Rename the enum ButtonState to CherrishButtonState to
avoid naming collisions; update all references/usages (e.g., the declaration
"enum ButtonState" and every place that types/constructs or switches on
ButtonState) to use CherrishButtonState and adjust any associated cases or
imports accordingly to maintain compilation and clarity.
| var body: some View { | ||
| Button(action: action) { | ||
| Text(title) | ||
| .typography(.title2_sb_16) | ||
| .foregroundStyle(type.textColor(for: state)) | ||
| } | ||
| .buttonStyle(CherrishButtonStyle(state: state, type: type)) | ||
| .disabled(type.isDisabled(for: state)) | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
접근성(Accessibility) 지원이 누락되었습니다.
VoiceOver 사용자를 위해 접근성 레이블을 추가하는 것이 좋습니다. 버튼이 비활성화된 경우 힌트도 제공하면 더 좋습니다.
♻️ 제안된 수정
var body: some View {
Button(action: action) {
Text(title)
.typography(.title2_sb_16)
.foregroundStyle(type.textColor(for: state))
}
.buttonStyle(CherrishButtonStyle(state: state, type: type))
.disabled(type.isDisabled(for: state))
+ .accessibilityLabel(title)
+ .accessibilityHint(type.isDisabled(for: state) ? "비활성화됨" : "")
}📝 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 body: some View { | |
| Button(action: action) { | |
| Text(title) | |
| .typography(.title2_sb_16) | |
| .foregroundStyle(type.textColor(for: state)) | |
| } | |
| .buttonStyle(CherrishButtonStyle(state: state, type: type)) | |
| .disabled(type.isDisabled(for: state)) | |
| } | |
| var body: some View { | |
| Button(action: action) { | |
| Text(title) | |
| .typography(.title2_sb_16) | |
| .foregroundStyle(type.textColor(for: state)) | |
| } | |
| .buttonStyle(CherrishButtonStyle(state: state, type: type)) | |
| .disabled(type.isDisabled(for: state)) | |
| .accessibilityLabel(title) | |
| .accessibilityHint(type.isDisabled(for: state) ? "비활성화됨" : "") | |
| } |
🤖 Prompt for AI Agents
In
@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift
around lines 29 - 37, The Button in CherrishButton's body is missing VoiceOver
support; add accessibility metadata on the Button (the var body view) by
providing an accessibilityLabel derived from the title (or a localized label),
add an accessibilityHint when the button is disabled (use type.isDisabled(for:
state) to choose the hint text), and set appropriate traits (e.g.,
.accessibilityAddTraits(.isButton) and .accessibilityAddTraits(.isDisabled) when
disabled) so VoiceOver announces state; update the Button view (the same Button
wrapped with .buttonStyle(CherrishButtonStyle(...))) to include these
accessibility modifiers.
| func makeBody(configuration: Configuration) -> some View { | ||
| configuration.label | ||
| .frame( | ||
| width: configuration.isPressed ? type.width * 0.95 : type.width, | ||
| height: configuration.isPressed ? type.height * 0.95 : type.height | ||
| ) | ||
| .background(type.backgroundColor(for: state)) | ||
| .cornerRadius( | ||
| configuration.isPressed | ||
| ? type.cornerRadius * 0.95 | ||
| : type.cornerRadius | ||
| ) | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🌐 Web query:
SwiftUI cornerRadius deprecated iOS 17 clipShape replacement
💡 Result:
Short answer: cornerRadius was deprecated in iOS 17 (Xcode 15). Use clipShape (or fill/background APIs) with a RoundedRectangle (or new UnevenRoundedRectangle / .rect shorthands) instead.
Examples:
- Simple replacement:
Color.blue
.frame(width:200, height:120)
.clipShape(RoundedRectangle(cornerRadius: 16)) - With overlay border:
.clipShape(RoundedRectangle(cornerRadius:16))
.overlay(RoundedRectangle(cornerRadius:16).stroke(Color.gray, lineWidth:2)) - Per-corner radii (iOS 17+):
.clipShape(.rect(topLeadingRadius:24, bottomTrailingRadius:60)) // UnevenRoundedRectangle/.rect
Notes: Apple’s implementation shows cornerRadius forwards to clipShape/fill; you can also use fill/in background to avoid double-shaping when drawing borders. [1][2][3][4]
Sources:
[1] SwiftUI interface gist showing deprecation and forwarding to clipShape. (gist.github.com)
[2] Blog posts showing replacement examples. (lukaspistrol.com)
[3] Practical notes on borders/overlay and iOS17 fill APIs. (stackoverflow.com)
[4] UnevenRoundedRectangle / .rect per-corner example (iOS 17+). (alexpaul.dev)
Citations:
- 1: https://gist.github.com/erichoracek/1bbac7119ebcd4c0648c13ca12ee5af9?utm_source=openai
- 2: https://lukaspistrol.com/blog/swiftui-bring-back-corner-radius/?utm_source=openai
- 3: https://stackoverflow.com/questions/77755756/how-to-add-a-border-in-swiftui-with-clipshape?utm_source=openai
- 4: https://alexpaul.dev/2023/11/21/define-which-corner-receives-a-given-cornerradius-in-swiftui/?utm_source=openai
.cornerRadius() 수정자는 iOS 17부터 deprecated 되었습니다.
.clipShape(RoundedRectangle(cornerRadius:))를 사용하세요. 추가적으로 부드러운 애니메이션을 위해 .animation() 수정자를 추가하는 것이 좋습니다.
♻️ 제안된 수정
func makeBody(configuration: Configuration) -> some View {
configuration.label
.frame(
width: configuration.isPressed ? type.width * 0.95 : type.width,
height: configuration.isPressed ? type.height * 0.95 : type.height
)
.background(type.backgroundColor(for: state))
- .cornerRadius(
- configuration.isPressed
- ? type.cornerRadius * 0.95
- : type.cornerRadius
- )
+ .clipShape(
+ RoundedRectangle(
+ cornerRadius: configuration.isPressed
+ ? type.cornerRadius * 0.95
+ : type.cornerRadius
+ )
+ )
+ .animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
}📝 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.
| func makeBody(configuration: Configuration) -> some View { | |
| configuration.label | |
| .frame( | |
| width: configuration.isPressed ? type.width * 0.95 : type.width, | |
| height: configuration.isPressed ? type.height * 0.95 : type.height | |
| ) | |
| .background(type.backgroundColor(for: state)) | |
| .cornerRadius( | |
| configuration.isPressed | |
| ? type.cornerRadius * 0.95 | |
| : type.cornerRadius | |
| ) | |
| } | |
| func makeBody(configuration: Configuration) -> some View { | |
| configuration.label | |
| .frame( | |
| width: configuration.isPressed ? type.width * 0.95 : type.width, | |
| height: configuration.isPressed ? type.height * 0.95 : type.height | |
| ) | |
| .background(type.backgroundColor(for: state)) | |
| .clipShape( | |
| RoundedRectangle( | |
| cornerRadius: configuration.isPressed | |
| ? type.cornerRadius * 0.95 | |
| : type.cornerRadius | |
| ) | |
| ) | |
| .animation(.easeInOut(duration: 0.1), value: configuration.isPressed) | |
| } |
🤖 Prompt for AI Agents
In
@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift
around lines 45 - 57, In makeBody(configuration:) replace the deprecated
.cornerRadius(...) call with .clipShape(RoundedRectangle(cornerRadius: ...))
using the same corner radius expression (configuration.isPressed ?
type.cornerRadius * 0.95 : type.cornerRadius) and add an explicit animation on
the press state (e.g., .animation(.easeInOut(duration: 0.1), value:
configuration.isPressed)) so the size and corner radius transitions are
animated; update the chain on configuration.label inside the CherrishButton
style to use clipShape(RoundedRectangle(...)) and the animation modifier
referencing configuration.isPressed.
| var width: CGFloat { | ||
| switch self { | ||
| case .next: return 326 | ||
| case .confirm: return 126 | ||
| case .save: return 278 | ||
| case .addEvent: return 196 | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
고정 너비 값은 다양한 화면 크기에 적응하지 못할 수 있습니다.
하드코딩된 너비 값(326, 126, 278, 196)은 다른 기기나 Dynamic Type 설정에서 문제가 될 수 있습니다. 유연한 레이아웃이나 상대적인 크기를 고려해 보세요.
♻️ 예시: 화면 너비 기반 상대적 크기
var width: CGFloat {
let screenWidth = UIScreen.main.bounds.width
switch self {
case .next: return screenWidth - 48 // 양쪽 24pt 마진
case .confirm: return 126
case .save: return screenWidth - 96
case .addEvent: return 196
}
}🤖 Prompt for AI Agents
In
@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishButton.swift
around lines 63 - 70, The fixed-width switch in var width (inside
CherrishButton.swift) uses hardcoded values for cases .next, .confirm, .save,
.addEvent which won’t adapt to different screen sizes or Dynamic Type; change
the implementation to compute widths relative to available space (e.g., use
UIScreen.main.bounds.width or, preferably, pass a parent width via
GeometryReader/parent view and calculate widths as screenWidth minus desired
horizontal margins for cases like .next and .save while keeping small controls
like .confirm/.addEvent either a fixed min/max or a percentage); update the var
width logic to reference the calculated screenWidth and apply safe min/max
constraints so buttons remain accessible on all devices.
| func makeBody(configuration: Configuration) -> some View { | ||
| configuration.label | ||
| .frame( | ||
| width: configuration.isPressed ? type.width * 0.95 : type.width, | ||
| height: configuration.isPressed ? type.height * 0.95 : type.height | ||
| ) | ||
| .background(type.backgroundColor(for: state)) | ||
| .cornerRadius( | ||
| configuration.isPressed | ||
| ? type.cornerRadius * 0.95 | ||
| : type.cornerRadius | ||
| ) | ||
| } |
Feat/#19 CherrishButtoncomponent
🔗 연결된 이슈
📄 작업 내용
💻 주요 코드 설명