Skip to content

Feat/#129 경고문 구현#144

Merged
y-eonee merged 4 commits intodevelopfrom
feat/#129-datetextfieldwarningtext
Jan 21, 2026
Merged

Feat/#129 경고문 구현#144
y-eonee merged 4 commits intodevelopfrom
feat/#129-datetextfieldwarningtext

Conversation

@wotjs020708
Copy link
Contributor

🔗 연결된 이슈

📄 작업 내용

  • 경고문 구현
구현 내용

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

coderabbitai bot commented Jan 21, 2026

📝 Walkthrough

Walkthrough

날짜 입력 유효성 검증을 위한 경고 메시지 시스템을 추가했습니다. 새로운 TreatmentInputWarning 열거형으로 유효하지 않은 입력을 분류하고, 뷰 모델의 검증 로직을 확장하며, 경고 메시지를 표시하는 UI 컴포넌트를 통합했습니다.

Changes

코호트 / 파일 변경 요약
Model - 경고 상태 정의
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Model/TreatmentInputWarning.swift
새로운 TreatmentInputWarning 열거형 추가: none, pastDate, invalidFormat, beforeProcedureDate 케이스와 로컬라이즈된 한국어 메시지를 반환하는 message 계산 속성
View Models - 검증 로직 강화
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel.swift, Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel.swift
@Published private(set) var warning 상태 추가, 날짜 유효성 검증 로직 개선 (정수 파싱, 과거 날짜 확인), updateWarning(state:) 헬퍼 메서드 추가
Views - 경고 메시지 바인딩
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/TargetDdaySettingView.swift, Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/NoTreatment/NoTreatmentView.swift, Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentView.swift
TargetDdaySettingViewwarningMessage: Binding<String> 속성 추가, 호출 지점에서 뷰 모델의 경고 메시지 바인딩 전달
UI Component - 경고 메시지 표시
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/TreatmentWarningMessgeView.swift
새로운 TreatmentWarningMessgeView 컴포넌트 추가: 빨간색 스타일의 경고 메시지 텍스트를 가로 스택에 렌더링
기타
Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/HomeAPI.swift
공백 라인 추가 (형식 조정)

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant View as TargetDdaySettingView
    participant ViewModel as NoTreatment/<br/>TreatmentViewModel
    participant Model as TreatmentInputWarning
    participant UI as TreatmentWarningMessgeView

    User->>View: 날짜 입력 (year/month/day)
    View->>ViewModel: isDateTextFieldNotEmpty 호출
    
    alt 날짜 필드 비어있음
        ViewModel->>ViewModel: updateWarning(.none)
        ViewModel->>Model: warning = .none
    else 정수 파싱 실패
        ViewModel->>ViewModel: updateWarning(.invalidFormat)
        ViewModel->>Model: warning = .invalidFormat
    else 과거 날짜
        ViewModel->>ViewModel: updateWarning(.pastDate)
        ViewModel->>Model: warning = .pastDate
    else 유효한 날짜
        ViewModel->>ViewModel: updateWarning(.none)
        ViewModel->>Model: warning = .none
    end
    
    ViewModel-->>View: warning 상태 업데이트 (`@Published`)
    View->>View: warningMessage 바인딩 업데이트
    View->>UI: TreatmentWarningMessgeView(text: message)
    UI-->>User: 경고 메시지 표시 (빨간색)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 분

Possibly related PRs

  • PR #68: 캘린더/치료 UI 기반 제공 — 이 PR에서 추가된 TreatmentInputWarning 열거형과 경고 바인딩이 해당 PR의 구성 요소(TargetDdaySettingView, NoTreatmentView, TreatmentView 및 뷰 모델)에 직접 통합됨
  • PR #83: 동일한 Treatment 흐름 파일 수정 — 이 PR의 경고 상태 추가와 바인딩 변경이 PR #83에서 도입된 TreatmentViewModel, TargetDdaySettingView와 같은 파일에 영향을 미침
  • PR #107: Treatment 뷰 모델 변경 — 이 PR이 추가하는 TreatmentInputWarning 열거형과 경고 헬퍼 메서드가 PR #107에서 도입된 동일한 뷰/뷰 모델 파일을 수정함

Suggested reviewers

  • sum130
  • y-eonee
  • soseoyo12
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
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.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 경고문 구현이라는 주요 변경사항을 명확하게 요약하고 있으며, 변경 사항의 핵심을 잘 나타냅니다.
Description check ✅ Passed PR 설명이 경고문 구현 작업 내용과 관련되어 있으며, 이슈 #129와의 연결을 명시하고 스크린샷으로 시각적 증거를 제공합니다.
Linked Issues check ✅ Passed 변경 사항들이 이슈 #129의 요구사항인 날짜 유효성 검사 경고문 추가를 충족합니다. TreatmentInputWarning 열거형, 경고 메시지 뷰, 유효성 검사 로직이 모두 구현되어 있습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 이슈 #129의 경고문 구현 요구사항과 관련이 있으며, 범위를 벗어난 변경사항은 보이지 않습니다. HomeAPI.swift의 공백 줄 추가는 미미한 형식 변경입니다.

