Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Projects/DataSource/Sources/DTO/RecommendedRoutineDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ struct RecommendedRoutineDTO: Decodable {
let routineName: String
let routineDescription: String
let routineLevel: String?
let routineType: String
let subRoutines: [RecommendedSubRoutineDTO]

enum CodingKeys: String, CodingKey {
case id = "recommendedRoutineId"
case routineName = "recommendedRoutineName"
case routineDescription = "recommendedRoutineDescription"
case routineLevel = "recommendedRoutineLevel"
case routineType = "recommendedRoutineType"
case subRoutines = "recommendedSubRoutineSearchResult"
}
}
Expand All @@ -39,6 +41,7 @@ extension RecommendedRoutineDTO {
title: routineName,
description: routineDescription,
category: routineCategory,
type: RoutineCategoryType(rawValue: routineType) ?? .rest,
level: level,
subRoutines: subRoutines.compactMap({ $0.toRecommendedSubRoutineEntity() }))
}
Expand Down
3 changes: 3 additions & 0 deletions Projects/Domain/Sources/Entity/RecommendedRoutineEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public struct RecommendedRoutineEntity {
public let title: String
public let description: String
public let category: RoutineCategoryType?
public let type: RoutineCategoryType
public let level: RoutineLevelType?
public let subRoutines: [RecommendedSubRoutineEntity]

Expand All @@ -18,13 +19,15 @@ public struct RecommendedRoutineEntity {
title: String,
description: String,
category: RoutineCategoryType?,
type: RoutineCategoryType,
level: RoutineLevelType?,
subRoutines: [RecommendedSubRoutineEntity]
) {
self.id = id
self.title = title
self.description = description
self.category = category
self.type = type
self.level = level
self.subRoutines = subRoutines
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import Domain
import SnapKit
import UIKit

protocol RoutineCardViewDelegate: AnyObject {
func routineCardView(_ sender: RoutineCardView, didTapPlusButton routine: RecommendedRoutine)
}

final class RoutineCardView: UIView {
private enum Layout {
static let horizontalMargin: CGFloat = 16
Expand All @@ -29,7 +33,7 @@ final class RoutineCardView: UIView {
}

private let headerInfoStackView = UIStackView()
private let categoryIconView = RoutineCategoryIcon(routineCategory: .connection)
private lazy var categoryIconView = RoutineCategoryIcon(routineCategory: routine.routineType)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

새로 수정된 코드에서 이 categoryIconView 관련 코드를 찾아볼 수 없었는데요! 혹시 lazy var로 설정해주신 이유를 알 수 있을까요?!?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

RoutineCardView에서만 CategoryIconView를 사용할 것 같아서 같은 파일 내부에 fileprivate으로 구현해두었습니다 !!!

그리고 RoutineCardView의 생성자로 들어온 routine의 카테고리 값으로 초기화 해야 해가주구 lazy var로 설정했습니다 !!!

private let titleLabel = UILabel()
private let editButton = UIButton()
private let deleteButton = UIButton()
Expand All @@ -38,7 +42,10 @@ final class RoutineCardView: UIView {
private let subRoutineLabel = UILabel()
private let subRoutineStackView = UIStackView()

init() {
private let routine: RecommendedRoutine
weak var delegate: RoutineCardViewDelegate?
init(routine: RecommendedRoutine) {
self.routine = routine
super.init(frame: .zero)
configureAttribute()
configureLayout()
Expand All @@ -56,14 +63,19 @@ final class RoutineCardView: UIView {
headerInfoStackView.axis = .horizontal
headerInfoStackView.spacing = Layout.headerInfoStackViewSpacing

titleLabel.text = "개운하게 일어나기"
titleLabel.text = routine.mainTitle
titleLabel.font = BitnagilFont(style: .body1, weight: .semiBold).font
titleLabel.textColor = BitnagilColor.gray10

let plusImage = BitnagilIcon.plusIcon?
.resizeAspectFit(to: CGSize(width: Layout.plusImageSize, height: Layout.plusImageSize))
plusButton.setImage(plusImage, for: .normal)
plusButton.tintColor = BitnagilColor.gray10
plusButton.addAction(
UIAction { [weak self] _ in
guard let self else { return }
delegate?.routineCardView(self, didTapPlusButton: routine)
}, for: .touchUpInside)

Comment on lines +74 to 79
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

컴파일 오류: 클로저 내부에서 self 명시 및 프로퍼티 접근자 누락

클로저 내부에서 self를 명시해야 하며, delegate/routine 접근 시에도 self.가 필요합니다. 현재 코드는 컴파일되지 않습니다.

아래와 같이 수정해주세요.

-        plusButton.addAction(
-            UIAction { [weak self] _ in
-                guard let self else { return }
-                delegate?.routineCardView(self, didTapPlusButton: routine)
-            }, for: .touchUpInside)
+        plusButton.addAction(
+            UIAction { [weak self] _ in
+                guard let self = self else { return }
+                self.delegate?.routineCardView(self, didTapPlusButton: self.routine)
+            },
+            for: .touchUpInside
+        )
📝 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
plusButton.addAction(
UIAction { [weak self] _ in
guard let self else { return }
delegate?.routineCardView(self, didTapPlusButton: routine)
}, for: .touchUpInside)
plusButton.addAction(
UIAction { [weak self] _ in
guard let self = self else { return }
self.delegate?.routineCardView(self, didTapPlusButton: self.routine)
},
for: .touchUpInside
)
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Common/Component/RoutineCardView.swift around
lines 74–79, the closure uses an implicit guard let self and accesses
delegate/routine without self, causing a compile error; change the guard to
"guard let self = self else { return }" and prefix property accesses with self
(e.g., self.delegate and self.routine) inside the closure so the code compiles
and captures self weakly correctly.

grayLine.backgroundColor = BitnagilColor.gray97

Expand All @@ -78,7 +90,7 @@ final class RoutineCardView: UIView {
}
subRoutineStackView.addArrangedSubview(subRoutineLabel)

["물 마시기", "물 마시기", "물 마시기"].forEach {
routine.subRoutines.forEach {
let subRoutineTitleLabel = UILabel()
subRoutineTitleLabel.text = "• \($0)"
subRoutineTitleLabel.font = BitnagilFont(style: .body2, weight: .medium).font
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,26 @@ public struct RecommendedRoutine: OnboardingChoiceProtocol, Hashable {
let id: Int
let mainTitle: String
let subTitle: String?
let subRoutines: [String]
let routineCategory: RoutineCategoryType
let routineType: RoutineCategoryType
let routineLevel: RoutineLevelType

init(
id: Int,
mainTitle: String,
subTitle: String?,
subRoutines: [String],
routineCategory: RoutineCategoryType,
routineType: RoutineCategoryType,
routineLevel: RoutineLevelType
) {
self.id = id
self.mainTitle = mainTitle
self.subTitle = subTitle
self.subRoutines = subRoutines
self.routineCategory = routineCategory
self.routineType = routineType
self.routineLevel = routineLevel
}
}
Expand All @@ -35,7 +41,9 @@ extension RecommendedRoutineEntity {
id: id,
mainTitle: title,
subTitle: description,
subRoutines: subRoutines.map({ $0.title }),
routineCategory: category ?? .recommendation,
routineType: type,
routineLevel: level ?? .easy
)
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout
static let headerStackViewHeight: CGFloat = 40
static let recommendedRoutineStackViewSpacing: CGFloat = 12
static let recommendedRoutineScrollViewTopSpacing: CGFloat = 12
static let recommendedRoutineScrollViewBottomSpacing: CGFloat = 10
static let recommendedRoutineStackViewBottomSpacing: CGFloat = 50
static let recommendedRoutineStackViewBottomSpacing: CGFloat = 65
static let routineCardHeight: CGFloat = 80
static let registerEmotionButtonTopSpacing: CGFloat = 20
static let registerEmotionButtonHeight: CGFloat = 66
Expand All @@ -44,7 +43,7 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout

private let recommendedRoutineScrollView = UIScrollView()
private let recommendedRoutineStackView = UIStackView()
private var recommendedRoutineCards: [Int: RecommendedRoutineCardView] = [:]
private var recommendedRoutineCards: [Int: RoutineCardView] = [:]
private let registerEmotionButton = RegisterEmotionButtonView()
private let recommendedRoutineEmptyView = RecommendedRoutineEmptyView()

Expand Down Expand Up @@ -167,7 +166,7 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout
make.leading.equalTo(safeArea).offset(Layout.horizontalMargin)
make.trailing.equalTo(safeArea).inset(Layout.horizontalMargin)
make.top.equalTo(headerStackView.snp.bottom).offset(Layout.recommendedRoutineScrollViewTopSpacing)
make.bottom.equalTo(safeArea).inset(Layout.recommendedRoutineScrollViewBottomSpacing)
make.bottom.equalTo(safeArea)
}

recommendedRoutineStackView.snp.makeConstraints { make in
Expand Down Expand Up @@ -200,6 +199,7 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout
.sink { [weak self] selectedCategory in
self?.categoryView.updateSelectedCategory(selectedCategory: selectedCategory)
self?.showEmotionButton(isShowEmotionButton: selectedCategory == .recommendation)
self?.recommendedRoutineScrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
.store(in: &cancellables)

Expand Down Expand Up @@ -231,17 +231,12 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout
}
recommendedRoutineCards.removeAll()

// let testView = RoutineCardView()
// recommendedRoutineStackView.addArrangedSubview(testView)
recommendedRoutineEmptyView.isHidden = !recommendedRoutines.isEmpty
for routine in recommendedRoutines {
let routineCard = RecommendedRoutineCardView(recommendedRoutine: routine)
recommendedRoutineCards[routine.id] = routineCard
recommendedRoutineStackView.addArrangedSubview(routineCard)
routineCard.delegate = self
routineCard.snp.makeConstraints { make in
make.height.equalTo(Layout.routineCardHeight)
}
let routineCardView = RoutineCardView(routine: routine)
recommendedRoutineCards[routine.id] = routineCardView
routineCardView.delegate = self
recommendedRoutineStackView.addArrangedSubview(routineCardView)
}
}

Expand Down Expand Up @@ -298,25 +293,14 @@ extension RecommendedRoutineViewController: RoutineCategoryViewDelegate {
}
}

// MARK: RecommendedRoutineCardViewDelegate
extension RecommendedRoutineViewController: RecommendedRoutineCardViewDelegate {
func recommendedRoutineCardView(_ sender: RecommendedRoutineCardView, didTapRecommendedRoutine routine: RecommendedRoutine) {
guard let routineCreationViewModel = DIContainer.shared.resolve(type: RoutineCreationViewModel.self)
else { fatalError("routineCreationViewModel 의존성이 등록되지 않았습니다.") }

let routineCreationView = RoutineCreationViewController(viewModel: routineCreationViewModel, recommendRoutineId: routine.id)
routineCreationView.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(routineCreationView, animated: true)
}
}

// MARK: SelectableItemTableViewDelegate
extension RecommendedRoutineViewController: SelectableItemTableViewDelegate {
func selectableItemTableView<T: SelectableItem & CaseIterable & Equatable>(_ sender: SelectableItemTableView<T>, didSelectItem: T?) {
guard let didSelectLevel = didSelectItem as? RoutineLevelType?
else { return }
viewModel.action(input: .selectLevel(selectedLevel: didSelectLevel))
levelButton.updateButton(level: didSelectLevel)
recommendedRoutineScrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
}

Expand All @@ -333,6 +317,7 @@ extension RecommendedRoutineViewController: FloatingMenuViewDelegate {
}
}

// MARK: RegisterEmotionButtonViewDelegate
extension RecommendedRoutineViewController: RegisterEmotionButtonViewDelegate {
func registerEmotionButtonViewDidTapRegisterButton(_ sender: RegisterEmotionButtonView) {
guard let emotionRegisterViewModel = DIContainer.shared.resolve(type: EmotionRegisterViewModel.self)
Expand All @@ -343,3 +328,15 @@ extension RecommendedRoutineViewController: RegisterEmotionButtonViewDelegate {
self.navigationController?.pushViewController(emotionRegisterView, animated: true)
}
}

// MARK: RoutineCardViewDelegate
extension RecommendedRoutineViewController: RoutineCardViewDelegate {
func routineCardView(_ sender: RoutineCardView, didTapPlusButton routine: RecommendedRoutine) {
guard let routineCreationViewModel = DIContainer.shared.resolve(type: RoutineCreationViewModel.self)
else { fatalError("routineCreationViewModel 의존성이 등록되지 않았습니다.") }

let routineCreationView = RoutineCreationViewController(viewModel: routineCreationViewModel, recommendRoutineId: routine.id)
routineCreationView.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(routineCreationView, animated: true)
}
}
Loading