Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "check_circular.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "checkbox_active.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "checkbox_default.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "chevron_left.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "chevron_right.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "clock.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "close.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "deletebox.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "info.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "plus.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "radiobtn_default.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "radiobtn_selected.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "return.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "search.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "sun.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//
// CherrishButton.swift
// Cherrish-iOS
//
// Created by sumin Kong on 1/10/26.
//

import SwiftUI

enum ButtonState {
case normal
case active
}
Comment on lines +10 to +13
Copy link

Choose a reason for hiding this comment

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

🧹 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.

Suggested change
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.


enum CherrishButtonType {
case next
case confirm
case save
case addEvent
}
Comment on lines +15 to +20
Copy link

Choose a reason for hiding this comment

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

🧹 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.


struct CherrishButton: View {
Copy link

Choose a reason for hiding this comment

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

🛠️ 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.


let title: String
let type: CherrishButtonType
@Binding var state: ButtonState
let action: () -> Void

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))
}
Comment on lines +29 to +37
Copy link

Choose a reason for hiding this comment

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

🧹 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.

Suggested change
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.

}


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))
.clipShape(RoundedRectangle(cornerRadius: configuration.isPressed
? type.cornerRadius * 0.95
: type.cornerRadius))
}
Comment on lines 45 to 55
Copy link

Choose a reason for hiding this comment

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

🛠️ 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:


.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.

Suggested change
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.

Copy link
Contributor

Choose a reason for hiding this comment

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

요내용 확인하면 좋을 거 같아욤

}
Comment on lines 41 to 56
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

접근 제어 수정자를 추가하고 햅틱 피드백을 고려하세요.

CherrishButtonStylepublic 접근 제어 수정자가 필요합니다. 또한 더 나은 사용자 경험을 위해 버튼을 눌렀을 때 햅틱 피드백 추가를 고려해보세요.

♻️ 제안하는 개선사항
-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
}
}
Comment on lines +61 to +68
Copy link

Choose a reason for hiding this comment

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

🧹 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.


var height: CGFloat {
switch self {
case .save: return 44
default:
return 50
}
}

var cornerRadius: CGFloat {
switch self {
case .save: return 10
default: return 12
}
}

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
}
}
}