✏️ 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/#129-datetextfieldwarningtext

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

🤖 Fix all issues with AI agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/TreatmentWarningMessgeView.swift`:
- Around line 10-16: Rename the misspelled view struct and its usages: change
TreatmentWarningMessgeView to TreatmentWarningMessageView (update the type name
and the file name accordingly) and update any references in
TargetDdaySettingView to use the corrected type; also remove the rigid
.frame(height: 20.adjustedH) on TypographyText (or replace it with a flexible
constraint such as .frame(minHeight: <appropriate value>.adjustedH) or rely on
intrinsic size) so the warning text can wrap/expand vertically and avoid
clipping.

In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel.swift`:
- Around line 123-134: The current guard in NoTreatmentViewModel that validates
the parsed Ints and constructed DateComponents should not rely on DateComponents
equality; instead after creating date via Calendar.current.date(from:
components) extract let actual = Calendar.current.dateComponents([.year, .month,
.day], from: date) and compare actual.year == y && actual.month == m &&
actual.day == d (keeping the same updateWarning(state: .invalidFormat) return
false flow if the check fails); reference the existing variables year, month,
day, components, date and the updateWarning(state:) call when making this
change.

In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel.swift`:
- Around line 115-121: The equality check between the constructed DateComponents
(components) and Calendar.current.dateComponents(...) is flawed because
calendar/timeZone differences make == fail for otherwise valid dates; in
TreatmentViewModel.swift replace that equality check by verifying the date was
actually created and/or comparing only the numeric fields or using Calendar
methods: e.g., ensure Calendar.current.date(from: components) returns non-nil
(date != nil) and then either extract year/month/day from
Calendar.current.dateComponents(...) and compare their integer values to y/m/d,
or use Calendar.current.isDate(_:equalTo:toGranularity:) to confirm the same day
before calling updateWarning(state: .invalidFormat).

Comment on lines +10 to +16
struct TreatmentWarningMessgeView: View {
let text: String
var body: some View {
HStack(spacing: 0) {
TypographyText(text, style: .body1_r_14, color: .red700)
.frame(height: 20.adjustedH)
Spacer()
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:

fd -t f "TreatmentWarningMessgeView" --exec cat -n {} \;

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 600


🏁 Script executed:

find . -name "*TreatmentWarning*" -type f

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 175


🏁 Script executed:

rg -l "TreatmentWarningMessgeView" --type swift

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 272


🏁 Script executed:

rg "TreatmentWarningMessgeView" --type swift

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 524


🏁 Script executed:

rg "body1_r_14" --type swift -A 2 -B 2

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 14796


파일명 및 타입명의 오타 수정이 필요합니다.

TreatmentWarningMessgeView는 오타입니다(Message → Messge). 파일명과 타입명 모두 정정이 필요하며, TargetDdaySettingView.swift의 사용처도 함께 수정해야 합니다.

또한 고정 높이 20.adjustedH는 줄바꿈이나 큰 글꼴 표시 시 텍스트 잘림을 초래할 수 있습니다. 최소 높이로 변경하여 세로 확장을 허용하는 것이 안전합니다.

🛠️ 제안 수정안
-            TypographyText(text, style: .body1_r_14, color: .red700)
-                .frame(height: 20.adjustedH)
+            TypographyText(text, style: .body1_r_14, color: .red700)
+                .fixedSize(horizontal: false, vertical: true)
+                .frame(minHeight: 20.adjustedH, alignment: .leading)
📝 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
struct TreatmentWarningMessgeView: View {
let text: String
var body: some View {
HStack(spacing: 0) {
TypographyText(text, style: .body1_r_14, color: .red700)
.frame(height: 20.adjustedH)
Spacer()
struct TreatmentWarningMessgeView: View {
let text: String
var body: some View {
HStack(spacing: 0) {
TypographyText(text, style: .body1_r_14, color: .red700)
.fixedSize(horizontal: false, vertical: true)
.frame(minHeight: 20.adjustedH, alignment: .leading)
Spacer()
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/TreatmentWarningMessgeView.swift`
around lines 10 - 16, Rename the misspelled view struct and its usages: change
TreatmentWarningMessgeView to TreatmentWarningMessageView (update the type name
and the file name accordingly) and update any references in
TargetDdaySettingView to use the corrected type; also remove the rigid
.frame(height: 20.adjustedH) on TypographyText (or replace it with a flexible
constraint such as .frame(minHeight: <appropriate value>.adjustedH) or rely on
intrinsic size) so the warning text can wrap/expand vertically and avoid
clipping.

