[Feat] 활동 일지 화면 UI 구현#91
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
Walkthrough배지 이미지 애셋과 그래픽 리소스가 추가되고, ActivityBadge 모델, ActivityHistoryViewModel, 배지 섹션/전문가 컴포넌트 뷰, 감정 캘린더 셀/뷰, ActivityHistoryViewController, EmotionDetailViewController가 신규 구현되었다. 탭바에 "활동 일지" 탭이 추가되고 DI 컨테이너에 뷰모델이 등록되었으며, Date 확장에 Changes활동 일지 기능 추가
Estimated code review effort: 3 (Moderate) | ~30 minutes Sequence Diagram(s)sequenceDiagram
participant ActivityHistoryViewController
participant ActivityHistoryViewModel
participant EmotionCalendarView
participant EmotionDetailViewController
ActivityHistoryViewController->>ActivityHistoryViewModel: fetchMonthlyBadge / fetchMonthlyEmotionHistory
ActivityHistoryViewModel-->>ActivityHistoryViewController: 배지 목록 퍼블리시
ActivityHistoryViewController->>ActivityHistoryViewController: badgeSectionView.configureBadge
ActivityHistoryViewModel-->>ActivityHistoryViewController: 감정 기록 딕셔너리 퍼블리시
ActivityHistoryViewController->>EmotionCalendarView: update(records)
EmotionCalendarView-->>ActivityHistoryViewController: dateSelected(date, emotion)
ActivityHistoryViewController->>EmotionDetailViewController: 생성 후 시트 프리젠트
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@Projects/Presentation/Sources/ActivityHistory/View/ActivityHistoryViewController.swift`:
- Around line 174-192: `ActivityHistoryViewController.bind()` currently
subscribes to selected dates and monthly data publishers, but it misses
`EmotionCalendarView.pageChanged`, so month navigation never triggers a refresh.
Add a Combine subscription to `emotionCalendarView.pageChanged` in `bind()` and
forward the new page/month to the ViewModel’s fetch/update path used in `init`
for the current `.now` month, so `monthlyBadgePublisher` and
`monthlyEmotionHistoryPublisher` reload when the user changes months.
In
`@Projects/Presentation/Sources/ActivityHistory/View/Component/ActivityBadgeExpertComponentView.swift`:
- Around line 68-74: In
ActivityBadgeExpertComponentView.configure(expertTitle:), the icon state is only
updated for "예비 전문가" and is never restored for other titles, so subsequent calls
can leave iconView stuck in template/white mode. Update configure(expertTitle:)
to always set iconView back to its default image rendering and tint before
applying the special-case styling, using the existing iconView, expertLabel, and
BitnagilIcon.shineIcon references to locate the change.
In
`@Projects/Presentation/Sources/ActivityHistory/View/EmotionDetailViewController.swift`:
- Around line 26-30: Remove the leftover debug print from
EmotionDetailViewController.viewDidLoad and finish wiring up the intended UI. In
viewDidLoad, delete the print of emotion.koreanDescription, then either add the
label property to the view hierarchy and configure it with the emotion/date data
or remove the unused label and date properties if this screen is not ready yet.
Keep the fix centered on EmotionDetailViewController and its viewDidLoad setup.
In
`@Projects/Presentation/Sources/ActivityHistory/ViewModel/ActivityHistoryViewModel.swift`:
- Around line 32-52: The `ActivityHistoryViewModel.action(input:)`
implementation is sending `monthlyBadgeSubject` and
`monthlyEmotionHistorySubject` from a `Task {}` that may run off the main
thread, which can push UI work into background execution through
`ActivityHistoryViewController.bind()`. Remove the unnecessary `Task` wrappers
in the `.fetchMonthlyBadge` and `.fetchMonthlyEmotionHistory` cases and send the
mock values directly on the main thread, or alternatively make the subscription
in `bind()` use `.receive(on: DispatchQueue.main)` before the `sink` that calls
`configureBadge` and `emotionCalendarView.update`.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: fc7fbc6c-6648-4945-bbc4-586930f54e63
⛔ Files ignored due to path filters (24)
Projects/Presentation/Resources/Images.xcassets/Badge/badge_background_graphic.imageset/badge_background_graphic.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/badge_background_graphic.imageset/badge_background_graphic@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/badge_background_graphic.imageset/badge_background_graphic@3x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/emotion_multiple_badge.imageset/emotion_multiple_badge.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/emotion_multiple_badge.imageset/emotion_multiple_badge@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/emotion_multiple_badge.imageset/emotion_multiple_badge@3x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/emotion_single_badge.imageset/emotion_single_badge.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/emotion_single_badge.imageset/emotion_single_badge@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/emotion_single_badge.imageset/emotion_single_badge@3x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/none_badge.imageset/none_badge.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/none_badge.imageset/none_badge@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/none_badge.imageset/none_badge@3x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/report_multiple_badge.imageset/report_multiple_badge.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/report_multiple_badge.imageset/report_multiple_badge@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/report_multiple_badge.imageset/report_multiple_badge@3x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/report_single_badge.imageset/report_single_badge.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/report_single_badge.imageset/report_single_badge@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/report_single_badge.imageset/report_single_badge@3x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/routine_multiple_badge.imageset/routine_multiple_badge.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/routine_multiple_badge.imageset/routine_multiple_badge@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/routine_multiple_badge.imageset/routine_multiple_badge@3x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/routine_single_badge.imageset/routine_single_badge.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/routine_single_badge.imageset/routine_single_badge@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/Badge/routine_single_badge.imageset/routine_single_badge@3x.pngis excluded by!**/*.png
📒 Files selected for processing (21)
Projects/Presentation/Resources/Images.xcassets/Badge/Contents.jsonProjects/Presentation/Resources/Images.xcassets/Badge/badge_background_graphic.imageset/Contents.jsonProjects/Presentation/Resources/Images.xcassets/Badge/emotion_multiple_badge.imageset/Contents.jsonProjects/Presentation/Resources/Images.xcassets/Badge/emotion_single_badge.imageset/Contents.jsonProjects/Presentation/Resources/Images.xcassets/Badge/none_badge.imageset/Contents.jsonProjects/Presentation/Resources/Images.xcassets/Badge/report_multiple_badge.imageset/Contents.jsonProjects/Presentation/Resources/Images.xcassets/Badge/report_single_badge.imageset/Contents.jsonProjects/Presentation/Resources/Images.xcassets/Badge/routine_multiple_badge.imageset/Contents.jsonProjects/Presentation/Resources/Images.xcassets/Badge/routine_single_badge.imageset/Contents.jsonProjects/Presentation/Sources/ActivityHistory/Model/ActivityBadge.swiftProjects/Presentation/Sources/ActivityHistory/View/ActivityHistoryViewController.swiftProjects/Presentation/Sources/ActivityHistory/View/Component/ActivityBadgeExpertComponentView.swiftProjects/Presentation/Sources/ActivityHistory/View/Component/ActivityBadgeSectionView.swiftProjects/Presentation/Sources/ActivityHistory/View/Component/EmotionCalendarCell.swiftProjects/Presentation/Sources/ActivityHistory/View/Component/EmotionCalendarView.swiftProjects/Presentation/Sources/ActivityHistory/View/EmotionDetailViewController.swiftProjects/Presentation/Sources/ActivityHistory/ViewModel/ActivityHistoryViewModel.swiftProjects/Presentation/Sources/Common/DesignSystem/BitnagilGraphic.swiftProjects/Presentation/Sources/Common/PresentationDependencyAssembler.swiftProjects/Presentation/Sources/Common/View/TabBarView.swiftProjects/Shared/Sources/Extension/Date+.swift
| override func bind() { | ||
| emotionCalendarView.dateSelected.sink { [weak self] date, emotion in | ||
| guard let emotion else { return } | ||
| self?.presentEmotionDetail(date: date, emotion: emotion) | ||
| } | ||
| .store(in: &cancellables) | ||
|
|
||
| viewModel.output.monthlyBadgePublisher.sink { [weak self] badges in | ||
| guard let self else { return } | ||
| self.badgeSectionView.configureBadge(badges: badges) | ||
| } | ||
| .store(in: &cancellables) | ||
|
|
||
| viewModel.output.monthlyEmotionHistoryPublisher.sink { [weak self] emotionRecords in | ||
| guard let self else { return } | ||
| self.emotionCalendarView.update(records: emotionRecords) | ||
| } | ||
| .store(in: &cancellables) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
pageChanged 퍼블리셔가 구독되지 않아 월 이동 시 데이터가 갱신되지 않음
EmotionCalendarView에서 pageChanged 퍼블리셔가 선언되고 calendarCurrentPageDidChange에서 send되지만, bind()에서 구독하지 않습니다. init에서 .now 기준으로만 데이터를 fetch하므로, 사용자가 이전/다음 달 버튼이나 인접 월 날짜를 탭하여 월을 이동해도 ViewModel에 새 데이터 요청이 전달되지 않습니다. 결과적으로 이동한 월의 배지 및 감정 기록이 stale하거나 비어 있게 됩니다.
🐛 제안 수정안
override func bind() {
emotionCalendarView.dateSelected.sink { [weak self] date, emotion in
guard let emotion else { return }
self?.presentEmotionDetail(date: date, emotion: emotion)
}
.store(in: &cancellables)
+ emotionCalendarView.pageChanged.sink { [weak self] date in
+ self?.viewModel.action(input: .fetchMonthlyBadge(date: date))
+ self?.viewModel.action(input: .fetchMonthlyEmotionHistory(date: date))
+ }
+ .store(in: &cancellables)
+
viewModel.output.monthlyBadgePublisher.sink { [weak self] badges in📝 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.
| override func bind() { | |
| emotionCalendarView.dateSelected.sink { [weak self] date, emotion in | |
| guard let emotion else { return } | |
| self?.presentEmotionDetail(date: date, emotion: emotion) | |
| } | |
| .store(in: &cancellables) | |
| viewModel.output.monthlyBadgePublisher.sink { [weak self] badges in | |
| guard let self else { return } | |
| self.badgeSectionView.configureBadge(badges: badges) | |
| } | |
| .store(in: &cancellables) | |
| viewModel.output.monthlyEmotionHistoryPublisher.sink { [weak self] emotionRecords in | |
| guard let self else { return } | |
| self.emotionCalendarView.update(records: emotionRecords) | |
| } | |
| .store(in: &cancellables) | |
| } | |
| override func bind() { | |
| emotionCalendarView.dateSelected.sink { [weak self] date, emotion in | |
| guard let emotion else { return } | |
| self?.presentEmotionDetail(date: date, emotion: emotion) | |
| } | |
| .store(in: &cancellables) | |
| emotionCalendarView.pageChanged.sink { [weak self] date in | |
| self?.viewModel.action(input: .fetchMonthlyBadge(date: date)) | |
| self?.viewModel.action(input: .fetchMonthlyEmotionHistory(date: date)) | |
| } | |
| .store(in: &cancellables) | |
| viewModel.output.monthlyBadgePublisher.sink { [weak self] badges in | |
| guard let self else { return } | |
| self.badgeSectionView.configureBadge(badges: badges) | |
| } | |
| .store(in: &cancellables) | |
| viewModel.output.monthlyEmotionHistoryPublisher.sink { [weak self] emotionRecords in | |
| guard let self else { return } | |
| self.emotionCalendarView.update(records: emotionRecords) | |
| } | |
| .store(in: &cancellables) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@Projects/Presentation/Sources/ActivityHistory/View/ActivityHistoryViewController.swift`
around lines 174 - 192, `ActivityHistoryViewController.bind()` currently
subscribes to selected dates and monthly data publishers, but it misses
`EmotionCalendarView.pageChanged`, so month navigation never triggers a refresh.
Add a Combine subscription to `emotionCalendarView.pageChanged` in `bind()` and
forward the new page/month to the ViewModel’s fetch/update path used in `init`
for the current `.now` month, so `monthlyBadgePublisher` and
`monthlyEmotionHistoryPublisher` reload when the user changes months.
| func configure(expertTitle: String) { | ||
| expertLabel.text = expertTitle | ||
| if expertTitle == "예비 전문가" { | ||
| iconView.image = BitnagilIcon.shineIcon?.withRenderingMode(.alwaysTemplate) | ||
| iconView.tintColor = .white | ||
| } | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
configure 호출 시 아이콘 상태가 리셋되지 않는 버그
configure(expertTitle:)에서 "예비 전문가"일 때 iconView를 template rendering + white tint로 변경하지만, 이후 다른 title로 호출될 때 아이콘을 원래 상태로 되돌리지 않습니다. monthlyBadgePublisher가 월별로 다른 배지 개수를 방출하면, "예비 전문가" → "능숙한 전문가" 순서로 호출될 때 아이콘이 여전히 template/white 상태로 남아 의도하지 않은 UI가 표시됩니다.
🐛 제안 수정안
func configure(expertTitle: String) {
expertLabel.text = expertTitle
if expertTitle == "예비 전문가" {
iconView.image = BitnagilIcon.shineIcon?.withRenderingMode(.alwaysTemplate)
iconView.tintColor = .white
+ } else {
+ iconView.image = BitnagilIcon.shineIcon
+ iconView.tintColor = nil
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| func configure(expertTitle: String) { | |
| expertLabel.text = expertTitle | |
| if expertTitle == "예비 전문가" { | |
| iconView.image = BitnagilIcon.shineIcon?.withRenderingMode(.alwaysTemplate) | |
| iconView.tintColor = .white | |
| } | |
| } | |
| func configure(expertTitle: String) { | |
| expertLabel.text = expertTitle | |
| if expertTitle == "예비 전문가" { | |
| iconView.image = BitnagilIcon.shineIcon?.withRenderingMode(.alwaysTemplate) | |
| iconView.tintColor = .white | |
| } else { | |
| iconView.image = BitnagilIcon.shineIcon | |
| iconView.tintColor = nil | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@Projects/Presentation/Sources/ActivityHistory/View/Component/ActivityBadgeExpertComponentView.swift`
around lines 68 - 74, In
ActivityBadgeExpertComponentView.configure(expertTitle:), the icon state is only
updated for "예비 전문가" and is never restored for other titles, so subsequent calls
can leave iconView stuck in template/white mode. Update configure(expertTitle:)
to always set iconView back to its default image rendering and tint before
applying the special-case styling, using the existing iconView, expertLabel, and
BitnagilIcon.shineIcon references to locate the change.
| override func viewDidLoad() { | ||
| super.viewDidLoad() | ||
| print("\(emotion.koreanDescription)") | ||
| view.backgroundColor = .white | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
print 디버그 문과 미구현 UI
viewDidLoad에 print("\(emotion.koreanDescription)")가 남아있고, label 프로퍼티가 생성되었지만 view hierarchy에 추가되지 않았습니다. date 프로퍼티도 사용되지 않습니다. API 스펙 미확정으로 임시 구현일 수 있으나, print 문은 프로덕션 코드에서 제거해야 합니다.
🐛 제안 수정안
override func viewDidLoad() {
super.viewDidLoad()
- print("\(emotion.koreanDescription)")
view.backgroundColor = .white
}📝 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.
| override func viewDidLoad() { | |
| super.viewDidLoad() | |
| print("\(emotion.koreanDescription)") | |
| view.backgroundColor = .white | |
| } | |
| override func viewDidLoad() { | |
| super.viewDidLoad() | |
| view.backgroundColor = .white | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@Projects/Presentation/Sources/ActivityHistory/View/EmotionDetailViewController.swift`
around lines 26 - 30, Remove the leftover debug print from
EmotionDetailViewController.viewDidLoad and finish wiring up the intended UI. In
viewDidLoad, delete the print of emotion.koreanDescription, then either add the
label property to the view hierarchy and configure it with the emotion/date data
or remove the unused label and date properties if this screen is not ready yet.
Keep the fix centered on EmotionDetailViewController and its viewDidLoad setup.
| func action(input: Input) { | ||
| switch input { | ||
| case .fetchMonthlyBadge(let date): | ||
| // TODO: 추후 서버 로직 붙이기 | ||
| Task { | ||
| let badges: [ActivityBadge] = [] | ||
| monthlyBadgeSubject.send(badges) | ||
| } | ||
| case .fetchMonthlyEmotionHistory(let date): | ||
| // TODO: 추후 서버 로직 붙이기 | ||
| Task { | ||
| let mockEmotionRecords: [String: Marble] = [ | ||
| "2026-07-01": .ANXIETY, | ||
| "2026-07-05": .FATIGUE, | ||
| "2026-07-11": .LETHARGY, | ||
| "2026-07-16": .VITALITY, | ||
| "2026-07-23": .CALM | ||
| ] | ||
| monthlyEmotionHistorySubject.send(mockEmotionRecords) | ||
| } | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
백그라운드 스레드에서 UI 업데이트가 발생할 수 있습니다.
ActivityHistoryViewModel은 @MainActor가 아니므로, Task {} 블록은 cooperative thread pool(백그라운드)에서 실행됩니다. CurrentValueSubject.send는 호출 스레드에서 값을 전달하므로, ActivityHistoryViewController.bind()의 sink 클로저가 백그라운드 스레드에서 configureBadge 및 emotionCalendarView.update 같은 UI 작업을 수행하게 됩니다. 이는 UIKit 스레딩 규칙 위반입니다.
현재 Task 내부에 await가 없으므로 동기적으로 실행되는 코드입니다. Task 래퍼를 제거하고 메인 스레드에서 직접 send를 호출하거나, 구독부에 .receive(on: DispatchQueue.main)를 추가하세요.
🔧 제안: Task 제거 및 메인 스레드 전송
func action(input: Input) {
switch input {
case .fetchMonthlyBadge(let date):
// TODO: 추후 서버 로직 붙이기
- Task {
- let badges: [ActivityBadge] = []
- monthlyBadgeSubject.send(badges)
- }
+ let badges: [ActivityBadge] = []
+ monthlyBadgeSubject.send(badges)
case .fetchMonthlyEmotionHistory(let date):
// TODO: 추후 서버 로직 붙이기
- Task {
- let mockEmotionRecords: [String: Marble] = [
- "2026-07-01": .ANXIETY,
- "2026-07-05": .FATIGUE,
- "2026-07-11": .LETHARGY,
- "2026-07-16": .VITALITY,
- "2026-07-23": .CALM
- ]
- monthlyEmotionHistorySubject.send(mockEmotionRecords)
- }
+ let mockEmotionRecords: [String: Marble] = [
+ "2026-07-01": .ANXIETY,
+ "2026-07-05": .FATIGUE,
+ "2026-07-11": .LETHARGY,
+ "2026-07-16": .VITALITY,
+ "2026-07-23": .CALM
+ ]
+ monthlyEmotionHistorySubject.send(mockEmotionRecords)
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| func action(input: Input) { | |
| switch input { | |
| case .fetchMonthlyBadge(let date): | |
| // TODO: 추후 서버 로직 붙이기 | |
| Task { | |
| let badges: [ActivityBadge] = [] | |
| monthlyBadgeSubject.send(badges) | |
| } | |
| case .fetchMonthlyEmotionHistory(let date): | |
| // TODO: 추후 서버 로직 붙이기 | |
| Task { | |
| let mockEmotionRecords: [String: Marble] = [ | |
| "2026-07-01": .ANXIETY, | |
| "2026-07-05": .FATIGUE, | |
| "2026-07-11": .LETHARGY, | |
| "2026-07-16": .VITALITY, | |
| "2026-07-23": .CALM | |
| ] | |
| monthlyEmotionHistorySubject.send(mockEmotionRecords) | |
| } | |
| } | |
| func action(input: Input) { | |
| switch input { | |
| case .fetchMonthlyBadge(let date): | |
| // TODO: 추후 서버 로직 붙이기 | |
| let badges: [ActivityBadge] = [] | |
| monthlyBadgeSubject.send(badges) | |
| case .fetchMonthlyEmotionHistory(let date): | |
| // TODO: 추후 서버 로직 붙이기 | |
| let mockEmotionRecords: [String: Marble] = [ | |
| "2026-07-01": .ANXIETY, | |
| "2026-07-05": .FATIGUE, | |
| "2026-07-11": .LETHARGY, | |
| "2026-07-16": .VITALITY, | |
| "2026-07-23": .CALM | |
| ] | |
| monthlyEmotionHistorySubject.send(mockEmotionRecords) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@Projects/Presentation/Sources/ActivityHistory/ViewModel/ActivityHistoryViewModel.swift`
around lines 32 - 52, The `ActivityHistoryViewModel.action(input:)`
implementation is sending `monthlyBadgeSubject` and
`monthlyEmotionHistorySubject` from a `Task {}` that may run off the main
thread, which can push UI work into background execution through
`ActivityHistoryViewController.bind()`. Remove the unnecessary `Task` wrappers
in the `.fetchMonthlyBadge` and `.fetchMonthlyEmotionHistory` cases and send the
mock values directly on the main thread, or alternatively make the subscription
in `bind()` use `.receive(on: DispatchQueue.main)` before the `sink` that calls
`configureBadge` and `emotionCalendarView.update`.
🌁 Background
활동 일지 화면 구현에 들어갔어요 ~
📱 Screenshot
👩💻 Contents
ActivityHistoryViewController구현📝 Review Note
Summary by CodeRabbit