Skip to content

Comments

Feat/#93 다운타임 모달뷰 구현#98

Merged
wotjs020708 merged 25 commits intodevelopfrom
feat/#93-downtimemodelview
Jan 19, 2026
Merged

Feat/#93 다운타임 모달뷰 구현#98
wotjs020708 merged 25 commits intodevelopfrom
feat/#93-downtimemodelview

Conversation

@wotjs020708
Copy link
Contributor

@wotjs020708 wotjs020708 commented Jan 18, 2026

🔗 연결된 이슈

📄 작업 내용

  • 다운타임 모달 연결(수정)
구현 내용 IPhone 16 pro IPhone 13 mini
GIF

💻 주요 코드 설명

DowntimeBottomSheetView
-화면크기에 너비 자동 조절하게 구현

GeometryReader { geo in
            HStack(spacing: 4.adjustedW) {
                CherrishButton(title: "다운타임 없이 일정 추가", type: .addEvent, state: .constant(.normal), leadingIcon: .none, trailingIcon: .none, action: {})
                    .frame(width: geo.size.width * 2/3 - 2)

                CherrishButton(title: "확인", type: .small, state: .constant(.normal), leadingIcon: .none, trailingIcon: .none, action: {})
                    .frame(width: geo.size.width * 1/3 - 2)
            }
        }
  • selectedDowntime이 변경될 때마다 프로그래스바 크기 조정
.onChange(of: selectedDowntime) {
            rate = min(Double(selectedDowntime) / Double(betweenDays), 1.0)
        }

Int+

  • 날짜 차 구할 수 있는 extension
extension Int {
    static func daysBetween(
        fromYear: Int, fromMonth: Int, fromDay: Int,
        toYear: Int, toMonth: Int, toDay: Int
    ) -> Int {
        let calendar = Calendar.current
        
        guard let fromDate = calendar.date(from: DateComponents(year: fromYear, month: fromMonth, day: fromDay)),
              let toDate = calendar.date(from: DateComponents(year: toYear, month: toMonth, day: toDay)) else {
            return 0
        }
        
        return abs(calendar.dateComponents([.day], from: fromDate, to: toDate).day ?? 0)
    }
}

View+

  • 화면 클릭시 키보드 내려가는 extension
extension View {
    