Comment on lines +123 to +134
guard let y = Int(year), let m = Int(month), let d = Int(day) else {
updateWarning(state: .invalidFormat)
return false
}

let components = DateComponents(year: yearInt, month: monthInt, day: dayInt)
let components = DateComponents(year: y, month: m, day: d)

guard let date = Calendar.current.date(from: components),
Calendar.current.dateComponents([.year, .month, .day], from: date) == components else {
updateWarning(state: .invalidFormat)
return false
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

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

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 186


🏁 Script executed:

fd "NoTreatmentViewModel.swift" --type f

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 184


🏁 Script executed:

wc -l Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel.swift

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 188


🏁 Script executed:

sed -n '120,140p' Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel.swift | cat -n

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 936


🌐 Web query:

Swift DateComponents equality comparison calendar timeZone issue

💡 Result:

Short answer

  • DateComponents' synthesized equality compares the components/metadata actually stored in the struct (year/month/day/hour/… plus calendar/timeZone/etc.), so two DateComponents that “represent the same instant” can be unequal if they differ in which fields are set or in calendar/timeZone. [1][2]
  • To compare dates reliably, convert to Date using a known Calendar/timeZone (Calendar.date(from:)) and compare the Date values, or compare only the specific fields you care about after normalizing calendar/timeZone. [3][2]

Why this happens (brief)

  • Calendar.dateComponents(in:from:) often fills many fields (weekday, weekOfYear, yearForWeekOfYear, etc.). If you then mutate one field (e.g. month) the other fields become inconsistent, and equality (or converting back to Date) can give surprising results. The calendar/timeZone stored on DateComponents also affects conversions. [1][2]

What to do (recommended)

  • Normalize before comparing: set the same calendar and timeZone explicitly, and only set/compare the fields you mean (e.g. year/month/day). Or:
    • Best: let a Calendar produce Dates and compare Dates: let d1 = calendar.date(from: dc1); let d2 = calendar.date(from: dc2); compare d1 == d2.
    • Or implement comparison that compares only the subset of fields you need. [3][2]

Sources

  • Swift Forums discussion explaining extra filled fields and inconsistency. [1]
  • Foundation Calendar implementation/docs noting calendar/timeZone precedence and how unspecified fields are assumed. [2]
  • StackOverflow examples recommending converting DateComponents to Date (or normalizing) for reliable comparison. [3]

DateComponents 동등 비교로 정상 입력이 실패할 수 있습니다.

현재 코드의 Calendar.current.dateComponents([.year, .month, .day], from: date) == components 비교는 신뢰할 수 없습니다. DateComponents 동등 비교는 설정된 필드뿐 아니라 calendar, timeZone, 기타 메타데이터를 포함하여 비교하므로, 정상적으로 생성된 날짜도 이 검사에서 실패할 수 있습니다.

제안된 수정안처럼 날짜 생성과 필드 추출을 분리하고 연/월/일 필드를 직접 비교하세요.

🛠️ 제안 수정안
-        let components = DateComponents(year: y, month: m, day: d)
-        guard let date = Calendar.current.date(from: components),
-              Calendar.current.dateComponents([.year, .month, .day], from: date) == components else {
-            updateWarning(state: .invalidFormat)
-            return false
-        }
+        let calendar = Calendar.current
+        let components = DateComponents(year: y, month: m, day: d)
+        guard let date = calendar.date(from: components) else {
+            updateWarning(state: .invalidFormat)
+            return false
+        }
+        let normalized = calendar.dateComponents([.year, .month, .day], from: date)
+        guard normalized.year == y, normalized.month == m, normalized.day == d else {
+            updateWarning(state: .invalidFormat)
+            return false
+        }
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel.swift`
around lines 123 - 134, The current guard in NoTreatmentViewModel that validates
the parsed Ints and constructed DateComponents should not rely on DateComponents
equality; instead after creating date via Calendar.current.date(from:
components) extract let actual = Calendar.current.dateComponents([.year, .month,
.day], from: date) and compare actual.year == y && actual.month == m &&
actual.day == d (keeping the same updateWarning(state: .invalidFormat) return
false flow if the check fails); reference the existing variables year, month,
day, components, date and the updateWarning(state:) call when making this
change.

Comment on lines +115 to +121
let components = DateComponents(year: y, month: m, day: d)

guard let date = Calendar.current.date(from: components),
Calendar.current.dateComponents([.year, .month, .day], from: date) == components else {
updateWarning(state: .invalidFormat)
return false
}
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

🌐 Web query:

Swift DateComponents Equatable equality comparison calendar timeZone

💡 Result:

Short answer

  • DateComponents is just a bag of optional component values plus optional calendar/timeZone metadata. Which equality you get depends on how you compare them:
    • If you compare the raw stored fields (or synthesize Equatable yourself) you are comparing the components and any calendar/timeZone properties directly.
    • If you want to compare the actual moment in time those components represent, you must resolve them to a Date with a Calendar (and that Calendar's timeZone, or the DateComponents.timeZone if set) and compare the resulting Date(s). (nshipster.com)

What that means in practice

  • DateComponents does not magically mean “same instant” when two instances have different combinations of fields, calendars or time zones. Example differences include missing fields, different calendars, or different timeZones producing different Dates when fed to Calendar.date(from:). (nshipster.com)
  • Calendar methods (date(from:), nextDate(matching:), dateComponents(in:from:), etc.) use the Calendar (and its timeZone) to interpret components; if DateComponents.timeZone is set it overrides the calendar’s timeZone for that conversion. Use a Calendar with the desired .timeZone when you need a specific interpretation. (fuchsia.googlesource.com)

Recommended approaches

  • To test “same instant”: convert both to Date using the same Calendar (and timeZone) and compare the Dates.
    • e.g. let d1 = calendar.date(from: dc1); let d2 = calendar.date(from: dc2); then compare d1 == d2. (fuchsia.googlesource.com)
  • To test “same components + same metadata”: compare the stored properties (year/month/day/hour/… plus calendar/timeZone) — either manually or by implementing/synthesizing Equatable for DateComponents in the way you need. (stackoverflow.com)

If you want, I can:

  • show example code for both strategies (raw-field Equatable vs. calendar-resolved Date equality), or
  • help pick the right approach for your use case.

Citations:


🏁 Script executed:

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

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 182


🏁 Script executed:

cat -n ./Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel.swift | sed -n '105,130p'

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 1094


DateComponents 동등성 비교가 유효한 날짜를 invalid로 처리할 수 있습니다.

라인 115에서 생성한 DateComponents(year: y, month: m, day: d)calendartimeZone이 nil이지만, 라인 118의 Calendar.current.dateComponents(...)로 추출한 DateComponents는 Calendar.current의 calendar와 timeZone이 채워집니다. DateComponents의 == 연산자는 모든 필드(calendar/timeZone 포함)를 비교하므로, 실제로는 유효한 날짜도 비교에 실패하여 .invalidFormat으로 잘못 처리될 수 있습니다.

🔧 제안 수정안
-        guard let date = Calendar.current.date(from: components),
-              Calendar.current.dateComponents([.year, .month, .day], from: date) == components else {
+        guard let date = Calendar.current.date(from: components) else {
+            updateWarning(state: .invalidFormat)
+            return false
+        }
+        let validated = Calendar.current.dateComponents([.year, .month, .day], from: date)
+        guard validated.year == y, validated.month == m, validated.day == d else {
             updateWarning(state: .invalidFormat)
             return false
         }
📝 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
let components = DateComponents(year: y, month: m, day: d)
guard let date = Calendar.current.date(from: components),
Calendar.current.dateComponents([.year, .month, .day], from: date) == components else {
updateWarning(state: .invalidFormat)
return false
}
let components = DateComponents(year: y, month: m, day: d)
guard let date = Calendar.current.date(from: components) else {
updateWarning(state: .invalidFormat)
return false
}
let validated = Calendar.current.dateComponents([.year, .month, .day], from: date)
guard validated.year == y, validated.month == m, validated.day == d else {
updateWarning(state: .invalidFormat)
return false
}
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel.swift`
around lines 115 - 121, The equality check between the constructed
DateComponents (components) and Calendar.current.dateComponents(...) is flawed
because calendar/timeZone differences make == fail for otherwise valid dates; in
TreatmentViewModel.swift replace that equality check by verifying the date was
actually created and/or comparing only the numeric fields or using Calendar
methods: e.g., ensure Calendar.current.date(from: components) returns non-nil
(date != nil) and then either extract year/month/day from
Calendar.current.dateComponents(...) and compare their integer values to y/m/d,
or use Calendar.current.isDate(_:equalTo:toGranularity:) to confirm the same day
before calling updateWarning(state: .invalidFormat).

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.

어푸ㅜㅍ

@y-eonee y-eonee merged commit a982fa1 into develop Jan 21, 2026
4 checks passed
@y-eonee y-eonee deleted the feat/#129-datetextfieldwarningtext branch January 21, 2026 14:18
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