diff --git a/iOS/FlipMate/Feature/Category/.gitignore b/iOS/FlipMate/Feature/Category/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/iOS/FlipMate/Feature/Category/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/iOS/FlipMate/Feature/Category/Package.swift b/iOS/FlipMate/Feature/Category/Package.swift new file mode 100644 index 0000000..fb18dac --- /dev/null +++ b/iOS/FlipMate/Feature/Category/Package.swift @@ -0,0 +1,29 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Category", + platforms: [.iOS(.v14)], + products: [ + .library(name: "Category", targets: ["Category"]), + ], + + dependencies: [ + .package(name: "Core", path: "../../Core"), + .package(name: "DesignSystem", path: "../../DesignSystem"), + .package(name: "Domain", path: "../../Domain"), + .package(name: "Service", path: "../../Service") + ], + + targets: [ + .target(name: "Category", dependencies: [ + .product(name: "Core", package: "Core"), + .product(name: "DesignSystem", package: "DesignSystem"), + .product(name: "Domain", package: "Domain"), + .product(name: "CategoryService", package: "Service") + ]), + .testTarget(name: "CategoryTests", dependencies: ["Category"]), + ] +) diff --git a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/Model/CategorySettingSection.swift b/iOS/FlipMate/Feature/Category/Sources/Category/Model/CategorySettingSection.swift similarity index 100% rename from iOS/FlipMate/FlipMate/Timer/Sources/Timer/Model/CategorySettingSection.swift rename to iOS/FlipMate/Feature/Category/Sources/Category/Model/CategorySettingSection.swift diff --git a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/View/CategoryColorSelectView.swift b/iOS/FlipMate/Feature/Category/Sources/Category/View/CategoryColorSelectView.swift similarity index 100% rename from iOS/FlipMate/FlipMate/Timer/Sources/Timer/View/CategoryColorSelectView.swift rename to iOS/FlipMate/Feature/Category/Sources/Category/View/CategoryColorSelectView.swift diff --git a/iOS/FlipMate/Feature/Category/Sources/Category/View/CategorySettingCollectionViewCell.swift b/iOS/FlipMate/Feature/Category/Sources/Category/View/CategorySettingCollectionViewCell.swift new file mode 100644 index 0000000..8d32b9a --- /dev/null +++ b/iOS/FlipMate/Feature/Category/Sources/Category/View/CategorySettingCollectionViewCell.swift @@ -0,0 +1,50 @@ +// +// CategorySettingCollectionViewCell.swift +// +// +// Created by 임현규 on 6/16/24. +// + +import UIKit +import DesignSystem + +final class CategorySettingCollectionViewCell: UICollectionViewCell { + + // MARK: - UI Components + private let categoryView: CategoryInfoView = { + let view = CategoryInfoView(isTimerLabelHidden: true) + view.translatesAutoresizingMaskIntoConstraints = false + view.backgroundColor = .systemBackground + return view + }() + + // MARK: - init + override init(frame: CGRect) { + super.init(frame: frame) + configureUI() + configureCategoryCellLayer() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - UI Update Methods + func updateUI(_ subjectLabelText: String, _ circleBackgroundColor: UIColor?, _ timeLabelText: String?) { + categoryView.updateUI(subjectLabelText, circleBackgroundColor, timeLabelText) + } +} + +// MARK: - Private Methods +private extension CategorySettingCollectionViewCell { + func configureUI() { + [ categoryView ] .forEach { contentView.addSubview($0) } + + NSLayoutConstraint.activate([ + categoryView.topAnchor.constraint(equalTo: contentView.topAnchor), + categoryView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + categoryView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + categoryView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) + ]) + } +} diff --git a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/View/CategorySettingFooterView.swift b/iOS/FlipMate/Feature/Category/Sources/Category/View/CategorySettingFooterView.swift similarity index 100% rename from iOS/FlipMate/FlipMate/Timer/Sources/Timer/View/CategorySettingFooterView.swift rename to iOS/FlipMate/Feature/Category/Sources/Category/View/CategorySettingFooterView.swift diff --git a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewController/CategoryModifyViewController.swift b/iOS/FlipMate/Feature/Category/Sources/Category/ViewController/CategoryModifyViewController.swift similarity index 100% rename from iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewController/CategoryModifyViewController.swift rename to iOS/FlipMate/Feature/Category/Sources/Category/ViewController/CategoryModifyViewController.swift diff --git a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewController/CategorySettingViewController.swift b/iOS/FlipMate/Feature/Category/Sources/Category/ViewController/CategorySettingViewController.swift similarity index 97% rename from iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewController/CategorySettingViewController.swift rename to iOS/FlipMate/Feature/Category/Sources/Category/ViewController/CategorySettingViewController.swift index 5a37189..27c24bf 100644 --- a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewController/CategorySettingViewController.swift +++ b/iOS/FlipMate/Feature/Category/Sources/Category/ViewController/CategorySettingViewController.swift @@ -25,7 +25,7 @@ public final class CategorySettingViewController: BaseViewController { private lazy var collectionView: UICollectionView = { let collectionView = UICollectionView(frame: .zero, collectionViewLayout: setCollectionViewLayout()) collectionView.translatesAutoresizingMaskIntoConstraints = false - collectionView.register(CategoryListCollectionViewCell.self) + collectionView.register(CategorySettingCollectionViewCell.self) collectionView.register(CategorySettingFooterView.self, kind: .footer) return collectionView }() @@ -145,10 +145,9 @@ private extension CategorySettingViewController { cellProvider: { collectionView, indexPath, itemIdentifier in switch itemIdentifier { case .categoryCell(let category): - let cell: CategoryListCollectionViewCell = collectionView + let cell: CategorySettingCollectionViewCell = collectionView .dequeueReusableCell(for: indexPath) - cell.updateUI(category: category) - cell.setTimeLabelHidden(isHidden: true) + cell.updateUI(category.subject, UIColor(hexString: category.color), category.studyTime?.secondsToStringTime()) return cell } }) diff --git a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewModel/CategoryModifyViewModel.swift b/iOS/FlipMate/Feature/Category/Sources/Category/ViewModel/CategoryModifyViewModel.swift similarity index 99% rename from iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewModel/CategoryModifyViewModel.swift rename to iOS/FlipMate/Feature/Category/Sources/Category/ViewModel/CategoryModifyViewModel.swift index 3d828dd..61b7295 100644 --- a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewModel/CategoryModifyViewModel.swift +++ b/iOS/FlipMate/Feature/Category/Sources/Category/ViewModel/CategoryModifyViewModel.swift @@ -10,6 +10,7 @@ import Combine import Domain import Core +import CategoryService public struct CategoryModifyViewModelActions { public let didFinishCategoryModify: () -> Void diff --git a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewModel/CategoryViewModel.swift b/iOS/FlipMate/Feature/Category/Sources/Category/ViewModel/CategoryViewModel.swift similarity index 99% rename from iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewModel/CategoryViewModel.swift rename to iOS/FlipMate/Feature/Category/Sources/Category/ViewModel/CategoryViewModel.swift index 0595a78..75e0186 100644 --- a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewModel/CategoryViewModel.swift +++ b/iOS/FlipMate/Feature/Category/Sources/Category/ViewModel/CategoryViewModel.swift @@ -9,6 +9,7 @@ import Foundation import Combine import Domain +import CategoryService public struct CategoryViewModelActions { let showModifyCategory: (CategoryPurpose, StudyCategory?) -> Void diff --git a/iOS/FlipMate/Feature/Category/Tests/CategoryTests/CategoryTests.swift b/iOS/FlipMate/Feature/Category/Tests/CategoryTests/CategoryTests.swift new file mode 100644 index 0000000..5f12ad5 --- /dev/null +++ b/iOS/FlipMate/Feature/Category/Tests/CategoryTests/CategoryTests.swift @@ -0,0 +1,12 @@ +import XCTest +@testable import Category + +final class CategoryTests: XCTestCase { + func testExample() throws { + // XCTest Documentation + // https://developer.apple.com/documentation/xctest + + // Defining Test Cases and Test Methods + // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + } +} diff --git a/iOS/FlipMate/Feature/Service/.gitignore b/iOS/FlipMate/Feature/Service/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/iOS/FlipMate/Feature/Service/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/iOS/FlipMate/Feature/Service/Package.swift b/iOS/FlipMate/Feature/Service/Package.swift new file mode 100644 index 0000000..e194896 --- /dev/null +++ b/iOS/FlipMate/Feature/Service/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Service", + platforms: [.iOS(.v14)], + products: [ + .library(name: "CategoryService", targets: ["CategoryService"]), + ], + + dependencies: [ + .package(name: "Domain", path: "../Domain") + ], + + targets: [ + .target(name: "CategoryService", dependencies: [ + .product(name: "Domain", package: "Domain") + ]), + .testTarget(name: "ServiceTests", dependencies: [ + "CategoryService" + ]), + ] +) diff --git a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/Utils/CategoryManager.swift b/iOS/FlipMate/Feature/Service/Sources/CategoryService/CategoryServiceImplement/CategoryManager.swift similarity index 81% rename from iOS/FlipMate/FlipMate/Timer/Sources/Timer/Utils/CategoryManager.swift rename to iOS/FlipMate/Feature/Service/Sources/CategoryService/CategoryServiceImplement/CategoryManager.swift index 548129d..19747ac 100644 --- a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/Utils/CategoryManager.swift +++ b/iOS/FlipMate/Feature/Service/Sources/CategoryService/CategoryServiceImplement/CategoryManager.swift @@ -1,24 +1,14 @@ // -// File.swift -// +// CategoryManager.swift // -// Created by 권승용 on 5/31/24. +// +// Created by 임현규 on 6/14/24. // import Foundation import Combine import Domain -public protocol CategoryManageable { - var categoryDidChangePublisher: AnyPublisher<[StudyCategory], Never> { get } - func replace(categories: [StudyCategory]) - func change(category: StudyCategory) - func removeCategory(categoryId: Int) - func append(category: StudyCategory) - func findCategory(categoryId: Int) -> StudyCategory? - func numberOfCategory() -> Int -} - public final class CategoryManager: CategoryManageable { // MARK: - Properties private lazy var categoryDidChangeSubject = CurrentValueSubject<[StudyCategory], Never>(categories) diff --git a/iOS/FlipMate/Feature/Service/Sources/CategoryService/CategoryServiceInterface/CategoryManageable.swift b/iOS/FlipMate/Feature/Service/Sources/CategoryService/CategoryServiceInterface/CategoryManageable.swift new file mode 100644 index 0000000..7ea79a3 --- /dev/null +++ b/iOS/FlipMate/Feature/Service/Sources/CategoryService/CategoryServiceInterface/CategoryManageable.swift @@ -0,0 +1,20 @@ +// +// CategoryManageable.swift +// +// +// Created by 임현규 on 6/14/24. +// + +import Foundation +import Combine +import Domain + +public protocol CategoryManageable { + var categoryDidChangePublisher: AnyPublisher<[StudyCategory], Never> { get } + func replace(categories: [StudyCategory]) + func change(category: StudyCategory) + func removeCategory(categoryId: Int) + func append(category: StudyCategory) + func findCategory(categoryId: Int) -> StudyCategory? + func numberOfCategory() -> Int +} diff --git a/iOS/FlipMate/Feature/Service/Tests/ServiceTests/ServiceTests.swift b/iOS/FlipMate/Feature/Service/Tests/ServiceTests/ServiceTests.swift new file mode 100644 index 0000000..c9a8262 --- /dev/null +++ b/iOS/FlipMate/Feature/Service/Tests/ServiceTests/ServiceTests.swift @@ -0,0 +1,12 @@ +import XCTest +@testable import Service + +final class ServiceTests: XCTestCase { + func testExample() throws { + // XCTest Documentation + // https://developer.apple.com/documentation/xctest + + // Defining Test Cases and Test Methods + // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + } +} diff --git a/iOS/FlipMate/FlipMate.xcodeproj/project.pbxproj b/iOS/FlipMate/FlipMate.xcodeproj/project.pbxproj index b26addf..b4be5a0 100644 --- a/iOS/FlipMate/FlipMate.xcodeproj/project.pbxproj +++ b/iOS/FlipMate/FlipMate.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 2C04FF5C2BF78E6F0065075F /* DesignSystem in Frameworks */ = {isa = PBXBuildFile; productRef = 2C04FF5B2BF78E6F0065075F /* DesignSystem */; }; 2C6549C02BFDD6E900105D5A /* Data in Frameworks */ = {isa = PBXBuildFile; productRef = 2C6549BF2BFDD6E900105D5A /* Data */; }; + 2C82BD7F2C18482D00CAD08B /* Category in Frameworks */ = {isa = PBXBuildFile; productRef = 2C82BD7E2C18482D00CAD08B /* Category */; }; 2C8D20F62B7216EE00632718 /* AppFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C88DCF12B124238000B4686 /* AppFlowCoordinator.swift */; }; 2C8D20F72B7216EE00632718 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 600908BD2AFCD7DF0065DFFB /* AppDelegate.swift */; }; 2C8D20F82B7216EE00632718 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 600908BF2AFCD7DF0065DFFB /* SceneDelegate.swift */; }; @@ -30,6 +31,7 @@ 2C8D21422B7216EF00632718 /* (null) in Sources */ = {isa = PBXBuildFile; }; 2C8D21432B7216EF00632718 /* (null) in Sources */ = {isa = PBXBuildFile; }; 2C8D21442B7216EF00632718 /* (null) in Sources */ = {isa = PBXBuildFile; }; + 2CB24F4B2C1C234400327FA0 /* CategoryService in Frameworks */ = {isa = PBXBuildFile; productRef = 2CB24F4A2C1C234400327FA0 /* CategoryService */; }; 2CD272ED2B21A619000CB9E5 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2CD272EF2B21A619000CB9E5 /* Localizable.strings */; }; 600908CA2AFCD7E00065DFFB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 600908C82AFCD7E00065DFFB /* LaunchScreen.storyboard */; }; 600D81362B51344500670882 /* TimerViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60FBEE4D2B4B82B1006B1CF9 /* TimerViewModelTests.swift */; }; @@ -69,6 +71,7 @@ /* Begin PBXFileReference section */ 2C04FF5A2BF78AAE0065075F /* DesignSystem */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = DesignSystem; path = FlipMate/DesignSystem; sourceTree = ""; }; 2C24ECCF2BFDD4E4000664BC /* Data */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Data; path = FlipMate/Data; sourceTree = ""; }; + 2C82BD7D2C18406400CAD08B /* Category */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Category; sourceTree = ""; }; 2C88DCF12B124238000B4686 /* AppFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppFlowCoordinator.swift; sourceTree = ""; }; 2C88DCF32B124272000B4686 /* AppDIContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDIContainer.swift; sourceTree = ""; }; 2C88DCF52B124292000B4686 /* TimerSceneDIContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimerSceneDIContainer.swift; sourceTree = ""; }; @@ -80,6 +83,7 @@ 2C88DD082B1274FA000B4686 /* CategorySettingCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategorySettingCoordinator.swift; sourceTree = ""; }; 2C88DD0A2B1340E4000B4686 /* CategoryDIContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryDIContainer.swift; sourceTree = ""; }; 2C9F622E2B17505B00AE63F9 /* FriendAddViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FriendAddViewModelTests.swift; sourceTree = ""; }; + 2CB24F492C1C173D00327FA0 /* Service */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Service; path = Feature/Service; sourceTree = ""; }; 2CD272EE2B21A619000CB9E5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 2CD272F02B21A622000CB9E5 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = ""; }; 2CD272F12B21A62B000CB9E5 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; @@ -128,11 +132,13 @@ 60780C942C0C576800C1FC52 /* Chart in Frameworks */, 60C368E82BFBA1DA008C76ED /* Domain in Frameworks */, 604309E82BF1AFE800768D87 /* GoogleSignInSwift in Frameworks */, + 2C82BD7F2C18482D00CAD08B /* Category in Frameworks */, 6037BB572C0AE23100BE37AC /* Timer in Frameworks */, 601F39742BF399B9001689A0 /* Core in Frameworks */, 60BB72782BEA709D00E2A7E9 /* FMImageProvider in Frameworks */, 604309E62BF1AFE800768D87 /* GoogleSignIn in Frameworks */, 2C6549C02BFDD6E900105D5A /* Data in Frameworks */, + 2CB24F4B2C1C234400327FA0 /* CategoryService in Frameworks */, 6071B1C72BF73BF10044574F /* Network in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -181,6 +187,7 @@ 2C04FF5A2BF78AAE0065075F /* DesignSystem */, 602BF08C2BF4FB5B0088050A /* Network */, 601F396A2BF3836F001689A0 /* Core */, + 2CB24F492C1C173D00327FA0 /* Service */, 600908D32AFCD7E00065DFFB /* FlipMateTests */, 600908BB2AFCD7DF0065DFFB /* Products */, 601F396B2BF386DC001689A0 /* Frameworks */, @@ -372,6 +379,7 @@ 60FE503F2C01C7B600AA2475 /* Feature */ = { isa = PBXGroup; children = ( + 2C82BD7D2C18406400CAD08B /* Category */, 60590D9E2C0C3D9D0069B3E4 /* TabBar */, 60590DA32C0C46410069B3E4 /* Login */, 60FE50402C020EC200AA2475 /* Timer */, @@ -414,6 +422,8 @@ 60780C932C0C576800C1FC52 /* Chart */, 608920152C0C7F7100FFDB14 /* Social */, 609B1B922C0CCBB100F5A5A6 /* MyPage */, + 2C82BD7E2C18482D00CAD08B /* Category */, + 2CB24F4A2C1C234400327FA0 /* CategoryService */, ); productName = FlipMate; productReference = 600908BA2AFCD7DF0065DFFB /* FlipMate.app */; @@ -895,6 +905,14 @@ isa = XCSwiftPackageProductDependency; productName = Data; }; + 2C82BD7E2C18482D00CAD08B /* Category */ = { + isa = XCSwiftPackageProductDependency; + productName = Category; + }; + 2CB24F4A2C1C234400327FA0 /* CategoryService */ = { + isa = XCSwiftPackageProductDependency; + productName = CategoryService; + }; 601F39732BF399B9001689A0 /* Core */ = { isa = XCSwiftPackageProductDependency; productName = Core; diff --git a/iOS/FlipMate/FlipMate/Application/Coordinator/TimerFlow/CategorySettingCoordinator.swift b/iOS/FlipMate/FlipMate/Application/Coordinator/TimerFlow/CategorySettingCoordinator.swift index 2041731..3fcfe90 100644 --- a/iOS/FlipMate/FlipMate/Application/Coordinator/TimerFlow/CategorySettingCoordinator.swift +++ b/iOS/FlipMate/FlipMate/Application/Coordinator/TimerFlow/CategorySettingCoordinator.swift @@ -8,6 +8,7 @@ import UIKit import Timer import Domain +import Category protocol CategoryFlowCoordinatorDependencies { func makeCategorySettingViewController(actions: CategoryViewModelActions) -> UIViewController diff --git a/iOS/FlipMate/FlipMate/Application/DIContainer/AppDIContainer.swift b/iOS/FlipMate/FlipMate/Application/DIContainer/AppDIContainer.swift index 1b68336..b3e4fe1 100644 --- a/iOS/FlipMate/FlipMate/Application/DIContainer/AppDIContainer.swift +++ b/iOS/FlipMate/FlipMate/Application/DIContainer/AppDIContainer.swift @@ -9,6 +9,7 @@ import UIKit import Core import Network import Timer +import CategoryService final class AppDIContainer { lazy var provider: Provider = Provider(urlSession: URLSession.shared, keychainManager: KeychainManager()) diff --git a/iOS/FlipMate/FlipMate/Application/DIContainer/CategoryDIContainer.swift b/iOS/FlipMate/FlipMate/Application/DIContainer/CategoryDIContainer.swift index 6e2bd3d..b7fd98b 100644 --- a/iOS/FlipMate/FlipMate/Application/DIContainer/CategoryDIContainer.swift +++ b/iOS/FlipMate/FlipMate/Application/DIContainer/CategoryDIContainer.swift @@ -10,6 +10,8 @@ import Timer import Domain import Network import Data +import Category +import CategoryService final class CategoryDIContainer: CategoryFlowCoordinatorDependencies { struct Dependencies { diff --git a/iOS/FlipMate/FlipMate/Application/DIContainer/LoginDIContainer.swift b/iOS/FlipMate/FlipMate/Application/DIContainer/LoginDIContainer.swift index 140ff30..ebf1e55 100644 --- a/iOS/FlipMate/FlipMate/Application/DIContainer/LoginDIContainer.swift +++ b/iOS/FlipMate/FlipMate/Application/DIContainer/LoginDIContainer.swift @@ -12,6 +12,7 @@ import Domain import Network import Core import Data +import CategoryService final class LoginDIContainer: LoginFlowCoordinatorDependencies { struct Dependencies { diff --git a/iOS/FlipMate/FlipMate/Application/DIContainer/TabBarDIContainer.swift b/iOS/FlipMate/FlipMate/Application/DIContainer/TabBarDIContainer.swift index 6a08a0f..064cc69 100644 --- a/iOS/FlipMate/FlipMate/Application/DIContainer/TabBarDIContainer.swift +++ b/iOS/FlipMate/FlipMate/Application/DIContainer/TabBarDIContainer.swift @@ -10,6 +10,7 @@ import Network import TabBar import Timer import Core +import CategoryService final class TabBarDIContainer: TabBarFlowCoordinatorDependencies { diff --git a/iOS/FlipMate/FlipMate/Application/DIContainer/TimerSceneDIContainer.swift b/iOS/FlipMate/FlipMate/Application/DIContainer/TimerSceneDIContainer.swift index 128bf8b..d22788d 100644 --- a/iOS/FlipMate/FlipMate/Application/DIContainer/TimerSceneDIContainer.swift +++ b/iOS/FlipMate/FlipMate/Application/DIContainer/TimerSceneDIContainer.swift @@ -11,6 +11,7 @@ import Domain import Network import Data import Core +import CategoryService final class TimerSceneDIContainer: TimerFlowCoordinatorDependencies { struct Dependencies { diff --git a/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Category/CategoryInfoView.swift b/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Category/CategoryInfoView.swift new file mode 100644 index 0000000..e07ff3e --- /dev/null +++ b/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Category/CategoryInfoView.swift @@ -0,0 +1,103 @@ +// +// CategoryInfoView.swift +// +// +// Created by 임현규 on 6/16/24. +// + +import UIKit + +public final class CategoryInfoView: UIView { + + // MARK: - UI Components + private let subjectLabel: UILabel = { + let label = UILabel() + label.font = FlipMateFont.mediumBold.font + label.textColor = UIColor.label + label.text = Constants.subject + return label + }() + + private let timeLabel: UILabel = { + let label = UILabel() + label.font = FlipMateFont.mediumBold.font + label.textColor = UIColor.label + return label + }() + + private let colorCircle: UIView = { + let view = UIView() + view.layer.borderColor = FlipMateColor.gray2.color?.cgColor + view.layer.borderWidth = Constants.layerWidth + view.layer.cornerRadius = Constants.circleLayerRadius + return view + }() + + // MARK: - init + public override init(frame: CGRect) { + super.init(frame: frame) + configureUI() + } + + public init(isTimerLabelHidden: Bool) { + super.init(frame: .zero) + timeLabel.isHidden = isTimerLabelHidden + configureUI() + } + + required init?(coder: NSCoder) { + fatalError("Don't use storyboard") + } + + // MARK: - Public Methods + public func updateUI(_ subjectLabelText: String, _ circleBackgroundColor: UIColor?, _ timeLabelText: String?) { + subjectLabel.text = subjectLabelText + colorCircle.backgroundColor = circleBackgroundColor + timeLabel.text = timeLabelText + } +} + +// MARK: - Private Methods +private extension CategoryInfoView { + func configureUI() { + [colorCircle, subjectLabel, timeLabel].forEach { + addSubview($0) + $0.translatesAutoresizingMaskIntoConstraints = false + } + + NSLayoutConstraint.activate([ + colorCircle.leadingAnchor.constraint(equalTo: leadingAnchor, constant: Constants.leftMargin), + colorCircle.centerYAnchor.constraint(equalTo: centerYAnchor), + colorCircle.widthAnchor.constraint(equalToConstant: Constants.circleDiameter), + colorCircle.heightAnchor.constraint(equalToConstant: Constants.circleDiameter) + ]) + + NSLayoutConstraint.activate([ + timeLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: Constants.rightMargin), + timeLabel.centerYAnchor.constraint(equalTo: centerYAnchor), + timeLabel.widthAnchor.constraint(equalToConstant: Constants.timerLabelWidth), + timeLabel.heightAnchor.constraint(equalToConstant: Constants.heigth) + ]) + + NSLayoutConstraint.activate([ + subjectLabel.leadingAnchor.constraint(equalTo: colorCircle.trailingAnchor, constant: Constants.leftMargin), + subjectLabel.trailingAnchor.constraint(equalTo: timeLabel.leadingAnchor), + subjectLabel.centerYAnchor.constraint(equalTo: centerYAnchor), + timeLabel.heightAnchor.constraint(equalToConstant: Constants.heigth) + ]) + } +} + +private extension CategoryInfoView { + enum Constants { + static let subject = "과목명" + static let leftMargin: CGFloat = 24 + static let rightMargin: CGFloat = 24 + static let circleDiameter: CGFloat = 24 + static let heigth: CGFloat = 24 + static let timerLabelWidth: CGFloat = 120 + static let layerWidth: CGFloat = 1 + static let circleLayerRadius: CGFloat = 12 + static let viewLayerRadius: CGFloat = 8 + } +} diff --git a/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Extension/UICollectionViewCell++Extension.swift b/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Extension/UICollectionViewCell++Extension.swift index 3f4a399..a3418f7 100644 --- a/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Extension/UICollectionViewCell++Extension.swift +++ b/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Extension/UICollectionViewCell++Extension.swift @@ -16,3 +16,11 @@ extension ReusableView where Self: UIView { return String(describing: self) } } + +extension UICollectionViewCell { + public func configureCategoryCellLayer() { + layer.borderWidth = 1.0 + layer.cornerRadius = 8.0 + layer.borderColor = FlipMateColor.gray2.color?.cgColor + } +} diff --git a/iOS/FlipMate/FlipMate/Timer/Package.swift b/iOS/FlipMate/FlipMate/Timer/Package.swift index 2e0a3af..e741eee 100644 --- a/iOS/FlipMate/FlipMate/Timer/Package.swift +++ b/iOS/FlipMate/FlipMate/Timer/Package.swift @@ -14,7 +14,8 @@ let package = Package( ], dependencies: [ .package(path: "../../Core"), - .package(path: "../../Domain") + .package(path: "../../Domain"), + .package(path: "../../Service") ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. @@ -23,7 +24,9 @@ let package = Package( name: "Timer", dependencies: [ .product(name: "Core", package: "Core"), - .product(name: "Domain", package: "Domain") + .product(name: "Domain", package: "Domain"), + .product(name: "CategoryService", package: "Service") + ]), .testTarget( name: "TimerTests", diff --git a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/Model/CategoryListSection.swift b/iOS/FlipMate/FlipMate/Timer/Sources/Timer/Model/CategoryListSection.swift new file mode 100644 index 0000000..3c4baba --- /dev/null +++ b/iOS/FlipMate/FlipMate/Timer/Sources/Timer/Model/CategoryListSection.swift @@ -0,0 +1,52 @@ +// +// CategoryListSection.swift +// +// +// Created by 임현규 on 6/14/24. +// + +import UIKit +import Domain + +enum CategoryListSection: Hashable { + case categorySection([CategoryListItem]) +} + +enum CategoryListItem: Hashable { + case categoryCell(StudyCategory) + + var category: StudyCategory { + switch self { + case .categoryCell(let category): + return category + } + } +} + +extension CategoryListSection { + var itemSize: NSCollectionLayoutSize { + return NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(58)) + } + + var groupSize: NSCollectionLayoutSize { + return NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(58)) + } + + var footerSize: NSCollectionLayoutSize { + return NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .absolute(130)) + } + + var sectionInset: NSDirectionalEdgeInsets { + return NSDirectionalEdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 20) + } + + var itemSpacing: CGFloat { + return 10 + } +} diff --git a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/View/CategoryListCollectionViewCell.swift b/iOS/FlipMate/FlipMate/Timer/Sources/Timer/View/CategoryListCollectionViewCell.swift index ea51e2d..a676422 100644 --- a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/View/CategoryListCollectionViewCell.swift +++ b/iOS/FlipMate/FlipMate/Timer/Sources/Timer/View/CategoryListCollectionViewCell.swift @@ -1,59 +1,34 @@ // // CategoryListCollectionViewCell.swift +// // -// -// Created by 권승용 on 5/30/24. +// Created by 임현규 on 6/16/24. // import UIKit -import Combine import DesignSystem -import Domain final class CategoryListCollectionViewCell: UICollectionViewCell { - static let identifier = "CategoryListCollectionViewCell" - - var cancellable: AnyCancellable? - - private let subjectLabel: UILabel = { - let label = UILabel() - label.font = FlipMateFont.mediumBold.font - label.textColor = UIColor.label - label.text = "과목명" - return label - }() - - private let timeLabel: UILabel = { - let label = UILabel() - label.font = FlipMateFont.mediumBold.font - label.textColor = UIColor.label - return label - }() - private let colorCircle: UIView = { - let view = UIView() - view.layer.borderColor = FlipMateColor.gray2.color?.cgColor - view.layer.borderWidth = 1 - view.layer.cornerRadius = 12 + // MARK: - UI Components + private let categoryView: CategoryInfoView = { + let view = CategoryInfoView() + view.translatesAutoresizingMaskIntoConstraints = false return view }() + // MARK: - init override init(frame: CGRect) { super.init(frame: frame) configureUI() - configureCell() + configureCategoryCellLayer() } required init?(coder: NSCoder) { fatalError("Don't use storyboard") } - func updateUI(category: StudyCategory) { - subjectLabel.text = category.subject - colorCircle.backgroundColor = UIColor(hexString: category.color) - timeLabel.text = category.studyTime?.secondsToStringTime() - } - + // MARK: - UI Update Methods func updateShadow() { if isSelected { backgroundColor = FlipMateColor.gray2.color @@ -64,48 +39,21 @@ final class CategoryListCollectionViewCell: UICollectionViewCell { } } - func setTimeLabelHidden(isHidden: Bool) { - timeLabel.isHidden = isHidden + func updateUI(_ subjectLabelText: String, _ circleBackgroundColor: UIColor?, _ timeLabelText: String?) { + categoryView.updateUI(subjectLabelText, circleBackgroundColor, timeLabelText) } } +// MARK: - Private Methods private extension CategoryListCollectionViewCell { func configureUI() { - [colorCircle, subjectLabel, timeLabel].forEach { - contentView.addSubview($0) - $0.translatesAutoresizingMaskIntoConstraints = false - } + [ categoryView ].forEach { contentView.addSubview($0) } NSLayoutConstraint.activate([ - colorCircle.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 24.0), - colorCircle.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), - colorCircle.widthAnchor.constraint(equalToConstant: 24.0), - colorCircle.heightAnchor.constraint(equalToConstant: 24.0) - ]) - - NSLayoutConstraint.activate([ - timeLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 24.0), - timeLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), - timeLabel.widthAnchor.constraint(equalToConstant: 120.0), - timeLabel.heightAnchor.constraint(equalToConstant: 24.0) - ]) - - NSLayoutConstraint.activate([ - subjectLabel.leadingAnchor.constraint(equalTo: colorCircle.trailingAnchor, constant: 24.0), - subjectLabel.trailingAnchor.constraint(equalTo: timeLabel.leadingAnchor), - subjectLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), - timeLabel.heightAnchor.constraint(equalToConstant: 24.0) + categoryView.topAnchor.constraint(equalTo: contentView.topAnchor), + categoryView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + categoryView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + categoryView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) ]) } - - func configureCell() { - layer.borderWidth = 1.0 - layer.borderColor = FlipMateColor.gray2.color?.cgColor - layer.cornerRadius = 8.0 - } -} - -@available(iOS 17.0, *) -#Preview { - CategoryListCollectionViewCell() } diff --git a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewController/TimerViewController.swift b/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewController/TimerViewController.swift index 3fe68c2..bd44a43 100644 --- a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewController/TimerViewController.swift +++ b/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewController/TimerViewController.swift @@ -12,8 +12,8 @@ import Core import DesignSystem public final class TimerViewController: BaseViewController { - typealias CateogoryDataSource = UICollectionViewDiffableDataSource - typealias Snapshot = NSDiffableDataSourceSnapshot + typealias CateogoryDataSource = UICollectionViewDiffableDataSource + typealias Snapshot = NSDiffableDataSourceSnapshot // MARK: - Properties private var timerViewModel: TimerViewModelProtocol @@ -134,10 +134,10 @@ public final class TimerViewController: BaseViewController { self.instructionImage.isHidden = false } guard var snapShot = self.dataSource?.snapshot() else { return } - let sections: [CategorySettingSection] = [.categorySection([])] + let sections: [CategoryListSection] = [.categorySection([])] snapShot.deleteAllItems() snapShot.appendSections(sections) - snapShot.appendItems(categories.map { CategorySettingItem.categoryCell($0) }) + snapShot.appendItems(categories.map { CategoryListItem.categoryCell($0) }) self.dataSource?.apply(snapShot) self.setInstructionImage() } @@ -245,9 +245,8 @@ private extension TimerViewController { switch itemIdentifier { case .categoryCell(let category): let cell: CategoryListCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) - cell.updateUI(category: category) + cell.updateUI(category.subject, UIColor(hexString: category.color), category.studyTime?.secondsToStringTime()) cell.updateShadow() - cell.setTimeLabelHidden(isHidden: false) return cell } }) @@ -267,7 +266,7 @@ private extension TimerViewController { func setSnapshot() { var snapshot = Snapshot() - let sections: [CategorySettingSection] = [.categorySection([])] + let sections: [CategoryListSection] = [.categorySection([])] snapshot.appendSections(sections) dataSource?.apply(snapshot) } diff --git a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewModel/TimerViewModel.swift b/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewModel/TimerViewModel.swift index 6966609..d373bb3 100644 --- a/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewModel/TimerViewModel.swift +++ b/iOS/FlipMate/FlipMate/Timer/Sources/Timer/ViewModel/TimerViewModel.swift @@ -10,6 +10,7 @@ import Combine import OSLog import Core import Domain +import CategoryService public struct TimerViewModelActions { let showCategorySettingViewController: () -> Void