📝 구현하려는 로직
독서 계획을 세울 때, 책 제목은 2글자 이상, 독서 시간은 5분 이상이어야지만 일정 생성 버튼이 활성화
(활성화 시 배경색이 green으로 변경)

근데 지금 문제는 다음과 같다.
아래의 유효성 검사를 당하는 주체가 newCreatedItem 인데, 지금 이 주체는 저장을 해야하지만 활성화된다.
따라서 이런 유효성 검사를 하면 계속 비활성화가 된다.


따라서 AddPlanViewController에 있는 readItem을 ViewModel.newCreatedItem에 연결해야한다.

📌 MVVM + Combine 패턴에서 saveButton이 비활성화되는 문제 해결
✅ readItem과 newCreatedItem을 실시간으로 동기화해야 유효성 검사가 정상적으로 동작함.
✅ readItem에서 값이 변경될 때 newCreatedItem도 업데이트하고, validReadItemForm()을 실행하면 해결 가능!
🔴 saveButton이 계속 비활성화되는 이유
- validReadItemForm()이 newCreatedItem을 검사하지만, AddPlanViewController에서 readItem을 직접 사용 중.
- readItem이 변경되지만 newCreatedItem과 연결되지 않음 → isFormValid가 업데이트되지 않음.
- validReadItemForm()이 실행되지 않아 isFormValid 값이 변경되지 않음.
- 결과적으로 saveButton.isEnabled = false 상태가 계속 유지됨.
✅ 해결 방법: readItem과 newCreatedItem을 실시간으로 동기화
👉 방법: readItem이 변경될 때 newCreatedItem도 업데이트 + validReadItemForm() 실행
👉 Combine의 Published를 활용하여 실시간 데이터 바인딩
class ReadItemViewModel: ObservableObject {
// MARK: - Variables
// ✅ newCreatedItem에 기본 값 설정
@Published var newCreatedItem: ReadItemModel = ReadItemModel(
title: "",
startDate: Date(),
endDate: Date(),
dailyReadingTime: 0,
isCompleted: false
)
@Published var readItems: [ReadItemModel] = []
@Published var errorMessage: String?
@Published var isFormValid: Bool = false
private var cancellables: Set<AnyCancellable> = []
private let coredataManager = CoreDataManager.shared
// MARK: - Init
// ✅ init을 통해 유효성 검사 실행
init() {
setupBindings()
}
/// ✅ 유효성 검사 진행 - (newCreatedItem이 변경될 때 실행)
private func setupBindings() {
$newCreatedItem
.sink { [weak self] _ in
self?.validReadItemForm()
}
.store(in: &cancellables)
}
// MARK: - Function: 유효성 검사
func validReadItemForm() {
guard newCreatedItem.title.count >= 2,
newCreatedItem.dailyReadingTime >= 300 else {
isFormValid = false
print("❌ 유효성 검사 실패: 제목 2자 이상, 독서 시간 1분 이상 필요")
return
}
isFormValid = true
print("✅ 유효성 검사 통과")
}
📌 AddPlanViewController은 추후에 수정(Updated) 작업 시 사용할 예정 -> 이 점 고려
class AddPlanViewController: UIViewController {
// MARK: - Variables
private var viewModel: ReadItemViewModel = ReadItemViewModel()
private var cancellables: Set<AnyCancellable> = []
private var readItem: ReadItemModel
private var isFormValid: Bool = false
// MARK: - init
// ✅ 독서 계획을 수정할 경우 데이터를 받아 처리하고 그게 아니라면 새 데이터 생성
init(readItem: ReadItemModel? = nil) {
self.readItem = readItem ?? ReadItemModel(
title: "",
startDate: Date(),
endDate: Date(),
dailyReadingTime: 0,
isCompleted: false
)
super.init(nibName: nil, bundle: nil)
// ✅ viewModel.newCreatedItem과 연결
self.viewModel.newCreatedItem = self.readItem
}
viewModel의 isFormValid를 구독하여 실시간으로 데이터 반영
private func setupBinding() {
viewModel.$isFormValid
.sink { [weak self] isValid in
self?.saveButton.isEnabled = isValid
self?.saveButton.backgroundColor = isValid ? UIColor.systemGreen : UIColor.systemGray
print("🔄 saveButton 상태 변경: \(isValid ? "활성화됨" : "비활성화됨")")
}
.store(in: &cancellables)
}
각 셀에서부터 받아온 데이터를 viewModel의 newCreatedItem에 전달
// MARK: - Extension: BookCellDelegate
extension AddPlanViewController: BookCellDelegate {
func didUpdateTitle(_ title: String) {
//readItem.title = title
//self.validReadItemForm()
viewModel.newCreatedItem.title = title
viewModel.validReadItemForm()
}
}
// MARK: - Extension: TimeCellDelegate
extension AddPlanViewController: TimeCellDelegate {
func didUpdateReadingTime(_ time: Int) {
//readItem.dailyReadingTime = TimeInterval(time)
//self.validReadItemForm()
viewModel.newCreatedItem.dailyReadingTime = TimeInterval(time)
viewModel.validReadItemForm()
}
}
// MARK: - Extension: DateCellDelegate
extension AddPlanViewController: DateCellDelegate {
func didSelectedDate(with type: DateType, date: Date) {
switch type {
case .startDate:
//readItem.startDate = date
viewModel.newCreatedItem.startDate = date
viewModel.validReadItemForm()
case .endDate:
//readItem.endDate = date
viewModel.newCreatedItem.endDate = date
viewModel.validReadItemForm()
}
// selectedDates[type] = date
// printSelectedDates()
}
}

'Project > 30MinRead' 카테고리의 다른 글
🎉 타이머가 완료된 시점에 사용자에게 알림을 주기! (0) | 2025.03.23 |
---|---|
🔥 시작일 ➡️ 종료일 포함하여 날짜 계산! (0) | 2025.03.21 |
❌ 날짜 잘못 선택하면.. 오류창 띄워야 하는데.. (0) | 2025.03.20 |
❓ UICalendarSelectionMultiDate에서 특정 조건에서 선택을 막기! (UI 표시 X) (0) | 2025.03.17 |
📚 독서 타이머 일정 생성 앱 기획서 (0) | 2025.03.15 |