Conversation
📝 WalkthroughWalkthrough날짜 입력 유효성 검증을 위한 경고 메시지 시스템을 추가했습니다. 새로운 Changes
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: 경고 메시지 표시 (빨간색)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 분 Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 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).
| 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() |
There was a problem hiding this comment.
🧩 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 fRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 175
🏁 Script executed:
rg -l "TreatmentWarningMessgeView" --type swiftRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 272
🏁 Script executed:
rg "TreatmentWarningMessgeView" --type swiftRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 524
🏁 Script executed:
rg "body1_r_14" --type swift -A 2 -B 2Repository: 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.
| 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.
| 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 | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "NoTreatmentViewModel.swift" | head -5Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 186
🏁 Script executed:
fd "NoTreatmentViewModel.swift" --type fRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 184
🏁 Script executed:
wc -l Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel.swiftRepository: 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 -nRepository: 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.
| 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 | ||
| } |
There was a problem hiding this comment.
🧩 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:
- 1: https://nshipster.com/datecomponents/?utm_source=openai
- 2: https://nshipster.com/datecomponents/?utm_source=openai
- 3: https://fuchsia.googlesource.com/third_party/swift-corelibs-foundation/%2B/refs/heads/upstream/google/Foundation/Calendar.swift?utm_source=openai
- 4: https://fuchsia.googlesource.com/third_party/swift-corelibs-foundation/%2B/refs/heads/upstream/google/Foundation/Calendar.swift?utm_source=openai
- 5: https://stackoverflow.com/questions/64515641/compare-datecomponents-in-swift?utm_source=openai
🏁 Script executed:
find . -name "TreatmentViewModel.swift" -type fRepository: 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)는 calendar와 timeZone이 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.
| 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).
🔗 연결된 이슈
📄 작업 내용