    func hideKeyboard() {
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

162dd79f96ca43dd

@wotjs020708 wotjs020708 requested a review from a team January 18, 2026 16:31
@wotjs020708 wotjs020708 self-assigned this Jan 18, 2026
@wotjs020708 wotjs020708 requested review from sum130 and y-eonee and removed request for a team January 18, 2026 16:31
@wotjs020708 wotjs020708 requested a review from soseoyo12 January 18, 2026 16:31
@wotjs020708 wotjs020708 added the Feature 새로운 기능 구현 및 API 연결 label Jan 18, 2026
@wotjs020708 wotjs020708 linked an issue Jan 18, 2026 that may be closed by this pull request
1 task
@coderabbitai
Copy link

coderabbitai bot commented Jan 18, 2026

📝 Walkthrough

Walkthrough

다운타임 모달 뷰를 구성하는 여러 컴포넌트를 연결하고 동적 상태 관리를 추가합니다. DownTimeSettingView에서 선택된 치료 항목을 관리하고, DowntimeBottomSheetView에서 다운타임 기간을 계산하여 표시합니다.

Changes

코호트 / 파일(s) 변경 사항 요약
다운타임 모달 뷰 연결
DownTimeSettingView.swift, DowntimeBottomSheetView.swift
DownTimeSettingView에 selectedTreatment 상태 추가 및 날짜 데이터(today, setday) 전달. DowntimeBottomSheetView에서 TreatmentEntity, 날짜 튜플을 매개변수로 받아 내부 상태(selectedDowntime, rate, betweenDays)를 초기화하고, 동적 메시지 및 다운타임 범위 표시
TreatmentEntity 모델 확장
TreatmentEntity.swift
Identifiable 프로토콜 준수 추가, id 계산 속성 구현
뷰모델 유틸리티 추가
TreatmentViewModel.swift, NoTreatmentViewModel.swift
today 계산 속성 및 toInt() 헬퍼 메서드 추가로 날짜 및 문자열 변환 지원
뷰 초기화 업데이트
NoTreatmentView.swift
DownTimeSettingView 초기화 시 setday, today 매개변수 전달
레이아웃 및 구조 개선
TreatmentView.swift, TargetDdaySettingView.swift
TreatmentView 내용을 contentView(), bottomView() 헬퍼로 분리, TargetDdaySettingView를 ScrollView로 감싸기
UI 컴포넌트 개선
CherrishPicker.swift, TreatmentRowView.swift
피커 행 배경색 투명화 처리, TreatmentRowView 반응형 크기 조정(adjustedW, adjustedH)
확장 메서드 추가
PresentationDetent+.swift, Date+.swift, View+HideKeyboard.swift
PresentationDetent.extraLarge 상수, Date.daysBetween() 메서드, View.hideKeyboard()메서드 추가

Sequence Diagram

sequenceDiagram
    actor User
    participant NTV as NoTreatmentView
    participant DTSV as DownTimeSettingView
    participant DBSV as DowntimeBottomSheetView
    participant TVM as TreatmentViewModel
    
    User->>NTV: 다운타임 설정 선택
    NTV->>DTSV: treatments, today, setday 전달
    User->>DTSV: 치료 항목 선택
    DTSV->>DTSV: selectedTreatment 상태 업데이트
    DTSV->>DBSV: treatment, today, setday로 Sheet 표시
    DBSV->>DBSV: onAppear에서 상태 초기화<br/>(betweenDays, rate 계산)
    User->>DBSV: 다운타임 기간 조정
    DBSV->>DBSV: onChange로 rate 재계산
    User->>DBSV: 확인 버튼 클릭
    DBSV->>NTV: 모달 종료
    NTV->>TVM: 선택된 다운타임 저장
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • y-eonee
  • sum130
  • soseoyo12
🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive 대부분의 변경사항이 다운타임 모달 관련 기능에 집중되어 있으나, TreatmentRowView, TargetDdaySettingView, TreatmentView의 반응형 레이아웃 조정 등 일부 변경이 issue #93 범위를 벗어날 수 있습니다. TreatmentRowView의 반응형 너비 조정, TargetDdaySettingView의 ScrollView 추가, TreatmentView의 레이아웃 리팩토링이 다운타임 모달 구현과의 직접적인 연관성을 명확히 하거나, 별도 이슈로 분리하는 것을 검토해주세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경사항을 명확하게 반영하고 있으며, 다운타임 모달뷰 구현이라는 핵심 작업을 제목에 제시하고 있습니다.
Description check ✅ Passed PR 설명이 작업 내용, 코드 설명, 그리고 동작 GIF를 포함하여 변경사항과 관련된 상세 정보를 제공하고 있습니다.
Linked Issues check ✅ Passed PR의 모든 코드 변경사항이 issue #93의 목표인 다운타임 모달 뷰 연결 및 구현 요구사항을 충족하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#93-downtimemodelview

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishPicker.swift (1)

62-84: 배경색 초기화가 매 행 렌더링마다 반복됩니다

makeUIView에서도 동일 처리가 있어 중복 호출이 됩니다. 한 번만 수행되도록 가드하면 비용을 줄일 수 있습니다.

♻️ 제안 변경
 class Coordinator: NSObject, UIPickerViewDelegate, UIPickerViewDataSource {
     var parent: PickerViewRepresentable
+    private var didClearBackground = false
     
     init(_ parent: PickerViewRepresentable) {
         self.parent = parent
     }
@@
     func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
-        pickerView.subviews.forEach { subview in
-               subview.backgroundColor = .clear
-           }
+        if !didClearBackground {
+            pickerView.subviews.forEach { $0.backgroundColor = .clear }
+            didClearBackground = true
+        }
         
         let label = (view as? UILabel) ?? UILabel()
         label.text = "\(parent.range.lowerBound + row)"
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DownTimeSettingView.swift (1)

13-33: 시트 표시는 toggle() 대신 명시적으로 true 설정을 권장

의도는 “열기”이므로 isShowSheet = true가 더 안전하고 idempotent 합니다.

♻️ 제안 변경
- TreatmentRowView(displayMode: .completeBoxView, treatmentEntity: treatment, isSelected: .constant(false), isCompleted: .constant(false), action: { isShowSheet.toggle() })
+ TreatmentRowView(displayMode: .completeBoxView, treatmentEntity: treatment, isSelected: .constant(false), isCompleted: .constant(false), action: { isShowSheet = true })
🤖 Fix all issues with AI agents
In `@Cherrish-iOS/Cherrish-iOS/App/Cherrish_iOSApp.swift`:
- Line 19: Root view was changed to TreatmentView, bypassing AppCoordinatorView
(which handles SplashView, OnboardingCoordinatorView, and AppState routing);
restore AppCoordinatorView as the app entry point by replacing the
TreatmentView(...) instantiation in Cherrish_iOSApp (Cherrish_iOSApp.swift) with
AppCoordinatorView() (or initialize AppCoordinatorView with the same
dependencies TreatmentViewModel required if intentional), and if the change is
deliberate, add a brief comment documenting why AppCoordinatorView was removed
and how splash/onboarding/state flows are now handled.

In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DowntimeBottomSheetView.swift`:
- Around line 54-65: In DowntimeBottomSheetView replace the hardcoded
conditional in the TypographyText call (currently using `true ? ... : ...`) with
the real runtime boolean that determines whether the downtime is within allowed
range (e.g., a view state or computed property such as `isDowntimeWithinLimit`,
`viewModel.isDowntimeValid`, or a local computed like `isDowntimeValid()`), so
the correct message (the first or the else string) is shown based on actual
logic; update any surrounding code to compute that boolean from the
downtime/target-date inputs and pass it into the TypographyText expression
instead of the constant `true`.
- Around line 142-154: The GeometryReader-based width calc in buttonView
currently ignores the external .padding(.horizontal, 24.adjustedW); move the
horizontal padding inside the GeometryReader (apply it to the HStack) and change
the width calculations for the two CherrishButton frames to use (geo.size.width
- 48.adjustedW) as the available width (e.g. (geo.size.width - 48.adjustedW) *
2/3 - 2 and (geo.size.width - 48.adjustedW) * 1/3 - 2) so the buttons account
for both side paddings and avoid overflow.

var body: some Scene {
WindowGroup {
AppCoordinatorView()
TreatmentView(viewModel: TreatmentViewModel())
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's explore the repository structure to understand the app layout
find . -type f -name "*.swift" | grep -E "(App|Cherrish)" | head -20

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 1310


🏁 Script executed:

# Let's read the specific file mentioned in the review
cat -n "Cherrish-iOS/Cherrish-iOS/App/Cherrish_iOSApp.swift"

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 564


🏁 Script executed:

# Search for AppCoordinatorView to understand what it does
rg "AppCoordinatorView" --type swift -A 10 -B 2

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 2033


🏁 Script executed:

# Search for TreatmentView and its implementation
rg "struct TreatmentView" --type swift -A 10

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 1561


🏁 Script executed:

# Look for onboarding, login, or authentication related code
rg "(onboarding|login|authentication|auth|coordinator)" --type swift -i | head -30

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 3907


🏁 Script executed:

# Read the AppCoordinator to understand the full flow
cat -n "Cherrish-iOS/Cherrish-iOS/Presentation/Coordinator/AppCoordinator.swift"

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 838


🏁 Script executed:

# Check if there are any comments or TODOs about this change in recent commits
git log --oneline -20 --all -- "Cherrish-iOS/Cherrish-iOS/App/Cherrish_iOSApp.swift"

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 254


🏁 Script executed:

# Check the specific hotfix commit to understand what was changed
git show ac7bd58 --stat

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 331


🏁 Script executed:

# Look at the full diff of that hotfix to see what was replaced
git show ac7bd58

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 713


🏁 Script executed:

# Check the most recent commit to see what changed
git show 27a7246

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 50386


🏁 Script executed:

# Let's see the history more clearly with more context
git log -p --max-count=5 -- "Cherrish-iOS/Cherrish-iOS/App/Cherrish_iOSApp.swift"

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 52


앱 진입점이 AppCoordinatorView에서 TreatmentView로 변경됨

현재 코드에서 루트 뷰가 TreatmentView로 설정되어 있어 스플래시 화면, 온보딩, 앱 상태 관리를 담당하는 AppCoordinatorView가 완전히 우회됩니다. 이로 인해 다음 흐름이 모두 건너뜁니다:

  • SplashView (앱 초기화)
  • OnboardingCoordinatorView (온보딩 플로우)
  • AppState 기반 라우팅 (splash → onboarding → home)

TreatmentView는 기능별 뷰이지 앱 진입점이 아닙니다. AppCoordinatorView를 복구하거나 의도된 변경이라면 그 사유를 명시해주세요.

🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/App/Cherrish_iOSApp.swift` at line 19, Root view
was changed to TreatmentView, bypassing AppCoordinatorView (which handles
SplashView, OnboardingCoordinatorView, and AppState routing); restore
AppCoordinatorView as the app entry point by replacing the TreatmentView(...)
instantiation in Cherrish_iOSApp (Cherrish_iOSApp.swift) with
AppCoordinatorView() (or initialize AppCoordinatorView with the same
dependencies TreatmentViewModel required if intentional), and if the change is
deliberate, add a brief comment documenting why AppCoordinatorView was removed
and how splash/onboarding/state flows are now handled.

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 142 to 154
private var buttonView: some View {
GeometryReader { geo in
HStack(spacing: 4.adjustedW) {
CherrishButton(title: "다운타임 없이 일정 추가", type: .addEvent, state: .constant(.normal), leadingIcon: .none, trailingIcon: .none, action: {})
.frame(width: geo.size.width * 2/3 - 2)

CherrishButton(title: "확인", type: .small, state: .constant(.normal), leadingIcon: .none, trailingIcon: .none, action: {})
.frame(width: geo.size.width * 1/3 - 2)

}
}
.padding(.horizontal, 24.adjustedW)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "DowntimeBottomSheetView.swift" -type f

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 178


🏁 Script executed:

cat -n "Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DowntimeBottomSheetView.swift" | sed -n '130,160p'

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 1143


🏁 Script executed:

cat -n "Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DowntimeBottomSheetView.swift" | grep -A 20 "buttonView"

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 1650


🏁 Script executed:

wc -l "Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DowntimeBottomSheetView.swift"

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 180


🏁 Script executed:

cat -n "Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DowntimeBottomSheetView.swift" | sed -n '1,50p'

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 1650


GeometryReader 폭 계산이 외부 패딩을 고려하지 않아 오버플로우 위험

geo.size.width를 기반으로 버튼 너비를 계산한 뒤 GeometryReader 외부에 패딩을 적용하면, 패딩까지 포함된 전체 프레임이 할당된 공간을 초과할 수 있습니다. 패딩을 내부로 이동하고 폭 계산에서 양쪽 패딩(48.adjustedW)을 빼서 수정해주세요.

🛠️ 제안 변경
 private var buttonView: some View {
     GeometryReader { geo in
+        let total = geo.size.width - 48.adjustedW
         HStack(spacing: 4.adjustedW) {
             CherrishButton(title: "다운타임 없이 일정 추가", type: .addEvent, state: .constant(.normal), leadingIcon: .none, trailingIcon: .none, action: {})
-                .frame(width: geo.size.width * 2/3 - 2)
+                .frame(width: total * 2/3 - 2)

             CherrishButton(title: "확인", type: .small, state: .constant(.normal), leadingIcon: .none, trailingIcon: .none, action: {})
-                .frame(width: geo.size.width * 1/3 - 2)
+                .frame(width: total * 1/3 - 2)
             
         }
+        .padding(.horizontal, 24.adjustedW)
     }
-    .padding(.horizontal, 24.adjustedW)
 }
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DowntimeBottomSheetView.swift`
around lines 142 - 154, The GeometryReader-based width calc in buttonView
currently ignores the external .padding(.horizontal, 24.adjustedW); move the
horizontal padding inside the GeometryReader (apply it to the HStack) and change
the width calculations for the two CherrishButton frames to use (geo.size.width
- 48.adjustedW) as the available width (e.g. (geo.size.width - 48.adjustedW) *
2/3 - 2 and (geo.size.width - 48.adjustedW) * 1/3 - 2) so the buttons account
for both side paddings and avoid overflow.

Copy link
Contributor

@y-eonee y-eonee left a comment

Choose a reason for hiding this comment

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

image

UI가 너무 달라요.. 전체적으로 꼼꼼하게 다시 봐주세요

var body: some Scene {
WindowGroup {
AppCoordinatorView()
TreatmentView(viewModel: TreatmentViewModel())
Copy link
Contributor

Choose a reason for hiding this comment

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

님아!!이걸또올려요?

.padding(.horizontal, 18.adjustedW)
.offset(y: -4.adjustedH)
.padding(.top, 12.adjustedH)
.padding(.bottom, 20.59.adjustedH)
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 73 to 74
VStack(spacing: 0) {
HStack(spacing: 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

spacing 왜 다 없애요...?

Comment on lines 145 to 149
CherrishButton(title: "다운타임 없이 일정 추가", type: .addEvent, state: .constant(.normal), leadingIcon: .none, trailingIcon: .none, action: {})
.frame(width: geo.size.width * 2/3 - 2)

CherrishButton(title: "확인", type: .small, state: .constant(.normal), leadingIcon: .none, trailingIcon: .none, action: {})
.frame(width: geo.size.width * 1/3 - 2)
Copy link
Contributor

Choose a reason for hiding this comment

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

개행해주세염

ScrollView(.vertical, showsIndicators: false) {
ForEach(treatments, id: \.self) { treatment in
TreatmentRowView(displayMode: .completeBoxView, treatmentEntity: treatment, isSelected: .constant(false), isCompleted: .constant(false), action: {})
TreatmentRowView(displayMode: .completeBoxView, treatmentEntity: treatment, isSelected: .constant(false), isCompleted: .constant(false), action: { isShowSheet.toggle() })
Copy link
Contributor

Choose a reason for hiding this comment

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

여기도 개행~~

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

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/Feature/Calendar/Treatment/NoTreatment/View/NoTreatmentView.swift (1)

97-109: 버튼에 중복 패딩이 적용됩니다.

CherrishButton.padding(.horizontal, 25.adjustedW) (line 106)가 적용되고, 부모 VStack에도 .padding(.leading, 25.adjustedW).padding(.trailing, 24.adjustedW) (lines 108-109)가 적용됩니다. 이로 인해 버튼에 의도하지 않은 이중 패딩이 발생할 수 있습니다.

TreatmentView.swiftbottomView()와 비교했을 때, 해당 파일에는 VStack에 추가 패딩이 없습니다.

🔧 제안: 중복 패딩 제거
         .padding(.horizontal, 25.adjustedW)
     }
-    .padding(.leading, 25.adjustedW)
-    .padding(.trailing, 24.adjustedW)
 }
🤖 Fix all issues with AI agents
In `@Cherrish-iOS/Cherrish-iOS/Domain/Model/TreatmentEntity.swift`:
- Around line 10-11: TreatmentEntity currently uses var id: Self { self }, which
breaks SwiftUI identity when any property changes; change to a stable identifier
by adding a stored unique id (e.g., let uuid: UUID) and implement var id: UUID {
uuid } or, if name is guaranteed unique, implement var id: String { name } and
remove/adjust any uses expecting Self as id; update initializers to set uuid and
ensure Equatable/Hashable semantics rely on the stable id instead of whole-self
equality.

In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DowntimeBottomSheetView.swift`:
- Around line 62-66: The calculation for rate (in the view where rate,
selectedDowntime and betweenDays are used, including the onAppear and .onChange
blocks) can divide by zero when betweenDays == 0; change both places to guard
the denominator by checking if betweenDays > 0 and compute rate =
min(Double(selectedDowntime) / Double(betweenDays), 1.0) only then, otherwise
set rate = 0 (or 0.0), ensuring no infinite/NaN results when today == setday.

In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DownTimeSettingView.swift`:
- Line 11: Remove the unnecessary explicit nil initialization on the Optional
`@State` property by changing the declaration of selectedTreatment; since Optional
types default to nil in Swift, update the `@State` var selectedTreatment:
TreatmentEntity? = nil to simply `@State` var selectedTreatment: TreatmentEntity?
to clean up the code and adhere to Swift conventions.

In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Treatment/View/TreatmentView.swift`:
- Around line 67-81: The setday tuple passed into DownTimeSettingView is overly
verbose across multiple lines; simplify by constructing the tuple inline or by
creating a short local variable using viewModel.toInt for year/month/day and
passing that variable to DownTimeSettingView. Locate the DownTimeSettingView
invocation in TreatmentView.swift and replace the multi-line setday:
(viewModel.toInt(viewModel.year), viewModel.toInt(viewModel.month),
viewModel.toInt(viewModel.day)) with a single-line tuple or with a small let
setday = (year: viewModel.toInt(viewModel.year), month:
viewModel.toInt(viewModel.month), day: viewModel.toInt(viewModel.day)) and pass
setday to improve readability.

In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Treatment/ViewModel/TreatmentViewModel.swift`:
- Around line 28-36: The present `today` computed property and `toInt(_:)`
utility are duplicated in TreatmentViewModel (var today) and
NoTreatmentViewModel; extract them into shared extensions to remove duplication:
create a Date extension (e.g., Date.components or similar) that returns (year:
Int, month: Int, day: Int) and replace the `var today` in TreatmentViewModel and
NoTreatmentViewModel with Date().components, and create a String extension
(e.g., func toInt(default: Int = 0)) to replace the local `toInt(_:)`
implementations so both view models call the shared helpers instead.

In `@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/Int`+.swift:
- Around line 10-24: The daysBetween(_:fromYear:...) function is misplaced on
Int and returns 0 for invalid dates which confuses errors with real zero
differences; move the logic out of the Int extension into a Date-centric helper
(e.g. add a static Date.daysBetween(from:to:) or a DateHelper.type) and update
the function signature (daysBetween) to accept tuple or DateComponents inputs
and return an optional Int? so invalid date parsing returns nil; ensure you
locate and remove or deprecate the original Int.extension daysBetween and rewire
any callers to the new Date.daysBetween/DateHelper.daysBetween to preserve
behavior.
♻️ Duplicate comments (3)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DowntimeBottomSheetView.swift (1)

182-213: GeometryReader 폭 계산이 외부 패딩을 고려하지 않아 오버플로우 위험

이전 리뷰에서 지적된 것처럼, geo.size.width 기반 버튼 너비 계산 후 외부에 패딩을 적용하면 전체 프레임이 할당된 공간을 초과할 수 있습니다.

Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/NoTreatment/ViewModel/NoTreatmentViewModel.swift (1)

35-43: TreatmentViewModel과 중복되는 코드입니다.

todaytoInt(_:)TreatmentViewModel.swift에도 동일하게 구현되어 있습니다. 공통 유틸리티로 추출하면 코드 중복을 줄일 수 있습니다.

Also applies to: 53-55

Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/NoTreatment/View/NoTreatmentView.swift (1)

70-83: setday 튜플 포맷팅이 TreatmentView.swift와 동일하게 장황합니다.

가독성 향상을 위해 간소화를 고려해 주세요 (이전 리뷰 참조).

Comment on lines +10 to 11
struct TreatmentEntity: Identifiable, Equatable, Hashable {
var id: Self { self }
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Selfid로 사용하면 속성 변경 시 SwiftUI가 아이템 동일성을 잃을 수 있습니다.

var id: Self { self }는 모든 속성이 동일해야 같은 아이템으로 인식됩니다. 속성이 변경되면 SwiftUI가 이를 새 아이템으로 간주하여 리스트 애니메이션이나 상태 유지에 문제가 발생할 수 있습니다.

안정적인 식별자(예: UUID 또는 고유한 name)를 사용하는 것이 권장됩니다.

🛠️ 제안된 수정
-struct TreatmentEntity: Identifiable, Equatable, Hashable {
-    var id: Self { self }
+struct TreatmentEntity: Identifiable, Equatable, Hashable {
+    let id: UUID = UUID()
     let name: String
     let benefits: [String]
     let downtimeMin: Int
     let downtimeMax: Int
 }

또는 name이 고유하다면:

var id: String { name }
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Domain/Model/TreatmentEntity.swift` around lines 10
- 11, TreatmentEntity currently uses var id: Self { self }, which breaks SwiftUI
identity when any property changes; change to a stable identifier by adding a
stored unique id (e.g., let uuid: UUID) and implement var id: UUID { uuid } or,
if name is guaranteed unique, implement var id: String { name } and
remove/adjust any uses expecting Self as id; update initializers to set uuid and
ensure Equatable/Hashable semantics rely on the stable id instead of whole-self
equality.

Copy link
Contributor

Choose a reason for hiding this comment

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

id도 self 쓰나용??

Comment on lines 62 to 66
rate = min(Double(selectedDowntime) / Double(betweenDays), 1.0)
}
.onChange(of: selectedDowntime) {
rate = min(Double(selectedDowntime) / Double(betweenDays), 1.0)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

betweenDays가 0일 때 0으로 나누기 오류가 발생합니다.

todaysetday가 동일한 날짜일 경우 betweenDays가 0이 되어 Double(selectedDowntime) / Double(betweenDays)에서 무한대(inf) 또는 NaN이 발생합니다.

🐛 제안된 수정
 .onAppear {
     selectedDowntime = treatment.downtimeMax
     betweenDays = Int.daysBetween(
         fromYear: setday.year,
         fromMonth: setday.month,
         fromDay: setday.day,
         toYear: today.year,
         toMonth: today.month,
         toDay: today.day
     )
-    rate = min(Double(selectedDowntime) / Double(betweenDays), 1.0)
+    rate = betweenDays > 0 ? min(Double(selectedDowntime) / Double(betweenDays), 1.0) : 1.0
 }
 .onChange(of: selectedDowntime) {
-    rate = min(Double(selectedDowntime) / Double(betweenDays), 1.0)
+    rate = betweenDays > 0 ? min(Double(selectedDowntime) / Double(betweenDays), 1.0) : 1.0
 }
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DowntimeBottomSheetView.swift`
around lines 62 - 66, The calculation for rate (in the view where rate,
selectedDowntime and betweenDays are used, including the onAppear and .onChange
blocks) can divide by zero when betweenDays == 0; change both places to guard
the denominator by checking if betweenDays > 0 and compute rate =
min(Double(selectedDowntime) / Double(betweenDays), 1.0) only then, otherwise
set rate = 0 (or 0.0), ensuring no infinite/NaN results when today == setday.

Copy link
Contributor

Choose a reason for hiding this comment

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

요거! 확인해주세용~~~

import SwiftUI

struct DownTimeSettingView: View {
@State var selectedTreatment: TreatmentEntity? = nil
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Optional 변수를 nil로 초기화하는 것은 불필요합니다.

Swift에서 Optional 타입은 기본값이 nil이므로 명시적 초기화가 필요하지 않습니다.

♻️ 제안된 수정
-    `@State` var selectedTreatment: TreatmentEntity? = nil
+    `@State` var selectedTreatment: TreatmentEntity?
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 11-11: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)

🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DownTimeSettingView.swift`
at line 11, Remove the unnecessary explicit nil initialization on the Optional
`@State` property by changing the declaration of selectedTreatment; since Optional
types default to nil in Swift, update the `@State` var selectedTreatment:
TreatmentEntity? = nil to simply `@State` var selectedTreatment: TreatmentEntity?
to clean up the code and adhere to Swift conventions.

Comment on lines +67 to +81
DownTimeSettingView(
treatments: viewModel.selectedTreatments,
setday: (
viewModel.toInt(
viewModel.year
),
viewModel.toInt(
viewModel.month
),
viewModel.toInt(
viewModel.day
)
}
Spacer()
),
today: viewModel.today
)
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

setday 튜플 포맷팅 간소화를 고려해 주세요.

현재 각 요소가 여러 줄에 걸쳐 있어 가독성이 떨어집니다.

✨ 간소화 제안
         case .downTimeSetting:
             DownTimeSettingView(
                 treatments: viewModel.selectedTreatments,
-                setday: (
-                    viewModel.toInt(
-                        viewModel.year
-                    ),
-                    viewModel.toInt(
-                        viewModel.month
-                    ),
-                    viewModel.toInt(
-                        viewModel.day
-                    )
-                ),
+                setday: (viewModel.toInt(viewModel.year),
+                         viewModel.toInt(viewModel.month),
+                         viewModel.toInt(viewModel.day)),
                 today: viewModel.today
             )
📝 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
DownTimeSettingView(
treatments: viewModel.selectedTreatments,
setday: (
viewModel.toInt(
viewModel.year
),
viewModel.toInt(
viewModel.month
),
viewModel.toInt(
viewModel.day
)
}
Spacer()
),
today: viewModel.today
)
DownTimeSettingView(
treatments: viewModel.selectedTreatments,
setday: (viewModel.toInt(viewModel.year),
viewModel.toInt(viewModel.month),
viewModel.toInt(viewModel.day)),
today: viewModel.today
)
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Treatment/View/TreatmentView.swift`
around lines 67 - 81, The setday tuple passed into DownTimeSettingView is overly
verbose across multiple lines; simplify by constructing the tuple inline or by
creating a short local variable using viewModel.toInt for year/month/day and
passing that variable to DownTimeSettingView. Locate the DownTimeSettingView
invocation in TreatmentView.swift and replace the multi-line setday:
(viewModel.toInt(viewModel.year), viewModel.toInt(viewModel.month),
viewModel.toInt(viewModel.day)) with a single-line tuple or with a small let
setday = (year: viewModel.toInt(viewModel.year), month:
viewModel.toInt(viewModel.month), day: viewModel.toInt(viewModel.day)) and pass
setday to improve readability.

Comment on lines +28 to +36
var today: (year: Int, month: Int, day: Int) {
let calendar = Calendar.current
let now = Date()
return (
calendar.component(.year, from: now),
calendar.component(.month, from: now),
calendar.component(.day, from: now)
)
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

todaytoInt(_:) 유틸리티가 NoTreatmentViewModel과 중복됩니다.

NoTreatmentViewModel.swift의 lines 35-43과 53-55에 동일한 구현이 존재합니다. 이는 DRY 원칙 위반으로, 추후 수정 시 두 곳을 동시에 변경해야 하는 유지보수 부담이 발생합니다.

공통 유틸리티를 별도 extension이나 공유 헬퍼로 추출하는 것을 권장합니다.

♻️ 제안: 공통 Extension으로 추출

예시 - Date+Components.swift:

extension Date {
    var components: (year: Int, month: Int, day: Int) {
        let calendar = Calendar.current
        return (
            calendar.component(.year, from: self),
            calendar.component(.month, from: self),
            calendar.component(.day, from: self)
        )
    }
}

예시 - String+ToInt.swift:

extension String {
    func toInt(default defaultValue: Int = 0) -> Int {
        Int(self) ?? defaultValue
    }
}

이후 ViewModel에서:

var today: (year: Int, month: Int, day: Int) {
    Date().components
}

Also applies to: 61-63

🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Treatment/ViewModel/TreatmentViewModel.swift`
around lines 28 - 36, The present `today` computed property and `toInt(_:)`
utility are duplicated in TreatmentViewModel (var today) and
NoTreatmentViewModel; extract them into shared extensions to remove duplication:
create a Date extension (e.g., Date.components or similar) that returns (year:
Int, month: Int, day: Int) and replace the `var today` in TreatmentViewModel and
NoTreatmentViewModel with Date().components, and create a String extension
(e.g., func toInt(default: Int = 0)) to replace the local `toInt(_:)`
implementations so both view models call the shared helpers instead.

Comment on lines 10 to 24
extension Int {
static func daysBetween(
fromYear: Int, fromMonth: Int, fromDay: Int,
toYear: Int, toMonth: Int, toDay: Int
) -> Int {
let calendar = Calendar.current

guard let fromDate = calendar.date(from: DateComponents(year: fromYear, month: fromMonth, day: fromDay)),
let toDate = calendar.date(from: DateComponents(year: toYear, month: toMonth, day: toDay)) else {
return 0
}

return abs(calendar.dateComponents([.day], from: fromDate, to: toDate).day ?? 0)
}
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Int extension보다 Date 또는 별도 유틸리티 타입에 배치하는 것이 더 적절합니다.

daysBetween은 날짜 간 일수 차이를 계산하는 함수로, Int 타입과 의미적 연관성이 낮습니다. Date extension이나 DateHelper 같은 전용 유틸리티 타입에 배치하면 코드 검색성과 응집도가 향상됩니다.

또한, 유효하지 않은 날짜 입력 시 0을 반환하면 실제로 0일 차이인 경우와 구분이 불가능합니다.

♻️ 제안된 개선안
// DateHelper.swift 또는 Date+.swift로 이동 권장
extension Date {
    static func daysBetween(
        from: (year: Int, month: Int, day: Int),
        to: (year: Int, month: Int, day: Int)
    ) -> Int? {
        let calendar = Calendar.current
        
        guard let fromDate = calendar.date(from: DateComponents(year: from.year, month: from.month, day: from.day)),
              let toDate = calendar.date(from: DateComponents(year: to.year, month: to.month, day: to.day)) else {
            return nil
        }
        
        return abs(calendar.dateComponents([.day], from: fromDate, to: toDate).day ?? 0)
    }
}
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/Int`+.swift around
lines 10 - 24, The daysBetween(_:fromYear:...) function is misplaced on Int and
returns 0 for invalid dates which confuses errors with real zero differences;
move the logic out of the Int extension into a Date-centric helper (e.g. add a
static Date.daysBetween(from:to:) or a DateHelper.type) and update the function
signature (daysBetween) to accept tuple or DateComponents inputs and return an
optional Int? so invalid date parsing returns nil; ensure you locate and remove
or deprecate the original Int.extension daysBetween and rewire any callers to
the new Date.daysBetween/DateHelper.daysBetween to preserve behavior.

Copy link
Contributor

Choose a reason for hiding this comment

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

이거 한번만 봐주세여

Copy link
Contributor

@soseoyo12 soseoyo12 left a comment

Choose a reason for hiding this comment

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

수고하셨습니당~~

Copy link
Contributor

@sum130 sum130 left a comment

Choose a reason for hiding this comment

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

수고했어용~~ 전체적으로 개행! 한번씩 쭉~ 정리해주면 더 좋을 것 같아요ㅎㅎ

Comment on lines +10 to 11
struct TreatmentEntity: Identifiable, Equatable, Hashable {
var id: Self { self }
Copy link
Contributor

Choose a reason for hiding this comment

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

id도 self 쓰나용??

Comment on lines 90 to 92
.presentationDetents([.extraLarge])
.presentationBackground(.gray0)
.presentationDragIndicator(.visible)
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 62 to 66
rate = min(Double(selectedDowntime) / Double(betweenDays), 1.0)
}
.onChange(of: selectedDowntime) {
rate = min(Double(selectedDowntime) / Double(betweenDays), 1.0)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

요거! 확인해주세용~~~

Copy link
Contributor

@y-eonee y-eonee left a comment

Choose a reason for hiding this comment

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

수고하셨습니당
리뷰한번만 더 봐주세요!

Comment on lines +11 to +14
@State var selectedTreatment: TreatmentEntity? = nil
var treatments: [TreatmentEntity]

let setday: (year: Int, month: Int, day: Int)
let today: (year: Int, month: Int, day: Int)
Copy link
Contributor

Choose a reason for hiding this comment

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

이거 프라이빗 붙이면 좋을 것 같습니다!

.frame(height: 38.adjustedH)

bottomView()
Spacer().frame(height: 38.adjustedH)
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 +31 to +32
.padding(.leading, 34.adjustedW)
.padding(.trailing, 33.adjustedW)
Copy link
Contributor

Choose a reason for hiding this comment

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

요기 걍 34로 맞춥시당

Comment on lines +79 to +81
TypographyText("언제까지 회복이 완료되면 좋을까요?", style: .title1_sb_18, color: .gray1000)
case .no:
TypographyText("대략적인 회복 목표일을 정해볼까요?", style: .title1_sb_18, color: .gray1000)
TypographyText("대략적인 회복 목표일을 정해볼까요?", style: .title1_sb_18, color: .gray1000)
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 10 to 24
extension Int {
static func daysBetween(
fromYear: Int, fromMonth: Int, fromDay: Int,
toYear: Int, toMonth: Int, toDay: Int
) -> Int {
let calendar = Calendar.current

guard let fromDate = calendar.date(from: DateComponents(year: fromYear, month: fromMonth, day: fromDay)),
let toDate = calendar.date(from: DateComponents(year: toYear, month: toMonth, day: toDay)) else {
return 0
}

return abs(calendar.dateComponents([.day], from: fromDate, to: toDate).day ?? 0)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

이거 한번만 봐주세여

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DowntimeBottomSheetView.swift`:
- Around line 52-59: Clamp the initial selectedDowntime to the picker's allowed
range instead of assigning treatment.downtimeMax directly: in the .onAppear
block (the closure that currently sets selectedDowntime = treatment.downtimeMax,
betweenDays, rate) replace that assignment with a clamped value (e.g.,
min(max(treatment.downtimeMax, 1), 30) or use the actual CherrishPicker range)
so the initial selectedDowntime always falls inside CherrishPicker(range:
1...30); keep the rest of the onAppear logic (betweenDays, rate) unchanged.

In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DownTimeSettingView.swift`:
- Around line 32-44: The TreatmentRowView is passed a read-only Binding via
.constant(...) for isSelected which causes a runtime warning when
TreatmentCheckBoxView calls isSelected.toggle(); replace the .constant(...) with
a two-way Binding(get:set:) that returns whether selectedTreatment == treatment
and in the setter sets selectedTreatment = newValue ? treatment : nil (or clears
it appropriately) so toggling updates selectedTreatment; update the call site
where TreatmentRowView(...) is constructed to use this Binding and ensure
TreatmentCheckBoxView's isSelected.toggle() operates on that writable Binding.
♻️ Duplicate comments (2)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/TargetDdaySettingView.swift (1)

50-51: 반응형 높이 설정 누락

다른 Spacer들은 모두 .adjustedH를 사용하고 있는데, 이 부분만 40으로 하드코딩되어 있습니다. 화면 크기에 따른 일관된 레이아웃을 위해 수정이 필요합니다.

🔧 수정 제안
 Spacer()
-    .frame(height: 40)
+    .frame(height: 40.adjustedH)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DowntimeBottomSheetView.swift (1)

175-206: 버튼 폭 계산이 외부 패딩을 고려하지 않아 오버플로우 위험

GeometryReader 기준 폭으로 버튼 너비를 계산한 뒤 외부 패딩을 적용하면 전체 폭이 초과될 수 있습니다. 패딩을 내부로 옮기고 계산 폭에서 양쪽 패딩을 제외해 주세요.

🛠️ 제안 수정
 private var buttonView: some View {
     GeometryReader { geo in
+        let total = geo.size.width - 48.adjustedW
         HStack(spacing: 4.adjustedW) {
             CherrishButton(
                 title: "다운타임 없이 일정 추가",
                 type: .addEvent,
                 state: .constant(
                     .normal
                 ),
                 leadingIcon: .none,
                 trailingIcon: .none,
                 action: { })
-            .frame(width: geo.size.width * 2/3 - 2)
+            .frame(width: total * 2/3 - 2)
 
             CherrishButton(
                 title: "확인",
                 type: .small,
                 state: .constant(
                     .normal
                 ),
                 leadingIcon: .none,
                 trailingIcon: .none,
                 action: { })
             .frame(
-                width: geo.size.width * 1/3 - 2
+                width: total * 1/3 - 2
             )
 
         }
+        .padding(.horizontal, 24.adjustedW)
     }
     .frame(height: 50.adjustedH)
-    .padding(.horizontal, 24.adjustedW)
 }

Comment on lines +52 to 59
.onAppear {
selectedDowntime = treatment.downtimeMax
betweenDays = Date.daysBetween(from: setday, to: today) ?? 0
rate = betweenDays > 0 ? min(Double(selectedDowntime) / Double(betweenDays), 1.0) : 1.0
}
.onChange(of: selectedDowntime) {
rate = betweenDays > 0 ? min(Double(selectedDowntime) / Double(betweenDays), 1.0) : 1.0
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and examine the CherrishPicker component
fd -t f CherrishPicker

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 196


🏁 Script executed:

# Also find the Treatment model to check downtimeMax constraints
fd -t f Treatment

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 2047


🏁 Script executed:

# Let's examine the full DowntimeBottomSheetView file
fd -t f DowntimeBottomSheetView

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 176


🏁 Script executed:

# Examine the DowntimeBottomSheetView file
cat -n "Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DowntimeBottomSheetView.swift"

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 8107


🏁 Script executed:

# Search for CherrishPicker definition
rg "struct CherrishPicker|class CherrishPicker" -t swift -A 20

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 2366


🏁 Script executed:

# Examine Treatment model
cat -n "Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Treatment/Model/Treatment.swift"

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 1674


🏁 Script executed:

# Search for TreatmentEntity definition
cat -n "Cherrish-iOS/Cherrish-iOS/Domain/Model/TreatmentEntity.swift"

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 3569


🏁 Script executed:

# Also search for CherrishPicker full implementation
cat -n "Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishPicker.swift"

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 3984


초기 선택값이 피커 범위를 벗어날 수 있습니다.

selectedDowntimetreatment.downtimeMax로 직접 설정하면, downtimeMaxCherrishPicker(range: 1...30) 범위를 초과하는 경우 선택기가 정상적으로 작동하지 않을 수 있습니다. 초기값을 범위 내로 클램핑해 주세요.

🛠️ 제안 수정
-            selectedDowntime = treatment.downtimeMax
+            selectedDowntime = min(max(treatment.downtimeMax, 1), 30)
📝 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
.onAppear {
selectedDowntime = treatment.downtimeMax
betweenDays = Date.daysBetween(from: setday, to: today) ?? 0
rate = betweenDays > 0 ? min(Double(selectedDowntime) / Double(betweenDays), 1.0) : 1.0
}
.onChange(of: selectedDowntime) {
rate = betweenDays > 0 ? min(Double(selectedDowntime) / Double(betweenDays), 1.0) : 1.0
}
.onAppear {
selectedDowntime = min(max(treatment.downtimeMax, 1), 30)
betweenDays = Date.daysBetween(from: setday, to: today) ?? 0
rate = betweenDays > 0 ? min(Double(selectedDowntime) / Double(betweenDays), 1.0) : 1.0
}
.onChange(of: selectedDowntime) {
rate = betweenDays > 0 ? min(Double(selectedDowntime) / Double(betweenDays), 1.0) : 1.0
}
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DowntimeBottomSheetView.swift`
around lines 52 - 59, Clamp the initial selectedDowntime to the picker's allowed
range instead of assigning treatment.downtimeMax directly: in the .onAppear
block (the closure that currently sets selectedDowntime = treatment.downtimeMax,
betweenDays, rate) replace that assignment with a clamped value (e.g.,
min(max(treatment.downtimeMax, 1), 30) or use the actual CherrishPicker range)
so the initial selectedDowntime always falls inside CherrishPicker(range:
1...30); keep the rest of the onAppear logic (betweenDays, rate) unchanged.

Comment on lines +32 to +44
TreatmentRowView(
displayMode: .completeBoxView,
treatmentEntity: treatment,
isSelected: .constant(
selectedTreatment == treatment
),
isCompleted: .constant(
false
),
action: {
selectedTreatment = treatment

})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "DownTimeSettingView.swift" | head -5

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 174


🏁 Script executed:

cat -n ./Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DownTimeSettingView.swift

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 3800


🏁 Script executed:

find . -type f -name "*.swift" | xargs rg "struct TreatmentRowView" -l

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 161


🏁 Script executed:

cat -n ./Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/Treatment/TreatmentRowView.swift

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 6911


읽기 전용 Binding으로 인해 토글 시 런타임 경고 유발

TreatmentCheckBoxView에서 isSelected.toggle()을 호출하는데(167줄), .constant() 바인딩은 읽기 전용이므로 런타임 경고를 유발합니다. 선택/해제 토글이 제대로 작동하도록 Binding(get:set:)으로 수정하세요.

🛠️ 제안 수정
-                        isSelected: .constant(
-                            selectedTreatment == treatment
-                        ),
+                        isSelected: Binding(
+                            get: { selectedTreatment == treatment },
+                            set: { newValue in
+                                selectedTreatment = newValue ? treatment : nil
+                            }
+                        ),
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/DownTimeSettingView.swift`
around lines 32 - 44, The TreatmentRowView is passed a read-only Binding via
.constant(...) for isSelected which causes a runtime warning when
TreatmentCheckBoxView calls isSelected.toggle(); replace the .constant(...) with
a two-way Binding(get:set:) that returns whether selectedTreatment == treatment
and in the setter sets selectedTreatment = newValue ? treatment : nil (or clears
it appropriately) so toggling updates selectedTreatment; update the call site
where TreatmentRowView(...) is constructed to use this Binding and ensure
TreatmentCheckBoxView's isSelected.toggle() operates on that writable Binding.

@wotjs020708 wotjs020708 merged commit af7620e into develop Jan 19, 2026
1 check passed
@wotjs020708 wotjs020708 deleted the feat/#93-downtimemodelview branch January 19, 2026 15:26
Kimgyuilli pushed a commit that referenced this pull request Jan 21, 2026
y-eonee pushed a commit that referenced this pull request Jan 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature 새로운 기능 구현 및 API 연결 재선🐻

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 다운타임 모달 뷰 연결

4 participants