diff --git a/FLINT/Data/Sources/Networking/API/TermsAPI.swift b/FLINT/Data/Sources/Networking/API/TermsAPI.swift new file mode 100644 index 00000000..8e6a878d --- /dev/null +++ b/FLINT/Data/Sources/Networking/API/TermsAPI.swift @@ -0,0 +1,16 @@ +// +// TermsAPI.swift +// Data +// +// Created by 김호성 on 2026.05.29. +// + +import Foundation + +import Moya + +public enum TermsAPI { + case getTerms + case getTerm(id: String) + case agreeTerms(ids: [String]) +} diff --git a/FLINT/Domain/Sources/Entity/Term/SignUpTerm.swift b/FLINT/Domain/Sources/Entity/Term/SignUpTerm.swift new file mode 100644 index 00000000..0acff5d9 --- /dev/null +++ b/FLINT/Domain/Sources/Entity/Term/SignUpTerm.swift @@ -0,0 +1,68 @@ +// +// SignUpTerm.swift +// Domain +// +// Created by 김호성 on 2026.06.10. +// + +import Foundation + +public enum SignUpTerm: String, Sendable, CaseIterable { + case service = "SERVICE" + case privacy = "PRIVACY" +} + +extension SignUpTerm { + public init?(id: Int) { + switch id { + case 1: + self = .service + case 2: + self = .privacy + default: + return nil + } + } + + public var id: Int { + switch self { + case .service: + return 1 + case .privacy: + return 2 + } + } + + public var title: String { + switch self { + case .service: + return "서비스 이용 약관 동의" + case .privacy: + return "개인정보 처리 방침 동의" + } + } + + public var description: String { + switch self { + case .service: + return """ + 본 약관은 서비스 이용과 관련한 기본적인 권리·의무 및 책임사항을 규정합니다. + """ + case .privacy: + return """ + 서비스 제공을 위해 개인정보를 수집 · 이용합니다.
콘텐츠 추천, 컬렉션 생성 및 공유, 맞춤형 탐색 경험 제공을 위한 이용 기록 및 취향 정보 처리 내용이 포함됩니다. + + 수집 항목 : 계정 정보, 취향 정보, 컬렉션 및 콘텐츠 활동, 서비스 이용 기록 등 + + 수집 목적: 개인화 추천 제공, 컬렉션 생성 및 공유, 서비스 운영 및 이용자 보호 + """ + } + } + + public var isRequired: Bool { + switch self { + case .service, .privacy: + return true + } + } +} diff --git a/FLINT/FLINT/Dependency/Factory/ViewController/NicknameViewControllerFactory+.swift b/FLINT/FLINT/Dependency/Factory/ViewController/NicknameViewControllerFactory+.swift index 89c8fcda..901bba01 100644 --- a/FLINT/FLINT/Dependency/Factory/ViewController/NicknameViewControllerFactory+.swift +++ b/FLINT/FLINT/Dependency/Factory/ViewController/NicknameViewControllerFactory+.swift @@ -9,8 +9,8 @@ import Foundation import Presentation -extension NicknameViewControllerFactory where Self: OnboardingViewModelFactory & ViewControllerFactory { - func makeNicknameViewController() -> NicknameViewController { - return NicknameViewController(onboardingViewModel: makeOnboardingViewModel(), viewControllerFactory: self) +extension NicknameViewControllerFactory where Self: ViewControllerFactory { + func makeNicknameViewController(onboardingViewModel: OnboardingViewModel) -> NicknameViewController { + return NicknameViewController(onboardingViewModel: onboardingViewModel, viewControllerFactory: self) } } diff --git a/FLINT/FLINT/Dependency/Factory/ViewController/TermsAgreementViewControllerFactory+.swift b/FLINT/FLINT/Dependency/Factory/ViewController/TermsAgreementViewControllerFactory+.swift new file mode 100644 index 00000000..e50be062 --- /dev/null +++ b/FLINT/FLINT/Dependency/Factory/ViewController/TermsAgreementViewControllerFactory+.swift @@ -0,0 +1,16 @@ +// +// TermsAgreementViewControllerFactory+.swift +// FLINT +// +// Created by 김호성 on 2026.05.29. +// + +import Foundation + +import Presentation + +extension TermsAgreementViewControllerFactory where Self: OnboardingViewModelFactory & ViewControllerFactory { + func makeTermsAgreementViewController() -> TermsAgreementViewController { + return TermsAgreementViewController(onboardingViewModel: makeOnboardingViewModel(), viewControllerFactory: self) + } +} diff --git a/FLINT/Presentation/Package.resolved b/FLINT/Presentation/Package.resolved new file mode 100644 index 00000000..9a14f68f --- /dev/null +++ b/FLINT/Presentation/Package.resolved @@ -0,0 +1,60 @@ +{ + "originHash" : "c95b4af140641978b2f57a74958c9190bb0ba5ebed92bff618c56503a9d2c924", + "pins" : [ + { + "identity" : "alamofire", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Alamofire/Alamofire.git", + "state" : { + "revision" : "7595cbcf59809f9977c5f6378500de2ad73b7ddb", + "version" : "5.12.0" + } + }, + { + "identity" : "kakao-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kakao/kakao-ios-sdk.git", + "state" : { + "revision" : "1a2b530921ab9d1f4385ced84109b7a86d42cff8", + "version" : "2.27.1" + } + }, + { + "identity" : "kingfisher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/onevcat/Kingfisher.git", + "state" : { + "revision" : "d30a5fad881137e2267f96a8e3fc35c58999bb94", + "version" : "8.6.2" + } + }, + { + "identity" : "lottie-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/airbnb/lottie-ios.git", + "state" : { + "revision" : "a004050748dc197c56256a14dca49a035d74726c", + "version" : "4.5.2" + } + }, + { + "identity" : "snapkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SnapKit/SnapKit.git", + "state" : { + "revision" : "2842e6e84e82eb9a8dac0100ca90d9444b0307f4", + "version" : "5.7.1" + } + }, + { + "identity" : "then", + "kind" : "remoteSourceControl", + "location" : "https://github.com/devxoul/Then.git", + "state" : { + "revision" : "d41ef523faef0f911369f79c0b96815d9dbb6d7a", + "version" : "3.0.0" + } + } + ], + "version" : 3 +} diff --git a/FLINT/Presentation/Sources/View/Base/BaseCollectionViewListCell.swift b/FLINT/Presentation/Sources/View/Base/BaseCollectionViewListCell.swift new file mode 100644 index 00000000..36179c56 --- /dev/null +++ b/FLINT/Presentation/Sources/View/Base/BaseCollectionViewListCell.swift @@ -0,0 +1,38 @@ +// +// BaseCollectionViewListCell.swift +// Presentation +// +// Created by 김호성 on 2026.06.01. +// + +import UIKit + +public class BaseCollectionViewListCell: UICollectionViewListCell, ReuseIdentifiable { + + // MARK: - Init + + public override init(frame: CGRect) { + super.init(frame: frame) + setStyle() + setHierarchy() + setLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Lifecycle + + public override func prepareForReuse() { + super.prepareForReuse() + prepare() + } + + // MARK: - Override Points + + public func setStyle() { } + public func setHierarchy() { } + public func setLayout() { } + public func prepare() { } +} diff --git a/FLINT/Presentation/Sources/View/Component/FlintCheckbox.swift b/FLINT/Presentation/Sources/View/Component/FlintCheckbox.swift new file mode 100644 index 00000000..1a8b1f8b --- /dev/null +++ b/FLINT/Presentation/Sources/View/Component/FlintCheckbox.swift @@ -0,0 +1,22 @@ +// +// FlintCheckbox.swift +// Presentation +// +// Created by 김호성 on 2026.05.27. +// + +import UIKit + +package final class FlintCheckbox: UIButton { + + package init() { + super.init(frame: .zero) + + setImage(.icCheckboxEmpty, for: .normal) + setImage(.icCheckboxFill, for: .selected) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/FLINT/Presentation/Sources/View/Resource/Assets.xcassets/Icon/Common/24/ic_up.imageset/Contents.json b/FLINT/Presentation/Sources/View/Resource/Assets.xcassets/Icon/Common/24/ic_up.imageset/Contents.json new file mode 100644 index 00000000..139910c4 --- /dev/null +++ b/FLINT/Presentation/Sources/View/Resource/Assets.xcassets/Icon/Common/24/ic_up.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "icon.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/FLINT/Presentation/Sources/View/Resource/Assets.xcassets/Icon/Common/24/ic_up.imageset/icon.svg b/FLINT/Presentation/Sources/View/Resource/Assets.xcassets/Icon/Common/24/ic_up.imageset/icon.svg new file mode 100644 index 00000000..f0dc38f7 --- /dev/null +++ b/FLINT/Presentation/Sources/View/Resource/Assets.xcassets/Icon/Common/24/ic_up.imageset/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/FLINT/Presentation/Sources/View/Scene/Onboarding/TermsAgreement/Cell/TermAgreementCollectionViewCell.swift b/FLINT/Presentation/Sources/View/Scene/Onboarding/TermsAgreement/Cell/TermAgreementCollectionViewCell.swift new file mode 100644 index 00000000..51796d14 --- /dev/null +++ b/FLINT/Presentation/Sources/View/Scene/Onboarding/TermsAgreement/Cell/TermAgreementCollectionViewCell.swift @@ -0,0 +1,130 @@ +// +// TermAgreementCollectionViewCell.swift +// Presentation +// +// Created by 김호성 on 2026.05.30. +// + +import UIKit + +import Domain + +package final class TermAgreementCollectionViewCell: BaseCollectionViewListCell { + + // MARK: - Component + + package let termAgreeStackView = UIStackView().then { + $0.axis = .vertical + $0.spacing = 0 + $0.alignment = .fill + $0.distribution = .equalSpacing + } + + package let termAgreeHeaderView = UIView() + package let termAgreeCheckbox = FlintCheckbox() + package let termAgreeLabel = UILabel().then { + $0.textColor = .flintWhite + } + + package lazy var expandButton = UIButton().then { + $0.setImage(.icDown, for: .normal) + $0.setImage(.icUp, for: .selected) + $0.addTarget(self, action: #selector(touchUpInsideExpandButton(_:)), for: .touchUpInside) + } + + package let termDetailView = UIView().then { + $0.backgroundColor = .flintGray800 + $0.layer.cornerRadius = 8 + $0.isHidden = true + } + package let termDetailLabel = UILabel().then { + $0.textColor = .flintWhite + $0.numberOfLines = 0 + } + package let termDetailMoreButton = UIButton().then { + $0.setAttributedTitle( + NSMutableAttributedString(.pretendard(.body2_r_14, text: "자세히 보기")).configured { + $0.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: NSRange(location: 0, length: $0.length)) + }, + for: .normal + ) + $0.setTitleColor(.flintPrimary200, for: .normal) + } + + // MARK: - Basic + + package override init(frame: CGRect) { + super.init(frame: frame) + + contentView.backgroundColor = .flintBackground + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + package override func prepare() { + termAgreeCheckbox.removeTarget(nil, action: nil, for: .allEvents) + } + + // MARK: - Setup + + package override func setHierarchy() { + contentView.addSubview(termAgreeStackView) + termAgreeStackView.addArrangedSubviews( + termAgreeHeaderView, + termDetailView + ) + termAgreeHeaderView.addSubviews( + termAgreeCheckbox, + termAgreeLabel, + expandButton, + ) + termDetailView.addSubviews( + termDetailLabel, + termDetailMoreButton + ) + } + + package override func setLayout() { + termAgreeStackView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + termAgreeCheckbox.snp.makeConstraints { + $0.size.equalTo(48) + $0.leading.verticalEdges.equalToSuperview() + } + termAgreeLabel.snp.makeConstraints { + $0.centerY.equalToSuperview() + $0.leading.equalTo(termAgreeCheckbox.snp.trailing) + } + expandButton.snp.makeConstraints { + $0.size.equalTo(48) + $0.trailing.verticalEdges.equalToSuperview() + } + termDetailLabel.snp.makeConstraints { + $0.top.horizontalEdges.equalToSuperview().inset(12) + } + termDetailMoreButton.snp.makeConstraints { + $0.top.equalTo(termDetailLabel.snp.bottom) + $0.height.equalTo(48) + $0.trailing.equalToSuperview().inset(12) + $0.bottom.equalToSuperview() + } + } + + // MARK: - Public Function + + package func configure(_ signUpTerm: SignUpTerm) { + termAgreeLabel.attributedText = .pretendard(.body1_r_16, text: signUpTerm.title) + termDetailLabel.attributedText = .pretendard(.body2_r_14, text: signUpTerm.description) + } + + // MARK: - Private Function + + @objc private func touchUpInsideExpandButton(_ sender: UIButton) { + sender.isSelected.toggle() + termDetailView.isHidden = !sender.isSelected + invalidateIntrinsicContentSize() + } +} diff --git a/FLINT/Presentation/Sources/View/Scene/Onboarding/TermsAgreement/TermsAgreementView.swift b/FLINT/Presentation/Sources/View/Scene/Onboarding/TermsAgreement/TermsAgreementView.swift new file mode 100644 index 00000000..90b25168 --- /dev/null +++ b/FLINT/Presentation/Sources/View/Scene/Onboarding/TermsAgreement/TermsAgreementView.swift @@ -0,0 +1,117 @@ +// +// TermsAgreementView.swift +// Presentation +// +// Created by 김호성 on 2026.05.27. +// + +import UIKit + +import SnapKit +import Then + +public class TermsAgreementView: BaseView { + + // MARK: - Component + + package let titleLabel = UILabel().then { + $0.textColor = .flintWhite + $0.attributedText = .pretendard(.display2_m_28, text: "약관에 동의해주세요") + } + + package let allAgreeStackView = UIStackView().then { + $0.axis = .horizontal + $0.spacing = 0 + $0.alignment = .center + $0.distribution = .fill + } + package let allAgreeCheckbox = FlintCheckbox() + package let allAgreeLabel = UILabel().then { + $0.textColor = .flintWhite + $0.attributedText = .pretendard(.body1_r_16, text: "전체 동의") + } + + package let separatorView = UIView().then { + $0.backgroundColor = .flintGray600 + } + + package let nextButton = FlintButton(style: .able, title: "동의하기").then { + $0.isEnabled = false + } + + package let termAgreementsCollectionView: UICollectionView = { + var collectionLayoutListConfiguration = UICollectionLayoutListConfiguration(appearance: .grouped) + collectionLayoutListConfiguration.backgroundColor = .flintBackground + collectionLayoutListConfiguration.showsSeparators = false + let collectionViewLayout: UICollectionViewCompositionalLayout = UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in + let section = NSCollectionLayoutSection.list(using: collectionLayoutListConfiguration, layoutEnvironment: layoutEnvironment) + section.contentInsets = .init(top: 16, leading: 16, bottom: 12, trailing: 16) + section.interGroupSpacing = 8 + return section + } + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.allowsSelection = false + return collectionView + }() + + // MARK: - Basic + + public override init(frame: CGRect) { + super.init(frame: frame) + + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setup + + public override func setUI() { + backgroundColor = .flintBackground + } + + public override func setHierarchy() { + addSubviews( + titleLabel, + allAgreeStackView, + separatorView, + termAgreementsCollectionView, + nextButton + ) + allAgreeStackView.addArrangedSubviews( + allAgreeCheckbox, + allAgreeLabel, + ) + } + + public override func setLayout() { + titleLabel.snp.makeConstraints { + $0.top.equalToSuperview().inset(12) + $0.horizontalEdges.equalToSuperview().inset(16) + } + allAgreeStackView.snp.makeConstraints { + $0.top.equalTo(titleLabel.snp.bottom).offset(24) + $0.horizontalEdges.equalToSuperview().inset(16) + } + allAgreeCheckbox.snp.makeConstraints { + $0.size.equalTo(48) + } + separatorView.snp.makeConstraints { + $0.top.equalTo(allAgreeStackView.snp.bottom).offset(16) + $0.horizontalEdges.equalToSuperview() + $0.height.equalTo(4) + } + termAgreementsCollectionView.snp.makeConstraints { + $0.top.equalTo(separatorView.snp.bottom) + $0.horizontalEdges.equalToSuperview() + } + nextButton.snp.makeConstraints { + $0.top.equalTo(termAgreementsCollectionView.snp.bottom).offset(8) + $0.horizontalEdges.equalToSuperview().inset(16) + $0.height.equalTo(48) + $0.bottom.equalTo(safeAreaLayoutGuide) + } + } +} diff --git a/FLINT/Presentation/Sources/ViewController/Dependency/ViewControllerFactory.swift b/FLINT/Presentation/Sources/ViewController/Dependency/ViewControllerFactory.swift index 7b5b09ac..2ed18798 100644 --- a/FLINT/Presentation/Sources/ViewController/Dependency/ViewControllerFactory.swift +++ b/FLINT/Presentation/Sources/ViewController/Dependency/ViewControllerFactory.swift @@ -18,6 +18,7 @@ public typealias ViewControllerFactory = // MARK: - Onboarding + TermsAgreementViewControllerFactory & NicknameViewControllerFactory & ContentSelectViewControllerFactory & // OttSelectViewControllerFactory & diff --git a/FLINT/Presentation/Sources/ViewController/Scene/Login/LoginViewController.swift b/FLINT/Presentation/Sources/ViewController/Scene/Login/LoginViewController.swift index 9c5df527..9585568b 100644 --- a/FLINT/Presentation/Sources/ViewController/Scene/Login/LoginViewController.swift +++ b/FLINT/Presentation/Sources/ViewController/Scene/Login/LoginViewController.swift @@ -61,8 +61,8 @@ public final class LoginViewController: BaseViewController { } private func register() { - guard let nicknameViewController = viewControllerFactory?.makeNicknameViewController() else { return } - navigationController?.pushViewController(nicknameViewController, animated: true) + guard let termsAgreementViewController = viewControllerFactory?.makeTermsAgreementViewController() else { return } + navigationController?.pushViewController(termsAgreementViewController, animated: true) } private func pushToTabBar() { diff --git a/FLINT/Presentation/Sources/ViewController/Scene/Onboarding/Nickname/NicknameViewController.swift b/FLINT/Presentation/Sources/ViewController/Scene/Onboarding/Nickname/NicknameViewController.swift index d5f44a48..09157c79 100644 --- a/FLINT/Presentation/Sources/ViewController/Scene/Onboarding/Nickname/NicknameViewController.swift +++ b/FLINT/Presentation/Sources/ViewController/Scene/Onboarding/Nickname/NicknameViewController.swift @@ -15,7 +15,7 @@ import View import ViewModel public protocol NicknameViewControllerFactory { - func makeNicknameViewController() -> NicknameViewController + func makeNicknameViewController(onboardingViewModel: OnboardingViewModel) -> NicknameViewController } public final class NicknameViewController: BaseViewController { diff --git a/FLINT/Presentation/Sources/ViewController/Scene/Onboarding/TermsAgreement/TermsAgreementViewController.swift b/FLINT/Presentation/Sources/ViewController/Scene/Onboarding/TermsAgreement/TermsAgreementViewController.swift new file mode 100644 index 00000000..15f959b1 --- /dev/null +++ b/FLINT/Presentation/Sources/ViewController/Scene/Onboarding/TermsAgreement/TermsAgreementViewController.swift @@ -0,0 +1,117 @@ +// +// TermsAgreementViewController.swift +// Presentation +// +// Created by 김호성 on 2026.05.27. +// + +import UIKit + +import Domain + +import View +import ViewModel + +public protocol TermsAgreementViewControllerFactory { + func makeTermsAgreementViewController() -> TermsAgreementViewController +} + +public final class TermsAgreementViewController: BaseViewController { + + // MARK: - ViewModel + + private let onboardingViewModel: OnboardingViewModel + + // MARK: - DataSource + + private var termAgreementsCollectionViewDataSource: TermAgreementsCollectionViewDataSource? + + // MARK: - Basic + + public init(onboardingViewModel: OnboardingViewModel, viewControllerFactory: ViewControllerFactory) { + self.onboardingViewModel = onboardingViewModel + super.init(nibName: nil, bundle: nil) + self.viewControllerFactory = viewControllerFactory + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override func bind() { + onboardingViewModel.agreedTerms.sink { [weak self] agreedTerms in + guard let self else { return } + let isAllAgreed = agreedTerms.values.allSatisfy { $0 } + rootView.allAgreeCheckbox.isSelected = isAllAgreed + rootView.nextButton.isEnabled = isAllAgreed + termAgreementsCollectionViewDataSource?.apply(makeTermAgreementsCollectionViewSnapshot(), animatingDifferences: false) + } + .store(in: &cancellables) + } + + public override func viewDidLoad() { + super.viewDidLoad() + + setNavigationBar(.init(left: .back)) + + setupTermAgreementsCollectionView() + + rootView.allAgreeCheckbox.addTarget(self, action: #selector(touchUpInsideAllAgreeCheckbox(_:)), for: .touchUpInside) + rootView.nextButton.addTarget(self, action: #selector(touchUpInsideNextButton(_:)), for: .touchUpInside) + } + + @objc private func touchUpInsideAllAgreeCheckbox(_ sender: UIButton) { + let isAllAgreed = onboardingViewModel.agreedTerms.value.values.allSatisfy({ $0 }) + onboardingViewModel.agreedTerms.value = onboardingViewModel.agreedTerms.value.mapValues { _ in !isAllAgreed } + } + + @objc private func touchUpInsideNextButton(_ sender: UIButton) { + guard let nicknameViewController = viewControllerFactory?.makeNicknameViewController(onboardingViewModel: onboardingViewModel) else { return } + navigationController?.pushViewController(nicknameViewController, animated: true) + } +} + +extension TermsAgreementViewController { + + private typealias TermAgreementsCollectionViewDataSource = UICollectionViewDiffableDataSource + private typealias TermAgreementsCollectionViewSnapshot = NSDiffableDataSourceSnapshot + + private enum TermAgreementsCollectionViewSection: Sendable { + case main + } + + private enum TermAgreementsCollectionViewItem: Hashable, Sendable { + case term(SignUpTerm, agreed: Bool) + } + + private func setupTermAgreementsCollectionView() { + let termAgreementCollectionViewCellRegistration = UICollectionView.CellRegistration { [weak self] cell, indexPath, item in + let signUpTerm = item.0 + let agreed = item.1 + cell.configure(signUpTerm) + cell.termAgreeCheckbox.isSelected = agreed + cell.termAgreeCheckbox.addAction(UIAction(handler: { [weak self, weak sender = cell.termAgreeCheckbox] action in + guard let self, let sender, let term = SignUpTerm(id: indexPath.row+1) else { return } + sender.isSelected.toggle() + onboardingViewModel.agreedTerms.value[term] = sender.isSelected + }), for: .touchUpInside) + } + + rootView.termAgreementsCollectionView.dataSource = termAgreementsCollectionViewDataSource + + termAgreementsCollectionViewDataSource = TermAgreementsCollectionViewDataSource(collectionView: rootView.termAgreementsCollectionView, cellProvider: { collectionView, indexPath, itemIdentifier in + switch itemIdentifier { + case let .term(signUpTerm, agreed): + return collectionView.dequeueConfiguredReusableCell(using: termAgreementCollectionViewCellRegistration, for: indexPath, item: (signUpTerm, agreed: agreed)) + } + }) + termAgreementsCollectionViewDataSource?.apply(makeTermAgreementsCollectionViewSnapshot()) + } + + private func makeTermAgreementsCollectionViewSnapshot() -> TermAgreementsCollectionViewSnapshot { + var snapshot = TermAgreementsCollectionViewSnapshot() + snapshot.appendSections([.main]) + snapshot.appendItems(SignUpTerm.allCases.map { .term($0, agreed: onboardingViewModel.agreedTerms.value[$0] ?? false) }, toSection: .main) + return snapshot + } +} diff --git a/FLINT/Presentation/Sources/ViewModel/Scene/Onboarding/OnboardingViewModel.swift b/FLINT/Presentation/Sources/ViewModel/Scene/Onboarding/OnboardingViewModel.swift index 0ebce40b..69c091b3 100644 --- a/FLINT/Presentation/Sources/ViewModel/Scene/Onboarding/OnboardingViewModel.swift +++ b/FLINT/Presentation/Sources/ViewModel/Scene/Onboarding/OnboardingViewModel.swift @@ -27,8 +27,8 @@ public protocol OnboardingViewModelInput { } public protocol OnboardingViewModelOutput { - // terms - var agreedTermsIds: CurrentValueSubject<[String], Never> { get } + // term + var agreedTerms: CurrentValueSubject<[SignUpTerm: Bool], Never> { get } // nickname var nickname: CurrentValueSubject { get } @@ -52,8 +52,10 @@ public final class DefaultOnboardingViewModel: OnboardingViewModel { private let searchContentsUseCase: SearchContentsUseCase private let signupUseCase: SignupUseCase - #warning("TODO: - Temp. 약관 동의 구현 후 수정할 것!!!!") - public let agreedTermsIds: CurrentValueSubject<[String], Never> = .init(["1", "2"]) + public let agreedTerms: CurrentValueSubject<[SignUpTerm: Bool], Never> = .init([ + .service: false, + .privacy: false, + ]) public let nickname: CurrentValueSubject = .init("") public let nicknameValidState: CurrentValueSubject = .init(nil) @@ -155,7 +157,10 @@ public final class DefaultOnboardingViewModel: OnboardingViewModel { favoriteContentIds: selectedContents.value.compactMap({ content in Int(content.id) }), - agreedTermsIds: agreedTermsIds.value + agreedTermsIds: agreedTerms.value + .filter { $0.value } + .map(\.key.id) + .map { String($0) } ) ) .manageThread()