diff --git a/MLS/.swiftlint.yml b/MLS/.swiftlint.yml index 16cb8d52..904d2c12 100644 --- a/MLS/.swiftlint.yml +++ b/MLS/.swiftlint.yml @@ -1,3 +1,7 @@ +excluded: + - "**/.build" + - "**/SourcePackages" + # 기본(default) 룰이 아닌 룰들을 활성화 opt_in_rules: - sorted_imports diff --git a/MLS/Core/.DS_Store b/MLS/Core/.DS_Store deleted file mode 100644 index aba2f814..00000000 Binary files a/MLS/Core/.DS_Store and /dev/null differ diff --git a/MLS/Core/Core.xcodeproj/project.pbxproj b/MLS/Core/Core.xcodeproj/project.pbxproj deleted file mode 100644 index 9e05efaf..00000000 --- a/MLS/Core/Core.xcodeproj/project.pbxproj +++ /dev/null @@ -1,496 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXContainerItemProxy section */ - 77BEB57B2DC268AA002FFCFC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 084A257A2DB93DDB00C395C0 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 084A25822DB93DDB00C395C0; - remoteInfo = Core; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 084A25832DB93DDB00C395C0 /* Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77BEB5762DC268AA002FFCFC /* DIContainerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DIContainerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 084A25852DB93DDB00C395C0 /* Core */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = Core; - sourceTree = ""; - }; - 77BEB5772DC268AA002FFCFC /* DIContainerTests */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = DIContainerTests; - sourceTree = ""; - }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - 084A25802DB93DDB00C395C0 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 77BEB5732DC268AA002FFCFC /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 084A25792DB93DDB00C395C0 = { - isa = PBXGroup; - children = ( - 084A25852DB93DDB00C395C0 /* Core */, - 77BEB5772DC268AA002FFCFC /* DIContainerTests */, - 084A25842DB93DDB00C395C0 /* Products */, - ); - sourceTree = ""; - }; - 084A25842DB93DDB00C395C0 /* Products */ = { - isa = PBXGroup; - children = ( - 084A25832DB93DDB00C395C0 /* Core.framework */, - 77BEB5762DC268AA002FFCFC /* DIContainerTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 084A257E2DB93DDB00C395C0 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 084A25822DB93DDB00C395C0 /* Core */ = { - isa = PBXNativeTarget; - buildConfigurationList = 084A25892DB93DDB00C395C0 /* Build configuration list for PBXNativeTarget "Core" */; - buildPhases = ( - 084A257E2DB93DDB00C395C0 /* Headers */, - 084A257F2DB93DDB00C395C0 /* Sources */, - 084A25802DB93DDB00C395C0 /* Frameworks */, - 084A25812DB93DDB00C395C0 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - 084A25852DB93DDB00C395C0 /* Core */, - ); - name = Core; - packageProductDependencies = ( - ); - productName = Core; - productReference = 084A25832DB93DDB00C395C0 /* Core.framework */; - productType = "com.apple.product-type.framework"; - }; - 77BEB5752DC268AA002FFCFC /* DIContainerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 77BEB57D2DC268AA002FFCFC /* Build configuration list for PBXNativeTarget "DIContainerTests" */; - buildPhases = ( - 77BEB5722DC268AA002FFCFC /* Sources */, - 77BEB5732DC268AA002FFCFC /* Frameworks */, - 77BEB5742DC268AA002FFCFC /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 77BEB57C2DC268AA002FFCFC /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 77BEB5772DC268AA002FFCFC /* DIContainerTests */, - ); - name = DIContainerTests; - packageProductDependencies = ( - ); - productName = DIContainerTests; - productReference = 77BEB5762DC268AA002FFCFC /* DIContainerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 084A257A2DB93DDB00C395C0 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1620; - LastUpgradeCheck = 1620; - TargetAttributes = { - 084A25822DB93DDB00C395C0 = { - CreatedOnToolsVersion = 16.2; - LastSwiftMigration = 1620; - }; - 77BEB5752DC268AA002FFCFC = { - CreatedOnToolsVersion = 16.2; - }; - }; - }; - buildConfigurationList = 084A257D2DB93DDB00C395C0 /* Build configuration list for PBXProject "Core" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 084A25792DB93DDB00C395C0; - minimizedProjectReferenceProxies = 1; - packageReferences = ( - ); - preferredProjectObjectVersion = 77; - productRefGroup = 084A25842DB93DDB00C395C0 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 084A25822DB93DDB00C395C0 /* Core */, - 77BEB5752DC268AA002FFCFC /* DIContainerTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 084A25812DB93DDB00C395C0 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 77BEB5742DC268AA002FFCFC /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 084A257F2DB93DDB00C395C0 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 77BEB5722DC268AA002FFCFC /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 77BEB57C2DC268AA002FFCFC /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - platformFilter = ios; - target = 084A25822DB93DDB00C395C0 /* Core */; - targetProxy = 77BEB57B2DC268AA002FFCFC /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 084A258A2DB93DDB00C395C0 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.Core; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 084A258B2DB93DDB00C395C0 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.Core; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; - 084A258C2DB93DDB00C395C0 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 084A258D2DB93DDB00C395C0 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 77BEB57E2DC268AA002FFCFC /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = pinocchio22.DIContainerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 77BEB57F2DC268AA002FFCFC /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = pinocchio22.DIContainerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 084A257D2DB93DDB00C395C0 /* Build configuration list for PBXProject "Core" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 084A258C2DB93DDB00C395C0 /* Debug */, - 084A258D2DB93DDB00C395C0 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 084A25892DB93DDB00C395C0 /* Build configuration list for PBXNativeTarget "Core" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 084A258A2DB93DDB00C395C0 /* Debug */, - 084A258B2DB93DDB00C395C0 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 77BEB57D2DC268AA002FFCFC /* Build configuration list for PBXNativeTarget "DIContainerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 77BEB57E2DC268AA002FFCFC /* Debug */, - 77BEB57F2DC268AA002FFCFC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 084A257A2DB93DDB00C395C0 /* Project object */; -} diff --git a/MLS/Core/Core/DIContainer/DIContainer.swift b/MLS/Core/Core/DIContainer/DIContainer.swift deleted file mode 100644 index 58830dc9..00000000 --- a/MLS/Core/Core/DIContainer/DIContainer.swift +++ /dev/null @@ -1,66 +0,0 @@ -import Foundation - -/// 의존성 주입을 위한 컨테이너 -public final class DIContainer { - static let shared = DIContainer() - // 저장 타입식별자 : [이름 식별자 : 객체 클로저] 형태 - private var services: [ObjectIdentifier: [String: () -> Any]] = [:] - private var instances: [ObjectIdentifier: [String: Any]] = [:] - // 동시성 문제를 해결하기 위한 큐 생성 - private let serviceQueue = DispatchQueue(label: "com.dicontainer.serviceQueue") - - /// 구현체 등록을 위한 함수 - /// - Parameters: - /// - type: 등록하는 객체의 타입 - /// - name: 같은 인터페이스끼리의 식별을 위한 문자열 식별자 - /// - component: 저장할 객체 - public static func register(type: T.Type, name: String? = nil, object: @escaping () -> T) { - shared.register(type: type, name: name, object: object) - } - - /// 등록된 구현체를 가져오기 위한 함수 - /// - Parameters: - /// - type: 가져올 객체의 타입 - /// - name: 가져올 객체의 식별자 - /// - Returns: 저장되어있던 객체 - public static func resolve(type: T.Type, name: String? = nil) -> T { - shared.resolve(type: type, name: name) - } -} - -private extension DIContainer { - private func register(type: T.Type, name: String?, object: @escaping () -> T) { - serviceQueue.sync { - let key = ObjectIdentifier(type) - var namedServices = services[key] ?? [:] - // 같은 식별자로 이미 저장되어있는 객체가 있다면 fatalError - if namedServices[name ?? "default"] != nil { - fatalError("\(type)타입과 \(name ?? "default")이름이 이미 존재") - } - // 문자열 식별자를 입력하지 않으면 default로 간주하여 저장 - namedServices[name ?? "default"] = { object() } - services[key] = namedServices - } - } - - private func resolve(type: T.Type, name: String?) -> T { -// serviceQueue.sync { - let key = ObjectIdentifier(type) - let nameKey = name ?? "default" - - if let instance = instances[key]?[nameKey] as? T { - return instance - } - - guard let services = services[key]?[nameKey] else { - fatalError("\(type) 타입과 \(nameKey) 이름이 등록되어 있지 않습니다.") - } - - let newInstance = services() - var namedInstances = instances[key] ?? [:] - namedInstances[nameKey] = newInstance - instances[key] = namedInstances - return newInstance as! T -// } - } -} diff --git a/MLS/Core/Core/Logger/Loggable.swift b/MLS/Core/Core/Logger/Loggable.swift deleted file mode 100644 index 03b0f3ad..00000000 --- a/MLS/Core/Core/Logger/Loggable.swift +++ /dev/null @@ -1,56 +0,0 @@ -import OSLog - -public protocol Loggable {} - -public extension Loggable { - /// 모듈 이름을 자동으로 추출 - /// - Returns: 타입이 정의된 모듈 이름 (예: "AuthFeature", "BookmarkFeature") - static var subsystem: String { - let fullName = String(reflecting: Self.self) - // "AuthFeature.LoginViewModel" -> "AuthFeature" - return fullName.components(separatedBy: ".").first ?? "Unknown" - } - - /// 타입 이름을 카테고리로 사용 - /// - Returns: 타입 이름 (예: "LoginViewModel", "BookmarkManager") - static var category: String { - String(describing: Self.self) - } - - /// 타입별로 캐싱된 Logger 인스턴스 - private static var logger: Logger { - LoggerStorage.logger(subsystem: subsystem, category: category) - } - - /// 디버그 로그 (Debug 빌드에서만 출력) - /// - Parameter message: 로그 메시지 (.private로 보호되어 민감 정보도 안전하게 로깅) - func logDebug(_ message: String) { - #if DEBUG - Self.logger.debug("\(message, privacy: .private)") - #endif - } - - /// 정보성 로그 (Notice 레벨) - /// - Parameter message: 로그 메시지 - func logNotice(_ message: String) { - Self.logger.notice("\(message)") - } - - /// 경고 로그 (비정상적이지만 처리 가능한 상황) - /// - Parameter message: 로그 메시지 - func logWarning(_ message: String) { - Self.logger.warning("\(message)") - } - - /// 에러 로그 - /// - Parameter message: 로그 메시지 - func logError(_ message: String) { - Self.logger.error("\(message)") - } - - /// 치명적 에러 로그 (앱 크래시 직전 상황) - /// - Parameter message: 로그 메시지 - func logCritical(_ message: String) { - Self.logger.critical("\(message)") - } -} diff --git a/MLS/Core/Core/Logger/LoggerStorage.swift b/MLS/Core/Core/Logger/LoggerStorage.swift deleted file mode 100644 index 701a7b43..00000000 --- a/MLS/Core/Core/Logger/LoggerStorage.swift +++ /dev/null @@ -1,47 +0,0 @@ -import OSLog - -/// Logger를 NSCache에 저장하기 위한 Wrapper 클래스 -/// Logger는 struct이므로 class로 감싸야 NSCache에 저장 가능 -private final class LoggerBox { - let logger: Logger - - init(logger: Logger) { - self.logger = logger - } -} - -/// Logger 인스턴스를 캐싱하여 재사용하는 저장소 -/// NSCache를 사용하여 메모리 압박 시 자동으로 해제되며, Thread-safe하게 동작 -final class LoggerStorage { - static let shared = LoggerStorage() - - /// 캐싱된 Logger 인스턴스들 - /// NSCache는 메모리 부족 시 자동으로 오래된 항목을 제거 - private let cache = NSCache() - - private init() {} - - /// Logger 인스턴스를 가져오거나 생성 - /// - Parameters: - /// - subsystem: 모듈 이름 (예: "AuthFeature") - /// - category: 카테고리 이름 (예: "LoginViewModel") - /// - Returns: 캐싱되거나 새로 생성된 Logger 인스턴스 - static func logger(subsystem: String, category: String) -> Logger { - shared.getOrCreateLogger(subsystem: subsystem, category: category) - } - - private func getOrCreateLogger(subsystem: String, category: String) -> Logger { - let key = "\(subsystem).\(category)" as NSString - - // 캐시에서 확인 - if let box = cache.object(forKey: key) { - return box.logger - } - - // 없으면 새로 생성 (NSCache는 thread-safe) - let newLogger = Logger(subsystem: subsystem, category: category) - let box = LoggerBox(logger: newLogger) - cache.setObject(box, forKey: key) - return newLogger - } -} diff --git a/MLS/Core/DIContainerTests/DIContainerTests.swift b/MLS/Core/DIContainerTests/DIContainerTests.swift deleted file mode 100644 index d8988c2c..00000000 --- a/MLS/Core/DIContainerTests/DIContainerTests.swift +++ /dev/null @@ -1,113 +0,0 @@ -import XCTest - -@testable import Core - -class DIContainerTests: XCTestCase { - - /// 객체 등록 및 가져오기 테스트 - func testRegisterAndResolve() { - DIContainer.register(type: Service.self, name: "A") { ServiceA() } - DIContainer.register(type: Service.self, name: "B") { ServiceB() } - - let serviceA: Service? = DIContainer.resolve(type: Service.self, name: "A") - let serviceB: Service? = DIContainer.resolve(type: Service.self, name: "B") - - XCTAssertNotNil(serviceA) - XCTAssertNotNil(serviceB) - XCTAssertEqual(serviceA?.perform(), "ServiceA") - XCTAssertEqual(serviceB?.perform(), "ServiceB") - } - - /// 기본 이름 동작 테스트 - func testDefaultName() { - DIContainer.register(type: Service.self) { ServiceA() as Service } - - let service: Service? = DIContainer.resolve(type: Service.self) - - XCTAssertNotNil(service) - XCTAssertEqual(service?.perform(), "ServiceA") - } - - // 중복 등록 시 fatalError 테스트 - // fatalError 구문은 주석처리하고 테스트 진행 -// func testDuplicateRegistration() { -// DIContainer.register(type: Service.self, name: "A") { ServiceA() as Service } -// DIContainer.register(type: Service.self, name: "A") { ServiceB() as Service } -// -// let service: Service? = DIContainer.resolve(type: Service.self, name: "A") -// XCTAssertNotNil(service) -// XCTAssertEqual(service?.perform(), "ServiceB", "Expected ServiceB to override ServiceA for name 'A'") -// } - - /// 등록되지 않은 객체 불러오기 테스트 - func testResolveUnregistered() { - let service: Service? = DIContainer.resolve(type: Service.self, name: "Unknown") - - XCTAssertNil(service) - } - - /// 타입 안전성 테스트 - func testTypeSafety() { - DIContainer.register(type: Service.self, name: "A") { ServiceA() as Service } - - let anotherService: AnotherService? = DIContainer.resolve(type: AnotherService.self, name: "A") - - XCTAssertNil(anotherService) - } - - /// 동시성 테스트 (여러 스레드에서 register/resolve 호출) - func testConcurrency() { - let expectation = XCTestExpectation(description: "Concurrent register and resolve") - let queue = DispatchQueue(label: "test.concurrent.queue", attributes: .concurrent) - let group = DispatchGroup() - - // 동시에 등록 및 호출 - for index in 0..<100 { - group.enter() - queue.async { - let name = "Test\(index)" - DIContainer.register(type: Service.self, name: name) { - index % 2 == 0 ? ServiceA() as Service : ServiceB() as Service - } - let service: Service? = DIContainer.resolve(type: Service.self, name: name) - XCTAssertNotNil(service) - XCTAssertEqual(service?.perform(), index % 2 == 0 ? "ServiceA" : "ServiceB") - group.leave() - } - } - - group.notify(queue: .main) { - expectation.fulfill() - } - wait(for: [expectation], timeout: 5.0) - } -} - -/// 테스트를 위한 임시 객체 -extension DIContainerTests { - protocol Service { - func perform() -> String - } - - class ServiceA: Service { - func perform() -> String { - return "ServiceA" - } - } - - class ServiceB: Service { - func perform() -> String { - return "ServiceB" - } - } - - protocol AnotherService { - func execute() -> String - } - - class AnotherServiceX: AnotherService { - func execute() -> String { - return "AnotherServiceX" - } - } -} diff --git a/MLS/Core/DIContainerTests/LoggerTests.swift b/MLS/Core/DIContainerTests/LoggerTests.swift deleted file mode 100644 index 1201aeb1..00000000 --- a/MLS/Core/DIContainerTests/LoggerTests.swift +++ /dev/null @@ -1,119 +0,0 @@ -@testable import Core -import Testing - -struct TestViewModel: Loggable { - func testLogs() { - logDebug("Debug 로그 테스트") - logNotice("Notice 로그 테스트") - logWarning("Warning 로그 테스트") - logError("Error 로그 테스트") - logCritical("Critical 로그 테스트") - } -} - -class TestManager: Loggable { - func testLogs() { - logDebug("Debug 로그 (class)") - logNotice("Notice 로그 (class)") - logWarning("Warning 로그 (class)") - logError("Error 로그 (class)") - logCritical("Critical 로그 (class)") - } -} - -struct LoggerTests { - - @Test("Subsystem 자동 추출 검증 - Struct") - func testSubsystemExtractionStruct() async throws { - // subsystem은 타입이 정의된 모듈 이름이어야 함 - let subsystem = TestViewModel.subsystem - let category = TestViewModel.category - - // DIContainerTests 모듈에 정의되어 있으므로 - #expect(subsystem == "DIContainerTests") - #expect(category == "TestViewModel") - } - - @Test("Subsystem 자동 추출 검증 - Class") - func testSubsystemExtractionClass() async throws { - let subsystem = TestManager.subsystem - let category = TestManager.category - - #expect(subsystem == "DIContainerTests") - #expect(category == "TestManager") - } - - @Test("Struct에서 Loggable 사용 테스트") - func testLoggableWithStruct() async throws { - let viewModel = TestViewModel() - - // 로그 출력 (Console.app에서 확인 가능) - // subsystem: "DIContainerTests", category: "TestViewModel" - viewModel.testLogs() - - // 에러 없이 실행되면 성공 - #expect(true) - } - - @Test("Class에서 Loggable 사용 테스트") - func testLoggableWithClass() async throws { - let manager = TestManager() - - // 로그 출력 (Console.app에서 확인 가능) - // subsystem: "DIContainerTests", category: "TestManager" - manager.testLogs() - - // 에러 없이 실행되면 성공 - #expect(true) - } - - @Test("동시성 안전성 테스트 - 같은 Logger 동시 접근") - func testConcurrentLoggerAccess() async throws { - // 100개 스레드가 동시에 같은 Logger 사용 - await withTaskGroup(of: Void.self) { group in - for i in 0..<100 { - group.addTask { - let vm = TestViewModel() - vm.logNotice("동시 로그 \(i)") - } - } - } - - // 크래시 없이 완료되면 성공 - #expect(true) - } - - @Test("여러 타입에서 동시에 로깅") - func testMultipleTypesConcurrent() async throws { - let viewModel = TestViewModel() - let manager = TestManager() - - // 동시에 로그 출력 - await withTaskGroup(of: Void.self) { group in - group.addTask { - viewModel.testLogs() - } - group.addTask { - manager.testLogs() - } - } - - // 에러 없이 실행되면 성공 - #expect(true) - } - - @Test("NSCache 캐싱 검증 - 반복 접근 시 정상 동작") - func testLoggerCaching() async throws { - let vm1 = TestViewModel() - let vm2 = TestViewModel() - - // 같은 타입에서 여러 번 로깅 (NSCache에서 캐싱된 Logger 재사용) - vm1.logNotice("첫 번째 인스턴스") - vm2.logNotice("두 번째 인스턴스") - vm1.logNotice("다시 첫 번째") - vm2.logNotice("다시 두 번째") - - // 크래시 없이 정상 동작하면 성공 - #expect(true) - } -} diff --git a/MLS/Data/.DS_Store b/MLS/Data/.DS_Store deleted file mode 100644 index 6cc22ba6..00000000 Binary files a/MLS/Data/.DS_Store and /dev/null differ diff --git a/MLS/Data/Data.xcodeproj/project.pbxproj b/MLS/Data/Data.xcodeproj/project.pbxproj deleted file mode 100644 index d8c34ba4..00000000 --- a/MLS/Data/Data.xcodeproj/project.pbxproj +++ /dev/null @@ -1,652 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXBuildFile section */ - 080175492DCD274B00D0919F /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 080175482DCD274B00D0919F /* DomainInterface.framework */; }; - 084A25C82DB93E0500C395C0 /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 084A25C72DB93E0500C395C0 /* Core.framework */; }; - 08E425522DF17B5C00D6ACD3 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08E425512DF17B5C00D6ACD3 /* DomainInterface.framework */; }; - 08E425562DF17B6300D6ACD3 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08E425552DF17B6300D6ACD3 /* RxSwift */; }; - 08ED49342DCFDF78002C21A2 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08ED49332DCFDF78002C21A2 /* RxSwift */; }; - 775D78522E0DA93400DDAD2F /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0879294D2DB90F3A009C301F /* Data.framework */; }; - 77660F932DD259AA007A4EF3 /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 77660F922DD259AA007A4EF3 /* KakaoSDKAuth */; }; - 77660F952DD259AA007A4EF3 /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 77660F942DD259AA007A4EF3 /* KakaoSDKUser */; }; - 77F0E9522EA525CE007368F8 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 77F0E9512EA525CE007368F8 /* RxCocoa */; }; - 77FEEF222EC5B2300023197A /* AuthFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77FEEF202EC5B2300023197A /* AuthFeatureInterface.framework */; }; - 77FEEF242EC5B2300023197A /* DictionaryFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77FEEF212EC5B2300023197A /* DictionaryFeatureInterface.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 775D78542E0DA93400DDAD2F /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 087929442DB90F3A009C301F /* Project object */; - proxyType = 1; - remoteGlobalIDString = 0879294C2DB90F3A009C301F; - remoteInfo = Data; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 080175482DCD274B00D0919F /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 084A256E2DB93C1400C395C0 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 084A25722DB93C1900C395C0 /* MLSCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MLSCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 084A25C72DB93E0500C395C0 /* Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 0879294D2DB90F3A009C301F /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 08E4253B2DF17B3700D6ACD3 /* DataMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DataMock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 08E425512DF17B5C00D6ACD3 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77FEEF202EC5B2300023197A /* AuthFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77FEEF212EC5B2300023197A /* DictionaryFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DictionaryFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 0879294F2DB90F3A009C301F /* Data */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = Data; - sourceTree = ""; - }; - 08E4253C2DF17B3700D6ACD3 /* DataMock */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = DataMock; - sourceTree = ""; - }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - 0879294A2DB90F3A009C301F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 08ED49342DCFDF78002C21A2 /* RxSwift in Frameworks */, - 080175492DCD274B00D0919F /* DomainInterface.framework in Frameworks */, - 084A25C82DB93E0500C395C0 /* Core.framework in Frameworks */, - 77660F952DD259AA007A4EF3 /* KakaoSDKUser in Frameworks */, - 77660F932DD259AA007A4EF3 /* KakaoSDKAuth in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 08E425382DF17B3700D6ACD3 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 775D78522E0DA93400DDAD2F /* Data.framework in Frameworks */, - 08E425562DF17B6300D6ACD3 /* RxSwift in Frameworks */, - 77F0E9522EA525CE007368F8 /* RxCocoa in Frameworks */, - 77FEEF242EC5B2300023197A /* DictionaryFeatureInterface.framework in Frameworks */, - 77FEEF222EC5B2300023197A /* AuthFeatureInterface.framework in Frameworks */, - 08E425522DF17B5C00D6ACD3 /* DomainInterface.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 084A256D2DB93C1400C395C0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 77FEEF202EC5B2300023197A /* AuthFeatureInterface.framework */, - 77FEEF212EC5B2300023197A /* DictionaryFeatureInterface.framework */, - 08E425512DF17B5C00D6ACD3 /* DomainInterface.framework */, - 080175482DCD274B00D0919F /* DomainInterface.framework */, - 084A25C72DB93E0500C395C0 /* Core.framework */, - 084A25722DB93C1900C395C0 /* MLSCore.framework */, - 084A256E2DB93C1400C395C0 /* DomainInterface.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 087929432DB90F3A009C301F = { - isa = PBXGroup; - children = ( - 0879294F2DB90F3A009C301F /* Data */, - 08E4253C2DF17B3700D6ACD3 /* DataMock */, - 084A256D2DB93C1400C395C0 /* Frameworks */, - 0879294E2DB90F3A009C301F /* Products */, - ); - sourceTree = ""; - }; - 0879294E2DB90F3A009C301F /* Products */ = { - isa = PBXGroup; - children = ( - 0879294D2DB90F3A009C301F /* Data.framework */, - 08E4253B2DF17B3700D6ACD3 /* DataMock.framework */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 087929482DB90F3A009C301F /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 08E425362DF17B3700D6ACD3 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 0879294C2DB90F3A009C301F /* Data */ = { - isa = PBXNativeTarget; - buildConfigurationList = 087929532DB90F3A009C301F /* Build configuration list for PBXNativeTarget "Data" */; - buildPhases = ( - 087929482DB90F3A009C301F /* Headers */, - 087929492DB90F3A009C301F /* Sources */, - 0879294A2DB90F3A009C301F /* Frameworks */, - 0879294B2DB90F3A009C301F /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - 0879294F2DB90F3A009C301F /* Data */, - ); - name = Data; - packageProductDependencies = ( - 08ED49332DCFDF78002C21A2 /* RxSwift */, - 77660F922DD259AA007A4EF3 /* KakaoSDKAuth */, - 77660F942DD259AA007A4EF3 /* KakaoSDKUser */, - ); - productName = Data; - productReference = 0879294D2DB90F3A009C301F /* Data.framework */; - productType = "com.apple.product-type.framework"; - }; - 08E4253A2DF17B3700D6ACD3 /* DataMock */ = { - isa = PBXNativeTarget; - buildConfigurationList = 08E4254B2DF17B3700D6ACD3 /* Build configuration list for PBXNativeTarget "DataMock" */; - buildPhases = ( - 08E425362DF17B3700D6ACD3 /* Headers */, - 08E425372DF17B3700D6ACD3 /* Sources */, - 08E425382DF17B3700D6ACD3 /* Frameworks */, - 08E425392DF17B3700D6ACD3 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 775D78552E0DA93400DDAD2F /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 08E4253C2DF17B3700D6ACD3 /* DataMock */, - ); - name = DataMock; - packageProductDependencies = ( - 08E425552DF17B6300D6ACD3 /* RxSwift */, - 77F0E9512EA525CE007368F8 /* RxCocoa */, - ); - productName = DataMock; - productReference = 08E4253B2DF17B3700D6ACD3 /* DataMock.framework */; - productType = "com.apple.product-type.framework"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 087929442DB90F3A009C301F /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1620; - LastUpgradeCheck = 1620; - TargetAttributes = { - 0879294C2DB90F3A009C301F = { - CreatedOnToolsVersion = 16.2; - }; - 08E4253A2DF17B3700D6ACD3 = { - CreatedOnToolsVersion = 16.2; - LastSwiftMigration = 1620; - }; - }; - }; - buildConfigurationList = 087929472DB90F3A009C301F /* Build configuration list for PBXProject "Data" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 087929432DB90F3A009C301F; - minimizedProjectReferenceProxies = 1; - packageReferences = ( - 0858ABFE2DCFDC340060EBCA /* XCRemoteSwiftPackageReference "RxSwift" */, - 77660F912DD259AA007A4EF3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, - ); - preferredProjectObjectVersion = 77; - productRefGroup = 0879294E2DB90F3A009C301F /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 0879294C2DB90F3A009C301F /* Data */, - 08E4253A2DF17B3700D6ACD3 /* DataMock */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 0879294B2DB90F3A009C301F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 08E425392DF17B3700D6ACD3 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 087929492DB90F3A009C301F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 08E425372DF17B3700D6ACD3 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 775D78552E0DA93400DDAD2F /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 0879294C2DB90F3A009C301F /* Data */; - targetProxy = 775D78542E0DA93400DDAD2F /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 087929542DB90F3A009C301F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.Data; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 087929552DB90F3A009C301F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.Data; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; - 087929562DB90F3A009C301F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 087929572DB90F3A009C301F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 08E4254C2DF17B3700D6ACD3 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALLOW_TARGET_PLATFORM_SPECIALIZATION = NO; - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 15.2; - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.DataMock; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = auto; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - XROS_DEPLOYMENT_TARGET = 2.2; - }; - name = Debug; - }; - 08E4254D2DF17B3700D6ACD3 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALLOW_TARGET_PLATFORM_SPECIALIZATION = NO; - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 15.2; - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.DataMock; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = auto; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - XROS_DEPLOYMENT_TARGET = 2.2; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 087929472DB90F3A009C301F /* Build configuration list for PBXProject "Data" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 087929562DB90F3A009C301F /* Debug */, - 087929572DB90F3A009C301F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 087929532DB90F3A009C301F /* Build configuration list for PBXNativeTarget "Data" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 087929542DB90F3A009C301F /* Debug */, - 087929552DB90F3A009C301F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 08E4254B2DF17B3700D6ACD3 /* Build configuration list for PBXNativeTarget "DataMock" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 08E4254C2DF17B3700D6ACD3 /* Debug */, - 08E4254D2DF17B3700D6ACD3 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - 0858ABFE2DCFDC340060EBCA /* XCRemoteSwiftPackageReference "RxSwift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactiveX/RxSwift"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 6.9.0; - }; - }; - 77660F912DD259AA007A4EF3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/kakao/kakao-ios-sdk"; - requirement = { - branch = master; - kind = branch; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - 08E425552DF17B6300D6ACD3 /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 0858ABFE2DCFDC340060EBCA /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; - 08ED49332DCFDF78002C21A2 /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 0858ABFE2DCFDC340060EBCA /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; - 77660F922DD259AA007A4EF3 /* KakaoSDKAuth */ = { - isa = XCSwiftPackageProductDependency; - package = 77660F912DD259AA007A4EF3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; - productName = KakaoSDKAuth; - }; - 77660F942DD259AA007A4EF3 /* KakaoSDKUser */ = { - isa = XCSwiftPackageProductDependency; - package = 77660F912DD259AA007A4EF3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; - productName = KakaoSDKUser; - }; - 77F0E9512EA525CE007368F8 /* RxCocoa */ = { - isa = XCSwiftPackageProductDependency; - package = 0858ABFE2DCFDC340060EBCA /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxCocoa; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = 087929442DB90F3A009C301F /* Project object */; -} diff --git a/MLS/Data/Data/.DS_Store b/MLS/Data/Data/.DS_Store deleted file mode 100644 index caa2177c..00000000 Binary files a/MLS/Data/Data/.DS_Store and /dev/null differ diff --git a/MLS/Data/Data/Errors/NetworkError.swift b/MLS/Data/Data/Errors/NetworkError.swift deleted file mode 100644 index 203220d5..00000000 --- a/MLS/Data/Data/Errors/NetworkError.swift +++ /dev/null @@ -1,39 +0,0 @@ -import Foundation - -/// 네트워크 레이어에서 사용하는 에러 -public enum NetworkError: Error, Equatable { - case providerDeallocated - case urlRequest(Error) - case network(Error) - case invalidResponse - case noData - case decodeError(Error) - case httpError - case retryError(Error) - case statusError(Int, String) - case retry - - public static func == (lhs: NetworkError, rhs: NetworkError) -> Bool { - switch (lhs, rhs) { - case (.providerDeallocated, .providerDeallocated), - (.invalidResponse, .invalidResponse), - (.noData, .noData), - (.httpError, .httpError), - (.retry, .retry): - return true - - case let (.statusError(code1, msg1), .statusError(code2, msg2)): - return code1 == code2 && msg1 == msg2 - - // 연관된 Error 타입은 Equatable이 아니므로 항상 false로 비교 (혹은 타입 이름 비교 정도만 가능) - case (.urlRequest, .urlRequest), - (.network, .network), - (.decodeError, .decodeError), - (.retryError, .retryError): - return false - - default: - return false - } - } -} diff --git a/MLS/Data/Data/Network/.DS_Store b/MLS/Data/Data/Network/.DS_Store deleted file mode 100644 index a57661e1..00000000 Binary files a/MLS/Data/Data/Network/.DS_Store and /dev/null differ diff --git a/MLS/Data/Data/Network/DTO/AuthDTO/AuthResponseDTO.swift b/MLS/Data/Data/Network/DTO/AuthDTO/AuthResponseDTO.swift deleted file mode 100644 index c3b86950..00000000 --- a/MLS/Data/Data/Network/DTO/AuthDTO/AuthResponseDTO.swift +++ /dev/null @@ -1,24 +0,0 @@ -import DomainInterface - -public struct AuthResponseDTO: Decodable { - public let accessToken: String - public let refreshToken: String - public let member: MemberDTO? -} - -public extension AuthResponseDTO { - func toLoginDomain() -> LoginResponse { - return .init( - isRegister: member != nil, - accessToken: accessToken, - refreshToken: refreshToken - ) - } - - func toSignUpDomain() -> SignUpResponse { - return .init( - accessToken: accessToken, - refreshToken: refreshToken - ) - } -} diff --git a/MLS/Data/Data/Network/DTO/BookmarkDTO/SetBookmarkDTO.swift b/MLS/Data/Data/Network/DTO/BookmarkDTO/SetBookmarkDTO.swift deleted file mode 100644 index bb287fbb..00000000 --- a/MLS/Data/Data/Network/DTO/BookmarkDTO/SetBookmarkDTO.swift +++ /dev/null @@ -1,11 +0,0 @@ -import DomainInterface - -public struct SetBookmarkDTO: Decodable { - public let bookmarkId: Int - public let bookmarkType: String - public let resourceId: Int - - public func toDomain() -> Int { - return self.bookmarkId - } -} diff --git a/MLS/Data/Data/Network/DTO/CollectionDTO/CollectionListResponseDTO.swift b/MLS/Data/Data/Network/DTO/CollectionDTO/CollectionListResponseDTO.swift deleted file mode 100644 index 24b01d40..00000000 --- a/MLS/Data/Data/Network/DTO/CollectionDTO/CollectionListResponseDTO.swift +++ /dev/null @@ -1,12 +0,0 @@ -import DomainInterface - -public struct CollectionListResponseDTO: Decodable { - public let collectionId: Int - public let name: String - public let createdAt: [Int] - public let recentBookmarks: [BookmarkDTO] - - public func toDomain() -> CollectionResponse { - return CollectionResponse(collectionId: collectionId, name: name, createdAt: createdAt, recentBookmarks: recentBookmarks.toDomain()) - } -} diff --git a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryAllDTO.swift b/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryAllDTO.swift deleted file mode 100644 index 074cd7fe..00000000 --- a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryAllDTO.swift +++ /dev/null @@ -1,9 +0,0 @@ -public struct DictionaryAllDTO: DictionaryDTOProtocol { - public let originalId: Int - public let name: String - public let imageUrl: String? - public let level: Int? - public let type: String - public let bookmarkId: Int? - public var id: Int { originalId } -} diff --git a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryItemDTO.swift b/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryItemDTO.swift deleted file mode 100644 index 9d5de1f3..00000000 --- a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryItemDTO.swift +++ /dev/null @@ -1,9 +0,0 @@ -public struct DictionaryItemDTO: DictionaryDTOProtocol { - public let itemId: Int - public let name: String - public let imageUrl: String? - public let level: Int? - public let type: String - public let bookmarkId: Int? - public var id: Int { itemId } -} diff --git a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryMapDTO.swift b/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryMapDTO.swift deleted file mode 100644 index 00e44cd9..00000000 --- a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryMapDTO.swift +++ /dev/null @@ -1,9 +0,0 @@ -public struct DictionaryMapDTO: DictionaryDTOProtocol { - public let mapId: Int - public let name: String - public let imageUrl: String? - public let level: Int? - public let type: String - public let bookmarkId: Int? - public var id: Int { mapId } -} diff --git a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryMonsterDTO.swift b/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryMonsterDTO.swift deleted file mode 100644 index 86e39644..00000000 --- a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryMonsterDTO.swift +++ /dev/null @@ -1,9 +0,0 @@ -public struct DictionaryMonsterDTO: DictionaryDTOProtocol { - public let monsterId: Int - public let name: String - public let imageUrl: String? - public let level: Int? - public let type: String - public let bookmarkId: Int? - public var id: Int { monsterId } -} diff --git a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryNPCDTO.swift b/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryNPCDTO.swift deleted file mode 100644 index 9ce8ea14..00000000 --- a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryNPCDTO.swift +++ /dev/null @@ -1,9 +0,0 @@ -public struct DictionaryNPCDTO: DictionaryDTOProtocol { - public let npcId: Int - public let name: String - public let imageUrl: String? - public let level: Int? - public let type: String - public let bookmarkId: Int? - public var id: Int { npcId } -} diff --git a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryQuestDTO.swift b/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryQuestDTO.swift deleted file mode 100644 index 7382ac99..00000000 --- a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryQuestDTO.swift +++ /dev/null @@ -1,9 +0,0 @@ -public struct DictionaryQuestDTO: DictionaryDTOProtocol { - public let questId: Int - public let name: String - public let imageUrl: String? - public let level: Int? - public let type: String - public let bookmarkId: Int? - public var id: Int { questId } -} diff --git a/MLS/Data/Data/Network/Endpoints/AuthEndPoint.swift b/MLS/Data/Data/Network/Endpoints/AuthEndPoint.swift deleted file mode 100644 index b9167db3..00000000 --- a/MLS/Data/Data/Network/Endpoints/AuthEndPoint.swift +++ /dev/null @@ -1,142 +0,0 @@ -import DomainInterface - -public enum AuthEndPoint { - static let base = "https://mapleland.2megabytes.me" - - public static func fetchProfile() -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/auth/me", - method: .GET - ) - } - - public static func loginWithKakao(credential: Credential) -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/auth/login/kakao", - method: .POST, - headers: ["access-token": credential.token] - ) - } - - public static func loginWithApple(credential: Credential) -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/auth/login/apple", - method: .POST, - headers: ["id-token": credential.token] - ) - } - - public static func signupWithKakao(credential: String, body: Encodable) -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/auth/signup/kakao", - method: .POST, - headers: ["access-token": credential], - body: body - ) - } - - public static func signupWithApple(credential: String, body: Encodable) -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/auth/signup/apple", - method: .POST, - headers: ["id-token": credential], - body: body - ) - } - - public static func reIssueToken(refreshToken: String) -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/auth/reissue", - method: .POST, - headers: [ - "accept": "*/*", - "refresh-token": refreshToken - ] - ) - } - - public static func fcmToken(body: Encodable) -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/auth/member/fcm-token", - method: .PUT, - body: body - ) - } - - public static func withdraw() -> EndPoint { - .init( - baseURL: base, - path: "/api/v1/auth/member", - method: .DELETE - ) - } - - public static func updateMarketingAgreement(credential: String, body: Encodable) -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/auth/member/marketing-agreement", - method: .PUT, - headers: ["Authorization": "Bearer \(credential)"], - body: body - ) - } - - public static func fetchJobs() -> ResponsableEndPoint<[JobsDTO]> { - .init( - baseURL: base, - path: "/api/v1/jobs", - method: .GET - ) - } - - public static func fetchJob(jobId: String) -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/jobs/\(jobId)", - method: .GET - ) - } - - public static func updateCharacterInfo(body: Encodable) -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/auth/member/profile", - method: .PUT, - body: body - ) - } - - public static func updateNotification(body: Encodable) -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/auth/member/alert-agreement", - method: .PUT, - body: body - ) - } - - public static func updateNickName(body: Encodable) -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/auth/member/nickname", - method: .PUT, - body: body - ) - } - - public static func updateProfileImage(body: Encodable) -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/auth/member/profile-image", - method: .PUT, - body: body - ) - } -} diff --git a/MLS/Data/Data/Network/Endpoints/BookmarkEndPoint.swift b/MLS/Data/Data/Network/Endpoints/BookmarkEndPoint.swift deleted file mode 100644 index 41373970..00000000 --- a/MLS/Data/Data/Network/Endpoints/BookmarkEndPoint.swift +++ /dev/null @@ -1,76 +0,0 @@ -import DomainInterface - -public enum BookmarkEndPoint { - static let base = "https://mapleland.2megabytes.me" - - public static func setBookmark(body: Encodable) -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/bookmarks", - method: .POST, - body: body - ) - } - - public static func deleteBookmark(bookmarkId: Int) -> ResponsableEndPoint { - .init( - baseURL: base, - path: "/api/v1/bookmarks/\(bookmarkId)", - method: .DELETE - ) - } - - public static func fetchBookmark(query: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { - .init( - baseURL: base, - path: "/api/v1/bookmarks", - method: .GET, - query: query - ) - } - - public static func fetchMonsterBookmark(query: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { - .init( - baseURL: base, - path: "/api/v1/bookmarks/monsters", - method: .GET, - query: query - ) - } - - public static func fetchNPCBookmark(query: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { - .init( - baseURL: base, - path: "/api/v1/bookmarks/npcs", - method: .GET, - query: query - ) - } - - public static func fetchQuestBookmark(query: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { - .init( - baseURL: base, - path: "/api/v1/bookmarks/quests", - method: .GET, - query: query - ) - } - - public static func fetchItemBookmark(query: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { - .init( - baseURL: base, - path: "/api/v1/bookmarks/items", - method: .GET, - query: query - ) - } - - public static func fetchMapBookmark(query: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { - .init( - baseURL: base, - path: "/api/v1/bookmarks/maps", - method: .GET, - query: query - ) - } -} diff --git a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift deleted file mode 100644 index dcdf838c..00000000 --- a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift +++ /dev/null @@ -1,52 +0,0 @@ -import DomainInterface - -public enum CollectionEndPoint { - static let base = "https://mapleland.2megabytes.me" - - public static func fetchCollectionList(query: Encodable) -> ResponsableEndPoint<[CollectionListResponseDTO]> { - .init( - baseURL: base, - path: "/api/v1/collections", - method: .GET, - query: query - ) - } - - public static func createCollectionList(body: Encodable) -> EndPoint { - .init(baseURL: base, path: "/api/v1/collections", method: .POST, body: body) - } - - public static func fetchCollection(id: Int) -> ResponsableEndPoint<[BookmarkDTO]> { - .init( - baseURL: base, - path: "/api/v1/collections/\(id)/bookmarks", - method: .GET - ) - } - - public static func setCollectionName(id: Int, body: Encodable) -> EndPoint { - .init( - baseURL: base, - path: "/api/v1/collections/\(id)", - method: .PUT, - body: body - ) - } - - public static func deleteCollection(id: Int) -> EndPoint { - .init( - baseURL: base, - path: "/api/v1/collections/\(id)", - method: .DELETE - ) - } - - public static func addCollectionAndBookmark(body: Encodable) -> EndPoint { - .init( - baseURL: base, - path: "/api/v1/bookmark-collections", - method: .POST, - body: body - ) - } -} diff --git a/MLS/Data/Data/Network/Endpoints/EndPoint.swift b/MLS/Data/Data/Network/Endpoints/EndPoint.swift deleted file mode 100644 index fd56584f..00000000 --- a/MLS/Data/Data/Network/Endpoints/EndPoint.swift +++ /dev/null @@ -1,29 +0,0 @@ -import Foundation - -import DomainInterface - -/// 응답값이 없는 엔드포인트 -public struct EndPoint: Requestable { - public var baseURL: String - public var path: String - public var method: HTTPMethod - public var query: Encodable? - public var headers: [String: String]? - public var body: Encodable? - - public init( - baseURL: String, - path: String, - method: HTTPMethod, - query: Encodable? = nil, - headers: [String: String]? = nil, - body: Encodable? = nil - ) { - self.baseURL = baseURL - self.path = path - self.method = method - self.query = query - self.headers = headers - self.body = body - } -} diff --git a/MLS/Data/Data/Network/Endpoints/ResponsableEndPoint.swift b/MLS/Data/Data/Network/Endpoints/ResponsableEndPoint.swift deleted file mode 100644 index 1a00736c..00000000 --- a/MLS/Data/Data/Network/Endpoints/ResponsableEndPoint.swift +++ /dev/null @@ -1,31 +0,0 @@ -import Foundation - -import DomainInterface - -/// 응답값이 있는 엔드포인트 -public struct ResponsableEndPoint: Requestable, Responsable { - public typealias Response = T - - public var baseURL: String - public var path: String - public var method: HTTPMethod - public var query: (any Encodable)? - public var headers: [String: String]? - public var body: (any Encodable)? - - public init( - baseURL: String, - path: String, - method: HTTPMethod, - query: (any Encodable)? = nil, - headers: [String: String]? = nil, - body: (any Encodable)? = nil - ) { - self.baseURL = baseURL - self.path = path - self.method = method - self.query = query - self.headers = headers - self.body = body - } -} diff --git a/MLS/Data/Data/Providers/Network/NetworkProviderImpl.swift b/MLS/Data/Data/Providers/Network/NetworkProviderImpl.swift deleted file mode 100644 index 20a25e4e..00000000 --- a/MLS/Data/Data/Providers/Network/NetworkProviderImpl.swift +++ /dev/null @@ -1,176 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public final class NetworkProviderImpl: NetworkProvider { - - private let session: URLSession - - private let retryAttempt: Int = 2 - - public init() { - let configuration = URLSessionConfiguration.default - configuration.timeoutIntervalForRequest = 30 - configuration.timeoutIntervalForResource = 60 - self.session = URLSession(configuration: configuration) - } - - public func requestData(endPoint: T, interceptor: Interceptor?) -> Observable { - return Observable.create { [weak self] observer in - print("🚀 requestData: 요청 시작 - \(endPoint)") - - self?.sendRequest(endPoint: endPoint, interceptor: interceptor, completion: { result in - - switch result { - case .success(let data): - print("✅ requestData: 응답 수신") - - if let data = data { - print("📦 requestData: 응답 데이터 있음 - \(String(data: data, encoding: .utf8) ?? "디코딩 실패")") - do { - let decoded = try JSONDecoder().decode(APIDefaultResponseDTO.self, from: data) - print("🎯 requestData: 디코딩 성공 - \(decoded)") - if let decodedData = decoded.data { - observer.onNext(decodedData) - } else { - if T.Response.self == EmptyResponseDTO.self { - observer.onNext(EmptyResponseDTO() as! T.Response) - } else { - observer.onError(NetworkError.invalidResponse) - } - } - observer.onCompleted() - } catch { - print("❌ requestData: 디코딩 실패 - \(error)") - observer.onError(NetworkError.decodeError(error)) - } - } else { - print("⚠️ requestData: 응답 데이터 없음") - observer.onError(NetworkError.noData) - } - - case .failure(let error): - print("🔥 requestData: 네트워크 실패 - \(error)") - observer.onError(error) - } - }) - - return Disposables.create() - } - .retry(when: { (errors: Observable) in - errors - .enumerated() - .flatMap { attempt, error -> Observable in - print("🔁 requestData: 재시도 \(attempt + 1)회 - 에러: \(error)") - if attempt < self.retryAttempt, let networkError = error as? NetworkError, networkError == .retry { - return Observable.just(()) - } else { - return Observable.error(error) - } - } - }) - } - - public func requestData(endPoint: Requestable, interceptor: Interceptor?) -> Completable { - return Completable.create { [weak self] completable in - self?.sendRequest(endPoint: endPoint, interceptor: interceptor, completion: { result in - switch result { - case .success(let data): - if data != nil { - completable(.completed) - } else { - completable(.error(NetworkError.noData)) - } - case .failure(let error): - completable(.error(error)) - } - }) - return Disposables.create() - } - .retry(when: { (errors: Observable) in - errors - .enumerated() - .flatMap { attempt, error -> Observable in - if attempt < self.retryAttempt, let networkError = error as? NetworkError, networkError == .retry { - return Observable.just(()) - } else { - return Observable.error(error) - } - } - }) - } -} - -private extension NetworkProviderImpl { - /// 엔드 포인트를 이용하여 요청을 보내기 위한 함수 - /// - Parameters: - /// - endPoint: 요청을 위한 엔드포인트 객체 - /// - completion: 응답 결과 - func sendRequest(endPoint: T, interceptor: Interceptor?, completion: @escaping (Result) -> Void) { - do { - var request = try endPoint.getUrlRequest() - if let interceptor = interceptor { request = interceptor.adapt(request) } - let task = session.dataTask(with: request) { [weak self] data, response, error in - guard let self else { - completion(.failure(.providerDeallocated)) - return - } - let taskResult = checkValidation(data: data, response: response, error: error, interceptor: interceptor) - switch taskResult { - case .success(let data): - completion(.success(data)) - case .failure(let error): - completion(.failure(error)) - print("API 통신에러 \(error)") - } - } - task.resume() - } catch { - completion(.failure(NetworkError.urlRequest(error))) - } - } - - /// 통신간의 유효성 검사를 위한 함수 - /// - Parameters: - /// - data: 통신 결과로 돌려받은 데이터 - /// - response: 상태코드를 포함한 통신 응답 - /// - error: 통신간에 발생한 에러 - /// - Returns: 유효성검사 결과에 따른 데이터와 에러 - func checkValidation( - data: Data?, - response: URLResponse?, - error: Error?, - interceptor: Interceptor? - ) -> Result { - - // 1️⃣ 네트워크 레벨 에러 먼저 체크 - if let error { - if let urlError = error as? URLError, urlError.code == .unsupportedURL { - return .failure(.urlRequest(error)) - } - return .failure(.network(error)) - } - - // 2️⃣ HTTP 응답 객체 확인 - guard let httpResponse = response as? HTTPURLResponse else { - return .failure(.httpError) - } - - // 3️⃣ 상태 코드 기반 검사 - guard (200 ... 299).contains(httpResponse.statusCode) else { - // ❗️여기서만 인터셉터 개입 - if let interceptor = interceptor, - interceptor.retry(data: data, response: response, error: error) { - return .failure(.retry) - } - - let errorMessage = data.flatMap { String(data: $0, encoding: .utf8) } ?? "Unknown" - return .failure(.statusError(httpResponse.statusCode, errorMessage)) - } - - // ✅ 성공 응답 - return .success(data) - } -} diff --git a/MLS/Data/Data/Providers/SocialLogin/AppleCredential.swift b/MLS/Data/Data/Providers/SocialLogin/AppleCredential.swift deleted file mode 100644 index 52b8e7cf..00000000 --- a/MLS/Data/Data/Providers/SocialLogin/AppleCredential.swift +++ /dev/null @@ -1,11 +0,0 @@ -import DomainInterface - -public struct AppleCredential: Credential { - public let token: String - public let providerID: String - - public init(token: String, providerID: String) { - self.token = token - self.providerID = providerID - } -} diff --git a/MLS/Data/Data/Providers/SocialLogin/AppleLoginProviderImpl.swift b/MLS/Data/Data/Providers/SocialLogin/AppleLoginProviderImpl.swift deleted file mode 100644 index 6763e3a5..00000000 --- a/MLS/Data/Data/Providers/SocialLogin/AppleLoginProviderImpl.swift +++ /dev/null @@ -1,61 +0,0 @@ -import AuthenticationServices -import Foundation - -import DomainInterface - -import RxSwift - -public final class AppleLoginProviderImpl: NSObject, SocialAuthenticatableProvider { - override public init() {} - - private var authServiceResponse = PublishSubject() - - public func getCredential() -> Observable { - let subject = PublishSubject() - authServiceResponse = subject - performRequest() - return subject - } - - private func performRequest() { - let provider = ASAuthorizationAppleIDProvider() - let request = provider.createRequest() - let controller = ASAuthorizationController(authorizationRequests: [request]) - controller.delegate = self - controller.presentationContextProvider = self - controller.performRequests() - } -} - -// MARK: - Delegate -extension AppleLoginProviderImpl: ASAuthorizationControllerPresentationContextProviding, ASAuthorizationControllerDelegate { - public func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { - let scenes = UIApplication.shared.connectedScenes - let windowScene = scenes.first as? UIWindowScene - return windowScene?.windows.first ?? UIWindow() - } - - public func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { - guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential else { - authServiceResponse.onError(AuthError.unknown(message: "Invalid Apple credential")) - return - } - - guard let idTokenData = appleIDCredential.identityToken, - let idToken = String(data: idTokenData, encoding: .utf8), - let codeData = appleIDCredential.authorizationCode, - let authCode = String(data: codeData, encoding: .utf8) - else { - authServiceResponse.onError(AuthError.unknown(message: "Failed to parse Apple token or code")) - return - } - - let credential = AppleCredential(token: idToken, providerID: authCode) - authServiceResponse.onNext(credential) - authServiceResponse.onCompleted() - } - - public func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { - authServiceResponse.onError(error) - } -} diff --git a/MLS/Data/Data/Providers/SocialLogin/KakaoCredential.swift b/MLS/Data/Data/Providers/SocialLogin/KakaoCredential.swift deleted file mode 100644 index 7caeadbd..00000000 --- a/MLS/Data/Data/Providers/SocialLogin/KakaoCredential.swift +++ /dev/null @@ -1,11 +0,0 @@ -import DomainInterface - -public struct KakaoCredential: Credential { - public let token: String - public let providerID: String - - public init(token: String, providerID: String) { - self.token = token - self.providerID = providerID - } -} diff --git a/MLS/Data/Data/Providers/SocialLogin/KakaoLoginProviderImpl.swift b/MLS/Data/Data/Providers/SocialLogin/KakaoLoginProviderImpl.swift deleted file mode 100644 index 825059ae..00000000 --- a/MLS/Data/Data/Providers/SocialLogin/KakaoLoginProviderImpl.swift +++ /dev/null @@ -1,61 +0,0 @@ -import Foundation - -import DomainInterface - -import KakaoSDKAuth -import KakaoSDKUser -import RxSwift - -public final class KakaoLoginProviderImpl: SocialAuthenticatableProvider { - public init() {} - - public func getCredential() -> Observable { - return Observable.create { [weak self] observer in - let disposable = Disposables.create() - - let handleLogin: (OAuthToken?, Error?) -> Void = { oauthToken, error in - self?.fetchEmailAfterDelay(oauthToken: oauthToken, error: error, observer: observer) - } - - DispatchQueue.main.async { - if UserApi.isKakaoTalkLoginAvailable() { - // 카카오톡 앱 로그인 - UserApi.shared.loginWithKakaoTalk(completion: handleLogin) - } else { - // 웹 브라우저 로그인 - UserApi.shared.loginWithKakaoAccount(completion: handleLogin) - } - } - - return disposable - } - } - - /// accessToken 기반으로 사용자 정보를 가져오는 함수 (딜레이 포함) - private func fetchEmailAfterDelay(oauthToken: OAuthToken?, error: Error?, observer: AnyObserver) { - if let error = error { - observer.onError(error) - return - } - - guard let accessToken = oauthToken?.accessToken else { - observer.onError(AuthError.unknown(message: "토큰이 없어요")) - return - } - - // ✅ Kakao SDK 내부 토큰 저장 시간 보장 - DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { - UserApi.shared.me { user, error in - if let error = error { - observer.onError(error) - return - } - - let id = user?.id ?? 0 - let credential = KakaoCredential(token: accessToken, providerID: String(id)) - observer.onNext(credential) - observer.onCompleted() - } - } - } -} diff --git a/MLS/Data/Data/Repository/AuthAPIRepositoryImpl.swift b/MLS/Data/Data/Repository/AuthAPIRepositoryImpl.swift deleted file mode 100644 index d5bef287..00000000 --- a/MLS/Data/Data/Repository/AuthAPIRepositoryImpl.swift +++ /dev/null @@ -1,169 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class AuthAPIRepositoryImpl: AuthAPIRepository { - private let provider: NetworkProvider - private let tokenInterceptor: Interceptor - private let authInterceptor: Interceptor - - public init(provider: NetworkProvider, tokenInterceptor: Interceptor, authInterceptor: Interceptor) { - self.provider = provider - self.tokenInterceptor = tokenInterceptor - self.authInterceptor = authInterceptor - } - - public func fetchProfile() -> Observable { - let endpoint = AuthEndPoint.fetchProfile() - return provider.requestData(endPoint: endpoint, interceptor: tokenInterceptor) - .map { $0.toMyPageDomain() } - } - - public func loginWithKakao(credential: Credential) -> Observable { - let endpoint = AuthEndPoint.loginWithKakao(credential: credential) - return provider.requestData(endPoint: endpoint, interceptor: authInterceptor) - .map { $0.toLoginDomain() } - .catch { error in - if case NetworkError.statusError(let code, _) = error, code == 404 { - return Observable.error(AuthError.userNotFound(credential: credential)) - } else { - return Observable.error(error) - } - } - } - - public func loginWithApple(credential: Credential) -> Observable { - let endpoint = AuthEndPoint.loginWithApple(credential: credential) - return provider.requestData(endPoint: endpoint, interceptor: authInterceptor) - .map { $0.toLoginDomain() } - .catch { error in - if case NetworkError.statusError(let code, _) = error, code == 404 { - return Observable.error(AuthError.userNotFound(credential: credential)) - } else { - return Observable.error(error) - } - } - } - - public func signUpWithKakao(credential: Credential, isMarketingAgreement: Bool, fcmToken: String?) -> Observable { - let endpoint = AuthEndPoint.signupWithKakao( - credential: credential.token, - body: KakaoBody( - providerId: credential.providerID, - fcmToken: fcmToken, - marketingAgreement: isMarketingAgreement - ) - ) - return provider.requestData(endPoint: endpoint, interceptor: nil).map { $0.toSignUpDomain() } - } - - public func signUpWithApple(credential: Credential, isMarketingAgreement: Bool, fcmToken: String?) -> Observable { - let endpoint = AuthEndPoint.signupWithApple( - credential: credential.token, - body: AppleBody( - providerId: credential.providerID, - fcmToken: fcmToken, - marketingAgreement: isMarketingAgreement - ) - ) - return provider.requestData(endPoint: endpoint, interceptor: nil).map { $0.toSignUpDomain() } - } - - public func withdraw() -> Completable { - let endPoint = AuthEndPoint.withdraw() - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - } - - public func reissueToken(refreshToken: String) -> Observable { - let endPoint = AuthEndPoint.reIssueToken(refreshToken: refreshToken) - return provider.requestData(endPoint: endPoint, interceptor: nil).map { $0.toLoginDomain() } - } - - public func fcmToken(fcmToken: String?) -> Completable { - let endPoint = AuthEndPoint.fcmToken(body: FCMTokenBody(fcmToken: fcmToken)) - return provider.requestData(endPoint: endPoint, interceptor: authInterceptor) - } - - public func fetchJobList() -> Observable { - let endPoint = AuthEndPoint.fetchJobs() - return provider.requestData(endPoint: endPoint, interceptor: nil).map { $0.toDomain() } - } - - public func fetchJob(jobId: String) -> Observable { - let endPoint = AuthEndPoint.fetchJob(jobId: jobId) - return provider.requestData(endPoint: endPoint, interceptor: nil).map { $0.toDomain() } - } - - public func updateUserInfo(level: Int, selectedJobID: Int) -> Completable { - let endPoint = AuthEndPoint.updateCharacterInfo(body: UpdateInfoBody(level: level, jobId: selectedJobID)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - } - - public func updateMarketingAgreement(credential: String, isMarketingAgreement: Bool) -> Completable { - let endPoint = AuthEndPoint.updateMarketingAgreement(credential: credential, body: MarketingAgreementBody(marketingAgreement: isMarketingAgreement)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - } - - public func updateNotificationAgreement(noticeAgreement: Bool, patchNoteAgreement: Bool, eventAgreement: Bool) -> Completable { - let endPoint = AuthEndPoint.updateNotification(body: NotificationAgreementBody(noticeAgreement: noticeAgreement, patchNoteAgreement: patchNoteAgreement, eventAgreement: eventAgreement)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - } - - public func updateNickName(nickName: String) -> Observable { - let endPoint = AuthEndPoint.updateNickName(body: NickNameBody(nickname: nickName)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - .map { $0.toMyPageDomain() } - } - - public func updateProfileImage(url: String) -> Completable { - let endPoint = AuthEndPoint.updateProfileImage(body: UpdateProfileImageBody(profileImageUrl: url)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - } -} - -private extension AuthAPIRepositoryImpl { - struct KakaoBody: Encodable { - let provider = "KAKAO" - let providerId: String - let nickname: String? = nil - let fcmToken: String? - let marketingAgreement: Bool - } - - struct AppleBody: Encodable { - let provider = "APPLE" - let providerId: String - let nickname: String? = nil - let fcmToken: String? - let marketingAgreement: Bool - } - - struct FCMTokenBody: Encodable { - let fcmToken: String? - } - - struct MarketingAgreementBody: Encodable { - let marketingAgreement: Bool - } - - struct NotificationAgreementBody: Encodable { - let noticeAgreement: Bool - let patchNoteAgreement: Bool - let eventAgreement: Bool - } - - struct NickNameBody: Encodable { - let nickname: String - } - - struct UpdateInfoBody: Encodable { - let level: Int - let jobId: Int - } - - struct UpdateProfileImageBody: Encodable { - let profileImageUrl: String - } -} diff --git a/MLS/Data/Data/Repository/BookmarkRepositoryImpl.swift b/MLS/Data/Data/Repository/BookmarkRepositoryImpl.swift deleted file mode 100644 index 144b7b86..00000000 --- a/MLS/Data/Data/Repository/BookmarkRepositoryImpl.swift +++ /dev/null @@ -1,88 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class BookmarkRepositoryImpl: BookmarkRepository { - private let provider: NetworkProvider - private let tokenInterceptor: Interceptor - - public init(provider: NetworkProvider, interceptor: Interceptor) { - self.provider = provider - self.tokenInterceptor = interceptor - } - - public func setBookmark(bookmarkId: Int, type: DictionaryItemType) -> Observable { - let endPoint = BookmarkEndPoint.setBookmark(body: SetBookmarkQuery(bookmarkType: type.rawValue, resourceId: bookmarkId)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - .map { $0.toDomain() } - } - - public func deleteBookmark(bookmarkId: Int) -> Observable { - let endPoint = BookmarkEndPoint.deleteBookmark(bookmarkId: bookmarkId) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - .map { $0.toBookmarkDomain() } - } - - public func fetchBookmark(sort: String?) -> Observable<[BookmarkResponse]> { - let endPoint = BookmarkEndPoint.fetchBookmark(query: SortedQuery(sort: sort)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - .map { $0.toDomain() } - } - - public func fetchMonsterBookmark(minLevel: Int?, maxLevel: Int?, sort: String?) -> Observable<[BookmarkResponse]> { - let endPoint = BookmarkEndPoint.fetchMonsterBookmark(query: BookmarkMonsterQuery(minLevel: minLevel, maxLevel: maxLevel, sort: sort)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - .map { $0.toDomain() } - } - - public func fetchNPCBookmark(sort: String?) -> Observable<[BookmarkResponse]> { - let endPoint = BookmarkEndPoint.fetchNPCBookmark(query: SortedQuery(sort: sort)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - .map { $0.toDomain() } - } - - public func fetchQuestBookmark(sort: String?) -> Observable<[BookmarkResponse]> { - let endPoint = BookmarkEndPoint.fetchQuestBookmark(query: SortedQuery(sort: sort)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - .map { $0.toDomain() } - } - - public func fetchItemBookmark(jobId: Int?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, sort: String?) -> Observable<[BookmarkResponse]> { - let endPoint = BookmarkEndPoint.fetchItemBookmark(query: BookmarkItemQuery(jobId: jobId, minLevel: minLevel, maxLevel: maxLevel, categoryIds: categoryIds, sort: sort)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - .map { $0.toDomain() } - } - - public func fetchMapBookmark(sort: String?) -> Observable<[BookmarkResponse]> { - let endPoint = BookmarkEndPoint.fetchMapBookmark(query: SortedQuery(sort: sort)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - .map { $0.toDomain() } - } -} - -private extension BookmarkRepositoryImpl { - struct SortedQuery: Encodable { - let sort: String? - } - - struct SetBookmarkQuery: Encodable { - let bookmarkType: String - let resourceId: Int - } - - struct BookmarkMonsterQuery: Encodable { - let minLevel: Int? - let maxLevel: Int? - let sort: String? - } - - struct BookmarkItemQuery: Encodable { - let jobId: Int? - let minLevel: Int? - let maxLevel: Int? - let categoryIds: [Int]? - let sort: String? - } -} diff --git a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift deleted file mode 100644 index e3f7ad2a..00000000 --- a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift +++ /dev/null @@ -1,75 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class CollectionAPIRepositoryImpl: CollectionAPIRepository { - private let provider: NetworkProvider - private let tokenInterceptor: Interceptor - - public init(provider: NetworkProvider, tokenInterceptor: Interceptor) { - self.provider = provider - self.tokenInterceptor = tokenInterceptor - } - - public func fetchCollectionList(sort: String?) -> Observable<[CollectionResponse]> { - let endPoint = CollectionEndPoint.fetchCollectionList(query: FetchCollectionListQuery(sort: sort)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - .map { $0.map { $0.toDomain() } - } - } - - public func createCollectionList(name: String) -> Completable { - let endPoint = CollectionEndPoint.createCollectionList(body: CreateCollectionRequestDTO(name: name)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - } - - public func fetchCollectionUseCase(id: Int) -> Observable<[BookmarkResponse]> { - let endPoint = CollectionEndPoint.fetchCollection(id: id) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - .map { $0.toDomain() } - } - - public func updateCollectionName(collectionId: Int, name: String) -> Completable { - let endPoint = CollectionEndPoint.setCollectionName(id: collectionId, body: SetCollectionRequestBody(name: name)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - } - - public func deleteCollection(collectionId: Int) -> Completable { - let endPoint = CollectionEndPoint.deleteCollection(id: collectionId) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - } - - public func addCollectionAndBookmark(collectionIds: [Int], bookmarkIds: [Int]) -> Completable { - let endPoint = CollectionEndPoint.addCollectionAndBookmark(body: AddCollectionAndBookmarkBody(collectionIds: collectionIds, bookmarkIds: bookmarkIds)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - } -} - -private extension CollectionAPIRepositoryImpl { - struct FetchCollectionListQuery: Encodable { - let sort: String? - } - - struct CreateCollectionRequestDTO: Encodable { - let name: String - } - - struct AddBookmarkRequestBody: Encodable { - let bookmarkIds: [Int] - } - - struct AddCollectionRequestBody: Encodable { - let collectionIds: [Int] - } - - struct SetCollectionRequestBody: Encodable { - let name: String - } - - struct AddCollectionAndBookmarkBody: Encodable { - let collectionIds: [Int] - let bookmarkIds: [Int] - } -} diff --git a/MLS/Data/Data/Repository/UserDefaultsRepositoryImpl.swift b/MLS/Data/Data/Repository/UserDefaultsRepositoryImpl.swift deleted file mode 100644 index 55a01487..00000000 --- a/MLS/Data/Data/Repository/UserDefaultsRepositoryImpl.swift +++ /dev/null @@ -1,122 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public final class UserDefaultsRepositoryImpl: UserDefaultsRepository { - private let recentSearchkey = "recentSearch" - private let platformKey = "platformKey" - private let bookmarkkey = "bookmark" - private let dictionaryDetailkey = "dictionaryDetailkey" - - public init() {} - - public func fetchRecentSearch() -> Observable<[String]> { - return Observable.create { observer in - let current = UserDefaults.standard.stringArray(forKey: self.recentSearchkey) ?? [] - observer.onNext(current) - observer.onCompleted() - return Disposables.create() - } - } - - public func addRecentSearch(keyword: String) -> Completable { - return Completable.create { completable in - var current = UserDefaults.standard.stringArray(forKey: self.recentSearchkey) ?? [] - - // 중복 제거 - current.removeAll(where: { $0 == keyword }) - current.insert(keyword, at: 0) - - UserDefaults.standard.set(current, forKey: self.recentSearchkey) - completable(.completed) - return Disposables.create() - } - } - - public func removeRecentSearch(keyword: String) -> Completable { - return Completable.create { completable in - var current = UserDefaults.standard.stringArray(forKey: self.recentSearchkey) ?? [] - - // 해당 키워드 제거 - current.removeAll { $0 == keyword } - - // 다시 저장 - UserDefaults.standard.set(current, forKey: self.recentSearchkey) - - completable(.completed) - return Disposables.create() - } - } - - public func removeAllSearch() -> Completable { - return Completable.create { completable in - var current = UserDefaults.standard.stringArray(forKey: self.recentSearchkey) ?? [] - - // 해당 키워드 제거 - current.removeAll() - - // 다시 저장 - UserDefaults.standard.set(current, forKey: self.recentSearchkey) - - completable(.completed) - return Disposables.create() - } - } - - public func fetchPlatform() -> Observable { - return Observable.create { observer in - if let rawValue = UserDefaults.standard.string(forKey: self.platformKey), - let platform = LoginPlatform(rawValue: rawValue) { - observer.onNext(platform) - } else { - observer.onNext(nil) - } - observer.onCompleted() - return Disposables.create() - } - } - - public func savePlatform(platform: LoginPlatform) -> Completable { - return Completable.create { completable in - UserDefaults.standard.set(platform.rawValue, forKey: self.platformKey) - completable(.completed) - return Disposables.create() - } - } - - public func fetchBookmark() -> Observable { - return Observable.create { observer in - let hasVisited = UserDefaults.standard.bool(forKey: self.bookmarkkey) - observer.onNext(hasVisited) - observer.onCompleted() - return Disposables.create() - } - } - - public func saveBookmark() -> Completable { - return Completable.create { completable in - UserDefaults.standard.set(true, forKey: self.bookmarkkey) - completable(.completed) - return Disposables.create() - } - } - - public func fetchDictionaryDetail() -> Observable { - return Observable.create { observer in - let hasVisited = UserDefaults.standard.bool(forKey: self.dictionaryDetailkey) - observer.onNext(hasVisited) - observer.onCompleted() - return Disposables.create() - } - } - - public func saveDictionaryDetail() -> Completable { - return Completable.create { completable in - UserDefaults.standard.set(true, forKey: self.dictionaryDetailkey) - completable(.completed) - return Disposables.create() - } - } -} diff --git a/MLS/Data/DataMock/Factory/DictionaryFactoryMock.swift b/MLS/Data/DataMock/Factory/DictionaryFactoryMock.swift deleted file mode 100644 index 85d6d104..00000000 --- a/MLS/Data/DataMock/Factory/DictionaryFactoryMock.swift +++ /dev/null @@ -1,13 +0,0 @@ -import BaseFeature -import DictionaryFeatureInterface -import DomainInterface - -public final class DictionaryFactoryMock: DictionaryMainViewFactory { - public init() {} - - public func make() -> BaseViewController { - let viewController = BaseViewController() - viewController.view.backgroundColor = .blue - return viewController - } -} diff --git a/MLS/Data/DataMock/Factory/LoginFactoryMock.swift b/MLS/Data/DataMock/Factory/LoginFactoryMock.swift deleted file mode 100644 index 1bb84a2d..00000000 --- a/MLS/Data/DataMock/Factory/LoginFactoryMock.swift +++ /dev/null @@ -1,19 +0,0 @@ -import AuthFeatureInterface -import BaseFeature -import DictionaryFeatureInterface -import DomainInterface - -public final class LoginFactoryMock: LoginFactory { - public init() {} - - public func make(exitRoute: LoginExitRoute) -> BaseViewController { - let viewController = BaseViewController() - viewController.view.backgroundColor = .blue - return viewController - } - - public func make(exitRoute: LoginExitRoute, onLoginCompleted: (() -> Void)?) -> BaseViewController { - return BaseViewController() - } - -} diff --git a/MLS/Data/DataMock/Provider/AppleLoginProviderMock.swift b/MLS/Data/DataMock/Provider/AppleLoginProviderMock.swift deleted file mode 100644 index c6e4cdf3..00000000 --- a/MLS/Data/DataMock/Provider/AppleLoginProviderMock.swift +++ /dev/null @@ -1,14 +0,0 @@ -import Foundation - -import Data -import DomainInterface - -import RxSwift - -public final class AppleLoginProviderMock: SocialAuthenticatableProvider { - public init() {} - - public func getCredential() -> Observable { - return Observable.just(AppleCredential(token: "token", providerID: "token")) - } -} diff --git a/MLS/Data/DataMock/Provider/KakaoLoginProviderMock.swift b/MLS/Data/DataMock/Provider/KakaoLoginProviderMock.swift deleted file mode 100644 index fba5e5ac..00000000 --- a/MLS/Data/DataMock/Provider/KakaoLoginProviderMock.swift +++ /dev/null @@ -1,14 +0,0 @@ -import Foundation - -import Data -import DomainInterface - -import RxSwift - -public final class KakaoLoginProviderMock: SocialAuthenticatableProvider { - public init() {} - - public func getCredential() -> Observable { - return Observable.just(KakaoCredential(token: "Token", providerID: "email")) - } -} diff --git a/MLS/Data/DataMock/Repository/AuthAPIRepositoryMock.swift b/MLS/Data/DataMock/Repository/AuthAPIRepositoryMock.swift deleted file mode 100644 index 2e524dbd..00000000 --- a/MLS/Data/DataMock/Repository/AuthAPIRepositoryMock.swift +++ /dev/null @@ -1,104 +0,0 @@ -import Foundation - -import Data -import DomainInterface - -import RxSwift - -public class AuthAPIRepositoryMock: AuthAPIRepository { - public func fetchProfile() -> Observable { - return .empty() - } - - public func fetchJob(jobId: String) -> Observable { - return .empty() - } - - public func updateProfileImage(url: String) -> Completable { - return .empty() - } - - public func fetchProfile() -> Observable { - return .empty() - } - - public func signUpWithKakao(credential: any DomainInterface.Credential, isMarketingAgreement: Bool, fcmToken: String?) -> RxSwift.Observable { - if tryCount == 0 { - tryCount += 1 - let error = NSError(domain: "Auth", code: 401, userInfo: [NSLocalizedDescriptionKey: "애플 로그인 실패"]) - return Observable.error(error) - } else { - return Observable.just(.init(accessToken: "testToken", refreshToken: "testToken")) - } - } - - public func signUpWithApple(credential: any DomainInterface.Credential, isMarketingAgreement: Bool, fcmToken: String?) -> RxSwift.Observable { - return Observable.just(.init(accessToken: "testToken", refreshToken: "testToken")) - } - - public func fcmToken(fcmToken: String?) -> Completable { - return .empty() - } - - private var tryCount: Int = 0 - - private let provider: NetworkProvider - - public init(provider: NetworkProvider) { - self.provider = provider - } - - public func loginWithKakao(credential: Credential) -> Observable { - return Observable.just(.init(isRegister: true, accessToken: "", refreshToken: "")) - } - - public func loginWithApple(credential: Credential) -> Observable { - return Observable.just(.init(isRegister: false, accessToken: "", refreshToken: "")) - } - - public func reissueToken(refreshToken: String) -> Observable { - return Observable.just(.init(isRegister: true, accessToken: "testToken", refreshToken: "testToken")) - } - - public func withdraw() -> Completable { - return .empty() - } - - public func fetchJobList() -> Observable { - tryCount += 1 - if tryCount == 1 { - let error = NSError(domain: "Auth", code: 401, userInfo: [NSLocalizedDescriptionKey: "직업 리스트 조회 실패"]) - return Observable.error(error) - } else { - return Observable.just(.init(jobList: [ - Job(name: "마법사", id: 1) - ])) - } - } - - public func updateUserInfo(level: Int, selectedJob: String) -> Completable { - tryCount += 1 - if tryCount % 2 == 0 { - let error = NSError(domain: "Auth", code: 401, userInfo: [NSLocalizedDescriptionKey: "유저 정보 수정 실패"]) - return .error(error) - } else { - return .empty() - } - } - - public func updateMarketingAgreement(credential: String, isMarketingAgreement: Bool) -> Completable { - return .empty() - } - - public func updateUserInfo(level: Int, selectedJobID: Int) -> Completable { - return .empty() - } - - public func updateNotificationAgreement(noticeAgreement: Bool, patchNoteAgreement: Bool, eventAgreement: Bool) -> Completable { - return .empty() - } - - public func updateNickName(nickName: String) -> Observable { - return .empty() - } -} diff --git a/MLS/Domain/.DS_Store b/MLS/Domain/.DS_Store deleted file mode 100644 index 4895ceec..00000000 Binary files a/MLS/Domain/.DS_Store and /dev/null differ diff --git a/MLS/Domain/Domain.xcodeproj/project.pbxproj b/MLS/Domain/Domain.xcodeproj/project.pbxproj deleted file mode 100644 index febd6192..00000000 --- a/MLS/Domain/Domain.xcodeproj/project.pbxproj +++ /dev/null @@ -1,606 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXBuildFile section */ - 0801754B2DCD275300D0919F /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0801752E2DCD26B300D0919F /* DomainInterface.framework */; }; - 084A25CD2DB93E1E00C395C0 /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 084A25CC2DB93E1E00C395C0 /* Core.framework */; }; - 08ED490D2DCFDD63002C21A2 /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08ED490C2DCFDD63002C21A2 /* Core.framework */; }; - 08ED49322DCFDEF6002C21A2 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08ED49312DCFDEF6002C21A2 /* RxSwift */; }; - 08ED49362DCFDF8F002C21A2 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08ED49352DCFDF8F002C21A2 /* RxSwift */; }; - 779FC89A2E1411D100FD1551 /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 779FC8992E1411D100FD1551 /* RxRelay */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 0801754D2DCD275300D0919F /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0879291A2DB90F11009C301F /* Project object */; - proxyType = 1; - remoteGlobalIDString = 0801752D2DCD26B300D0919F; - remoteInfo = DomainInterface; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 0801752E2DCD26B300D0919F /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 0801754F2DCD275900D0919F /* Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 084A25672DB93C0C00C395C0 /* MLSCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MLSCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 084A256A2DB93C0F00C395C0 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 084A25CC2DB93E1E00C395C0 /* Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 087929232DB90F11009C301F /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 08ED490C2DCFDD63002C21A2 /* Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 0801752F2DCD26B300D0919F /* DomainInterface */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = DomainInterface; - sourceTree = ""; - }; - 087929252DB90F11009C301F /* Domain */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = Domain; - sourceTree = ""; - }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - 0801752B2DCD26B300D0919F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 779FC89A2E1411D100FD1551 /* RxRelay in Frameworks */, - 08ED49322DCFDEF6002C21A2 /* RxSwift in Frameworks */, - 08ED490D2DCFDD63002C21A2 /* Core.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 087929202DB90F11009C301F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 08ED49362DCFDF8F002C21A2 /* RxSwift in Frameworks */, - 0801754B2DCD275300D0919F /* DomainInterface.framework in Frameworks */, - 084A25CD2DB93E1E00C395C0 /* Core.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 084A25662DB93C0C00C395C0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 08ED490C2DCFDD63002C21A2 /* Core.framework */, - 0801754F2DCD275900D0919F /* Core.framework */, - 084A25CC2DB93E1E00C395C0 /* Core.framework */, - 084A256A2DB93C0F00C395C0 /* DomainInterface.framework */, - 084A25672DB93C0C00C395C0 /* MLSCore.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 087929192DB90F11009C301F = { - isa = PBXGroup; - children = ( - 087929252DB90F11009C301F /* Domain */, - 0801752F2DCD26B300D0919F /* DomainInterface */, - 084A25662DB93C0C00C395C0 /* Frameworks */, - 087929242DB90F11009C301F /* Products */, - ); - sourceTree = ""; - }; - 087929242DB90F11009C301F /* Products */ = { - isa = PBXGroup; - children = ( - 087929232DB90F11009C301F /* Domain.framework */, - 0801752E2DCD26B300D0919F /* DomainInterface.framework */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 080175292DCD26B300D0919F /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0879291E2DB90F11009C301F /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 0801752D2DCD26B300D0919F /* DomainInterface */ = { - isa = PBXNativeTarget; - buildConfigurationList = 080175332DCD26B300D0919F /* Build configuration list for PBXNativeTarget "DomainInterface" */; - buildPhases = ( - 080175292DCD26B300D0919F /* Headers */, - 0801752A2DCD26B300D0919F /* Sources */, - 0801752B2DCD26B300D0919F /* Frameworks */, - 0801752C2DCD26B300D0919F /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - 0801752F2DCD26B300D0919F /* DomainInterface */, - ); - name = DomainInterface; - packageProductDependencies = ( - 08ED49312DCFDEF6002C21A2 /* RxSwift */, - 779FC8992E1411D100FD1551 /* RxRelay */, - ); - productName = DomainInterface; - productReference = 0801752E2DCD26B300D0919F /* DomainInterface.framework */; - productType = "com.apple.product-type.framework"; - }; - 087929222DB90F11009C301F /* Domain */ = { - isa = PBXNativeTarget; - buildConfigurationList = 087929292DB90F11009C301F /* Build configuration list for PBXNativeTarget "Domain" */; - buildPhases = ( - 0879291E2DB90F11009C301F /* Headers */, - 0879291F2DB90F11009C301F /* Sources */, - 087929202DB90F11009C301F /* Frameworks */, - 087929212DB90F11009C301F /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 0801754E2DCD275300D0919F /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 087929252DB90F11009C301F /* Domain */, - ); - name = Domain; - packageProductDependencies = ( - 08ED49352DCFDF8F002C21A2 /* RxSwift */, - ); - productName = Domain; - productReference = 087929232DB90F11009C301F /* Domain.framework */; - productType = "com.apple.product-type.framework"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 0879291A2DB90F11009C301F /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastUpgradeCheck = 1620; - TargetAttributes = { - 0801752D2DCD26B300D0919F = { - CreatedOnToolsVersion = 16.2; - }; - 087929222DB90F11009C301F = { - CreatedOnToolsVersion = 16.2; - LastSwiftMigration = 1620; - }; - }; - }; - buildConfigurationList = 0879291D2DB90F11009C301F /* Build configuration list for PBXProject "Domain" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 087929192DB90F11009C301F; - minimizedProjectReferenceProxies = 1; - packageReferences = ( - 0858AC052DCFDC640060EBCA /* XCRemoteSwiftPackageReference "RxSwift" */, - ); - preferredProjectObjectVersion = 77; - productRefGroup = 087929242DB90F11009C301F /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 087929222DB90F11009C301F /* Domain */, - 0801752D2DCD26B300D0919F /* DomainInterface */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 0801752C2DCD26B300D0919F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 087929212DB90F11009C301F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 0801752A2DCD26B300D0919F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0879291F2DB90F11009C301F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 0801754E2DCD275300D0919F /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 0801752D2DCD26B300D0919F /* DomainInterface */; - targetProxy = 0801754D2DCD275300D0919F /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 080175342DCD26B300D0919F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.DomainInterface; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 080175352DCD26B300D0919F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.DomainInterface; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; - 0879292A2DB90F11009C301F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.Domain; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 0879292B2DB90F11009C301F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.Domain; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; - 0879292C2DB90F11009C301F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 0879292D2DB90F11009C301F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 080175332DCD26B300D0919F /* Build configuration list for PBXNativeTarget "DomainInterface" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 080175342DCD26B300D0919F /* Debug */, - 080175352DCD26B300D0919F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 0879291D2DB90F11009C301F /* Build configuration list for PBXProject "Domain" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0879292C2DB90F11009C301F /* Debug */, - 0879292D2DB90F11009C301F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 087929292DB90F11009C301F /* Build configuration list for PBXNativeTarget "Domain" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0879292A2DB90F11009C301F /* Debug */, - 0879292B2DB90F11009C301F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - 0858AC052DCFDC640060EBCA /* XCRemoteSwiftPackageReference "RxSwift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactiveX/RxSwift"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 6.9.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - 08ED49312DCFDEF6002C21A2 /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 0858AC052DCFDC640060EBCA /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; - 08ED49352DCFDF8F002C21A2 /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 0858AC052DCFDC640060EBCA /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; - 779FC8992E1411D100FD1551 /* RxRelay */ = { - isa = XCSwiftPackageProductDependency; - package = 0858AC052DCFDC640060EBCA /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxRelay; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = 0879291A2DB90F11009C301F /* Project object */; -} diff --git a/MLS/Domain/Domain.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MLS/Domain/Domain.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 94b2795e..00000000 --- a/MLS/Domain/Domain.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/MLS/Domain/Domain/Interceptor/TokenInterceptor.swift b/MLS/Domain/Domain/Interceptor/TokenInterceptor.swift deleted file mode 100644 index 089a9c95..00000000 --- a/MLS/Domain/Domain/Interceptor/TokenInterceptor.swift +++ /dev/null @@ -1,29 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class TokenInterceptor: Interceptor { - private let fetchTokenUseCase: FetchTokenFromLocalUseCase - - public init(fetchTokenUseCase: FetchTokenFromLocalUseCase) { - self.fetchTokenUseCase = fetchTokenUseCase - } - - public func adapt(_ request: URLRequest) -> URLRequest { - let accessFetchResult = fetchTokenUseCase.execute(type: .accessToken) - switch accessFetchResult { - case .success(let token): - var adaptedRequest = request - adaptedRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") - return adaptedRequest - case .failure: - return request - } - } - - public func retry(data: Data?, response: URLResponse?, error: Error?) -> Bool { - return false - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchAllAlarmUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchAllAlarmUseCaseImpl.swift deleted file mode 100644 index 9622cd52..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchAllAlarmUseCaseImpl.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class FetchAllAlarmUseCaseImpl: FetchAllAlarmUseCase { - private var repository: AlarmAPIRepository - - public init(repository: AlarmAPIRepository) { - self.repository = repository - } - - public func execute(id: Int?, pageSize: Int) -> Observable> { - return repository.fetchAll(cursor: id, pageSize: pageSize) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchNoticesUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchNoticesUseCaseImpl.swift deleted file mode 100644 index b464aeed..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchNoticesUseCaseImpl.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class FetchNoticesUseCaseImpl: FetchNoticesUseCase { - private var repository: AlarmAPIRepository - - public init(repository: AlarmAPIRepository) { - self.repository = repository - } - - public func execute(id: Int?, pageSize: Int) -> Observable> { - return repository.fetchNotices(cursor: id, pageSize: pageSize) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchOngoingEventsUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchOngoingEventsUseCaseImpl.swift deleted file mode 100644 index 2a1c961f..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchOngoingEventsUseCaseImpl.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class FetchOngoingEventsUseCaseImpl: FetchOngoingEventsUseCase { - private var repository: AlarmAPIRepository - - public init(repository: AlarmAPIRepository) { - self.repository = repository - } - - public func execute(id: Int?, pageSize: Int) -> Observable> { - return repository.fetchOngoingEvents(cursor: id, pageSize: pageSize) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchOutdatedEventsUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchOutdatedEventsUseCaseImpl.swift deleted file mode 100644 index ba778aa4..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchOutdatedEventsUseCaseImpl.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class FetchOutdatedEventsUseCaseImpl: FetchOutdatedEventsUseCase { - private var repository: AlarmAPIRepository - - public init(repository: AlarmAPIRepository) { - self.repository = repository - } - - public func execute(id: Int?, pageSize: Int) -> Observable> { - return repository.fetchOutdatedEvents(cursor: id, pageSize: pageSize) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchPatchNotesUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchPatchNotesUseCaseImpl.swift deleted file mode 100644 index 122fa2ec..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Alarm/FetchPatchNotesUseCaseImpl.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class FetchPatchNotesUseCaseImpl: FetchPatchNotesUseCase { - private var repository: AlarmAPIRepository - - public init(repository: AlarmAPIRepository) { - self.repository = repository - } - - public func execute(id: Int?, pageSize: Int) -> Observable> { - return repository.fetchPatchNotes(cursor: id, pageSize: pageSize) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Alarm/SetReadUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Alarm/SetReadUseCaseImpl.swift deleted file mode 100644 index 9ce49f4d..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Alarm/SetReadUseCaseImpl.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class SetReadUseCaseImpl: SetReadUseCase { - private var repository: AlarmAPIRepository - - public init(repository: AlarmAPIRepository) { - self.repository = repository - } - - public func execute(alarmLink: String) -> Completable { - return repository.setRead(alarmLink: alarmLink) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/CheckLoginUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/CheckLoginUseCaseImpl.swift deleted file mode 100644 index 5b285263..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/CheckLoginUseCaseImpl.swift +++ /dev/null @@ -1,45 +0,0 @@ -import DomainInterface - -import RxRelay -import RxSwift - -public final class CheckLoginUseCaseImpl: CheckLoginUseCase { - private let authRepository: AuthAPIRepository - private let tokenRepository: TokenRepository - - public init(authRepository: AuthAPIRepository, tokenRepository: TokenRepository) { - self.authRepository = authRepository - self.tokenRepository = tokenRepository - } - - public func execute() -> Observable { - switch tokenRepository.fetchToken(type: .refreshToken) { - case .success(let token): - guard !token.isEmpty else { return .just(false) } - - return authRepository.reissueToken(refreshToken: token) - .map { [weak self] response in - guard let self else { return false } - - let accessResult = self.tokenRepository.saveToken(type: .accessToken, value: response.accessToken) - let refreshResult = self.tokenRepository.saveToken(type: .refreshToken, value: response.refreshToken) - - switch (accessResult, refreshResult) { - case (.success, .success): - return true - case (.failure(let error), _), - (_, .failure(let error)): - print("Token 저장 실패:", error.localizedDescription) - return false - } - } - .catch { error in - print("reissueToken 실패:", error.localizedDescription) - return .just(false) - } - - case .failure: - return .just(false) - } - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/FetchJobListUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/FetchJobListUseCaseImpl.swift deleted file mode 100644 index 9521f325..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/FetchJobListUseCaseImpl.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class FetchJobListUseCaseImpl: FetchJobListUseCase { - private var repository: AuthAPIRepository - - public init(repository: AuthAPIRepository) { - self.repository = repository - } - - public func execute() -> Observable { - return repository.fetchJobList() - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/FetchPlatformUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/FetchPlatformUseCaseImpl.swift deleted file mode 100644 index 40e7100d..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/FetchPlatformUseCaseImpl.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class FetchPlatformUseCaseImpl: FetchPlatformUseCase { - private var repository: UserDefaultsRepository - - public init(repository: UserDefaultsRepository) { - self.repository = repository - } - - public func execute() -> Observable { - return repository.fetchPlatform() - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/LoginWithAppleUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/LoginWithAppleUseCaseImpl.swift deleted file mode 100644 index 1819bf20..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/LoginWithAppleUseCaseImpl.swift +++ /dev/null @@ -1,50 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class LoginWithAppleUseCaseImpl: LoginWithAppleUseCase { - private var authRepository: AuthAPIRepository - private let tokenRepository: TokenRepository - private var userDefaultsRepository: UserDefaultsRepository - - public init(authRepository: AuthAPIRepository, tokenRepository: TokenRepository, userDefaultsRepository: UserDefaultsRepository) { - self.authRepository = authRepository - self.tokenRepository = tokenRepository - self.userDefaultsRepository = userDefaultsRepository - } - - // 로그인할때 토큰 저장 필요 - public func execute(credential: Credential) -> Observable { - return authRepository.loginWithApple(credential: credential) - .flatMap { response -> Observable in - let saveAccess = self.tokenRepository.saveToken(type: .accessToken, value: response.accessToken) - let saveRefresh = self.tokenRepository.saveToken(type: .refreshToken, value: response.refreshToken) - let savePlatform = self.userDefaultsRepository.savePlatform(platform: .apple) - - guard case (.success, .success) = (saveAccess, saveRefresh) else { - return Observable.error(TokenRepositoryError.dataConversionError(message: "Failed to save tokens")) - } - - var fcmToken: String? - if case .success(let token) = self.tokenRepository.fetchToken(type: .fcmToken) { - fcmToken = token - } - - let fcmUpdate = if let fcmToken { - self.authRepository.fcmToken(fcmToken: fcmToken) - .catch { error in - print("FCM token update failed: \(error)") - return .empty() - } - } else { - Completable.empty() - } - return fcmUpdate.andThen(savePlatform).andThen(Observable.just(response)) - } - .catch { error in - Observable.error(error) - } - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/LoginWithKakaoUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/LoginWithKakaoUseCaseImpl.swift deleted file mode 100644 index db4e8719..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/LoginWithKakaoUseCaseImpl.swift +++ /dev/null @@ -1,49 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class LoginWithKakaoUseCaseImpl: LoginWithKakaoUseCase { - private var authRepository: AuthAPIRepository - private let tokenRepository: TokenRepository - private var userDefaultsRepository: UserDefaultsRepository - - public init(authRepository: AuthAPIRepository, tokenRepository: TokenRepository, userDefaultsRepository: UserDefaultsRepository) { - self.authRepository = authRepository - self.tokenRepository = tokenRepository - self.userDefaultsRepository = userDefaultsRepository - } - - public func execute(credential: Credential) -> Observable { - return authRepository.loginWithKakao(credential: credential) - .flatMap { response -> Observable in - let saveAccess = self.tokenRepository.saveToken(type: .accessToken, value: response.accessToken) - let saveRefresh = self.tokenRepository.saveToken(type: .refreshToken, value: response.refreshToken) - let savePlatform = self.userDefaultsRepository.savePlatform(platform: .kakao) - - guard case (.success, .success) = (saveAccess, saveRefresh) else { - return Observable.error(TokenRepositoryError.dataConversionError(message: "Failed to save tokens")) - } - - var fcmToken: String? - if case .success(let token) = self.tokenRepository.fetchToken(type: .fcmToken) { - fcmToken = token - } - - let fcmUpdate = if let fcmToken { - self.authRepository.fcmToken(fcmToken: fcmToken) - .catch { error in - print("FCM token update failed: \(error)") - return .empty() - } - } else { - Completable.empty() - } - return fcmUpdate.andThen(savePlatform).andThen(Observable.just(response)) - } - .catch { error in - Observable.error(error) - } - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/OpenNotificationSettingUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/OpenNotificationSettingUseCaseImpl.swift deleted file mode 100644 index cb716070..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/OpenNotificationSettingUseCaseImpl.swift +++ /dev/null @@ -1,14 +0,0 @@ -import UIKit - -import DomainInterface - -public final class OpenNotificationSettingUseCaseImpl: OpenNotificationSettingUseCase { - public init() {} - - public func execute() { - guard let url = URL(string: UIApplication.openSettingsURLString) else { return } - if UIApplication.shared.canOpenURL(url) { - UIApplication.shared.open(url, options: [:], completionHandler: nil) - } - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/PutFCMTokenUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/PutFCMTokenUseCaseImpl.swift deleted file mode 100644 index 8f08eba0..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/PutFCMTokenUseCaseImpl.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class PutFCMTokenUseCaseImpl: PutFCMTokenUseCase { - private var repository: AuthAPIRepository - - public init(repository: AuthAPIRepository) { - self.repository = repository - } - - public func execute(fcmToken: String?) -> Completable { - return repository.fcmToken(fcmToken: fcmToken) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/ReissueUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/ReissueUseCaseImpl.swift deleted file mode 100644 index 4f632e16..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/ReissueUseCaseImpl.swift +++ /dev/null @@ -1,37 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public final class ReissueUseCaseImpl: ReissueUseCase { - private let repository: AuthAPIRepository - private let tokenRepository: TokenRepository - - public init( - repository: AuthAPIRepository, - tokenRepository: TokenRepository - ) { - self.repository = repository - self.tokenRepository = tokenRepository - } - - public func execute(refreshToken: String) -> Observable { - return repository.reissueToken(refreshToken: refreshToken) - .flatMap { [weak self] response -> Observable in - guard let self = self else { return .empty() } - - let saveAccess = self.tokenRepository.saveToken(type: .accessToken, value: response.accessToken) - let saveRefresh = self.tokenRepository.saveToken(type: .refreshToken, value: response.refreshToken) - - switch (saveAccess, saveRefresh) { - case (.success, .success): - print("✅ 새 토큰 저장 완료") - return .just(response) - default: - print("❌ 토큰 저장 실패") - return .error(TokenRepositoryError.dataConversionError(message: "Failed to save new tokens")) - } - } - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/SignUpWithAppleUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/SignUpWithAppleUseCaseImpl.swift deleted file mode 100644 index 6a1ed88a..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/SignUpWithAppleUseCaseImpl.swift +++ /dev/null @@ -1,47 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public final class SignUpWithAppleUseCaseImpl: SignUpWithAppleUseCase { - private let authRepository: AuthAPIRepository - private let tokenRepository: TokenRepository - private let userDefaultsRepository: UserDefaultsRepository - - public init( - authRepository: AuthAPIRepository, - tokenRepository: TokenRepository, - userDefaultsRepository: UserDefaultsRepository - ) { - self.authRepository = authRepository - self.tokenRepository = tokenRepository - self.userDefaultsRepository = userDefaultsRepository - } - - public func execute( - credential: Credential, - isMarketingAgreement: Bool, - fcmToken: String? - ) -> Observable { - return authRepository - .signUpWithApple(credential: credential, isMarketingAgreement: isMarketingAgreement, fcmToken: fcmToken) - .flatMap { response -> Observable in - let saveAccess = self.tokenRepository.saveToken(type: .accessToken, value: response.accessToken) - let saveRefresh = self.tokenRepository.saveToken(type: .refreshToken, value: response.refreshToken) - let savePlatform = self.userDefaultsRepository.savePlatform(platform: .apple) - - switch (saveAccess, saveRefresh) { - case (.success, .success): - return savePlatform.andThen(Observable.just(response)) - default: - return Observable.error( - TokenRepositoryError.dataConversionError(message: "Failed to save tokens") - ) - } - } - .catch { error in - Observable.error(error) - } - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/SignUpWithKakaoUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/SignUpWithKakaoUseCaseImpl.swift deleted file mode 100644 index 7316776b..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/SignUpWithKakaoUseCaseImpl.swift +++ /dev/null @@ -1,45 +0,0 @@ -import DomainInterface -import Foundation -import RxSwift - -public final class SignUpWithKakaoUseCaseImpl: SignUpWithKakaoUseCase { - private let authRepository: AuthAPIRepository - private let tokenRepository: TokenRepository - private let userDefaultsRepository: UserDefaultsRepository - - public init( - authRepository: AuthAPIRepository, - tokenRepository: TokenRepository, - userDefaultsRepository: UserDefaultsRepository - ) { - self.authRepository = authRepository - self.tokenRepository = tokenRepository - self.userDefaultsRepository = userDefaultsRepository - } - - public func execute( - credential: Credential, - isMarketingAgreement: Bool, - fcmToken: String? - ) -> Observable { - return authRepository - .signUpWithKakao(credential: credential, isMarketingAgreement: isMarketingAgreement, fcmToken: fcmToken) - .flatMap { response -> Observable in - let saveAccess = self.tokenRepository.saveToken(type: .accessToken, value: response.accessToken) - let saveRefresh = self.tokenRepository.saveToken(type: .refreshToken, value: response.refreshToken) - let savePlatform = self.userDefaultsRepository.savePlatform(platform: .kakao) - - switch (saveAccess, saveRefresh) { - case (.success, .success): - return savePlatform.andThen(Observable.just(response)) - default: - return Observable.error( - TokenRepositoryError.dataConversionError(message: "Failed to save tokens") - ) - } - } - .catch { error in - Observable.error(error) - } - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/UpdateMarketingAgreementUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/UpdateMarketingAgreementUseCaseImpl.swift deleted file mode 100644 index c0a4d6f4..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/UpdateMarketingAgreementUseCaseImpl.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class UpdateMarketingAgreementUseCaseImpl: UpdateMarketingAgreementUseCase { - private let authRepository: AuthAPIRepository - private let tokenRepository: TokenRepository - - public init(authRepository: AuthAPIRepository, tokenRepository: TokenRepository) { - self.authRepository = authRepository - self.tokenRepository = tokenRepository - } - - public func execute(credential: String, isMarketingAgreement: Bool) -> Completable { - switch tokenRepository.fetchToken(type: .accessToken) { - case .success(let token): - return authRepository.updateMarketingAgreement(credential: token, isMarketingAgreement: isMarketingAgreement) - case .failure(let error): - return .error(error) - } - - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/UpdateNotificationAgreementUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/UpdateNotificationAgreementUseCaseImpl.swift deleted file mode 100644 index 2d95c3d7..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/UpdateNotificationAgreementUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public class UpdateNotificationAgreementUseCaseImpl: UpdateNotificationAgreementUseCase { - private let repository: AuthAPIRepository - - public init(authRepository: AuthAPIRepository) { - self.repository = authRepository - } - - public func execute(noticeAgreement: Bool, patchNoteAgreement: Bool, eventAgreement: Bool) -> Completable { - return repository.updateNotificationAgreement(noticeAgreement: noticeAgreement, patchNoteAgreement: patchNoteAgreement, eventAgreement: eventAgreement) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/UpdateUserInfoUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/UpdateUserInfoUseCaseImpl.swift deleted file mode 100644 index c0320dd6..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/UpdateUserInfoUseCaseImpl.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class UpdateUserInfoUseCaseImpl: UpdateUserInfoUseCase { - private var repository: AuthAPIRepository - - public init(repository: AuthAPIRepository) { - self.repository = repository - } - - public func execute(level: Int, selectedJobID: Int) -> Completable { - return repository.updateUserInfo(level: level, selectedJobID: selectedJobID) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchBookmarkUseCaseImpl.swift deleted file mode 100644 index 9ad0aca7..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchBookmarkUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchBookmarkUseCaseImpl: FetchBookmarkUseCase { - private let repository: BookmarkRepository - - public init(repository: BookmarkRepository) { - self.repository = repository - } - - public func execute(sort: SortType?) -> Observable<[BookmarkResponse]> { - return repository.fetchBookmark(sort: sort?.sortParameter) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchItemBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchItemBookmarkUseCaseImpl.swift deleted file mode 100644 index 9ea2cf69..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchItemBookmarkUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchItemBookmarkUseCaseImpl: FetchItemBookmarkUseCase { - private let repository: BookmarkRepository - - public init(repository: BookmarkRepository) { - self.repository = repository - } - - public func execute(jobId: Int?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, sort: SortType?) -> Observable<[BookmarkResponse]> { - return repository.fetchItemBookmark(jobId: jobId, minLevel: minLevel, maxLevel: maxLevel, categoryIds: categoryIds, sort: sort?.sortParameter) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchMapBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchMapBookmarkUseCaseImpl.swift deleted file mode 100644 index 820a6ebc..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchMapBookmarkUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchMapBookmarkUseCaseImpl: FetchMapBookmarkUseCase { - private let repository: BookmarkRepository - - public init(repository: BookmarkRepository) { - self.repository = repository - } - - public func execute(sort: SortType?) -> Observable<[BookmarkResponse]> { - return repository.fetchMapBookmark(sort: sort?.sortParameter) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchMonsterBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchMonsterBookmarkUseCaseImpl.swift deleted file mode 100644 index 486c066a..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchMonsterBookmarkUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchMonsterBookmarkUseCaseImpl: FetchMonsterBookmarkUseCase { - private let repository: BookmarkRepository - - public init(repository: BookmarkRepository) { - self.repository = repository - } - - public func execute(minLevel: Int?, maxLevel: Int?, sort: SortType?) -> Observable<[BookmarkResponse]> { - return repository.fetchMonsterBookmark(minLevel: minLevel, maxLevel: maxLevel, sort: sort?.sortParameter) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchNPCBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchNPCBookmarkUseCaseImpl.swift deleted file mode 100644 index ed56836c..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchNPCBookmarkUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchNPCBookmarkUseCaseImpl: FetchNPCBookmarkUseCase { - private let repository: BookmarkRepository - - public init(repository: BookmarkRepository) { - self.repository = repository - } - - public func execute(sort: SortType?) -> Observable<[BookmarkResponse]> { - return repository.fetchNPCBookmark(sort: sort?.sortParameter) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchQuestBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchQuestBookmarkUseCaseImpl.swift deleted file mode 100644 index eca2d814..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchQuestBookmarkUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchQuestBookmarkUseCaseImpl: FetchQuestBookmarkUseCase { - private let repository: BookmarkRepository - - public init(repository: BookmarkRepository) { - self.repository = repository - } - - public func execute(sort: SortType?) -> Observable<[BookmarkResponse]> { - return repository.fetchQuestBookmark(sort: sort?.sortParameter) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchVisitBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchVisitBookmarkUseCaseImpl.swift deleted file mode 100644 index 1424d1b5..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Bookmark/FetchVisitBookmarkUseCaseImpl.swift +++ /dev/null @@ -1,23 +0,0 @@ -import DomainInterface -import Foundation - -import RxSwift - -public class FetchVisitBookmarkUseCaseImpl: FetchVisitBookmarkUseCase { - var repository: UserDefaultsRepository - public init(repository: UserDefaultsRepository) { - self.repository = repository - } - - public func execute() -> Observable { - return repository.fetchBookmark() - .flatMap { hasVisited -> Observable in - if hasVisited { - return .just(true) - } else { - return self.repository.saveBookmark() - .andThen(.just(false)) - } - } - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Bookmark/SetBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Bookmark/SetBookmarkUseCaseImpl.swift deleted file mode 100644 index 0a45e333..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Bookmark/SetBookmarkUseCaseImpl.swift +++ /dev/null @@ -1,22 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class SetBookmarkUseCaseImpl: SetBookmarkUseCase { - private let repository: BookmarkRepository - - public init(repository: BookmarkRepository) { - self.repository = repository - } - - public func execute(bookmarkId: Int, isBookmark: IsBookmark) -> Observable { - switch isBookmark { - case .set(let type): - return repository - .setBookmark(bookmarkId: bookmarkId, type: type) - .map { Optional($0) } - case .delete: - return repository.deleteBookmark(bookmarkId: bookmarkId) - } - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionAndBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionAndBookmarkUseCaseImpl.swift deleted file mode 100644 index 55b640e6..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionAndBookmarkUseCaseImpl.swift +++ /dev/null @@ -1,14 +0,0 @@ -import DomainInterface -import RxSwift - -public final class AddCollectionAndBookmarkUseCaseImpl: AddCollectionAndBookmarkUseCase { - private let repository: CollectionAPIRepository - - public init(repository: CollectionAPIRepository) { - self.repository = repository - } - - public func execute(collectionIds: [Int], bookmarkIds: [Int]) -> Completable { - return repository.addCollectionAndBookmark(collectionIds: collectionIds, bookmarkIds: bookmarkIds) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/CreateCollectionListUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/CreateCollectionListUseCaseImpl.swift deleted file mode 100644 index 5325ee16..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Collection/CreateCollectionListUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class CreateCollectionListUseCaseImpl: CreateCollectionListUseCase { - private let repository: CollectionAPIRepository - - public init(repository: CollectionAPIRepository) { - self.repository = repository - } - - public func execute(name: String) -> Completable { - return repository.createCollectionList(name: name) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/DeleteCollectionUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/DeleteCollectionUseCaseImpl.swift deleted file mode 100644 index 40b318f3..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Collection/DeleteCollectionUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class DeleteCollectionUseCaseImpl: DeleteCollectionUseCase { - private let repository: CollectionAPIRepository - - public init(repository: CollectionAPIRepository) { - self.repository = repository - } - - public func execute(collectionId: Int) -> Completable { - return repository.deleteCollection(collectionId: collectionId) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/FetchCollectionListUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/FetchCollectionListUseCaseImpl.swift deleted file mode 100644 index a1f88c48..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Collection/FetchCollectionListUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchCollectionListUseCaseImpl: FetchCollectionListUseCase { - private let repository: CollectionAPIRepository - - public init(repository: CollectionAPIRepository) { - self.repository = repository - } - - public func execute(sort: SortType?) -> Observable<[CollectionResponse]> { - return repository.fetchCollectionList(sort: sort?.sortParameter) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/FetchCollectionUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/FetchCollectionUseCaseImpl.swift deleted file mode 100644 index 94067888..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Collection/FetchCollectionUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchCollectionUseCaseImpl: FetchCollectionUseCase { - private let repository: CollectionAPIRepository - - public init(repository: CollectionAPIRepository) { - self.repository = repository - } - - public func execute(id: Int) -> Observable<[BookmarkResponse]> { - return repository.fetchCollectionUseCase(id: id) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/UpdateCollectionUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/UpdateCollectionUseCaseImpl.swift deleted file mode 100644 index f8dc3727..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Collection/UpdateCollectionUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class UpdateCollectionUseCaseImpl: UpdateCollectionUseCase { - private let repository: CollectionAPIRepository - - public init(repository: CollectionAPIRepository) { - self.repository = repository - } - - public func execute(collectionId: Int, name: String) -> Completable { - return repository.updateCollectionName(collectionId: collectionId, name: name) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailItemMonsterUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailItemMonsterUseCaseImpl.swift deleted file mode 100644 index 322f67d1..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailItemMonsterUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchDictionaryDetailItemDropMonsterUseCaseImpl: FetchDictionaryDetailItemDropMonsterUseCase { - private let repository: DictionaryDetailAPIRepository - - public init(repository: DictionaryDetailAPIRepository) { - self.repository = repository - } - - public func execute(id: Int, sort: String?) -> Observable<[DictionaryDetailItemDropMonsterResponse]> { - return repository.fetchItemDetailDropMonster(id: id, sort: sort) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailItemUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailItemUseCaseImpl.swift deleted file mode 100644 index f3fde890..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailItemUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchDictionaryDetailItemUseCaseImpl: FetchDictionaryDetailItemUseCase { - private let repository: DictionaryDetailAPIRepository - - public init(repository: DictionaryDetailAPIRepository) { - self.repository = repository - } - - public func execute(id: Int) -> Observable { - return repository.fetchItemDetail(id: id) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMapNpcUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMapNpcUseCaseImpl.swift deleted file mode 100644 index aa65dcef..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMapNpcUseCaseImpl.swift +++ /dev/null @@ -1,13 +0,0 @@ -import DomainInterface -import RxSwift - -public final class FetchDictionaryDetailMapNpcUseCaseImpl: FetchDictionaryDetailMapNpcUseCase { - private let repository: DictionaryDetailAPIRepository - public init(repository: DictionaryDetailAPIRepository) { - self.repository = repository - } - - public func execute(id: Int) -> Observable<[DictionaryDetailMapNpcResponse]> { - return repository.fetchMapDetailNpc(id: id) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMapSpawnMonsterUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMapSpawnMonsterUseCaseImpl.swift deleted file mode 100644 index 97ed90a1..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMapSpawnMonsterUseCaseImpl.swift +++ /dev/null @@ -1,13 +0,0 @@ -import DomainInterface -import RxSwift - -public final class FetchDictionaryDetailMapSpawnMonsterUseCaseImpl: FetchDictionaryDetailMapSpawnMonsterUseCase { - private let repository: DictionaryDetailAPIRepository - public init(repository: DictionaryDetailAPIRepository) { - self.repository = repository - } - - public func execute(id: Int, sort: String?) -> Observable<[DictionaryDetailMapSpawnMonsterResponse]> { - return repository.fetchMapDetailSpawnMonster(id: id, sort: sort) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMapUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMapUseCaseImpl.swift deleted file mode 100644 index 73569a93..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMapUseCaseImpl.swift +++ /dev/null @@ -1,14 +0,0 @@ -import DomainInterface -import RxSwift - -public final class FetchDictionaryDetailMapUseCaseImpl: FetchDictionaryDetailMapUseCase { - private let repository: DictionaryDetailAPIRepository - - public init(repository: DictionaryDetailAPIRepository) { - self.repository = repository - } - - public func execute(id: Int) -> Observable { - return repository.fetchMapDetail(id: id) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMonsterDropItemUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMonsterDropItemUseCaseImpl.swift deleted file mode 100644 index c55067ac..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMonsterDropItemUseCaseImpl.swift +++ /dev/null @@ -1,14 +0,0 @@ -import DomainInterface -import RxSwift - -public final class FetchDictionaryDetailMonsterDropItemUseCaseImpl: FetchDictionaryDetailMonsterItemsUseCase { - private let repository: DictionaryDetailAPIRepository - public init(repository: DictionaryDetailAPIRepository) { - self.repository = repository - } - - public func execute(id: Int, sort: String?) -> Observable<[DictionaryDetailMonsterDropItemResponse]> { - return repository.fetchMonsterDetailDropItem(id: id, sort: sort) - } - -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMonsterMapUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMonsterMapUseCaseImpl.swift deleted file mode 100644 index f617cd0e..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMonsterMapUseCaseImpl.swift +++ /dev/null @@ -1,12 +0,0 @@ -import DomainInterface -import RxSwift - -public final class FetchDictionaryDetailMonsterMapUseCaseImpl: FetchDictionaryDetailMonsterMapUseCase { - private let repository: DictionaryDetailAPIRepository - public init(repository: DictionaryDetailAPIRepository) { - self.repository = repository - } - public func execute(id: Int) -> Observable<[DictionaryDetailMonsterMapResponse]> { - return repository.fetchMonsterDetailMap(id: id) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMonsterUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMonsterUseCaseImpl.swift deleted file mode 100644 index 962dcca0..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailMonsterUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchDictionaryDetailMonsterUseCaseImpl: FetchDictionaryDetailMonsterUseCase { - private let repository: DictionaryDetailAPIRepository - - public init(repository: DictionaryDetailAPIRepository) { - self.repository = repository - } - - public func execute(id: Int) -> Observable { - return repository.fetchMonsterDetail(id: id) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailNpcMapUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailNpcMapUseCaseImpl.swift deleted file mode 100644 index 33dab664..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailNpcMapUseCaseImpl.swift +++ /dev/null @@ -1,12 +0,0 @@ -import DomainInterface -import RxSwift - -public final class FetchDictionaryDetailNpcMapUseCaseImpl: FetchDictionaryDetailNpcMapUseCase { - private let repository: DictionaryDetailAPIRepository - public init(repository: DictionaryDetailAPIRepository) { - self.repository = repository - } - public func execute(id: Int) -> Observable<[DictionaryDetailMonsterMapResponse]> { - return repository.fetchNpcDetailMap(id: id) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailNpcQuestUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailNpcQuestUseCaseImpl.swift deleted file mode 100644 index 5eee5646..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailNpcQuestUseCaseImpl.swift +++ /dev/null @@ -1,13 +0,0 @@ -import DomainInterface -import RxSwift - -public final class FetchDictionaryDetailNpcQuestUseCaseImpl: FetchDictionaryDetailNpcQuestUseCase { - private let repository: DictionaryDetailAPIRepository - public init(repository: DictionaryDetailAPIRepository) { - self.repository = repository - } - - public func execute(id: Int, sort: String?) -> Observable<[DictionaryDetailNpcQuestResponse]> { - return repository.fetchNpcDetailQuest(id: id, sort: sort) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailNpcUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailNpcUseCaseImpl.swift deleted file mode 100644 index 2f5da66b..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailNpcUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchDictionaryDetailNpcUseCaseImpl: FetchDictionaryDetailNpcUseCase { - private let repository: DictionaryDetailAPIRepository - - public init(repository: DictionaryDetailAPIRepository) { - self.repository = repository - } - - public func execute(id: Int) -> Observable { - return repository.fetchNpcDetail(id: id) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailQuestLinkedQuestsUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailQuestLinkedQuestsUseCaseImpl.swift deleted file mode 100644 index 1a601571..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailQuestLinkedQuestsUseCaseImpl.swift +++ /dev/null @@ -1,14 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchDictionaryDetailQuestLinkedQuestsUseCaseImpl: FetchDictionaryDetailQuestLinkedQuestsUseCase { - private let repository: DictionaryDetailAPIRepository - public init(repository: DictionaryDetailAPIRepository) { - self.repository = repository - } - - public func execute(id: Int) -> Observable { - return repository.fetchQuestDetailLinkedQuestsDetail(id: id) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailQuestUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailQuestUseCaseImpl.swift deleted file mode 100644 index ff339e51..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchDictionaryDetailQuestUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchDictionaryDetailQuestUseCaseImpl: FetchDictionaryDetailQuestUseCase { - private let repository: DictionaryDetailAPIRepository - - public init(repository: DictionaryDetailAPIRepository) { - self.repository = repository - } - - public func execute(id: Int) -> Observable { - return repository.fetchQuestDetail(id: id) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryAllListUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryAllListUseCaseImpl.swift deleted file mode 100644 index b146cee6..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryAllListUseCaseImpl.swift +++ /dev/null @@ -1,16 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchDictionaryAllListUseCaseImpl: FetchDictionaryAllListUseCase { - - private let repository: DictionaryListAPIRepository - - public init(repository: DictionaryListAPIRepository) { - self.repository = repository - } - - public func execute(keyword: String?, page: Int?) -> Observable { - return repository.fetchAllList(keyword: keyword, page: page) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryItemListUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryItemListUseCaseImpl.swift deleted file mode 100644 index c9395f7b..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryItemListUseCaseImpl.swift +++ /dev/null @@ -1,16 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchDictionaryItemListUseCaseImpl: FetchDictionaryItemListUseCase { - - private let repository: DictionaryListAPIRepository - - public init(repository: DictionaryListAPIRepository) { - self.repository = repository - } - - public func execute(keyword: String?, jobId: [Int]?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, page: Int?, size: Int?, sort: String?) -> Observable { - return repository.fetchItemList(keyword: keyword, jobId: jobId, minLevel: minLevel, maxLevel: maxLevel, categoryIds: categoryIds, page: page, size: size, sort: sort) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryMapListUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryMapListUseCaseImpl.swift deleted file mode 100644 index 9b04e6bc..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryMapListUseCaseImpl.swift +++ /dev/null @@ -1,16 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchDictionaryMapListUseCaseImpl: FetchDictionaryMapListUseCase { - - private let repository: DictionaryListAPIRepository - - public init(repository: DictionaryListAPIRepository) { - self.repository = repository - } - - public func execute(keyword: String, page: Int, size: Int, sort: String?) -> Observable { - return repository.fetchMapList(keyword: keyword, page: page, size: size, sort: sort) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryMonsterListUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryMonsterListUseCaseImpl.swift deleted file mode 100644 index f7edd9d6..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryMonsterListUseCaseImpl.swift +++ /dev/null @@ -1,16 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchDictionaryMonsterListUseCaseImpl: FetchDictionaryMonsterListUseCase { - - private let repository: DictionaryListAPIRepository - - public init(repository: DictionaryListAPIRepository) { - self.repository = repository - } - public func execute(type: DictionaryType, query: DictionaryListQuery) -> Observable { - return repository.fetchMonsterList(keyword: query.keyword, minLevel: query.minLevel, maxLevel: query.maxLevel, page: query.page, size: query.size, sort: query.sort) - - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryNpcListUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryNpcListUseCaseImpl.swift deleted file mode 100644 index 95ea2602..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryNpcListUseCaseImpl.swift +++ /dev/null @@ -1,16 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchDictionaryNpcListUseCaseImpl: FetchDictionaryNpcListUseCase { - - private let repository: DictionaryListAPIRepository - - public init(repository: DictionaryListAPIRepository) { - self.repository = repository - } - - public func execute(keyword: String, page: Int, size: Int, sort: String?) -> Observable { - return repository.fetchNpcList(keyword: keyword, page: page, size: size, sort: sort) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryQuestListUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryQuestListUseCaseImpl.swift deleted file mode 100644 index efac0f6b..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryQuestListUseCaseImpl.swift +++ /dev/null @@ -1,16 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchDictionaryQuestListUseCaseImpl: FetchDictionaryQuestListUseCase { - - private let repository: DictionaryListAPIRepository - - public init(repository: DictionaryListAPIRepository) { - self.repository = repository - } - - public func execute(keyword: String, page: Int, size: Int, sort: String?) -> Observable { - return repository.fetchQuestList(keyword: keyword, page: page, size: size, sort: sort) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionarySearchListUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionarySearchListUseCaseImpl.swift deleted file mode 100644 index 69e0fcb4..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionarySearchListUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchDictionarySearchListUseCaseImpl: FetchDictionarySearchListUseCase { - private let repository: DictionaryListAPIRepository - - public init(repository: DictionaryListAPIRepository) { - self.repository = repository - } - - public func execute(keyword: String) -> Observable { - return repository.fetchAllList(keyword: keyword, page: nil) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/ToggleBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/ToggleBookmarkUseCaseImpl.swift deleted file mode 100644 index 2b89ed51..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/ToggleBookmarkUseCaseImpl.swift +++ /dev/null @@ -1,16 +0,0 @@ -// import DomainInterface -// import RxSwift -// -// public final class ToggleBookmarkUseCaseImpl: ToggleBookmarkUseCase { -// private let repository: DictionaryListRepository -// -// public init(repository: DictionaryListRepository) { -// self.repository = repository -// } -// -// public func execute(id: String, type: DictionaryType) -> Observable<[DictionaryItem]> { -// return repository.toggleBookmark(id: id) -// .withLatestFrom(repository.observeItems(type: type)) -// .take(1) -// } -// } diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryListCount/FetchDictionaryListCountUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryListCount/FetchDictionaryListCountUseCaseImpl.swift deleted file mode 100644 index eb58efa8..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryListCount/FetchDictionaryListCountUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class FetchDictionaryListCountUseCaseImpl: FetchDictionaryListCountUseCase { - private let repository: DictionaryListAPIRepository - - public init(repository: DictionaryListAPIRepository) { - self.repository = repository - } - - public func execute(type: String, keyword: String?) -> Observable { - return repository.fetchSearchListCount(type: type, keyword: keyword) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/LocalToken/DeleteTokenFromLocalUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/LocalToken/DeleteTokenFromLocalUseCaseImpl.swift deleted file mode 100644 index ad6a4a04..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/LocalToken/DeleteTokenFromLocalUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation - -import DomainInterface - -public class DeleteTokenFromLocalUseCaseImpl: DeleteTokenFromLocalUseCase { - var repository: TokenRepository - - public init(repository: TokenRepository) { - self.repository = repository - } - - public func execute(type: TokenType) -> Result { - return repository.deleteToken(type: type) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/LocalToken/FetchTokenFromLocalUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/LocalToken/FetchTokenFromLocalUseCaseImpl.swift deleted file mode 100644 index 175257ce..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/LocalToken/FetchTokenFromLocalUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation - -import DomainInterface - -public class FetchTokenFromLocalUseCaseImpl: FetchTokenFromLocalUseCase { - var repository: TokenRepository - - public init(repository: TokenRepository) { - self.repository = repository - } - - public func execute(type: TokenType) -> Result { - return repository.fetchToken(type: type) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/LocalToken/SaveTokenToLocalUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/LocalToken/SaveTokenToLocalUseCaseImpl.swift deleted file mode 100644 index 069d3188..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/LocalToken/SaveTokenToLocalUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation - -import DomainInterface - -public class SaveTokenToLocalUseCaseImpl: SaveTokenToLocalUseCase { - var repository: TokenRepository - - public init(repository: TokenRepository) { - self.repository = repository - } - - public func execute(type: TokenType, value: String) -> Result { - return repository.saveToken(type: type, value: value) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/MyPage/FetchJobUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/MyPage/FetchJobUseCaseImpl.swift deleted file mode 100644 index 3e6f714b..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/MyPage/FetchJobUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public class FetchJobUseCaseImpl: FetchJobUseCase { - private var repository: AuthAPIRepository - - public init(repository: AuthAPIRepository) { - self.repository = repository - } - - public func execute(jobId: String) -> Observable { - repository.fetchJob(jobId: jobId) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/MyPage/UpdateNickNameUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/MyPage/UpdateNickNameUseCaseImpl.swift deleted file mode 100644 index 64fabd1e..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/MyPage/UpdateNickNameUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public class UpdateNickNameUseCaseImpl: UpdateNickNameUseCase { - private var repository: AuthAPIRepository - - public init(repository: AuthAPIRepository) { - self.repository = repository - } - - public func execute(nickName: String) -> Observable { - return repository.updateNickName(nickName: nickName) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/MyPage/UpdateProfileImageUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/MyPage/UpdateProfileImageUseCaseImpl.swift deleted file mode 100644 index 188a6d9b..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/MyPage/UpdateProfileImageUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public class UpdateProfileImageUseCaseImpl: UpdateProfileImageUseCase { - private var repository: AuthAPIRepository - - public init(repository: AuthAPIRepository) { - self.repository = repository - } - - public func execute(url: String) -> Completable { - return repository.updateProfileImage(url: url) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchAddUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchAddUseCaseImpl.swift deleted file mode 100644 index 929ec5f6..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchAddUseCaseImpl.swift +++ /dev/null @@ -1,17 +0,0 @@ -import DomainInterface -import Foundation - -import RxSwift - -public class RecentSearchAddUseCaseImpl: RecentSearchAddUseCase { - var repository: UserDefaultsRepository - - public init(repository: UserDefaultsRepository) { - self.repository = repository - } - - public func add(keyword: String) -> Completable { - guard !keyword.isEmpty else { return .empty() } - return repository.addRecentSearch(keyword: keyword) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchFetchUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchFetchUseCaseImpl.swift deleted file mode 100644 index e4e440d7..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchFetchUseCaseImpl.swift +++ /dev/null @@ -1,15 +0,0 @@ -import DomainInterface - -import RxSwift - -public final class RecentSearchFetchUseCaseImpl: RecentSearchFetchUseCase { - private let repository: UserDefaultsRepository - - public init(repository: UserDefaultsRepository) { - self.repository = repository - } - - public func fetch() -> Observable<[String]> { - return repository.fetchRecentSearch() - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchRemoveUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchRemoveUseCaseImpl.swift deleted file mode 100644 index f130bd25..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchRemoveUseCaseImpl.swift +++ /dev/null @@ -1,20 +0,0 @@ -import DomainInterface -import Foundation - -import RxSwift - -public class RecentSearchRemoveUseCaseImpl: RecentSearchRemoveUseCase { - var repository: UserDefaultsRepository - - public init(repository: UserDefaultsRepository) { - self.repository = repository - } - - public func remove(keyword: String) -> Completable { - return repository.removeRecentSearch(keyword: keyword) - } - - public func removeAll() -> Completable { - return repository.removeAllSearch() - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Shared/CheckEmptyLevelAndRoleUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Shared/CheckEmptyLevelAndRoleUseCaseImpl.swift deleted file mode 100644 index db4e50a3..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Shared/CheckEmptyLevelAndRoleUseCaseImpl.swift +++ /dev/null @@ -1,13 +0,0 @@ -import DomainInterface - -import RxSwift - -public class CheckEmptyLevelAndRoleUseCaseImpl: CheckEmptyLevelAndRoleUseCase { - public init() {} - - public func execute(level: Int?, job: String?) -> Observable { - let isValidLevel = level.map { (1 ... 200).contains($0) } ?? false - let isValidRole = job != nil && job != "" - return .just(isValidLevel && isValidRole) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Shared/CheckValidLevelUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Shared/CheckValidLevelUseCaseImpl.swift deleted file mode 100644 index ad38bda5..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Shared/CheckValidLevelUseCaseImpl.swift +++ /dev/null @@ -1,14 +0,0 @@ -import DomainInterface - -import RxSwift - -public class CheckValidLevelUseCaseImpl: CheckValidLevelUseCase { - public init() {} - - public func execute(level: Int?) -> Observable { - guard let level = level else { - return .just(nil) - } - return .just((1 ... 200).contains(level)) - } -} diff --git a/MLS/Domain/Domain/UseCaseImpl/Shared/SocialLoginUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Shared/SocialLoginUseCaseImpl.swift deleted file mode 100644 index 9d59f9f3..00000000 --- a/MLS/Domain/Domain/UseCaseImpl/Shared/SocialLoginUseCaseImpl.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -import DomainInterface - -import RxSwift - -public class SocialLoginUseCaseImpl: FetchSocialCredentialUseCase { - public var provider: any SocialAuthenticatableProvider - - public init(provider: any SocialAuthenticatableProvider) { - self.provider = provider - } - - public func execute() -> Observable { - return provider.getCredential() - } -} diff --git a/MLS/Domain/DomainInterface/Entity/AuthAPI/Credential.swift b/MLS/Domain/DomainInterface/Entity/AuthAPI/Credential.swift deleted file mode 100644 index 7abf603a..00000000 --- a/MLS/Domain/DomainInterface/Entity/AuthAPI/Credential.swift +++ /dev/null @@ -1,4 +0,0 @@ -public protocol Credential: Encodable { - var token: String { get } - var providerID: String { get } -} diff --git a/MLS/Domain/DomainInterface/Entity/AuthAPI/JobListResponse.swift b/MLS/Domain/DomainInterface/Entity/AuthAPI/JobListResponse.swift deleted file mode 100644 index 44e49789..00000000 --- a/MLS/Domain/DomainInterface/Entity/AuthAPI/JobListResponse.swift +++ /dev/null @@ -1,19 +0,0 @@ -import Foundation - -public struct JobListResponse { - public var jobList: [Job] - - public init(jobList: [Job]) { - self.jobList = jobList - } -} - -public struct Job: Equatable { - public let name: String - public let id: Int - - public init(name: String, id: Int) { - self.name = name - self.id = id - } -} diff --git a/MLS/Domain/DomainInterface/Entity/AuthAPI/LoginResponse.swift b/MLS/Domain/DomainInterface/Entity/AuthAPI/LoginResponse.swift deleted file mode 100644 index 2d2e0105..00000000 --- a/MLS/Domain/DomainInterface/Entity/AuthAPI/LoginResponse.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Foundation - -public struct LoginResponse { - public var isRegister: Bool - public var accessToken: String - public var refreshToken: String - - public init(isRegister: Bool, accessToken: String, refreshToken: String) { - self.isRegister = isRegister - self.accessToken = accessToken - self.refreshToken = refreshToken - } -} diff --git a/MLS/Domain/DomainInterface/Entity/AuthAPI/SignUpResponse.swift b/MLS/Domain/DomainInterface/Entity/AuthAPI/SignUpResponse.swift deleted file mode 100644 index 1f4091e4..00000000 --- a/MLS/Domain/DomainInterface/Entity/AuthAPI/SignUpResponse.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation - -public struct SignUpResponse { - public var accessToken: String - public var refreshToken: String - - public init(accessToken: String, refreshToken: String) { - self.accessToken = accessToken - self.refreshToken = refreshToken - } -} diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryItem.swift b/MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryItem.swift deleted file mode 100644 index 109d8340..00000000 --- a/MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryItem.swift +++ /dev/null @@ -1,28 +0,0 @@ -import UIKit - -public struct DictionaryItem: Equatable { - public let id: Int - public let type: DictionaryItemType - public let mainText: String - public let subText: String - public let image: UIImage - public var isBookmarked: Bool - - public static func == (lhs: DictionaryItem, rhs: DictionaryItem) -> Bool { - return lhs.id == rhs.id && - lhs.type == rhs.type && - lhs.mainText == rhs.mainText && - lhs.subText == rhs.subText && - lhs.image == rhs.image && - lhs.isBookmarked == rhs.isBookmarked - } - - public init(id: Int, type: DictionaryItemType, mainText: String, subText: String, image: UIImage, isBookmarked: Bool) { - self.id = id - self.type = type - self.mainText = mainText - self.subText = subText - self.image = image - self.isBookmarked = isBookmarked - } -} diff --git a/MLS/Domain/DomainInterface/Entity/Filter/FilterType.swift b/MLS/Domain/DomainInterface/Entity/Filter/FilterType.swift deleted file mode 100644 index 2546f395..00000000 --- a/MLS/Domain/DomainInterface/Entity/Filter/FilterType.swift +++ /dev/null @@ -1,4 +0,0 @@ -enum FilterType { - case monster - case item -} diff --git a/MLS/Domain/DomainInterface/Entity/Shared/LoginPlatform.swift b/MLS/Domain/DomainInterface/Entity/Shared/LoginPlatform.swift deleted file mode 100644 index 35c4f7ff..00000000 --- a/MLS/Domain/DomainInterface/Entity/Shared/LoginPlatform.swift +++ /dev/null @@ -1,6 +0,0 @@ -import Foundation - -public enum LoginPlatform: String { - case kakao - case apple -} diff --git a/MLS/Domain/DomainInterface/Error/AuthError.swift b/MLS/Domain/DomainInterface/Error/AuthError.swift deleted file mode 100644 index abc34978..00000000 --- a/MLS/Domain/DomainInterface/Error/AuthError.swift +++ /dev/null @@ -1,5 +0,0 @@ -public enum AuthError: Error { - case unknown(message: String) - case userNotFound(credential: Credential) - case tokenExpired -} diff --git a/MLS/Domain/DomainInterface/Interface/.DS_Store b/MLS/Domain/DomainInterface/Interface/.DS_Store deleted file mode 100644 index 6237231b..00000000 Binary files a/MLS/Domain/DomainInterface/Interface/.DS_Store and /dev/null differ diff --git a/MLS/Domain/DomainInterface/Interface/Network/HTTPMethod.swift b/MLS/Domain/DomainInterface/Interface/Network/HTTPMethod.swift deleted file mode 100644 index 818b9aff..00000000 --- a/MLS/Domain/DomainInterface/Interface/Network/HTTPMethod.swift +++ /dev/null @@ -1,5 +0,0 @@ -import Foundation - -public enum HTTPMethod: String { - case GET, POST, DELETE, PUT -} diff --git a/MLS/Domain/DomainInterface/Interface/Network/Interceptor.swift b/MLS/Domain/DomainInterface/Interface/Network/Interceptor.swift deleted file mode 100644 index 400e935e..00000000 --- a/MLS/Domain/DomainInterface/Interface/Network/Interceptor.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation - -import RxSwift - -public protocol Interceptor { - /// Request에 요소를 추가하기 위한 함수 - /// - Parameter request: 엔드포인트로 부터 만들어진 Request - /// - Returns: 요소가 추가된 Request - func adapt(_ request: URLRequest) -> URLRequest - - /// 필요에 따라 재요청을 하기 위한 함수 - /// - Parameters: - /// - request: 엔드포인트로 부터 만들어진 Request - /// - response: 돌려받은 응답 - /// - error: 통신간에 발생한 에러 - /// - Returns: 재요청이 필요하면 true, 필요없으면 false - func retry(data: Data?, response: URLResponse?, error: Error?) -> Bool -} diff --git a/MLS/Domain/DomainInterface/Interface/Network/Requestable.swift b/MLS/Domain/DomainInterface/Interface/Network/Requestable.swift deleted file mode 100644 index 53da0f3d..00000000 --- a/MLS/Domain/DomainInterface/Interface/Network/Requestable.swift +++ /dev/null @@ -1,51 +0,0 @@ -import Foundation - -public protocol Requestable { - var baseURL: String { get set } - var path: String { get set } - var method: HTTPMethod { get set } - var query: Encodable? { get set } - var headers: [String: String]? { get set } - var body: Encodable? { get set } -} - -extension Requestable { - /// 엔드포인트 객체의 속성들을 이용하여 Request를 만드는 함수 - /// - Returns: API 통신에 필요한 요청 -> Request - public func getUrlRequest() throws -> URLRequest { - guard var base = URL(string: baseURL) else { - throw URLError(.badURL) - } - - base.appendPathComponent(path) - - guard var components = URLComponents(url: base, resolvingAgainstBaseURL: false) else { - throw URLError(.badURL) - } - - if let query = query { - let queryData = try JSONEncoder().encode(query) - let dictionary = try JSONSerialization.jsonObject(with: queryData, options: []) as? [String: Any] - components.queryItems = dictionary?.map { - URLQueryItem(name: $0.key, value: "\($0.value)") - } - } - - guard let url = components.url else { - throw URLError(.badURL) - } - - var request = URLRequest(url: url) - request.httpMethod = method.rawValue - - headers?.forEach { key, value in - request.setValue(value, forHTTPHeaderField: key) - } - - if let body = body { - request.httpBody = try JSONEncoder().encode(body) - request.setValue("application/json", forHTTPHeaderField: "Content-Type") - } - return request - } -} diff --git a/MLS/Domain/DomainInterface/Interface/Network/Responsable.swift b/MLS/Domain/DomainInterface/Interface/Network/Responsable.swift deleted file mode 100644 index d185fd71..00000000 --- a/MLS/Domain/DomainInterface/Interface/Network/Responsable.swift +++ /dev/null @@ -1,5 +0,0 @@ -import Foundation - -public protocol Responsable { - associatedtype Response: Decodable -} diff --git a/MLS/Domain/DomainInterface/Providers/NetworkProvider.swift b/MLS/Domain/DomainInterface/Providers/NetworkProvider.swift deleted file mode 100644 index 1adb9ac2..00000000 --- a/MLS/Domain/DomainInterface/Providers/NetworkProvider.swift +++ /dev/null @@ -1,19 +0,0 @@ -import Foundation - -import RxSwift - -public protocol NetworkProvider { - /// 엔드포인트를 이용하여 응답이 있는 통신을 위한 함수 - /// - Parameters: - /// - endPoint: 목적지에 대한 정보를 담고있는 엔드포인트 객체 - /// - interceptor: Request에 필요한 인터셉터 - /// - Returns: 응답값을 포함한 Response - func requestData(endPoint: T, interceptor: Interceptor?) -> Observable - - /// 엔드포인트를 이용하여 응답이 없는 통신을 위한 함수 - /// - Parameters: - /// - endPoint: 목적지에 대한 정보를 담고있는 엔드포인트 객체 - /// - interceptor: Request에 필요한 인터셉터 - /// - Returns: 통신이 완료되었는지만 확인 가능 - func requestData(endPoint: Requestable, interceptor: Interceptor?) -> Completable -} diff --git a/MLS/Domain/DomainInterface/Providers/SocialAuthenticatableProvider.swift b/MLS/Domain/DomainInterface/Providers/SocialAuthenticatableProvider.swift deleted file mode 100644 index 090ccb67..00000000 --- a/MLS/Domain/DomainInterface/Providers/SocialAuthenticatableProvider.swift +++ /dev/null @@ -1,7 +0,0 @@ -import Foundation - -import RxSwift - -public protocol SocialAuthenticatableProvider { - func getCredential() -> Observable -} diff --git a/MLS/Domain/DomainInterface/Repository/AuthAPIRepository.swift b/MLS/Domain/DomainInterface/Repository/AuthAPIRepository.swift deleted file mode 100644 index 2e8b1224..00000000 --- a/MLS/Domain/DomainInterface/Repository/AuthAPIRepository.swift +++ /dev/null @@ -1,68 +0,0 @@ -import Foundation - -import RxSwift - -/// 사용자 인증 및 회원 관련 API 호출을 담당하는 레포지토리 프로토콜 -public protocol AuthAPIRepository { - - /// 카카오 로그인 API 호출 - /// - /// - Parameter credential: 카카오 로그인에 필요한 자격 증명 (예: access token 등) - /// - Returns: 로그인 응답을 담은 Observable - func loginWithKakao(credential: Credential) -> Observable - - /// 애플 로그인 API 호출 - /// - /// - Parameter credential: 애플 로그인에 필요한 자격 증명 (예: identity token 등) - /// - Returns: 로그인 응답을 담은 Observable - func loginWithApple(credential: Credential) -> Observable - - /// 카카오 회원가입 API 호출 - /// - /// - Parameters: - /// - credential: 회원가입에 필요한 사용자 정보 - /// - isMarketingAgreement: 마케팅 수신 동의 여부 - /// - Returns: 회원가입 응답을 담은 Observable - func signUpWithKakao(credential: Credential, isMarketingAgreement: Bool, fcmToken: String?) -> Observable - - /// 애플 회원가입 API 호출 - /// - /// - Parameters: - /// - credential: 회원가입에 필요한 사용자 정보 - /// - isMarketingAgreement: 마케팅 수신 동의 여부 - /// - Returns: 회원가입 응답을 담은 Observable - func signUpWithApple(credential: Credential, isMarketingAgreement: Bool, fcmToken: String?) -> Observable - - func withdraw() -> Completable - - /// 직업 목록 조회 API 호출 - /// - /// - Returns: 직업 목록 응답을 담은 Observable - func fetchJobList() -> Observable - - func fetchJob(jobId: String) -> Observable - - /// 사용자 정보 수정 API 호출 - /// - /// - Note: 레벨과 직업을 업데이트 - /// - Returns: 작업 완료 여부를 나타내는 Completable - func updateUserInfo(level: Int, selectedJobID: Int) -> Completable - - /// 토큰 재발행 API 호출 - /// - /// - Parameter credential: refreshToken - /// - Returns: 토큰 갱신 응답을 담은 Observable - func reissueToken(refreshToken: String) -> Observable - - func fcmToken(fcmToken: String?) -> Completable - - func updateMarketingAgreement(credential: String, isMarketingAgreement: Bool) -> Completable - - func updateNotificationAgreement(noticeAgreement: Bool, patchNoteAgreement: Bool, eventAgreement: Bool) -> Completable - - func updateNickName(nickName: String) -> Observable - - func updateProfileImage(url: String) -> Completable - - func fetchProfile() -> Observable -} diff --git a/MLS/Domain/DomainInterface/Repository/TokenRepository.swift b/MLS/Domain/DomainInterface/Repository/TokenRepository.swift deleted file mode 100644 index d9043fe2..00000000 --- a/MLS/Domain/DomainInterface/Repository/TokenRepository.swift +++ /dev/null @@ -1,34 +0,0 @@ -import Foundation -import Security - -import RxSwift - -public enum TokenRepositoryError: Error { - case noValueFound(message: String) // 해당 키에 대해 값을 찾을 수 없을 때 발생 - case unhandledError(status: OSStatus) // 예상치 못한 OSStatus 오류가 발생했을 때 발생 - case dataConversionError(message: String) // 데이터 변환 중 오류가 발생했을 때 발생 -} - -public enum TokenType: String { - case accessToken // 액세스 토큰 - case refreshToken // 리프레시 토큰 - case fcmToken // fcm토큰 -} - -public protocol TokenRepository { - /// 토큰을 가져오는 메서드 - /// - Parameter type: 가져오려는 토큰의 타입 (`accessToken` 또는 `refreshToken`) - /// - Returns: 가져온 토큰을 담은 `Single` - func fetchToken(type: TokenType) -> Result - - /// 토큰을 저장하는 메서드 - /// - Parameter type: 저장하려는 토큰의 타입 (`accessToken` 또는 `refreshToken`) - /// - Parameter value: 저장할 토큰의 값 - /// - Returns: 완료 시 `Completable` - func saveToken(type: TokenType, value: String) -> Result - - /// 토큰을 삭제하는 메서드 - /// - Parameter type: 삭제하려는 토큰의 타입 (`accessToken` 또는 `refreshToken`) - /// - Returns: 완료 시 `Completable` - func deleteToken(type: TokenType) -> Result -} diff --git a/MLS/Domain/DomainInterface/Repository/UserDefaultsRepository.swift b/MLS/Domain/DomainInterface/Repository/UserDefaultsRepository.swift deleted file mode 100644 index f837248e..00000000 --- a/MLS/Domain/DomainInterface/Repository/UserDefaultsRepository.swift +++ /dev/null @@ -1,17 +0,0 @@ -import RxSwift - -public protocol UserDefaultsRepository { - func fetchRecentSearch() -> Observable<[String]> - func addRecentSearch(keyword: String) -> Completable - func removeRecentSearch(keyword: String) -> Completable - func removeAllSearch() -> Completable - - func fetchPlatform() -> Observable - func savePlatform(platform: LoginPlatform) -> Completable - - func fetchBookmark() -> Observable - func saveBookmark() -> Completable - - func fetchDictionaryDetail() -> Observable - func saveDictionaryDetail() -> Completable -} diff --git a/MLS/Domain/DomainInterface/UseCase/Alarm/FetchNoticesUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Alarm/FetchNoticesUseCase.swift deleted file mode 100644 index 3f2e85fb..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Alarm/FetchNoticesUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchNoticesUseCase { - func execute(id: Int?, pageSize: Int) -> Observable> -} diff --git a/MLS/Domain/DomainInterface/UseCase/Alarm/FetchOngoingEventsUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Alarm/FetchOngoingEventsUseCase.swift deleted file mode 100644 index d1ca0894..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Alarm/FetchOngoingEventsUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchOngoingEventsUseCase { - func execute(id: Int?, pageSize: Int) -> Observable> -} diff --git a/MLS/Domain/DomainInterface/UseCase/Alarm/FetchOutdatedEventsUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Alarm/FetchOutdatedEventsUseCase.swift deleted file mode 100644 index e322d2af..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Alarm/FetchOutdatedEventsUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchOutdatedEventsUseCase { - func execute(id: Int?, pageSize: Int) -> Observable> -} diff --git a/MLS/Domain/DomainInterface/UseCase/Alarm/FetchPatchNotesUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Alarm/FetchPatchNotesUseCase.swift deleted file mode 100644 index 2439fb39..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Alarm/FetchPatchNotesUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchPatchNotesUseCase { - func execute(id: Int?, pageSize: Int) -> Observable> -} diff --git a/MLS/Domain/DomainInterface/UseCase/Alarm/SetReadUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Alarm/SetReadUseCase.swift deleted file mode 100644 index f953f489..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Alarm/SetReadUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol SetReadUseCase { - func execute(alarmLink: String) -> Completable -} diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/FetchJobListUseCase.swift b/MLS/Domain/DomainInterface/UseCase/AuthAPI/FetchJobListUseCase.swift deleted file mode 100644 index 12ab91bb..00000000 --- a/MLS/Domain/DomainInterface/UseCase/AuthAPI/FetchJobListUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchJobListUseCase { - func execute() -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/FetchPlatformUseCase.swift b/MLS/Domain/DomainInterface/UseCase/AuthAPI/FetchPlatformUseCase.swift deleted file mode 100644 index ce1113f9..00000000 --- a/MLS/Domain/DomainInterface/UseCase/AuthAPI/FetchPlatformUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchPlatformUseCase { - func execute() -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/LoginWithAppleUseCase.swift b/MLS/Domain/DomainInterface/UseCase/AuthAPI/LoginWithAppleUseCase.swift deleted file mode 100644 index d938224b..00000000 --- a/MLS/Domain/DomainInterface/UseCase/AuthAPI/LoginWithAppleUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol LoginWithAppleUseCase { - func execute(credential: Credential) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/LoginWithKakaoUseCase.swift b/MLS/Domain/DomainInterface/UseCase/AuthAPI/LoginWithKakaoUseCase.swift deleted file mode 100644 index 8f856400..00000000 --- a/MLS/Domain/DomainInterface/UseCase/AuthAPI/LoginWithKakaoUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol LoginWithKakaoUseCase { - func execute(credential: Credential) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/OpenNotificationSettingUseCase.swift b/MLS/Domain/DomainInterface/UseCase/AuthAPI/OpenNotificationSettingUseCase.swift deleted file mode 100644 index b2f485ca..00000000 --- a/MLS/Domain/DomainInterface/UseCase/AuthAPI/OpenNotificationSettingUseCase.swift +++ /dev/null @@ -1,3 +0,0 @@ -public protocol OpenNotificationSettingUseCase { - func execute() -} diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/PutFCMTokenUseCase.swift b/MLS/Domain/DomainInterface/UseCase/AuthAPI/PutFCMTokenUseCase.swift deleted file mode 100644 index 283f676c..00000000 --- a/MLS/Domain/DomainInterface/UseCase/AuthAPI/PutFCMTokenUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol PutFCMTokenUseCase { - func execute(fcmToken: String?) -> Completable -} diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/ReissueUseCase.swift b/MLS/Domain/DomainInterface/UseCase/AuthAPI/ReissueUseCase.swift deleted file mode 100644 index 4ab43bb7..00000000 --- a/MLS/Domain/DomainInterface/UseCase/AuthAPI/ReissueUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol ReissueUseCase { - func execute(refreshToken: String) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/SignUpWithAppleUseCase.swift b/MLS/Domain/DomainInterface/UseCase/AuthAPI/SignUpWithAppleUseCase.swift deleted file mode 100644 index 77c0033c..00000000 --- a/MLS/Domain/DomainInterface/UseCase/AuthAPI/SignUpWithAppleUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol SignUpWithAppleUseCase { - func execute(credential: Credential, isMarketingAgreement: Bool, fcmToken: String?) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/SignUpWithKakaoUseCase.swift b/MLS/Domain/DomainInterface/UseCase/AuthAPI/SignUpWithKakaoUseCase.swift deleted file mode 100644 index 480bc0d9..00000000 --- a/MLS/Domain/DomainInterface/UseCase/AuthAPI/SignUpWithKakaoUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol SignUpWithKakaoUseCase { - func execute(credential: Credential, isMarketingAgreement: Bool, fcmToken: String?) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/UpdateMarketingAgreementUseCase.swift b/MLS/Domain/DomainInterface/UseCase/AuthAPI/UpdateMarketingAgreementUseCase.swift deleted file mode 100644 index 5311731f..00000000 --- a/MLS/Domain/DomainInterface/UseCase/AuthAPI/UpdateMarketingAgreementUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol UpdateMarketingAgreementUseCase { - func execute(credential: String, isMarketingAgreement: Bool) -> Completable -} diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/UpdateNotificationAgreementUseCase.swift b/MLS/Domain/DomainInterface/UseCase/AuthAPI/UpdateNotificationAgreementUseCase.swift deleted file mode 100644 index f7d052be..00000000 --- a/MLS/Domain/DomainInterface/UseCase/AuthAPI/UpdateNotificationAgreementUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol UpdateNotificationAgreementUseCase { - func execute(noticeAgreement: Bool, patchNoteAgreement: Bool, eventAgreement: Bool) -> Completable -} diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/UpdateUserInfoUseCase.swift b/MLS/Domain/DomainInterface/UseCase/AuthAPI/UpdateUserInfoUseCase.swift deleted file mode 100644 index 8e4e8cb9..00000000 --- a/MLS/Domain/DomainInterface/UseCase/AuthAPI/UpdateUserInfoUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol UpdateUserInfoUseCase { - func execute(level: Int, selectedJobID: Int) -> Completable -} diff --git a/MLS/Domain/DomainInterface/UseCase/Bookmark/DeleteBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Bookmark/DeleteBookmarkUseCase.swift deleted file mode 100644 index 09f11d50..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Bookmark/DeleteBookmarkUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol DeleteBookmarkUseCase { - func execute(bookmarkId: Int) -> Completable -} diff --git a/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchBookmarkUseCase.swift deleted file mode 100644 index 149d541c..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchBookmarkUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchBookmarkUseCase { - func execute(sort: SortType?) -> Observable<[BookmarkResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchItemBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchItemBookmarkUseCase.swift deleted file mode 100644 index af6533ba..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchItemBookmarkUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchItemBookmarkUseCase { - func execute(jobId: Int?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, sort: SortType?) -> Observable<[BookmarkResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchMapBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchMapBookmarkUseCase.swift deleted file mode 100644 index 1888ad12..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchMapBookmarkUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchMapBookmarkUseCase { - func execute(sort: SortType?) -> Observable<[BookmarkResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchMonsterBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchMonsterBookmarkUseCase.swift deleted file mode 100644 index 3b5d9298..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchMonsterBookmarkUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchMonsterBookmarkUseCase { - func execute(minLevel: Int?, maxLevel: Int?, sort: SortType?) -> Observable<[BookmarkResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchNPCBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchNPCBookmarkUseCase.swift deleted file mode 100644 index 6511d45c..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchNPCBookmarkUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchNPCBookmarkUseCase { - func execute(sort: SortType?) -> Observable<[BookmarkResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchQuestBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchQuestBookmarkUseCase.swift deleted file mode 100644 index c62018b0..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchQuestBookmarkUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchQuestBookmarkUseCase { - func execute(sort: SortType?) -> Observable<[BookmarkResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchVisitBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchVisitBookmarkUseCase.swift deleted file mode 100644 index 7df67704..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Bookmark/FetchVisitBookmarkUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchVisitBookmarkUseCase { - func execute() -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/Bookmark/SetBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Bookmark/SetBookmarkUseCase.swift deleted file mode 100644 index 94117213..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Bookmark/SetBookmarkUseCase.swift +++ /dev/null @@ -1,10 +0,0 @@ -import RxSwift - -public protocol SetBookmarkUseCase { - func execute(bookmarkId: Int, isBookmark: IsBookmark) -> Observable -} - -public enum IsBookmark { - case set(DictionaryItemType) - case delete -} diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift deleted file mode 100644 index 50cc8f34..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -// import RxSwift -// -// public protocol AddBookmarksToCollectionUseCase { -// func execute(collectionId: Int, bookmarkIds: [Int]) -> Completable -// } diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionAndBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionAndBookmarkUseCase.swift deleted file mode 100644 index b8f641d4..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionAndBookmarkUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol AddCollectionAndBookmarkUseCase { - func execute(collectionIds: [Int], bookmarkIds: [Int]) -> Completable -} diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift deleted file mode 100644 index 0fe0a91d..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -// import RxSwift -// -// public protocol AddCollectionsToBookmarkUseCase { -// func execute(bookmarkId: Int, collectionIds: [Int]) -> Completable -// } diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/CreateCollectionListUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/CreateCollectionListUseCase.swift deleted file mode 100644 index 1e2adbf8..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Collection/CreateCollectionListUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol CreateCollectionListUseCase { - func execute(name: String) -> Completable -} diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/DeleteCollectionUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/DeleteCollectionUseCase.swift deleted file mode 100644 index 738ccbb9..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Collection/DeleteCollectionUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol DeleteCollectionUseCase { - func execute(collectionId: Int) -> Completable -} diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/FetchCollectionListUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/FetchCollectionListUseCase.swift deleted file mode 100644 index 96513970..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Collection/FetchCollectionListUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchCollectionListUseCase { - func execute(sort: SortType?) -> Observable<[CollectionResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/FetchCollectionUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/FetchCollectionUseCase.swift deleted file mode 100644 index 29d8226d..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Collection/FetchCollectionUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchCollectionUseCase { - func execute(id: Int) -> Observable<[BookmarkResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/UpdateCollectionUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/UpdateCollectionUseCase.swift deleted file mode 100644 index efeb3308..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Collection/UpdateCollectionUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol UpdateCollectionUseCase { - func execute(collectionId: Int, name: String) -> Completable -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailItemDropMonsterUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailItemDropMonsterUseCase.swift deleted file mode 100644 index 4846a970..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailItemDropMonsterUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryDetailItemDropMonsterUseCase { - func execute(id: Int, sort: String?) -> Observable<[DictionaryDetailItemDropMonsterResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailItemUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailItemUseCase.swift deleted file mode 100644 index ec2f31e8..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailItemUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryDetailItemUseCase { - func execute(id: Int) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMapNpcUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMapNpcUseCase.swift deleted file mode 100644 index 3ed6927d..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMapNpcUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryDetailMapNpcUseCase { - func execute(id: Int) -> Observable<[DictionaryDetailMapNpcResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMapSpawnMonsterUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMapSpawnMonsterUseCase.swift deleted file mode 100644 index 11e6816c..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMapSpawnMonsterUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryDetailMapSpawnMonsterUseCase { - func execute(id: Int, sort: String?) -> Observable<[DictionaryDetailMapSpawnMonsterResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMapUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMapUseCase.swift deleted file mode 100644 index 81dc05d6..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMapUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryDetailMapUseCase { - func execute(id: Int) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMonsterItemsUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMonsterItemsUseCase.swift deleted file mode 100644 index 85b3e261..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMonsterItemsUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryDetailMonsterItemsUseCase { - func execute(id: Int, sort: String?) -> Observable<[DictionaryDetailMonsterDropItemResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMonsterMapUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMonsterMapUseCase.swift deleted file mode 100644 index 4e683ddd..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMonsterMapUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryDetailMonsterMapUseCase { - func execute(id: Int) -> Observable<[DictionaryDetailMonsterMapResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMonsterUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMonsterUseCase.swift deleted file mode 100644 index 969a146c..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailMonsterUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryDetailMonsterUseCase { - func execute(id: Int) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailNpcMapUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailNpcMapUseCase.swift deleted file mode 100644 index 1069e032..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailNpcMapUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryDetailNpcMapUseCase { - func execute(id: Int) -> Observable<[DictionaryDetailMonsterMapResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailNpcQuestUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailNpcQuestUseCase.swift deleted file mode 100644 index cad5aa6a..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailNpcQuestUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryDetailNpcQuestUseCase { - func execute(id: Int, sort: String?) -> Observable<[DictionaryDetailNpcQuestResponse]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailNpcUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailNpcUseCase.swift deleted file mode 100644 index 29a55429..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailNpcUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryDetailNpcUseCase { - func execute(id: Int) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailQuestLinkedQuestsUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailQuestLinkedQuestsUseCase.swift deleted file mode 100644 index 605d360e..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailQuestLinkedQuestsUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryDetailQuestLinkedQuestsUseCase { - func execute(id: Int) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailQuestUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailQuestUseCase.swift deleted file mode 100644 index b5cb19c4..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchDictionaryDetailQuestUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryDetailQuestUseCase { - func execute(id: Int) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryAllListUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryAllListUseCase.swift deleted file mode 100644 index 47923622..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryAllListUseCase.swift +++ /dev/null @@ -1,6 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryAllListUseCase { - // 쿼리없이 keyword, page만 전달 - func execute(keyword: String?, page: Int?) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryItemListUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryItemListUseCase.swift deleted file mode 100644 index 16ecffba..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryItemListUseCase.swift +++ /dev/null @@ -1,6 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryItemListUseCase { - /// 어떤 타입인지에 따라서 쿼리가 결정됨 -> 타입 마다 쿼리가 댜름. - func execute(keyword: String?, jobId: [Int]?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, page: Int?, size: Int?, sort: String?) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryMapListUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryMapListUseCase.swift deleted file mode 100644 index 4b8ceb53..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryMapListUseCase.swift +++ /dev/null @@ -1,6 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryMapListUseCase { - /// 어떤 타입인지에 따라서 쿼리가 결정됨 -> 타입 마다 쿼리가 댜름. - func execute(keyword: String, page: Int, size: Int, sort: String?) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryMonsterListUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryMonsterListUseCase.swift deleted file mode 100644 index 3f21d6ea..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryMonsterListUseCase.swift +++ /dev/null @@ -1,6 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryMonsterListUseCase { - /// 어떤 타입인지에 따라서 쿼리가 결정됨 -> 타입 마다 쿼리가 댜름. - func execute(type: DictionaryType, query: DictionaryListQuery) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryNpcListUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryNpcListUseCase.swift deleted file mode 100644 index 94dd500f..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryNpcListUseCase.swift +++ /dev/null @@ -1,6 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryNpcListUseCase { - /// 어떤 타입인지에 따라서 쿼리가 결정됨 -> 타입 마다 쿼리가 댜름. - func execute(keyword: String, page: Int, size: Int, sort: String?) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryQuestListUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryQuestListUseCase.swift deleted file mode 100644 index 65d08d20..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryQuestListUseCase.swift +++ /dev/null @@ -1,6 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryQuestListUseCase { - /// 어떤 타입인지에 따라서 쿼리가 결정됨 -> 타입 마다 쿼리가 댜름. - func execute(keyword: String, page: Int, size: Int, sort: String?) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionarySearchListUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionarySearchListUseCase.swift deleted file mode 100644 index d7b537c3..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionarySearchListUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionarySearchListUseCase { - func execute(keyword: String) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryListCount/FetchDictionaryListCountUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryListCount/FetchDictionaryListCountUseCase.swift deleted file mode 100644 index 4874676a..00000000 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryListCount/FetchDictionaryListCountUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchDictionaryListCountUseCase { - func execute(type: String, keyword: String?) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/LocalToken/DeleteTokenFromLocalUseCase.swift b/MLS/Domain/DomainInterface/UseCase/LocalToken/DeleteTokenFromLocalUseCase.swift deleted file mode 100644 index b135567e..00000000 --- a/MLS/Domain/DomainInterface/UseCase/LocalToken/DeleteTokenFromLocalUseCase.swift +++ /dev/null @@ -1,8 +0,0 @@ -import Foundation - -public protocol DeleteTokenFromLocalUseCase { - /// 토큰을 삭제하는 메서드 - /// - Parameter type: 삭제하려는 토큰의 타입 (`accessToken` 또는 `refreshToken`) - /// - Returns: 완료 시 `Result` - func execute(type: TokenType) -> Result -} diff --git a/MLS/Domain/DomainInterface/UseCase/LocalToken/FetchTokenFromLocalUseCase.swift b/MLS/Domain/DomainInterface/UseCase/LocalToken/FetchTokenFromLocalUseCase.swift deleted file mode 100644 index 1d914adf..00000000 --- a/MLS/Domain/DomainInterface/UseCase/LocalToken/FetchTokenFromLocalUseCase.swift +++ /dev/null @@ -1,8 +0,0 @@ -import Foundation - -public protocol FetchTokenFromLocalUseCase { - /// 토큰을 가져오는 메서드 - /// - Parameter type: 가져오려는 토큰의 타입 (`accessToken` 또는 `refreshToken`) - /// - Returns: 가져온 토큰을 담은 `Result` - func execute(type: TokenType) -> Result -} diff --git a/MLS/Domain/DomainInterface/UseCase/LocalToken/SaveTokenToLocalUseCase.swift b/MLS/Domain/DomainInterface/UseCase/LocalToken/SaveTokenToLocalUseCase.swift deleted file mode 100644 index 5c18a369..00000000 --- a/MLS/Domain/DomainInterface/UseCase/LocalToken/SaveTokenToLocalUseCase.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Foundation - -public protocol SaveTokenToLocalUseCase { - /// 토큰을 저장하는 메서드 - /// - Parameter type: 저장하려는 토큰의 타입 (`accessToken` 또는 `refreshToken`) - /// - Parameter value: 저장할 토큰의 값 - /// - Returns: 완료 시 `Result` - func execute(type: TokenType, value: String) -> Result -} diff --git a/MLS/Domain/DomainInterface/UseCase/MyPage/FetchJobUseCase.swift b/MLS/Domain/DomainInterface/UseCase/MyPage/FetchJobUseCase.swift deleted file mode 100644 index bf7e719f..00000000 --- a/MLS/Domain/DomainInterface/UseCase/MyPage/FetchJobUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol FetchJobUseCase { - func execute(jobId: String) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/MyPage/UpdateNickNameUseCase.swift b/MLS/Domain/DomainInterface/UseCase/MyPage/UpdateNickNameUseCase.swift deleted file mode 100644 index 19638955..00000000 --- a/MLS/Domain/DomainInterface/UseCase/MyPage/UpdateNickNameUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol UpdateNickNameUseCase { - func execute(nickName: String) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/MyPage/UpdateProfileImageUseCase.swift b/MLS/Domain/DomainInterface/UseCase/MyPage/UpdateProfileImageUseCase.swift deleted file mode 100644 index 89f44160..00000000 --- a/MLS/Domain/DomainInterface/UseCase/MyPage/UpdateProfileImageUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol UpdateProfileImageUseCase { - func execute(url: String) -> Completable -} diff --git a/MLS/Domain/DomainInterface/UseCase/OnBoarding/CheckEmptyLevelAndRoleUseCase.swift b/MLS/Domain/DomainInterface/UseCase/OnBoarding/CheckEmptyLevelAndRoleUseCase.swift deleted file mode 100644 index f044413f..00000000 --- a/MLS/Domain/DomainInterface/UseCase/OnBoarding/CheckEmptyLevelAndRoleUseCase.swift +++ /dev/null @@ -1,10 +0,0 @@ -import RxSwift - -public protocol CheckEmptyLevelAndRoleUseCase { - /// 레벨의 범위가 0~200에 속하는지 + 직업이 유효한 값인지 확인 - /// - Parameters: - /// - level: 현재 입력된 레벨 - /// - job: 현재 입력된 직업 - /// - Returns: true / false - func execute(level: Int?, job: String?) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/OnBoarding/CheckValidLevelUseCase.swift b/MLS/Domain/DomainInterface/UseCase/OnBoarding/CheckValidLevelUseCase.swift deleted file mode 100644 index 08b4683d..00000000 --- a/MLS/Domain/DomainInterface/UseCase/OnBoarding/CheckValidLevelUseCase.swift +++ /dev/null @@ -1,8 +0,0 @@ -import RxSwift - -public protocol CheckValidLevelUseCase { - /// 입력된 레벨의 범위가 0~200에 속하는지 확인 - /// - Parameter level: 입력된 레벨 - /// - Returns:true / false - func execute(level: Int?) -> Observable -} diff --git a/MLS/Domain/DomainInterface/UseCase/RecentSearch/RecentSearchAddUseCase.swift b/MLS/Domain/DomainInterface/UseCase/RecentSearch/RecentSearchAddUseCase.swift deleted file mode 100644 index c72bce96..00000000 --- a/MLS/Domain/DomainInterface/UseCase/RecentSearch/RecentSearchAddUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol RecentSearchAddUseCase { - func add(keyword: String) -> Completable -} diff --git a/MLS/Domain/DomainInterface/UseCase/RecentSearch/RecentSearchFetchUseCase.swift b/MLS/Domain/DomainInterface/UseCase/RecentSearch/RecentSearchFetchUseCase.swift deleted file mode 100644 index e03f81fc..00000000 --- a/MLS/Domain/DomainInterface/UseCase/RecentSearch/RecentSearchFetchUseCase.swift +++ /dev/null @@ -1,5 +0,0 @@ -import RxSwift - -public protocol RecentSearchFetchUseCase { - func fetch() -> Observable<[String]> -} diff --git a/MLS/Domain/DomainInterface/UseCase/RecentSearch/RecentSearchRemoveUseCase.swift b/MLS/Domain/DomainInterface/UseCase/RecentSearch/RecentSearchRemoveUseCase.swift deleted file mode 100644 index 5a6e9792..00000000 --- a/MLS/Domain/DomainInterface/UseCase/RecentSearch/RecentSearchRemoveUseCase.swift +++ /dev/null @@ -1,6 +0,0 @@ -import RxSwift - -public protocol RecentSearchRemoveUseCase { - func remove(keyword: String) -> Completable - func removeAll() -> Completable -} diff --git a/MLS/Domain/DomainInterface/UseCase/Shared/FetchSocialCredentialUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Shared/FetchSocialCredentialUseCase.swift deleted file mode 100644 index c883ebd9..00000000 --- a/MLS/Domain/DomainInterface/UseCase/Shared/FetchSocialCredentialUseCase.swift +++ /dev/null @@ -1,7 +0,0 @@ -import Foundation - -import RxSwift - -public protocol FetchSocialCredentialUseCase { - func execute() -> Observable -} diff --git a/MLS/MLS.xcodeproj/project.pbxproj b/MLS/MLS.xcodeproj/project.pbxproj index 2de28dda..416b2436 100644 --- a/MLS/MLS.xcodeproj/project.pbxproj +++ b/MLS/MLS.xcodeproj/project.pbxproj @@ -7,25 +7,11 @@ objects = { /* Begin PBXBuildFile section */ - 0801759F2DCD27E600D0919F /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0801759E2DCD27E600D0919F /* DomainInterface.framework */; }; - 080175A02DCD27E600D0919F /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0801759E2DCD27E600D0919F /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 084A25522DB93BC500C395C0 /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 084A25512DB93BC500C395C0 /* Domain.framework */; }; - 084A25532DB93BC500C395C0 /* Domain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 084A25512DB93BC500C395C0 /* Domain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 084A25552DB93BC800C395C0 /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 084A25542DB93BC800C395C0 /* Data.framework */; }; - 084A25562DB93BC800C395C0 /* Data.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 084A25542DB93BC800C395C0 /* Data.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 084A25D72DB93E2C00C395C0 /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 084A25D62DB93E2C00C395C0 /* Core.framework */; }; - 084A25D82DB93E2C00C395C0 /* Core.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 084A25D62DB93E2C00C395C0 /* Core.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 0858ABFC2DCFDBF20060EBCA /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0858ABFB2DCFDBF20060EBCA /* DesignSystem.framework */; }; - 0858ABFD2DCFDBF20060EBCA /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0858ABFB2DCFDBF20060EBCA /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 085A7F752DAF99570046663F /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = 085A7F742DAF99570046663F /* .swiftlint.yml */; }; - 085BDF5E2DF6B6B3009CFB90 /* DataMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 085BDF5D2DF6B6B3009CFB90 /* DataMock.framework */; }; - 085BDF5F2DF6B6B3009CFB90 /* DataMock.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 085BDF5D2DF6B6B3009CFB90 /* DataMock.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 088636972FB4664E00006D4A /* MLSRecommendationFeatureTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 088636962FB4664E00006D4A /* MLSRecommendationFeatureTesting */; }; + 0898D45E2FD913470031ED17 /* MLSBookmarkFeatureTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 0898D45D2FD913470031ED17 /* MLSBookmarkFeatureTesting */; }; 08DA51B42E1B9827009097A6 /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = 08DA51B32E1B9827009097A6 /* FirebaseFirestore */; }; 08DA51B62E1B9827009097A6 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 08DA51B52E1B9827009097A6 /* FirebaseMessaging */; }; - 08DA58A72E1E5BE3009097A6 /* DictionaryFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08DA58A62E1E5BE3009097A6 /* DictionaryFeature.framework */; }; - 08DA58A82E1E5BE3009097A6 /* DictionaryFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 08DA58A62E1E5BE3009097A6 /* DictionaryFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 08DA58AA2E1E5BEB009097A6 /* DictionaryFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08DA58A92E1E5BEB009097A6 /* DictionaryFeatureInterface.framework */; }; - 08DA58AB2E1E5BEB009097A6 /* DictionaryFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 08DA58A92E1E5BEB009097A6 /* DictionaryFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 08ED49282DCFDED4002C21A2 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 08ED49272DCFDED4002C21A2 /* RxCocoa */; }; 08ED492A2DCFDED4002C21A2 /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 08ED49292DCFDED4002C21A2 /* RxRelay */; }; 08ED492C2DCFDED4002C21A2 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08ED492B2DCFDED4002C21A2 /* RxSwift */; }; @@ -33,37 +19,41 @@ 08F7AA012F86745C00EF5C06 /* MLSAuthFeature in Frameworks */ = {isa = PBXBuildFile; productRef = 08F7AA022F86745C00EF5C06 /* MLSAuthFeature */; }; 08F7AA032F86745C00EF5C06 /* MLSAuthFeatureInterface in Frameworks */ = {isa = PBXBuildFile; productRef = 08F7AA042F86745C00EF5C06 /* MLSAuthFeatureInterface */; }; 08F7AA052F86745C00EF5C06 /* MLSAuthFeatureTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 08F7AA062F86745C00EF5C06 /* MLSAuthFeatureTesting */; }; + 08F7DC802F9DEA8100EF5C06 /* MLSRecommendationFeature in Frameworks */ = {isa = PBXBuildFile; productRef = 08F7DC812F9DEA8100EF5C06 /* MLSRecommendationFeature */; }; + 08F7DC822F9DEA8100EF5C06 /* MLSRecommendationFeatureInterface in Frameworks */ = {isa = PBXBuildFile; productRef = 08F7DC832F9DEA8100EF5C06 /* MLSRecommendationFeatureInterface */; }; + 08F7DC842F9DEA8100EF5C06 /* MLSCore in Frameworks */ = {isa = PBXBuildFile; productRef = 08F7DC852F9DEA8100EF5C06 /* MLSCore */; }; 770ADB1F2E433EDA00270506 /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 770ADB1E2E433EDA00270506 /* RxKeyboard */; }; - 772199F22E0E7EC800A7B58C /* AuthFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 772199F12E0E7EC800A7B58C /* AuthFeatureInterface.framework */; }; - 772199F32E0E7EC800A7B58C /* AuthFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 772199F12E0E7EC800A7B58C /* AuthFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7721A5042E0EE7AE00A7B58C /* BaseFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7721A5032E0EE7AE00A7B58C /* BaseFeature.framework */; }; - 7721A5052E0EE7AE00A7B58C /* BaseFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7721A5032E0EE7AE00A7B58C /* BaseFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 77217FD42F9A05D7000915EF /* MLSMyPageFeature in Frameworks */ = {isa = PBXBuildFile; productRef = 77217FD32F9A05D7000915EF /* MLSMyPageFeature */; }; 7721A5082E0EE7F100A7B58C /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7721A5072E0EE7F100A7B58C /* ReactorKit */; }; + 7736814E2F9A2CE3002DC773 /* MLSMyPageFeatureInterface in Frameworks */ = {isa = PBXBuildFile; productRef = 7736814D2F9A2CE3002DC773 /* MLSMyPageFeatureInterface */; }; + 773681502F9A2CE3002DC773 /* MLSMyPageFeatureTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 7736814F2F9A2CE3002DC773 /* MLSMyPageFeatureTesting */; }; 77660AD22DD0D361007A4EF3 /* KakaoConfig.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 77660AD12DD0D361007A4EF3 /* KakaoConfig.xcconfig */; }; 77660AD52DD0D3DD007A4EF3 /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 77660AD42DD0D3DD007A4EF3 /* KakaoSDKAuth */; }; 77660AD72DD0D3DD007A4EF3 /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 77660AD62DD0D3DD007A4EF3 /* KakaoSDKUser */; }; - 7777F7022E9EAB8400F53D68 /* BookmarkFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7777F7002E9EAB8400F53D68 /* BookmarkFeature.framework */; }; - 7777F7032E9EAB8400F53D68 /* BookmarkFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7777F7002E9EAB8400F53D68 /* BookmarkFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7777F7042E9EAB8400F53D68 /* BookmarkFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7777F7012E9EAB8400F53D68 /* BookmarkFeatureInterface.framework */; }; - 7777F7052E9EAB8400F53D68 /* BookmarkFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7777F7012E9EAB8400F53D68 /* BookmarkFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7777F7082E9EAC0D00F53D68 /* MyPageFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7777F7062E9EAC0D00F53D68 /* MyPageFeature.framework */; }; - 7777F7092E9EAC0D00F53D68 /* MyPageFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7777F7062E9EAC0D00F53D68 /* MyPageFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7777F70A2E9EAC0D00F53D68 /* MyPageFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7777F7072E9EAC0D00F53D68 /* MyPageFeatureInterface.framework */; }; - 7777F70B2E9EAC0D00F53D68 /* MyPageFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7777F7072E9EAC0D00F53D68 /* MyPageFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 779A490D2E1AD26700ABDE4F /* BookmarkFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 779A490C2E1AD26700ABDE4F /* BookmarkFeature.framework */; }; - 779A490E2E1AD26700ABDE4F /* BookmarkFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 779A490C2E1AD26700ABDE4F /* BookmarkFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 779A49102E1AD26D00ABDE4F /* BookmarkFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 779A490F2E1AD26D00ABDE4F /* BookmarkFeatureInterface.framework */; }; - 779A49112E1AD26D00ABDE4F /* BookmarkFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 779A490F2E1AD26D00ABDE4F /* BookmarkFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 77947A342FD802FC0010E7E9 /* MLSAppFeatureInterface in Frameworks */ = {isa = PBXBuildFile; productRef = 77947A332FD802FC0010E7E9 /* MLSAppFeatureInterface */; }; 77A293312F79989200845081 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77A293302F79989200845081 /* DesignSystem.framework */; }; 77A293322F79989200845081 /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77A293302F79989200845081 /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 77AB59ED2FBE20FD00BAB8A3 /* MLSAuthFeatureTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 77AB59EC2FBE20FD00BAB8A3 /* MLSAuthFeatureTesting */; }; + 77AB59EF2FBE20FD00BAB8A3 /* MLSMyPageFeatureTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 77AB59EE2FBE20FD00BAB8A3 /* MLSMyPageFeatureTesting */; }; + 77AF68822FD839E9008AAE68 /* MLSCore in Frameworks */ = {isa = PBXBuildFile; productRef = 77AF68812FD839E9008AAE68 /* MLSCore */; }; + 77AF68842FD839F3008AAE68 /* MLSDesignSystem in Frameworks */ = {isa = PBXBuildFile; productRef = 77AF68832FD839F3008AAE68 /* MLSDesignSystem */; }; 77B1F9952EE06A4E00AE4B4D /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 77B1F9942EE06A4E00AE4B4D /* RxGesture */; }; + 77CEA6EE2FD70F2700E052BC /* MLSAppFeature in Frameworks */ = {isa = PBXBuildFile; productRef = 77CEA6ED2FD70F2700E052BC /* MLSAppFeature */; }; + 77DE6DEF2F9F9EA1007FD8AC /* MLSAuthFeatureTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 77DE6DEE2F9F9EA1007FD8AC /* MLSAuthFeatureTesting */; }; 77E260412EEABEC40059E889 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 77E260402EEABEC40059E889 /* Settings.bundle */; }; - 77EB18D62DED9256004FB380 /* AuthFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77EB18D52DED9256004FB380 /* AuthFeature.framework */; }; - 77EB18D72DED9256004FB380 /* AuthFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77EB18D52DED9256004FB380 /* AuthFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 77F7C2262FBCB2A200D515A8 /* MLSDictionaryFeature in Frameworks */ = {isa = PBXBuildFile; productRef = 77F7C2252FBCB2A200D515A8 /* MLSDictionaryFeature */; }; + 77F7C2282FBCB2A200D515A8 /* MLSDictionaryFeatureTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 77F7C2272FBCB2A200D515A8 /* MLSDictionaryFeatureTesting */; }; + 77F7C22C2FBE18D400D515A8 /* MLSDictionaryFeatureInterface in Frameworks */ = {isa = PBXBuildFile; productRef = 77F7C22B2FBE18D400D515A8 /* MLSDictionaryFeatureInterface */; }; 77FA68B82F72C9C10064B6EB /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 77FA68B72F72C9C10064B6EB /* RxCocoa */; }; 77FA68BA2F72C9C10064B6EB /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 77FA68B92F72C9C10064B6EB /* RxSwift */; }; 77FA68BC2F72C9C70064B6EB /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 77FA68BB2F72C9C70064B6EB /* SnapKit */; }; 77FA68BE2F72CA490064B6EB /* MLSDesignSystem in Frameworks */ = {isa = PBXBuildFile; productRef = 77FA68BD2F72CA490064B6EB /* MLSDesignSystem */; }; + BBCC0B002FA000000000BBCC /* MLSBookmarkFeature in Frameworks */ = {isa = PBXBuildFile; productRef = BBCC0C002FA000000000BBCC /* MLSBookmarkFeature */; }; + BBCC0D002FA000000000BBCC /* MLSBookmarkFeatureInterface in Frameworks */ = {isa = PBXBuildFile; productRef = BBCC0E002FA000000000BBCC /* MLSBookmarkFeatureInterface */; }; + BBCC0F002FA000000000BBCC /* MLSBookmarkFeatureTesting in Frameworks */ = {isa = PBXBuildFile; productRef = BBCC10002FA000000000BBCC /* MLSBookmarkFeatureTesting */; }; + BBCC11002FA000000000BBCC /* MLSCore in Frameworks */ = {isa = PBXBuildFile; productRef = BBCC12002FA000000000BBCC /* MLSCore */; }; + BBCC13002FA000000000BBCC /* MLSAuthFeatureInterface in Frameworks */ = {isa = PBXBuildFile; productRef = BBCC14002FA000000000BBCC /* MLSAuthFeatureInterface */; }; + BBCC15002FA000000000BBCC /* MLSDesignSystem in Frameworks */ = {isa = PBXBuildFile; productRef = BBCC16002FA000000000BBCC /* MLSDesignSystem */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -77,33 +67,6 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - 08A6761F2DB939A40028F72A /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 080175A02DCD27E600D0919F /* DomainInterface.framework in Embed Frameworks */, - 7777F70B2E9EAC0D00F53D68 /* MyPageFeatureInterface.framework in Embed Frameworks */, - 779A49112E1AD26D00ABDE4F /* BookmarkFeatureInterface.framework in Embed Frameworks */, - 7777F7092E9EAC0D00F53D68 /* MyPageFeature.framework in Embed Frameworks */, - 772199F32E0E7EC800A7B58C /* AuthFeatureInterface.framework in Embed Frameworks */, - 084A25562DB93BC800C395C0 /* Data.framework in Embed Frameworks */, - 08DA58A82E1E5BE3009097A6 /* DictionaryFeature.framework in Embed Frameworks */, - 7777F7052E9EAB8400F53D68 /* BookmarkFeatureInterface.framework in Embed Frameworks */, - 08DA58AB2E1E5BEB009097A6 /* DictionaryFeatureInterface.framework in Embed Frameworks */, - 084A25D82DB93E2C00C395C0 /* Core.framework in Embed Frameworks */, - 0858ABFD2DCFDBF20060EBCA /* DesignSystem.framework in Embed Frameworks */, - 779A490E2E1AD26700ABDE4F /* BookmarkFeature.framework in Embed Frameworks */, - 084A25532DB93BC500C395C0 /* Domain.framework in Embed Frameworks */, - 085BDF5F2DF6B6B3009CFB90 /* DataMock.framework in Embed Frameworks */, - 7721A5052E0EE7AE00A7B58C /* BaseFeature.framework in Embed Frameworks */, - 77EB18D72DED9256004FB380 /* AuthFeature.framework in Embed Frameworks */, - 7777F7032E9EAB8400F53D68 /* BookmarkFeature.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; 77A293332F79989200845081 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -139,6 +102,8 @@ 08DA58A62E1E5BE3009097A6 /* DictionaryFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DictionaryFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 08DA58A92E1E5BEB009097A6 /* DictionaryFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DictionaryFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 08F7A9232F86745C00EF5C06 /* MLSAuthFeatureExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MLSAuthFeatureExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 08F7DC612F9DEA8000EF5C06 /* MLSRecommendationFeatureExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MLSRecommendationFeatureExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 77217FBE2F9A04CF000915EF /* MLSMyPageFeatureExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MLSMyPageFeatureExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 772199F12E0E7EC800A7B58C /* AuthFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7721A5032E0EE7AE00A7B58C /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 77660AD12DD0D361007A4EF3 /* KakaoConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = KakaoConfig.xcconfig; sourceTree = ""; }; @@ -146,13 +111,30 @@ 7777F7012E9EAB8400F53D68 /* BookmarkFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BookmarkFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7777F7062E9EAC0D00F53D68 /* MyPageFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MyPageFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7777F7072E9EAC0D00F53D68 /* MyPageFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MyPageFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7794E1062FBCA8AA001474A2 /* MLSDictionaryFeatureExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MLSDictionaryFeatureExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 779A490C2E1AD26700ABDE4F /* BookmarkFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BookmarkFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 779A490F2E1AD26D00ABDE4F /* BookmarkFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BookmarkFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 77A293302F79989200845081 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 77BEB0402DBA84B0002FFCFC /* MLSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MLSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 77E260402EEABEC40059E889 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = Settings.bundle; path = MLS/Resource/Settings.bundle; sourceTree = ""; }; + 77E7B7112FD73F630077358E /* AuthFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 77E7B7122FD73F630077358E /* AuthFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 77E7B7132FD73F630077358E /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 77E7B7142FD73F630077358E /* BookmarkFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BookmarkFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 77E7B7152FD73F630077358E /* BookmarkFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BookmarkFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 77E7B7162FD73F630077358E /* Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 77E7B7172FD73F630077358E /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 77E7B7182FD73F630077358E /* DataMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DataMock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 77E7B7192FD73F630077358E /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 77E7B71A2FD73F630077358E /* DictionaryFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DictionaryFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 77E7B71B2FD73F630077358E /* DictionaryFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DictionaryFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 77E7B71C2FD73F630077358E /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 77E7B71D2FD73F630077358E /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 77E7B71E2FD73F630077358E /* MyPageFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MyPageFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 77E7B71F2FD73F630077358E /* MyPageFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MyPageFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 77EB18D52DED9256004FB380 /* AuthFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 77FA687A2F72C7360064B6EB /* MLSDesignSystemExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MLSDesignSystemExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + BBCC02002FA000000000BBCC /* MLSBookmarkFeatureExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MLSBookmarkFeatureExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -170,6 +152,27 @@ ); target = 08F7A9222F86745C00EF5C06 /* MLSAuthFeatureExample */; }; + 08F7DC722F9DEA8100EF5C06 /* Exceptions for "MLSRecommendationFeatureExample" folder in "MLSRecommendationFeatureExample" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = 08F7DC602F9DEA8000EF5C06 /* MLSRecommendationFeatureExample */; + }; + 77217FCF2F9A04D0000915EF /* Exceptions for "MLSMyPageFeatureExample" folder in "MLSMyPageFeatureExample" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = 77217FBD2F9A04CF000915EF /* MLSMyPageFeatureExample */; + }; + 7794E1172FBCA8AB001474A2 /* Exceptions for "MLSDictionaryFeatureExample" folder in "MLSDictionaryFeatureExample" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = 7794E1052FBCA8AA001474A2 /* MLSDictionaryFeatureExample */; + }; 77FA688B2F72C7380064B6EB /* Exceptions for "MLSDesignSystemExample" folder in "MLSDesignSystemExample" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( @@ -177,6 +180,13 @@ ); target = 77FA68792F72C7360064B6EB /* MLSDesignSystemExample */; }; + BBCC04002FA000000000BBCC /* Exceptions for "MLSBookmarkFeatureExample" folder in "MLSBookmarkFeatureExample" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = BBCC01002FA000000000BBCC /* MLSBookmarkFeatureExample */; + }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -196,6 +206,30 @@ path = MLSAuthFeatureExample; sourceTree = ""; }; + 08F7DC622F9DEA8000EF5C06 /* MLSRecommendationFeatureExample */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 08F7DC722F9DEA8100EF5C06 /* Exceptions for "MLSRecommendationFeatureExample" folder in "MLSRecommendationFeatureExample" target */, + ); + path = MLSRecommendationFeatureExample; + sourceTree = ""; + }; + 77217FBF2F9A04CF000915EF /* MLSMyPageFeatureExample */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 77217FCF2F9A04D0000915EF /* Exceptions for "MLSMyPageFeatureExample" folder in "MLSMyPageFeatureExample" target */, + ); + path = MLSMyPageFeatureExample; + sourceTree = ""; + }; + 7794E1072FBCA8AA001474A2 /* MLSDictionaryFeatureExample */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 7794E1172FBCA8AB001474A2 /* Exceptions for "MLSDictionaryFeatureExample" folder in "MLSDictionaryFeatureExample" target */, + ); + path = MLSDictionaryFeatureExample; + sourceTree = ""; + }; 77BEB0412DBA84B0002FFCFC /* MLSTests */ = { isa = PBXFileSystemSynchronizedRootGroup; path = MLSTests; @@ -209,6 +243,14 @@ path = MLSDesignSystemExample; sourceTree = ""; }; + BBCC03002FA000000000BBCC /* MLSBookmarkFeatureExample */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + BBCC04002FA000000000BBCC /* Exceptions for "MLSBookmarkFeatureExample" folder in "MLSBookmarkFeatureExample" target */, + ); + path = MLSBookmarkFeatureExample; + sourceTree = ""; + }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -216,34 +258,20 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 77AF68842FD839F3008AAE68 /* MLSDesignSystem in Frameworks */, 08DA51B62E1B9827009097A6 /* FirebaseMessaging in Frameworks */, - 7777F70A2E9EAC0D00F53D68 /* MyPageFeatureInterface.framework in Frameworks */, 770ADB1F2E433EDA00270506 /* RxKeyboard in Frameworks */, + 77AF68822FD839E9008AAE68 /* MLSCore in Frameworks */, + 77CEA6EE2FD70F2700E052BC /* MLSAppFeature in Frameworks */, 08ED492C2DCFDED4002C21A2 /* RxSwift in Frameworks */, - 7721A5042E0EE7AE00A7B58C /* BaseFeature.framework in Frameworks */, - 08DA58AA2E1E5BEB009097A6 /* DictionaryFeatureInterface.framework in Frameworks */, 77B1F9952EE06A4E00AE4B4D /* RxGesture in Frameworks */, 7721A5082E0EE7F100A7B58C /* ReactorKit in Frameworks */, - 0801759F2DCD27E600D0919F /* DomainInterface.framework in Frameworks */, - 7777F7022E9EAB8400F53D68 /* BookmarkFeature.framework in Frameworks */, - 08DA58A72E1E5BE3009097A6 /* DictionaryFeature.framework in Frameworks */, 08DA51B42E1B9827009097A6 /* FirebaseFirestore in Frameworks */, - 7777F7042E9EAB8400F53D68 /* BookmarkFeatureInterface.framework in Frameworks */, - 084A25552DB93BC800C395C0 /* Data.framework in Frameworks */, - 084A25D72DB93E2C00C395C0 /* Core.framework in Frameworks */, 08ED49282DCFDED4002C21A2 /* RxCocoa in Frameworks */, - 7777F7082E9EAC0D00F53D68 /* MyPageFeature.framework in Frameworks */, - 0858ABFC2DCFDBF20060EBCA /* DesignSystem.framework in Frameworks */, - 779A490D2E1AD26700ABDE4F /* BookmarkFeature.framework in Frameworks */, 77660AD52DD0D3DD007A4EF3 /* KakaoSDKAuth in Frameworks */, - 772199F22E0E7EC800A7B58C /* AuthFeatureInterface.framework in Frameworks */, - 779A49102E1AD26D00ABDE4F /* BookmarkFeatureInterface.framework in Frameworks */, - 084A25522DB93BC500C395C0 /* Domain.framework in Frameworks */, 77660AD72DD0D3DD007A4EF3 /* KakaoSDKUser in Frameworks */, - 085BDF5E2DF6B6B3009CFB90 /* DataMock.framework in Frameworks */, 08ED492A2DCFDED4002C21A2 /* RxRelay in Frameworks */, 08ED4DB12DCFE098002C21A2 /* SnapKit in Frameworks */, - 77EB18D62DED9256004FB380 /* AuthFeature.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -251,12 +279,48 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 77947A342FD802FC0010E7E9 /* MLSAppFeatureInterface in Frameworks */, 08F7AA012F86745C00EF5C06 /* MLSAuthFeature in Frameworks */, 08F7AA032F86745C00EF5C06 /* MLSAuthFeatureInterface in Frameworks */, 08F7AA052F86745C00EF5C06 /* MLSAuthFeatureTesting in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; + 08F7DC5E2F9DEA8000EF5C06 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0898D45E2FD913470031ED17 /* MLSBookmarkFeatureTesting in Frameworks */, + 08F7DC802F9DEA8100EF5C06 /* MLSRecommendationFeature in Frameworks */, + 08F7DC822F9DEA8100EF5C06 /* MLSRecommendationFeatureInterface in Frameworks */, + 088636972FB4664E00006D4A /* MLSRecommendationFeatureTesting in Frameworks */, + 08F7DC842F9DEA8100EF5C06 /* MLSCore in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 77217FBB2F9A04CF000915EF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 77217FD42F9A05D7000915EF /* MLSMyPageFeature in Frameworks */, + 77DE6DEF2F9F9EA1007FD8AC /* MLSAuthFeatureTesting in Frameworks */, + 773681502F9A2CE3002DC773 /* MLSMyPageFeatureTesting in Frameworks */, + 7736814E2F9A2CE3002DC773 /* MLSMyPageFeatureInterface in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7794E1032FBCA8AA001474A2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 77F7C2262FBCB2A200D515A8 /* MLSDictionaryFeature in Frameworks */, + 77AB59ED2FBE20FD00BAB8A3 /* MLSAuthFeatureTesting in Frameworks */, + 77F7C2282FBCB2A200D515A8 /* MLSDictionaryFeatureTesting in Frameworks */, + 77AB59EF2FBE20FD00BAB8A3 /* MLSMyPageFeatureTesting in Frameworks */, + 77F7C22C2FBE18D400D515A8 /* MLSDictionaryFeatureInterface in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 77BEB03D2DBA84B0002FFCFC /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -276,6 +340,19 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BBCC06002FA000000000BBCC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BBCC0B002FA000000000BBCC /* MLSBookmarkFeature in Frameworks */, + BBCC0D002FA000000000BBCC /* MLSBookmarkFeatureInterface in Frameworks */, + BBCC0F002FA000000000BBCC /* MLSBookmarkFeatureTesting in Frameworks */, + BBCC11002FA000000000BBCC /* MLSCore in Frameworks */, + BBCC13002FA000000000BBCC /* MLSAuthFeatureInterface in Frameworks */, + BBCC15002FA000000000BBCC /* MLSDesignSystem in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -324,6 +401,21 @@ 084A25312DB93A5400C395C0 /* Frameworks */ = { isa = PBXGroup; children = ( + 77E7B7112FD73F630077358E /* AuthFeature.framework */, + 77E7B7122FD73F630077358E /* AuthFeatureInterface.framework */, + 77E7B7132FD73F630077358E /* BaseFeature.framework */, + 77E7B7142FD73F630077358E /* BookmarkFeature.framework */, + 77E7B7152FD73F630077358E /* BookmarkFeatureInterface.framework */, + 77E7B7162FD73F630077358E /* Core.framework */, + 77E7B7172FD73F630077358E /* Data.framework */, + 77E7B7182FD73F630077358E /* DataMock.framework */, + 77E7B7192FD73F630077358E /* DesignSystem.framework */, + 77E7B71A2FD73F630077358E /* DictionaryFeature.framework */, + 77E7B71B2FD73F630077358E /* DictionaryFeatureInterface.framework */, + 77E7B71C2FD73F630077358E /* Domain.framework */, + 77E7B71D2FD73F630077358E /* DomainInterface.framework */, + 77E7B71E2FD73F630077358E /* MyPageFeature.framework */, + 77E7B71F2FD73F630077358E /* MyPageFeatureInterface.framework */, 77A293302F79989200845081 /* DesignSystem.framework */, 7777F7062E9EAC0D00F53D68 /* MyPageFeature.framework */, 7777F7072E9EAC0D00F53D68 /* MyPageFeatureInterface.framework */, @@ -357,9 +449,13 @@ 77660AD12DD0D361007A4EF3 /* KakaoConfig.xcconfig */, 085A7F742DAF99570046663F /* .swiftlint.yml */, 087D3EEA2DA7972C002F924D /* MLS */, - 77BEB0412DBA84B0002FFCFC /* MLSTests */, - 77FA687B2F72C7360064B6EB /* MLSDesignSystemExample */, 08F7A9242F86745C00EF5C06 /* MLSAuthFeatureExample */, + BBCC03002FA000000000BBCC /* MLSBookmarkFeatureExample */, + 77FA687B2F72C7360064B6EB /* MLSDesignSystemExample */, + 7794E1072FBCA8AA001474A2 /* MLSDictionaryFeatureExample */, + 77217FBF2F9A04CF000915EF /* MLSMyPageFeatureExample */, + 08F7DC622F9DEA8000EF5C06 /* MLSRecommendationFeatureExample */, + 77BEB0412DBA84B0002FFCFC /* MLSTests */, 084A25312DB93A5400C395C0 /* Frameworks */, 087D3EE92DA7972C002F924D /* Products */, ); @@ -372,6 +468,10 @@ 77BEB0402DBA84B0002FFCFC /* MLSTests.xctest */, 77FA687A2F72C7360064B6EB /* MLSDesignSystemExample.app */, 08F7A9232F86745C00EF5C06 /* MLSAuthFeatureExample.app */, + 77217FBE2F9A04CF000915EF /* MLSMyPageFeatureExample.app */, + 08F7DC612F9DEA8000EF5C06 /* MLSRecommendationFeatureExample.app */, + BBCC02002FA000000000BBCC /* MLSBookmarkFeatureExample.app */, + 7794E1062FBCA8AA001474A2 /* MLSDictionaryFeatureExample.app */, ); name = Products; sourceTree = ""; @@ -387,7 +487,6 @@ 087D3EE42DA7972C002F924D /* Sources */, 087D3EE52DA7972C002F924D /* Frameworks */, 087D3EE62DA7972C002F924D /* Resources */, - 08A6761F2DB939A40028F72A /* Embed Frameworks */, ); buildRules = ( ); @@ -409,6 +508,9 @@ 08DA51B52E1B9827009097A6 /* FirebaseMessaging */, 770ADB1E2E433EDA00270506 /* RxKeyboard */, 77B1F9942EE06A4E00AE4B4D /* RxGesture */, + 77CEA6ED2FD70F2700E052BC /* MLSAppFeature */, + 77AF68812FD839E9008AAE68 /* MLSCore */, + 77AF68832FD839F3008AAE68 /* MLSDesignSystem */, ); productName = MLS; productReference = 087D3EE82DA7972C002F924D /* MLS.app */; @@ -434,11 +536,92 @@ 08F7AA022F86745C00EF5C06 /* MLSAuthFeature */, 08F7AA042F86745C00EF5C06 /* MLSAuthFeatureInterface */, 08F7AA062F86745C00EF5C06 /* MLSAuthFeatureTesting */, + 77947A332FD802FC0010E7E9 /* MLSAppFeatureInterface */, ); productName = MLSAuthFeatureExample; productReference = 08F7A9232F86745C00EF5C06 /* MLSAuthFeatureExample.app */; productType = "com.apple.product-type.application"; }; + 08F7DC602F9DEA8000EF5C06 /* MLSRecommendationFeatureExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 08F7DC732F9DEA8100EF5C06 /* Build configuration list for PBXNativeTarget "MLSRecommendationFeatureExample" */; + buildPhases = ( + 08F7DC5D2F9DEA8000EF5C06 /* Sources */, + 08F7DC5E2F9DEA8000EF5C06 /* Frameworks */, + 08F7DC5F2F9DEA8000EF5C06 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 08F7DC622F9DEA8000EF5C06 /* MLSRecommendationFeatureExample */, + ); + name = MLSRecommendationFeatureExample; + packageProductDependencies = ( + 08F7DC812F9DEA8100EF5C06 /* MLSRecommendationFeature */, + 08F7DC832F9DEA8100EF5C06 /* MLSRecommendationFeatureInterface */, + 08F7DC852F9DEA8100EF5C06 /* MLSCore */, + 088636962FB4664E00006D4A /* MLSRecommendationFeatureTesting */, + 0898D45D2FD913470031ED17 /* MLSBookmarkFeatureTesting */, + ); + productName = MLSRecommendationFeatureExample; + productReference = 08F7DC612F9DEA8000EF5C06 /* MLSRecommendationFeatureExample.app */; + productType = "com.apple.product-type.application"; + }; + 77217FBD2F9A04CF000915EF /* MLSMyPageFeatureExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 77217FD02F9A04D0000915EF /* Build configuration list for PBXNativeTarget "MLSMyPageFeatureExample" */; + buildPhases = ( + 77217FBA2F9A04CF000915EF /* Sources */, + 77217FBB2F9A04CF000915EF /* Frameworks */, + 77217FBC2F9A04CF000915EF /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 77217FBF2F9A04CF000915EF /* MLSMyPageFeatureExample */, + ); + name = MLSMyPageFeatureExample; + packageProductDependencies = ( + 77217FD32F9A05D7000915EF /* MLSMyPageFeature */, + 7736814D2F9A2CE3002DC773 /* MLSMyPageFeatureInterface */, + 7736814F2F9A2CE3002DC773 /* MLSMyPageFeatureTesting */, + 77DE6DEE2F9F9EA1007FD8AC /* MLSAuthFeatureTesting */, + ); + productName = MLSMyPageFeatureExample; + productReference = 77217FBE2F9A04CF000915EF /* MLSMyPageFeatureExample.app */; + productType = "com.apple.product-type.application"; + }; + 7794E1052FBCA8AA001474A2 /* MLSDictionaryFeatureExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7794E1182FBCA8AB001474A2 /* Build configuration list for PBXNativeTarget "MLSDictionaryFeatureExample" */; + buildPhases = ( + 7794E1022FBCA8AA001474A2 /* Sources */, + 7794E1032FBCA8AA001474A2 /* Frameworks */, + 7794E1042FBCA8AA001474A2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 7794E1072FBCA8AA001474A2 /* MLSDictionaryFeatureExample */, + ); + name = MLSDictionaryFeatureExample; + packageProductDependencies = ( + 77F7C2252FBCB2A200D515A8 /* MLSDictionaryFeature */, + 77F7C2272FBCB2A200D515A8 /* MLSDictionaryFeatureTesting */, + 77F7C22B2FBE18D400D515A8 /* MLSDictionaryFeatureInterface */, + 77AB59EC2FBE20FD00BAB8A3 /* MLSAuthFeatureTesting */, + 77AB59EE2FBE20FD00BAB8A3 /* MLSMyPageFeatureTesting */, + ); + productName = MLSDictionaryFeatureExample; + productReference = 7794E1062FBCA8AA001474A2 /* MLSDictionaryFeatureExample.app */; + productType = "com.apple.product-type.application"; + }; 77BEB03F2DBA84B0002FFCFC /* MLSTests */ = { isa = PBXNativeTarget; buildConfigurationList = 77BEB0482DBA84B0002FFCFC /* Build configuration list for PBXNativeTarget "MLSTests" */; @@ -489,6 +672,34 @@ productReference = 77FA687A2F72C7360064B6EB /* MLSDesignSystemExample.app */; productType = "com.apple.product-type.application"; }; + BBCC01002FA000000000BBCC /* MLSBookmarkFeatureExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = BBCC08002FA000000000BBCC /* Build configuration list for PBXNativeTarget "MLSBookmarkFeatureExample" */; + buildPhases = ( + BBCC05002FA000000000BBCC /* Sources */, + BBCC06002FA000000000BBCC /* Frameworks */, + BBCC07002FA000000000BBCC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + BBCC03002FA000000000BBCC /* MLSBookmarkFeatureExample */, + ); + name = MLSBookmarkFeatureExample; + packageProductDependencies = ( + BBCC0C002FA000000000BBCC /* MLSBookmarkFeature */, + BBCC0E002FA000000000BBCC /* MLSBookmarkFeatureInterface */, + BBCC10002FA000000000BBCC /* MLSBookmarkFeatureTesting */, + BBCC12002FA000000000BBCC /* MLSCore */, + BBCC14002FA000000000BBCC /* MLSAuthFeatureInterface */, + BBCC16002FA000000000BBCC /* MLSDesignSystem */, + ); + productName = MLSBookmarkFeatureExample; + productReference = BBCC02002FA000000000BBCC /* MLSBookmarkFeatureExample.app */; + productType = "com.apple.product-type.application"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -505,6 +716,15 @@ 08F7A9222F86745C00EF5C06 = { CreatedOnToolsVersion = 26.1.1; }; + 08F7DC602F9DEA8000EF5C06 = { + CreatedOnToolsVersion = 26.1.1; + }; + 77217FBD2F9A04CF000915EF = { + CreatedOnToolsVersion = 26.1.1; + }; + 7794E1052FBCA8AA001474A2 = { + CreatedOnToolsVersion = 26.1.1; + }; 77BEB03F2DBA84B0002FFCFC = { CreatedOnToolsVersion = 16.2; TestTargetID = 087D3EE72DA7972C002F924D; @@ -567,6 +787,10 @@ 77BEB03F2DBA84B0002FFCFC /* MLSTests */, 77FA68792F72C7360064B6EB /* MLSDesignSystemExample */, 08F7A9222F86745C00EF5C06 /* MLSAuthFeatureExample */, + 77217FBD2F9A04CF000915EF /* MLSMyPageFeatureExample */, + 08F7DC602F9DEA8000EF5C06 /* MLSRecommendationFeatureExample */, + BBCC01002FA000000000BBCC /* MLSBookmarkFeatureExample */, + 7794E1052FBCA8AA001474A2 /* MLSDictionaryFeatureExample */, ); }; /* End PBXProject section */ @@ -589,6 +813,27 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 08F7DC5F2F9DEA8000EF5C06 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 77217FBC2F9A04CF000915EF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7794E1042FBCA8AA001474A2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 77BEB03E2DBA84B0002FFCFC /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -603,6 +848,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BBCC07002FA000000000BBCC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -641,6 +893,27 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 08F7DC5D2F9DEA8000EF5C06 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 77217FBA2F9A04CF000915EF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7794E1022FBCA8AA001474A2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 77BEB03C2DBA84B0002FFCFC /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -655,6 +928,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BBCC05002FA000000000BBCC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -694,7 +974,7 @@ "@executable_path/Frameworks", ); MACH_O_TYPE = mh_execute; - MARKETING_VERSION = 3.0.3; + MARKETING_VERSION = 3.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLS; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -737,7 +1017,7 @@ "@executable_path/Frameworks", ); MACH_O_TYPE = mh_execute; - MARKETING_VERSION = 3.0.3; + MARKETING_VERSION = 3.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLS; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -883,10 +1163,9 @@ INFOPLIST_FILE = MLSAuthFeatureExample/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 26.1; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -895,12 +1174,16 @@ PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLSAuthFeatureExample; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -916,10 +1199,9 @@ INFOPLIST_FILE = MLSAuthFeatureExample/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 26.1; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -928,96 +1210,329 @@ PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLSAuthFeatureExample; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - 77BEB0462DBA84B0002FFCFC /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = pinocchio22.MLSTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MLS.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/MLS"; - }; - name = Debug; - }; - 77BEB0472DBA84B0002FFCFC /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = pinocchio22.MLSTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MLS.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/MLS"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; - 77FA688D2F72C7380064B6EB /* Debug */ = { + 08F7DC742F9DEA8100EF5C06 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; ENABLE_USER_SCRIPT_SANDBOXING = YES; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = MLSDesignSystemExample/Info.plist; + INFOPLIST_FILE = MLSRecommendationFeatureExample/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; + IPHONEOS_DEPLOYMENT_TARGET = 26.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLSDesignSystemExample.MLSDesignSystemExample; + PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLS.RecommendationFeatureExample; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = MLSRecommendationFeatureProvisioningProfileDev; STRING_CATALOG_GENERATE_SYMBOLS = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; - 77FA688E2F72C7380064B6EB /* Release */ = { + 08F7DC752F9DEA8100EF5C06 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MLSRecommendationFeatureExample/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 26.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLS.RecommendationFeatureExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = MLSRecommendationFeatureProvisioningProfile; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 77217FD12F9A04D0000915EF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MLSMyPageFeatureExample/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLSDesignSystemExample.MLSMyPageFeatureExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 77217FD22F9A04D0000915EF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MLSMyPageFeatureExample/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLSDesignSystemExample.MLSMyPageFeatureExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; + 7794E1192FBCA8AB001474A2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MLSDictionaryFeatureExample/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLSDesignSystemExample.MLSDictionaryFeatureExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 7794E11A2FBCA8AB001474A2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MLSDictionaryFeatureExample/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLSDesignSystemExample.MLSDictionaryFeatureExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; + 77BEB0462DBA84B0002FFCFC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = pinocchio22.MLSTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MLS.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/MLS"; + }; + name = Debug; + }; + 77BEB0472DBA84B0002FFCFC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = pinocchio22.MLSTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MLS.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/MLS"; + }; + name = Release; + }; + 77FA688D2F72C7380064B6EB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MLSDesignSystemExample/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLSDesignSystemExample.MLSDesignSystemExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 77FA688E2F72C7380064B6EB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; @@ -1051,6 +1566,78 @@ }; name = Release; }; + BBCC09002FA000000000BBCC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MLSBookmarkFeatureExample/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLS.BookmarkFeatureExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + BBCC0A002FA000000000BBCC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MLSBookmarkFeatureExample/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLS.BookmarkFeatureExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -1081,6 +1668,33 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 08F7DC732F9DEA8100EF5C06 /* Build configuration list for PBXNativeTarget "MLSRecommendationFeatureExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 08F7DC742F9DEA8100EF5C06 /* Debug */, + 08F7DC752F9DEA8100EF5C06 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 77217FD02F9A04D0000915EF /* Build configuration list for PBXNativeTarget "MLSMyPageFeatureExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 77217FD12F9A04D0000915EF /* Debug */, + 77217FD22F9A04D0000915EF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7794E1182FBCA8AB001474A2 /* Build configuration list for PBXNativeTarget "MLSDictionaryFeatureExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7794E1192FBCA8AB001474A2 /* Debug */, + 7794E11A2FBCA8AB001474A2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 77BEB0482DBA84B0002FFCFC /* Build configuration list for PBXNativeTarget "MLSTests" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -1099,6 +1713,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + BBCC08002FA000000000BBCC /* Build configuration list for PBXNativeTarget "MLSBookmarkFeatureExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BBCC09002FA000000000BBCC /* Debug */, + BBCC0A002FA000000000BBCC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ @@ -1161,6 +1784,14 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 088636962FB4664E00006D4A /* MLSRecommendationFeatureTesting */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSRecommendationFeatureTesting; + }; + 0898D45D2FD913470031ED17 /* MLSBookmarkFeatureTesting */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSBookmarkFeatureTesting; + }; 08DA51B32E1B9827009097A6 /* FirebaseFirestore */ = { isa = XCSwiftPackageProductDependency; package = 08DA51B22E1B9827009097A6 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; @@ -1203,16 +1834,40 @@ isa = XCSwiftPackageProductDependency; productName = MLSAuthFeatureTesting; }; + 08F7DC812F9DEA8100EF5C06 /* MLSRecommendationFeature */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSRecommendationFeature; + }; + 08F7DC832F9DEA8100EF5C06 /* MLSRecommendationFeatureInterface */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSRecommendationFeatureInterface; + }; + 08F7DC852F9DEA8100EF5C06 /* MLSCore */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSCore; + }; 770ADB1E2E433EDA00270506 /* RxKeyboard */ = { isa = XCSwiftPackageProductDependency; package = 770ADB1D2E433EDA00270506 /* XCRemoteSwiftPackageReference "RxKeyboard" */; productName = RxKeyboard; }; + 77217FD32F9A05D7000915EF /* MLSMyPageFeature */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSMyPageFeature; + }; 7721A5072E0EE7F100A7B58C /* ReactorKit */ = { isa = XCSwiftPackageProductDependency; package = 7721A5062E0EE7F100A7B58C /* XCRemoteSwiftPackageReference "ReactorKit" */; productName = ReactorKit; }; + 7736814D2F9A2CE3002DC773 /* MLSMyPageFeatureInterface */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSMyPageFeatureInterface; + }; + 7736814F2F9A2CE3002DC773 /* MLSMyPageFeatureTesting */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSMyPageFeatureTesting; + }; 77660AD42DD0D3DD007A4EF3 /* KakaoSDKAuth */ = { isa = XCSwiftPackageProductDependency; package = 77660AD32DD0D3DD007A4EF3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; @@ -1223,11 +1878,51 @@ package = 77660AD32DD0D3DD007A4EF3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; productName = KakaoSDKUser; }; + 77947A332FD802FC0010E7E9 /* MLSAppFeatureInterface */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSAppFeatureInterface; + }; + 77AB59EC2FBE20FD00BAB8A3 /* MLSAuthFeatureTesting */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSAuthFeatureTesting; + }; + 77AB59EE2FBE20FD00BAB8A3 /* MLSMyPageFeatureTesting */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSMyPageFeatureTesting; + }; + 77AF68812FD839E9008AAE68 /* MLSCore */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSCore; + }; + 77AF68832FD839F3008AAE68 /* MLSDesignSystem */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSDesignSystem; + }; 77B1F9942EE06A4E00AE4B4D /* RxGesture */ = { isa = XCSwiftPackageProductDependency; package = 77B1F9932EE06A4E00AE4B4D /* XCRemoteSwiftPackageReference "RxGesture" */; productName = RxGesture; }; + 77CEA6ED2FD70F2700E052BC /* MLSAppFeature */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSAppFeature; + }; + 77DE6DEE2F9F9EA1007FD8AC /* MLSAuthFeatureTesting */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSAuthFeatureTesting; + }; + 77F7C2252FBCB2A200D515A8 /* MLSDictionaryFeature */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSDictionaryFeature; + }; + 77F7C2272FBCB2A200D515A8 /* MLSDictionaryFeatureTesting */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSDictionaryFeatureTesting; + }; + 77F7C22B2FBE18D400D515A8 /* MLSDictionaryFeatureInterface */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSDictionaryFeatureInterface; + }; 77FA68B72F72C9C10064B6EB /* RxCocoa */ = { isa = XCSwiftPackageProductDependency; package = 08ED49202DCFDE9C002C21A2 /* XCRemoteSwiftPackageReference "RxSwift" */; @@ -1247,6 +1942,30 @@ isa = XCSwiftPackageProductDependency; productName = MLSDesignSystem; }; + BBCC0C002FA000000000BBCC /* MLSBookmarkFeature */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSBookmarkFeature; + }; + BBCC0E002FA000000000BBCC /* MLSBookmarkFeatureInterface */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSBookmarkFeatureInterface; + }; + BBCC10002FA000000000BBCC /* MLSBookmarkFeatureTesting */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSBookmarkFeatureTesting; + }; + BBCC12002FA000000000BBCC /* MLSCore */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSCore; + }; + BBCC14002FA000000000BBCC /* MLSAuthFeatureInterface */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSAuthFeatureInterface; + }; + BBCC16002FA000000000BBCC /* MLSDesignSystem */ = { + isa = XCSwiftPackageProductDependency; + productName = MLSDesignSystem; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 087D3EE02DA7972C002F924D /* Project object */; diff --git a/MLS/MLS.xcworkspace/contents.xcworkspacedata b/MLS/MLS.xcworkspace/contents.xcworkspacedata index 99fde6a0..711ac7d4 100644 --- a/MLS/MLS.xcworkspace/contents.xcworkspacedata +++ b/MLS/MLS.xcworkspace/contents.xcworkspacedata @@ -4,36 +4,8 @@ - - - - - - - - - - - - - - - - - - - + @@ -45,6 +17,15 @@ location = "group:MLSAuthFeature"> + location = "group:MLSBookmarkFeature"> + + + + + + diff --git a/MLS/MLS.xcworkspace/xcshareddata/swiftpm/Package.resolved b/MLS/MLS.xcworkspace/xcshareddata/swiftpm/Package.resolved index 386ec176..00da3d9a 100644 --- a/MLS/MLS.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/MLS/MLS.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "bfbb1a7b185edd3389821cec79c508d208318809d55699ccbc2cee82e1a49c3d", + "originHash" : "77b034e581c0b3380d1189e9436d587eda4c5fe2ed62f75f4c83882cd5ab8a4c", "pins" : [ { "identity" : "abseil-cpp-binary", diff --git a/MLS/MLS/Application/AppDelegate.swift b/MLS/MLS/Application/AppDelegate.swift index 909c3fae..3678ace6 100644 --- a/MLS/MLS/Application/AppDelegate.swift +++ b/MLS/MLS/Application/AppDelegate.swift @@ -1,29 +1,18 @@ -// swiftlint:disable function_body_length -// swiftlint:disable file_length - -import AuthFeature -import AuthFeatureInterface -import BaseFeature -import BookmarkFeature -import BookmarkFeatureInterface -import Core -import Data -import DataMock -import DesignSystem -import DictionaryFeature -import DictionaryFeatureInterface -import Domain -import DomainInterface -import Firebase -import KakaoSDKCommon -import MyPageFeature -import MyPageFeatureInterface import os import UIKit import UserNotifications +import MLSAppFeature +import MLSCore +import MLSDesignSystem + +import Firebase +import KakaoSDKCommon + @main class AppDelegate: UIResponder, UIApplicationDelegate { + private let applauncher = AppLauncher() + func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication @@ -37,13 +26,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // MARK: - Modules Set ImageLoader.shared.configure.diskCacheCountLimit = 10 // ImageLoader FontManager.registerFonts() // FontManager + applauncher.register() // MARK: - KakaoSDK Set let kakaoNativeAppKey: String = Bundle.main.infoDictionary?["KAKAO_NATIVE_APP_KEY"] as? String ?? "" KakaoSDK.initSDK(appKey: kakaoNativeAppKey) - - registerDependencies() return true } @@ -82,1171 +70,11 @@ extension AppDelegate: UNUserNotificationCenterDelegate, MessagingDelegate { _ messaging: Messaging, didReceiveRegistrationToken fcmToken: String? ) { - let dataDict: [String: String] = ["token": fcmToken ?? ""] - NotificationCenter.default.post( - name: Notification.Name("FCMToken"), - object: nil, - userInfo: dataDict - ) + let tokenLauncher = TokenLauncher() guard let fcmToken = fcmToken else { os_log("FCM token is nil") return } - - let saveTokenUseCase = DIContainer.resolve(type: SaveTokenToLocalUseCase.self) - let saveResult = saveTokenUseCase.execute(type: .fcmToken, value: fcmToken) - - switch saveResult { - case .success: - os_log("fcmToken Save Success: \(fcmToken)") - case .failure: - os_log("fcmToken Save Failure") - } - - let fetchTokenUseCase = DIContainer.resolve(type: FetchTokenFromLocalUseCase.self) - let putFCMTokenUseCase = DIContainer.resolve(type: PutFCMTokenUseCase.self) - - if case .success(let accessToken) = fetchTokenUseCase.execute(type: .accessToken), - !accessToken.isEmpty { - _ = putFCMTokenUseCase.execute(fcmToken: fcmToken) - os_log("Request to update FCM token on server") - } else { - os_log("Not logged in yet, skipping FCM update to server") - } - } -} - -// MARK: - registerDependencies -private extension AppDelegate { - func registerDependencies() { - registerProvider() - registerRepository() - registerUseCase() - registerFactory() - - DIContainer.register(type: AppCoordinatorProtocol.self) { - AppCoordinator( - window: nil, - dictionaryMainViewFactory: DIContainer.resolve( - type: DictionaryMainViewFactory.self - ), - bookmarkMainFactory: DIContainer.resolve( - type: BookmarkMainFactory.self - ), - myPageMainFactory: DIContainer.resolve( - type: MyPageMainFactory.self - ), - loginFactory: DIContainer.resolve(type: LoginFactory.self) - ) - } - } - - func registerProvider() { - DIContainer.register(type: NetworkProvider.self) { - NetworkProviderImpl() - } - DIContainer.register( - type: SocialAuthenticatableProvider.self, - name: "kakao" - ) { - KakaoLoginProviderImpl() - } - DIContainer.register( - type: SocialAuthenticatableProvider.self, - name: "apple" - ) { - AppleLoginProviderImpl() - } - DIContainer.register(type: Interceptor.self, name: "tokenInterceptor") { - TokenInterceptor( - fetchTokenUseCase: DIContainer.resolve( - type: FetchTokenFromLocalUseCase.self - ) - ) - } - - DIContainer.register(type: Interceptor.self, name: "authInterceptor") { - AuthInterceptor(tokenRepository: DIContainer.resolve(type: TokenRepository.self), authRepository: { DIContainer.resolve(type: AuthAPIRepository.self) }) - } - } - - func registerRepository() { - DIContainer.register(type: AuthAPIRepository.self) { - AuthAPIRepositoryImpl( - provider: DIContainer.resolve(type: NetworkProvider.self), - tokenInterceptor: DIContainer.resolve(type: Interceptor.self, name: "tokenInterceptor"), - authInterceptor: DIContainer.resolve(type: Interceptor.self, name: "authInterceptor") - ) - } - DIContainer.register(type: TokenRepository.self) { - KeyChainRepositoryImpl() - } - DIContainer.register(type: DictionaryDetailAPIRepository.self) { - DictionaryDetailAPIRepositoryImpl( - provider: DIContainer.resolve(type: NetworkProvider.self), - tokenInterceptor: DIContainer.resolve(type: Interceptor.self, name: "tokenInterceptor") - ) - } - DIContainer.register(type: DictionaryListAPIRepository.self) { - DictionaryListAPIRepositoryImpl( - provider: DIContainer.resolve(type: NetworkProvider.self), - tokenInterceptor: DIContainer.resolve(type: Interceptor.self, name: "tokenInterceptor") - ) - } - DIContainer.register(type: BookmarkRepository.self) { - BookmarkRepositoryImpl( - provider: DIContainer.resolve(type: NetworkProvider.self), - interceptor: DIContainer.resolve(type: Interceptor.self, name: "tokenInterceptor") - ) - } - DIContainer.register(type: UserDefaultsRepository.self) { - UserDefaultsRepositoryImpl() - } - DIContainer.register(type: AlarmAPIRepository.self) { - AlarmAPIRepositoryImpl( - provider: DIContainer.resolve(type: NetworkProvider.self), - interceptor: DIContainer.resolve(type: Interceptor.self, name: "tokenInterceptor") - ) - } - DIContainer.register(type: CollectionAPIRepository.self) { - CollectionAPIRepositoryImpl( - provider: DIContainer.resolve(type: NetworkProvider.self), - tokenInterceptor: DIContainer.resolve(type: Interceptor.self, name: "tokenInterceptor") - ) - } - } - - func registerUseCase() { - DIContainer.register( - type: FetchSocialCredentialUseCase.self, - name: "kakao" - ) { - let provider = DIContainer.resolve( - type: SocialAuthenticatableProvider.self, - name: "kakao" - ) - return SocialLoginUseCaseImpl(provider: provider) - } - DIContainer.register( - type: FetchSocialCredentialUseCase.self, - name: "apple" - ) { - let provider = DIContainer.resolve( - type: SocialAuthenticatableProvider.self, - name: "apple" - ) - return SocialLoginUseCaseImpl(provider: provider) - } - DIContainer.register(type: CheckEmptyLevelAndRoleUseCase.self) { - CheckEmptyLevelAndRoleUseCaseImpl() - } - DIContainer.register(type: CheckValidLevelUseCase.self) { - CheckValidLevelUseCaseImpl() - } - DIContainer.register(type: FetchJobListUseCase.self) { - FetchJobListUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self) - ) - } - DIContainer.register(type: LoginWithAppleUseCase.self) { - LoginWithAppleUseCaseImpl( - authRepository: DIContainer.resolve( - type: AuthAPIRepository.self - ), - tokenRepository: DIContainer.resolve( - type: TokenRepository.self - ), - userDefaultsRepository: DIContainer.resolve( - type: UserDefaultsRepository.self - ) - ) - } - DIContainer.register(type: LoginWithKakaoUseCase.self) { - LoginWithKakaoUseCaseImpl( - authRepository: DIContainer.resolve( - type: AuthAPIRepository.self - ), - tokenRepository: DIContainer.resolve( - type: TokenRepository.self - ), - userDefaultsRepository: DIContainer.resolve( - type: UserDefaultsRepository.self - ) - ) - } - DIContainer.register(type: SignUpWithAppleUseCase.self) { - SignUpWithAppleUseCaseImpl( - authRepository: DIContainer.resolve(type: AuthAPIRepository.self), - tokenRepository: DIContainer.resolve(type: TokenRepository.self), - userDefaultsRepository: DIContainer.resolve(type: UserDefaultsRepository.self) - ) - } - DIContainer.register(type: SignUpWithKakaoUseCase.self) { - SignUpWithKakaoUseCaseImpl( - authRepository: DIContainer.resolve(type: AuthAPIRepository.self), - tokenRepository: DIContainer.resolve(type: TokenRepository.self), - userDefaultsRepository: DIContainer.resolve(type: UserDefaultsRepository.self) - ) - } - DIContainer.register(type: UpdateUserInfoUseCase.self) { - UpdateUserInfoUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self) - ) - } - DIContainer.register(type: ReissueUseCase.self) { - ReissueUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self), tokenRepository: DIContainer.resolve(type: TokenRepository.self) - ) - } - DIContainer.register(type: PutFCMTokenUseCase.self) { - PutFCMTokenUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self) - ) - } - DIContainer.register(type: FetchTokenFromLocalUseCase.self) { - FetchTokenFromLocalUseCaseImpl( - repository: DIContainer.resolve(type: TokenRepository.self) - ) - } - DIContainer.register(type: SaveTokenToLocalUseCase.self) { - SaveTokenToLocalUseCaseImpl( - repository: DIContainer.resolve(type: TokenRepository.self) - ) - } - DIContainer.register(type: DeleteTokenFromLocalUseCase.self) { - DeleteTokenFromLocalUseCaseImpl( - repository: DIContainer.resolve(type: TokenRepository.self) - ) - } - DIContainer.register(type: UpdateMarketingAgreementUseCase.self) { - UpdateMarketingAgreementUseCaseImpl( - authRepository: DIContainer.resolve( - type: AuthAPIRepository.self - ), - tokenRepository: DIContainer.resolve(type: TokenRepository.self) - ) - } - DIContainer.register(type: CheckNotificationPermissionUseCase.self) { - CheckNotificationPermissionUseCaseImpl() - } - DIContainer.register(type: OpenNotificationSettingUseCase.self) { - OpenNotificationSettingUseCaseImpl() - } - DIContainer.register(type: UpdateNotificationAgreementUseCase.self) { - UpdateNotificationAgreementUseCaseImpl( - authRepository: DIContainer.resolve( - type: AuthAPIRepository.self - ) - ) - } - DIContainer.register(type: CheckLoginUseCase.self) { - CheckLoginUseCaseImpl( - authRepository: DIContainer.resolve( - type: AuthAPIRepository.self - ), - tokenRepository: DIContainer.resolve(type: TokenRepository.self) - ) - } - DIContainer.register(type: FetchDictionaryAllListUseCase.self) { - FetchDictionaryAllListUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self - ) - ) - } - DIContainer.register(type: SetBookmarkUseCase.self) { - SetBookmarkUseCaseImpl( - repository: DIContainer.resolve(type: BookmarkRepository.self) - ) - } - DIContainer.register(type: FetchPlatformUseCase.self) { - FetchPlatformUseCaseImpl( - repository: DIContainer.resolve( - type: UserDefaultsRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryMapListUseCase.self) { - FetchDictionaryMapListUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryItemListUseCase.self) { - FetchDictionaryItemListUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryQuestListUseCase.self) { - FetchDictionaryQuestListUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryNpcListUseCase.self) { - FetchDictionaryNpcListUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryMonsterListUseCase.self) { - FetchDictionaryMonsterListUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryDetailMonsterUseCase.self) { - FetchDictionaryDetailMonsterUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self - ) - ) - } - DIContainer.register( - type: FetchDictionaryDetailMonsterItemsUseCase.self - ) { - FetchDictionaryDetailMonsterDropItemUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryDetailMonsterMapUseCase.self) { - FetchDictionaryDetailMonsterMapUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryDetailNpcUseCase.self) { - FetchDictionaryDetailNpcUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryDetailNpcQuestUseCase.self) { - FetchDictionaryDetailNpcQuestUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryDetailNpcMapUseCase.self) { - FetchDictionaryDetailNpcMapUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryDetailItemUseCase.self) { - FetchDictionaryDetailItemUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self - ) - ) - } - DIContainer.register( - type: FetchDictionaryDetailItemDropMonsterUseCase.self - ) { - FetchDictionaryDetailItemDropMonsterUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryDetailQuestUseCase.self) { - FetchDictionaryDetailQuestUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self - ) - ) - } - DIContainer.register( - type: FetchDictionaryDetailQuestLinkedQuestsUseCase.self - ) { - FetchDictionaryDetailQuestLinkedQuestsUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryDetailMapUseCase.self) { - FetchDictionaryDetailMapUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self - ) - ) - } - DIContainer.register( - type: FetchDictionaryDetailMapSpawnMonsterUseCase.self - ) { - FetchDictionaryDetailMapSpawnMonsterUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryDetailMapNpcUseCase.self) { - FetchDictionaryDetailMapNpcUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self - ) - ) - } - DIContainer.register(type: RecentSearchRemoveUseCase.self) { - RecentSearchRemoveUseCaseImpl( - repository: DIContainer.resolve( - type: UserDefaultsRepository.self - ) - ) - } - DIContainer.register(type: RecentSearchAddUseCase.self) { - RecentSearchAddUseCaseImpl( - repository: DIContainer.resolve( - type: UserDefaultsRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionaryListCountUseCase.self) { - FetchDictionaryListCountUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchDictionarySearchListUseCase.self) { - FetchDictionarySearchListUseCaseImpl( - repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self - ) - ) - } - DIContainer.register(type: RecentSearchFetchUseCase.self) { - RecentSearchFetchUseCaseImpl( - repository: DIContainer.resolve( - type: UserDefaultsRepository.self - ) - ) - } - DIContainer.register(type: FetchBookmarkUseCase.self) { - FetchBookmarkUseCaseImpl( - repository: DIContainer.resolve(type: BookmarkRepository.self) - ) - } - DIContainer.register(type: FetchMonsterBookmarkUseCase.self) { - FetchMonsterBookmarkUseCaseImpl( - repository: DIContainer.resolve(type: BookmarkRepository.self) - ) - } - DIContainer.register(type: FetchItemBookmarkUseCase.self) { - FetchItemBookmarkUseCaseImpl( - repository: DIContainer.resolve(type: BookmarkRepository.self) - ) - } - DIContainer.register(type: FetchNPCBookmarkUseCase.self) { - FetchNPCBookmarkUseCaseImpl( - repository: DIContainer.resolve(type: BookmarkRepository.self) - ) - } - DIContainer.register(type: FetchQuestBookmarkUseCase.self) { - FetchQuestBookmarkUseCaseImpl( - repository: DIContainer.resolve(type: BookmarkRepository.self) - ) - } - DIContainer.register(type: FetchMapBookmarkUseCase.self) { - FetchMapBookmarkUseCaseImpl( - repository: DIContainer.resolve(type: BookmarkRepository.self) - ) - } - DIContainer.register(type: UpdateProfileImageUseCase.self) { - UpdateProfileImageUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self) - ) - } - DIContainer.register(type: FetchJobUseCase.self) { - FetchJobUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self) - ) - } - DIContainer.register(type: FetchProfileUseCase.self) { - FetchProfileUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self), - fetchJobUseCase: DIContainer.resolve(type: FetchJobUseCase.self) - ) - } - DIContainer.register(type: CheckNickNameUseCase.self) { - CheckNickNameUseCaseImpl() - } - DIContainer.register(type: UpdateNickNameUseCase.self) { - UpdateNickNameUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self) - ) - } - DIContainer.register(type: LogoutUseCase.self) { - LogoutUseCaseImpl( - repository: DIContainer.resolve(type: TokenRepository.self) - ) - } - DIContainer.register(type: WithdrawUseCase.self) { - WithdrawUseCaseImpl( - authRepository: DIContainer.resolve( - type: AuthAPIRepository.self - ), - tokenRepository: DIContainer.resolve(type: TokenRepository.self) - ) - } - DIContainer.register(type: FetchNoticesUseCase.self) { - FetchNoticesUseCaseImpl( - repository: DIContainer.resolve(type: AlarmAPIRepository.self) - ) - } - DIContainer.register(type: FetchOngoingEventsUseCase.self) { - FetchOngoingEventsUseCaseImpl( - repository: DIContainer.resolve(type: AlarmAPIRepository.self) - ) - } - DIContainer.register(type: FetchOutdatedEventsUseCase.self) { - FetchOutdatedEventsUseCaseImpl( - repository: DIContainer.resolve(type: AlarmAPIRepository.self) - ) - } - DIContainer.register(type: FetchPatchNotesUseCase.self) { - FetchPatchNotesUseCaseImpl( - repository: DIContainer.resolve(type: AlarmAPIRepository.self) - ) - } - DIContainer.register(type: SetReadUseCase.self) { - SetReadUseCaseImpl( - repository: DIContainer.resolve(type: AlarmAPIRepository.self) - ) - } - DIContainer.register(type: FetchAllAlarmUseCase.self) { - FetchAllAlarmUseCaseImpl( - repository: DIContainer.resolve(type: AlarmAPIRepository.self) - ) - } - DIContainer.register(type: ParseItemFilterResultUseCase.self) { - ParseItemFilterResultUseCaseImpl() - } - DIContainer.register(type: FetchCollectionListUseCase.self) { - FetchCollectionListUseCaseImpl( - repository: DIContainer.resolve( - type: CollectionAPIRepository.self - ) - ) - } - DIContainer.register(type: CreateCollectionListUseCase.self) { - CreateCollectionListUseCaseImpl( - repository: DIContainer.resolve( - type: CollectionAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchCollectionUseCase.self) { - FetchCollectionUseCaseImpl( - repository: DIContainer.resolve( - type: CollectionAPIRepository.self - ) - ) - } - DIContainer.register(type: UpdateCollectionUseCase.self) { - UpdateCollectionUseCaseImpl( - repository: DIContainer.resolve( - type: CollectionAPIRepository.self - ) - ) - } - DIContainer.register(type: DeleteCollectionUseCase.self) { - DeleteCollectionUseCaseImpl( - repository: DIContainer.resolve( - type: CollectionAPIRepository.self - ) - ) - } - DIContainer.register(type: AddCollectionAndBookmarkUseCase.self) { - AddCollectionAndBookmarkUseCaseImpl( - repository: DIContainer.resolve( - type: CollectionAPIRepository.self - ) - ) - } - DIContainer.register(type: FetchVisitBookmarkUseCase.self) { - FetchVisitBookmarkUseCaseImpl( - repository: DIContainer.resolve( - type: UserDefaultsRepository.self - ) - ) - } - DIContainer.register(type: FetchVisitDictionaryDetailUseCase.self) { - FetchVisitDictionaryDetailUseCaseImpl(repository: DIContainer.resolve(type: UserDefaultsRepository.self)) - } - } - - func registerFactory() { - DIContainer.register(type: ItemFilterBottomSheetFactory.self) { - ItemFilterBottomSheetFactoryImpl() - } - DIContainer.register(type: MonsterFilterBottomSheetFactory.self) { - MonsterFilterBottomSheetFactoryImpl() - } - DIContainer.register(type: SortedBottomSheetFactory.self) { - SortedBottomSheetFactoryImpl() - } - DIContainer.register(type: AddCollectionFactory.self) { - AddCollectionFactoryImpl( - createCollectionListUseCase: DIContainer.resolve( - type: CreateCollectionListUseCase.self - ), - setCollectionUseCase: DIContainer.resolve( - type: UpdateCollectionUseCase.self - ) - ) - } - DIContainer.register(type: BookmarkModalFactory.self) { - BookmarkModalFactoryImpl( - addCollectionFactory: DIContainer.resolve( - type: AddCollectionFactory.self - ), - fetchCollectionListUseCase: DIContainer.resolve( - type: FetchCollectionListUseCase.self - ), - addCollectionAndBookmarkUseCase: DIContainer.resolve( - type: AddCollectionAndBookmarkUseCase.self - ) - ) - } - DIContainer.register(type: LoginFactory.self) { - LoginFactoryImpl( - termsAgreementsFactory: DIContainer.resolve( - type: TermsAgreementFactory.self - ), - appleLoginUseCase: DIContainer.resolve( - type: FetchSocialCredentialUseCase.self, - name: "apple" - ), - kakaoLoginUseCase: DIContainer.resolve( - type: FetchSocialCredentialUseCase.self, - name: "kakao" - ), - loginWithAppleUseCase: DIContainer.resolve( - type: LoginWithAppleUseCase.self - ), - loginWithKakaoUseCase: DIContainer.resolve( - type: LoginWithKakaoUseCase.self - ), - fetchTokenUseCase: DIContainer.resolve( - type: FetchTokenFromLocalUseCase.self - ), - putFCMTokenUseCase: DIContainer.resolve( - type: PutFCMTokenUseCase.self - ), - fetchPlatformUseCase: DIContainer.resolve( - type: FetchPlatformUseCase.self - ) - ) - } - DIContainer.register(type: DictionaryDetailFactory.self) { - DictionaryDetailFactoryImpl( - loginFactory: { DIContainer.resolve(type: LoginFactory.self) }, - bookmarkModalFactory: DIContainer.resolve( - type: BookmarkModalFactory.self - ), - dictionaryDetailFactory: { - DIContainer - .resolve(type: DictionaryDetailFactory.self) - }, - detailOnBoardingFactory: DIContainer.resolve( - type: DetailOnBoardingFactory.self - ), - appCoordinator: { - DIContainer.resolve(type: AppCoordinatorProtocol.self) - }, - dictionaryDetailMapUseCase: DIContainer.resolve( - type: FetchDictionaryDetailMapUseCase.self - ), - dictionaryDetailMapSpawnMonsterUseCase: DIContainer.resolve( - type: FetchDictionaryDetailMapSpawnMonsterUseCase.self - ), - dictionaryDetailMapNpcUseCase: DIContainer.resolve( - type: FetchDictionaryDetailMapNpcUseCase.self - ), - dictionaryDetailQuestLinkedQuestsUseCase: DIContainer.resolve( - type: FetchDictionaryDetailQuestLinkedQuestsUseCase.self - ), - dictionaryDetailQuestUseCase: DIContainer.resolve( - type: FetchDictionaryDetailQuestUseCase.self - ), - dictionaryDetailItemDropMonsterUseCase: DIContainer.resolve( - type: FetchDictionaryDetailItemDropMonsterUseCase.self - ), - dictionaryDetailItemUseCase: DIContainer.resolve( - type: FetchDictionaryDetailItemUseCase.self - ), - dictionaryDetailNpcUseCase: DIContainer.resolve( - type: FetchDictionaryDetailNpcUseCase.self - ), - dictionaryDetailNpcQuestUseCase: DIContainer.resolve( - type: FetchDictionaryDetailNpcQuestUseCase.self - ), - dictionaryDetailNpcMapUseCase: DIContainer.resolve( - type: FetchDictionaryDetailNpcMapUseCase.self - ), - dictionaryDetailMonsterUseCase: DIContainer.resolve( - type: FetchDictionaryDetailMonsterUseCase.self - ), - dictionaryDetailMonsterDropItemUseCase: DIContainer.resolve( - type: FetchDictionaryDetailMonsterItemsUseCase.self - ), - dictionaryDetailMonsterMapUseCase: DIContainer.resolve( - type: FetchDictionaryDetailMonsterMapUseCase.self - ), - checkLoginUseCase: DIContainer.resolve( - type: CheckLoginUseCase.self - ), - setBookmarkUseCase: DIContainer.resolve( - type: SetBookmarkUseCase.self - ), - fetchVisitDictionaryDetailUseCase: DIContainer.resolve( - type: FetchVisitDictionaryDetailUseCase.self - ) - ) - } - DIContainer.register(type: DictionaryMainListFactory.self) { - DictionaryListFactoryImpl( - checkLoginUseCase: DIContainer.resolve( - type: CheckLoginUseCase.self - ), - dictionaryAllListItemUseCase: DIContainer.resolve( - type: FetchDictionaryAllListUseCase.self - ), - dictionaryMapListItemUseCase: DIContainer.resolve( - type: FetchDictionaryMapListUseCase.self - ), - dictionaryItemListItemUseCase: DIContainer.resolve( - type: FetchDictionaryItemListUseCase.self - ), - dictionaryQuestListItemUseCase: DIContainer.resolve( - type: FetchDictionaryQuestListUseCase.self - ), - dictionaryNpcListItemUseCase: - DIContainer - .resolve(type: FetchDictionaryNpcListUseCase.self), - dictionaryListItemUseCase: DIContainer.resolve( - type: FetchDictionaryMonsterListUseCase.self - ), - setBookmarkUseCase: DIContainer.resolve( - type: SetBookmarkUseCase.self - ), - parseItemFilterResultUseCase: DIContainer.resolve( - type: ParseItemFilterResultUseCase.self - ), - itemFilterFactory: DIContainer.resolve( - type: ItemFilterBottomSheetFactory.self - ), - monsterFilterFactory: DIContainer.resolve( - type: MonsterFilterBottomSheetFactory.self - ), - sortedFactory: DIContainer.resolve( - type: SortedBottomSheetFactory.self - ), - bookmarkModalFactory: DIContainer.resolve( - type: BookmarkModalFactory.self - ), - detailFactory: DIContainer.resolve( - type: DictionaryDetailFactory.self - ), - loginFactory: { DIContainer.resolve(type: LoginFactory.self) } - ) - } - DIContainer.register(type: DictionarySearchResultFactory.self) { - DictionarySearchResultFactoryImpl( - dictionaryListCountUseCase: DIContainer.resolve( - type: FetchDictionaryListCountUseCase.self - ), - dictionaryMainListFactory: - DIContainer - .resolve(type: DictionaryMainListFactory.self), - dictionarySearchListUseCase: DIContainer.resolve( - type: FetchDictionarySearchListUseCase.self - ), recentSearchAddUseCase: DIContainer.resolve( - type: RecentSearchAddUseCase.self) - ) - } - DIContainer.register(type: DictionarySearchFactory.self) { - DictionarySearchFactoryImpl( - recentSearchRemoveUseCase: DIContainer.resolve( - type: RecentSearchRemoveUseCase.self - ), - recentSearchAddUseCase: DIContainer.resolve( - type: RecentSearchAddUseCase.self - ), - searchResultFactory: - DIContainer - .resolve(type: DictionarySearchResultFactory.self), - recentSearchFetchUseCase: DIContainer.resolve( - type: RecentSearchFetchUseCase.self - ) - ) - } - DIContainer.register(type: NotificationSettingFactory.self) { - NotificationSettingFactoryImpl( - checkNotificationPermissionUseCase: DIContainer.resolve( - type: CheckNotificationPermissionUseCase.self - ), - updateNotificationAgreementUseCase: DIContainer.resolve( - type: UpdateNotificationAgreementUseCase.self - ) - ) - } - DIContainer.register(type: DictionaryNotificationFactory.self) { - DictionaryNotificationFactoryImpl( - notificationSettingFactory: DIContainer.resolve( - type: NotificationSettingFactory.self - ), - fetchAllAlarmUseCase: DIContainer.resolve( - type: FetchAllAlarmUseCase.self - ), - fetchProfileUseCase: DIContainer.resolve( - type: FetchProfileUseCase.self - ), checkNotificationPermissionUseCase: DIContainer.resolve(type: CheckNotificationPermissionUseCase.self), - setReadUseCase: DIContainer.resolve( - type: SetReadUseCase.self) - ) - } - DIContainer.register(type: DictionaryMainViewFactory.self) { - DictionaryMainViewFactoryImpl( - dictionaryMainListFactory: DIContainer.resolve( - type: DictionaryMainListFactory.self - ), - searchFactory: DIContainer.resolve( - type: DictionarySearchFactory.self - ), - notificationFactory: DIContainer.resolve( - type: DictionaryNotificationFactory.self - ), - loginFactory: DIContainer.resolve(type: LoginFactory.self), - fetchProfileUseCase: DIContainer.resolve( - type: FetchProfileUseCase.self - ) - ) - } - DIContainer.register(type: OnBoardingNotificationSheetFactory.self) { - OnBoardingNotificationSheetFactoryImpl( - checkNotificationPermissionUseCase: - DIContainer - .resolve(type: CheckNotificationPermissionUseCase.self), - openNotificationSettingUseCase: - DIContainer - .resolve(type: OpenNotificationSettingUseCase.self), - updateNotificationAgreementUseCase: - DIContainer - .resolve(type: UpdateNotificationAgreementUseCase.self), - updateUserInfoUseCase: DIContainer.resolve( - type: UpdateUserInfoUseCase.self - ), - appCoordinator: { - DIContainer.resolve( - type: AppCoordinatorProtocol.self - ) - } - ) - } - DIContainer.register(type: OnBoardingInputFactory.self) { - OnBoardingInputFactoryImpl( - checkEmptyUseCase: DIContainer.resolve( - type: CheckEmptyLevelAndRoleUseCase.self - ), - checkValidLevelUseCase: DIContainer.resolve( - type: CheckValidLevelUseCase.self - ), - fetchJobListUseCase: DIContainer.resolve( - type: FetchJobListUseCase.self - ), - updateUserInfoUseCase: DIContainer.resolve( - type: UpdateUserInfoUseCase.self - ), - onBoardingNotificationFactory: DIContainer.resolve( - type: OnBoardingNotificationFactory.self - ), - appCoordinator: { - DIContainer.resolve(type: AppCoordinatorProtocol.self) - } - ) - } - DIContainer.register(type: OnBoardingQuestionFactory.self) { - OnBoardingQuestionFactoryImpl( - onBoardingInputFactory: DIContainer.resolve( - type: OnBoardingInputFactory.self - ) - ) - } - DIContainer.register(type: TermsAgreementFactory.self) { - TermsAgreementFactoryImpl( - onBoardingQuestionFactory: DIContainer.resolve( - type: OnBoardingQuestionFactory.self - ), - signUpWithKakaoUseCase: DIContainer.resolve( - type: SignUpWithKakaoUseCase.self - ), - signUpWithAppleUseCase: DIContainer.resolve( - type: SignUpWithAppleUseCase.self - ), - fetchTokenUseCase: DIContainer.resolve( - type: FetchTokenFromLocalUseCase.self - ), - updateMarketingAgreementUseCase: DIContainer.resolve( - type: UpdateMarketingAgreementUseCase.self - ) - ) - } - DIContainer.register(type: OnBoardingNotificationFactory.self) { - OnBoardingNotificationFactoryImpl( - onBoardingNotificationSheetFactory: DIContainer.resolve( - type: OnBoardingNotificationSheetFactory.self - ), - appCoordinator: { - DIContainer.resolve( - type: AppCoordinatorProtocol.self - ) - } - ) - } - DIContainer.register(type: BookmarkMainFactory.self) { - BookmarkMainFactoryImpl( - setBookmarkUseCase: - DIContainer - .resolve(type: SetBookmarkUseCase.self), - checkLoginUseCase: - DIContainer - .resolve(type: CheckLoginUseCase.self), - fetchVisitBookmarkUseCase: - DIContainer - .resolve(type: FetchVisitBookmarkUseCase.self), - onBoardingFactory: - DIContainer - .resolve(type: BookmarkOnBoardingFactory.self), - bookmarkListFactory: - DIContainer - .resolve(type: BookmarkListFactory.self), - collectionListFactory: - DIContainer - .resolve(type: CollectionListFactory.self), - searchFactory: - DIContainer - .resolve(type: DictionarySearchFactory.self), - notificationFactory: DIContainer.resolve( - type: DictionaryNotificationFactory.self - ), - loginFactory: DIContainer.resolve(type: LoginFactory.self) - ) - } - DIContainer.register(type: BookmarkOnBoardingFactory.self) { - BookmarkOnBoardingFactoryImpl() - } - DIContainer.register(type: BookmarkListFactory.self) { - BookmarkListFactoryImpl( - itemFilterFactory: DIContainer.resolve( - type: ItemFilterBottomSheetFactory.self - ), - monsterFilterFactory: DIContainer.resolve( - type: MonsterFilterBottomSheetFactory.self - ), - sortedFactory: DIContainer.resolve( - type: SortedBottomSheetFactory.self - ), - bookmarkModalFactory: DIContainer.resolve( - type: BookmarkModalFactory.self - ), - loginFactory: DIContainer.resolve(type: LoginFactory.self), - dictionaryDetailFactory: DIContainer.resolve( - type: DictionaryDetailFactory.self - ), - collectionEditFactory: DIContainer.resolve( - type: CollectionEditFactory.self - ), - setBookmarkUseCase: DIContainer.resolve( - type: SetBookmarkUseCase.self - ), - fetchProfileUseCase: DIContainer.resolve( - type: FetchProfileUseCase.self - ), - fetchBookmarkUseCase: DIContainer.resolve( - type: FetchBookmarkUseCase.self - ), - fetchMonsterBookmarkUseCase: DIContainer.resolve( - type: FetchMonsterBookmarkUseCase.self - ), - fetchItemBookmarkUseCase: DIContainer.resolve( - type: FetchItemBookmarkUseCase.self - ), - fetchNPCBookmarkUseCase: DIContainer.resolve( - type: FetchNPCBookmarkUseCase.self - ), - fetchQuestBookmarkUseCase: DIContainer.resolve( - type: FetchQuestBookmarkUseCase.self - ), - fetchMapBookmarkUseCase: DIContainer.resolve( - type: FetchMapBookmarkUseCase.self - ), - parseItemFilterResultUseCase: DIContainer.resolve( - type: ParseItemFilterResultUseCase.self - ) - ) - } - DIContainer.register(type: CollectionListFactory.self) { - CollectionListFactoryImpl( - fetchCollectionListUseCase: DIContainer.resolve( - type: FetchCollectionListUseCase.self - ), - addCollectionFactory: DIContainer.resolve( - type: AddCollectionFactory.self - ), - bookmarkDetailFactory: DIContainer.resolve( - type: CollectionDetailFactory.self - ), - sortedBottomSheetFactory: - DIContainer - .resolve(type: SortedBottomSheetFactory.self) - ) - } - DIContainer.register(type: CollectionDetailFactory.self) { - CollectionDetailFactoryImpl( - bookmarkModalFactory: - DIContainer - .resolve(type: BookmarkModalFactory.self), - collectionSettingFactory: - DIContainer - .resolve(type: CollectionSettingFactory.self), - addCollectionFactory: - DIContainer - .resolve(type: AddCollectionFactory.self), - collectionEditFactory: - DIContainer - .resolve(type: CollectionEditFactory.self), - dictionaryDetailFactory: - DIContainer - .resolve(type: DictionaryDetailFactory.self), - setBookmarkUseCase: - DIContainer - .resolve(type: SetBookmarkUseCase.self), - fetchCollectionUseCase: DIContainer.resolve( - type: FetchCollectionUseCase.self - ), - deleteCollectionUseCase: - DIContainer - .resolve(type: DeleteCollectionUseCase.self), - addCollectionAndBookmarkUseCase: DIContainer.resolve( - type: AddCollectionAndBookmarkUseCase.self - ) - ) - } - DIContainer.register(type: CollectionSettingFactory.self) { - CollectionSettingFactoryImpl() - } - DIContainer.register(type: CollectionEditFactory.self) { - CollectionEditFactoryImpl( - setBookmarkUseCase: DIContainer.resolve( - type: SetBookmarkUseCase.self - ), - bookmarkModalFactory: DIContainer.resolve( - type: BookmarkModalFactory.self - ) - ) - } - DIContainer.register(type: MyPageMainFactory.self) { - MyPageMainFactoryImpl( - loginFactory: DIContainer.resolve(type: LoginFactory.self), - setProfileFactory: - DIContainer - .resolve(type: SetProfileFactory.self), - customerSupportFactory: - DIContainer - .resolve(type: CustomerSupportFactory.self), - notificationSettingFactory: - DIContainer - .resolve(type: NotificationSettingFactory.self), - setCharacterFactory: - DIContainer - .resolve(type: SetCharacterFactory.self), - fetchProfileUseCase: DIContainer.resolve( - type: FetchProfileUseCase.self - ) - ) - } - DIContainer.register(type: CustomerSupportFactory.self) { - CustomerSupportBaseViewFactoryImpl( - policyFactory: DIContainer.resolve( - type: PolicyFactory.self), - fetchNoticesUseCase: DIContainer.resolve( - type: FetchNoticesUseCase.self - ), - fetchOngoingEventsUseCase: DIContainer.resolve( - type: FetchOngoingEventsUseCase.self - ), - fetchOutdatedEventsUseCase: DIContainer.resolve( - type: FetchOutdatedEventsUseCase.self - ), - fetchPatchNotesUseCase: DIContainer.resolve( - type: FetchPatchNotesUseCase.self - ), - setReadUseCase: DIContainer.resolve(type: SetReadUseCase.self) - ) - } - DIContainer.register(type: SetProfileFactory.self) { - SetProfileFactoryImpl( - selectImageFactory: DIContainer.resolve( - type: SelectImageFactory.self - ), - checkNickNameUseCase: DIContainer.resolve( - type: CheckNickNameUseCase.self - ), - updateNickNameUseCase: DIContainer.resolve( - type: UpdateNickNameUseCase.self - ), - logoutUseCase: DIContainer.resolve(type: LogoutUseCase.self), - withdrawUseCase: DIContainer.resolve( - type: WithdrawUseCase.self - ), - fetchProfileUseCase: DIContainer.resolve( - type: FetchProfileUseCase.self - ) - ) - } - DIContainer.register(type: SetCharacterFactory.self) { - SetCharacterFactoryImpl( - checkEmptyUseCase: - DIContainer - .resolve(type: CheckEmptyLevelAndRoleUseCase.self), - checkValidLevelUseCase: - DIContainer - .resolve(type: CheckValidLevelUseCase.self), - fetchJobListUseCase: - DIContainer - .resolve(type: FetchJobListUseCase.self), - updateUserInfoUseCase: - DIContainer - .resolve(type: UpdateUserInfoUseCase.self) - ) - } - DIContainer.register(type: SelectImageFactory.self) { - SelectImageFactoryImpl( - updateProfileImageUseCase: DIContainer.resolve( - type: UpdateProfileImageUseCase.self - ) - ) - } - DIContainer.register(type: DetailOnBoardingFactory.self) { - DetailOnBoardingFactoryImpl() - } - DIContainer.register(type: PolicyFactory.self) { - PolicyFactoryImpl() - } + tokenLauncher.didReceiveFCMToken(fcmToken) } } diff --git a/MLS/MLS/Application/SceneDelegate.swift b/MLS/MLS/Application/SceneDelegate.swift index cf82223f..9b510fad 100644 --- a/MLS/MLS/Application/SceneDelegate.swift +++ b/MLS/MLS/Application/SceneDelegate.swift @@ -1,20 +1,13 @@ import UIKit -import AuthFeatureInterface -import BaseFeature -import BookmarkFeatureInterface -import Core -import DictionaryFeatureInterface -import DomainInterface -import MyPageFeatureInterface +import MLSAppFeature import KakaoSDKAuth import RxSwift final class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - var appCoordinator: AppCoordinatorProtocol? - var disposeBag = DisposeBag() + private let launcher = AppLauncher() func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } @@ -22,12 +15,7 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate { let window = UIWindow(windowScene: windowScene) window.makeKeyAndVisible() self.window = window - - let coordinator = DIContainer.resolve(type: AppCoordinatorProtocol.self) - coordinator.window = window - appCoordinator = coordinator - - startScene(coordinator: coordinator) + launcher.launch(window: window) } func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { @@ -37,32 +25,4 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate { } } } - - private func startScene(coordinator: AppCoordinatorProtocol) { - let fetchTokenUseCase = DIContainer.resolve(type: FetchTokenFromLocalUseCase.self) - let reissueUseCase = DIContainer.resolve(type: ReissueUseCase.self) - - let fetchResult = fetchTokenUseCase.execute(type: .refreshToken) - - switch fetchResult { - case .success(let refreshToken): - #if DEBUG - print("refreshToken: \(refreshToken)") - #endif - reissueUseCase.execute(refreshToken: refreshToken) - .observe(on: MainScheduler.instance) - .subscribe( - onNext: { _ in - coordinator.showMainTab() - }, - onError: { _ in - coordinator.showLogin(exitRoute: .home) - } - ) - .disposed(by: disposeBag) - - case .failure: - coordinator.showLogin(exitRoute: .home) - } - } } diff --git a/MLS/MLS/Application/ViewController.swift b/MLS/MLS/Application/ViewController.swift deleted file mode 100644 index d5b12d72..00000000 --- a/MLS/MLS/Application/ViewController.swift +++ /dev/null @@ -1,13 +0,0 @@ -import UIKit - -import Data - -import RxCocoa -import RxSwift - -class ViewController: UIViewController { - override func viewDidLoad() { - super.viewDidLoad() - view.backgroundColor = .red - } -} diff --git a/MLS/MLSAppFeature/Package.swift b/MLS/MLSAppFeature/Package.swift index 895561cb..aff5bedf 100644 --- a/MLS/MLSAppFeature/Package.swift +++ b/MLS/MLSAppFeature/Package.swift @@ -23,16 +23,43 @@ let package = Package( targets: ["MLSAppFeatureTesting"] ) ], + dependencies: [ + .package(path: "../MLSAppFeature"), + .package(path: "../MLSAuthFeature"), + .package(path: "../MLSBookmarkFeature"), + .package(path: "../MLSCore"), + .package(path: "../MLSDictionaryFeature"), + .package(path: "../MLSMyPageFeature"), + .package(path: "../MLSRecommendationFeature"), + .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "6.7.0") + ], targets: [ // Interface 모듈 (도메인 모델 및 프로토콜) .target( name: "MLSAppFeatureInterface", - dependencies: [] + dependencies: [ + .product(name: "MLSCore", package: "MLSCore") + ] ), // Feature 모듈 (실제 구현) .target( name: "MLSAppFeature", - dependencies: ["MLSAppFeatureInterface"] + dependencies: [ + "MLSAppFeatureInterface", + .product(name: "MLSAuthFeature", package: "MLSAuthFeature"), + .product(name: "MLSAppFeatureInterface", package: "MLSAppFeature"), + .product(name: "MLSAuthFeatureInterface", package: "MLSAuthFeature"), + .product(name: "MLSBookmarkFeature", package: "MLSBookmarkFeature"), + .product(name: "MLSBookmarkFeatureInterface", package: "MLSBookmarkFeature"), + .product(name: "MLSCore", package: "MLSCore"), + .product(name: "MLSDictionaryFeature", package: "MLSDictionaryFeature"), + .product(name: "MLSDictionaryFeatureInterface", package: "MLSDictionaryFeature"), + .product(name: "MLSMyPageFeature", package: "MLSMyPageFeature"), + .product(name: "MLSMyPageFeatureInterface", package: "MLSMyPageFeature"), + .product(name: "MLSRecommendationFeature", package: "MLSRecommendationFeature"), + .product(name: "MLSRecommendationFeatureInterface", package: "MLSRecommendationFeature"), + .product(name: "RxSwift", package: "RxSwift") + ] ), // Testing 모듈 (Mock 객체) .target( diff --git a/MLS/MLSAppFeature/Sources/MLSAppFeature/AppInfo.swift b/MLS/MLSAppFeature/Sources/MLSAppFeature/AppInfo.swift new file mode 100644 index 00000000..e62a49bc --- /dev/null +++ b/MLS/MLSAppFeature/Sources/MLSAppFeature/AppInfo.swift @@ -0,0 +1,5 @@ +enum AppInfo { + // TODO: 앱스토어 출시 후 실제 앱 ID로 교체 + static let appStoreID = "6477212894" + static let appStoreURL = "itms-apps://itunes.apple.com/app/id\(appStoreID)" +} diff --git a/MLS/MLS/Application/AppCoordinator.swift b/MLS/MLSAppFeature/Sources/MLSAppFeature/Coordinator/AppCoordinator.swift similarity index 57% rename from MLS/MLS/Application/AppCoordinator.swift rename to MLS/MLSAppFeature/Sources/MLSAppFeature/Coordinator/AppCoordinator.swift index 947e4dea..edcf8585 100644 --- a/MLS/MLS/Application/AppCoordinator.swift +++ b/MLS/MLSAppFeature/Sources/MLSAppFeature/Coordinator/AppCoordinator.swift @@ -1,18 +1,22 @@ import UIKit -import AuthFeature -import AuthFeatureInterface -import BaseFeature -import BookmarkFeatureInterface -import DesignSystem -import DictionaryFeatureInterface -import MyPageFeatureInterface +import MLSAppFeatureInterface +import MLSAuthFeature +import MLSAuthFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface +import MLSMyPageFeatureInterface +import MLSRecommendationFeatureInterface import RxSwift +@MainActor public final class AppCoordinator: AppCoordinatorProtocol { // MARK: - Properties public var window: UIWindow? + private let recommendationMainFactory: RecommendationMainFactory private let dictionaryMainViewFactory: DictionaryMainViewFactory private let bookmarkMainFactory: BookmarkMainFactory private let myPageMainFactory: MyPageMainFactory @@ -23,12 +27,14 @@ public final class AppCoordinator: AppCoordinatorProtocol { // MARK: - Init public init( window: UIWindow?, + recommendationMainFactory: RecommendationMainFactory, dictionaryMainViewFactory: DictionaryMainViewFactory, bookmarkMainFactory: BookmarkMainFactory, myPageMainFactory: MyPageMainFactory, loginFactory: LoginFactory ) { self.window = window + self.recommendationMainFactory = recommendationMainFactory self.dictionaryMainViewFactory = dictionaryMainViewFactory self.bookmarkMainFactory = bookmarkMainFactory self.myPageMainFactory = myPageMainFactory @@ -36,12 +42,24 @@ public final class AppCoordinator: AppCoordinatorProtocol { } // MARK: - Public Methods - public func showMainTab() { - let tabBar = BottomTabBarController(viewControllers: [ - dictionaryMainViewFactory.make(), - bookmarkMainFactory.make(), - myPageMainFactory.make() - ]) + public func showMainTab(selectedIndex: Int = 0) { + let tabItems: [TabItem] = [ + TabItem(title: "도감", icon: DesignSystemAsset.image(named: "dictionary")), + TabItem(title: "추천", icon: DesignSystemAsset.image(named: "favorite") + ), + TabItem(title: "북마크", icon: DesignSystemAsset.image(named: "bookmarkList")), + TabItem(title: "MY", icon: DesignSystemAsset.image(named: "mypage")) + ] + let tabBar = BottomTabBarController( + viewControllers: [ + dictionaryMainViewFactory.make(), + recommendationMainFactory.make(), + bookmarkMainFactory.make(bottomInset: 64), + myPageMainFactory.make() + ], + tabItems: tabItems, + initialIndex: selectedIndex + ) let navigationController = UINavigationController(rootViewController: tabBar) navigationController.isNavigationBarHidden = true diff --git a/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/DependencyAssembler.swift b/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/DependencyAssembler.swift new file mode 100644 index 00000000..289073ed --- /dev/null +++ b/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/DependencyAssembler.swift @@ -0,0 +1,40 @@ +import UIKit + +import MLSAppFeatureInterface +import MLSAuthFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface +import MLSMyPageFeatureInterface +import MLSRecommendationFeatureInterface + +public enum DependencyAssembler { + public static func register() { + ProviderAssembly.register() + RepositoryAssembly.register() + UseCaseAssembly.register() + FactoryAssembly.register() + } + + @MainActor + public static func launch(window: UIWindow?) { + DIContainer.register(type: AppCoordinatorProtocol.self) { + AppCoordinator( + window: window, + recommendationMainFactory: DIContainer.resolve( + type: RecommendationMainFactory.self + ), + dictionaryMainViewFactory: DIContainer.resolve( + type: DictionaryMainViewFactory.self + ), + bookmarkMainFactory: DIContainer.resolve( + type: BookmarkMainFactory.self + ), + myPageMainFactory: DIContainer.resolve( + type: MyPageMainFactory.self + ), + loginFactory: DIContainer.resolve(type: LoginFactory.self) + ) + } + } +} diff --git a/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/FactoryAssembly.swift b/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/FactoryAssembly.swift new file mode 100644 index 00000000..e95bb306 --- /dev/null +++ b/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/FactoryAssembly.swift @@ -0,0 +1,465 @@ +// swiftlint:disable type_body_length +// swiftlint:disable function_body_length + +import MLSAppFeatureInterface +import MLSAuthFeature +import MLSAuthFeatureInterface +import MLSBookmarkFeature +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDictionaryFeature +import MLSDictionaryFeatureInterface +import MLSMyPageFeature +import MLSMyPageFeatureInterface +import MLSRecommendationFeature +import MLSRecommendationFeatureInterface + +public enum FactoryAssembly { + public static func register() { + DIContainer.register(type: ItemFilterBottomSheetFactory.self) { + ItemFilterBottomSheetFactoryImpl() + } + DIContainer.register(type: MonsterFilterBottomSheetFactory.self) { + MonsterFilterBottomSheetFactoryImpl() + } + DIContainer.register(type: SortedBottomSheetFactory.self) { + SortedBottomSheetFactoryImpl() + } + DIContainer.register(type: AddCollectionFactory.self) { + AddCollectionFactoryImpl( + collectionRepository: DIContainer.resolve( + type: CollectionRepository.self + ) + ) + } + DIContainer.register(type: BookmarkModalFactory.self) { + BookmarkModalFactoryImpl( + addCollectionFactory: DIContainer.resolve( + type: AddCollectionFactory.self + ), + collectionRepository: DIContainer.resolve( + type: CollectionRepository.self + ) + ) + } + DIContainer.register(type: LoginFactory.self) { + LoginFactoryImpl( + termsAgreementsFactory: DIContainer.resolve( + type: TermsAgreementFactory.self + ), + appleProvider: DIContainer.resolve( + type: SocialCredentialProvider.self, name: "apple" + ), + kakaoProvider: DIContainer.resolve( + type: SocialCredentialProvider.self, name: "kakao" + ), + socialLoginUseCase: DIContainer.resolve( + type: SocialLoginUseCase.self + ), + userDefaultsRepository: DIContainer.resolve( + type: UserDefaultsRepository.self) + ) + } + DIContainer.register(type: DictionaryDetailFactory.self) { + DictionaryDetailFactoryImpl( + loginFactory: { + DIContainer.resolve(type: LoginFactory.self) + }, + bookmarkModalFactory: DIContainer.resolve( + type: BookmarkModalFactory.self + ), + detailOnBoardingFactory: DIContainer.resolve( + type: DetailOnBoardingFactory.self + ), + appCoordinator: { + DIContainer.resolve(type: AppCoordinatorProtocol.self) + }, + dictionaryDetailAPIRepository: DIContainer.resolve( + type: DictionaryDetailAPIRepository.self + ), + bookmarkRepository: DIContainer + .resolve(type: BookmarkRepository.self), + checkLoginUseCase: DIContainer.resolve( + type: CheckLoginUseCase.self + ), + fetchVisitDictionaryDetailUseCase: DIContainer.resolve( + type: FetchVisitDictionaryDetailUseCase.self + ) + ) + } + DIContainer.register(type: DictionaryMainListFactory.self) { + DictionaryListFactoryImpl( + checkLoginUseCase: DIContainer.resolve( + type: CheckLoginUseCase.self + ), + bookmarkRepository: DIContainer.resolve( + type: BookmarkRepository.self + ), + parseItemFilterResultUseCase: DIContainer.resolve( + type: ParseItemFilterResultUseCase.self + ), + dictionaryListAPIRepository: DIContainer.resolve( + type: DictionaryListAPIRepository.self + ), + itemFilterFactory: DIContainer.resolve( + type: ItemFilterBottomSheetFactory.self + ), + monsterFilterFactory: DIContainer.resolve( + type: MonsterFilterBottomSheetFactory.self + ), + sortedFactory: DIContainer.resolve( + type: SortedBottomSheetFactory.self + ), + bookmarkModalFactory: DIContainer.resolve( + type: BookmarkModalFactory.self + ), + detailFactory: DIContainer.resolve( + type: DictionaryDetailFactory.self + ) + ) { + DIContainer.resolve(type: LoginFactory.self) + } + } + DIContainer.register(type: DictionarySearchResultFactory.self) { + DictionarySearchResultFactoryImpl( + dictionaryListAPIRepository: DIContainer.resolve( + type: DictionaryListAPIRepository.self + ), + recentSearchRepository: DIContainer.resolve( + type: RecentSearchRepository.self + ), + dictionaryMainListFactory: DIContainer.resolve( + type: DictionaryMainListFactory.self + ) + ) + } + DIContainer.register(type: DictionarySearchFactory.self) { + DictionarySearchFactoryImpl( + recentSearchRepository: + DIContainer + .resolve(type: RecentSearchRepository.self), + searchResultFactory: + DIContainer + .resolve(type: DictionarySearchResultFactory.self) + ) + } + DIContainer.register(type: NotificationSettingFactory.self) { + NotificationSettingFactoryImpl( + notificationRepository: DIContainer.resolve( + type: NotificationPermissionRepository.self + ), + authRepository: DIContainer.resolve( + type: AuthAPIRepository.self + ) + ) + } + DIContainer.register(type: DictionaryNotificationFactory.self) { + DictionaryNotificationFactoryImpl( + notificationSettingFactory: + DIContainer + .resolve( + type: NotificationSettingFactory.self + ), + fetchProfileUseCase: + DIContainer + .resolve(type: FetchProfileUseCase.self), + checkNotificationPermissionUseCase: + DIContainer + .resolve(type: CheckNotificationPermissionUseCase.self), + alarmRepository: + DIContainer + .resolve(type: AlarmRepository.self) + ) + } + DIContainer.register(type: DictionaryMainViewFactory.self) { + DictionaryMainViewFactoryImpl( + dictionaryMainListFactory: DIContainer.resolve( + type: DictionaryMainListFactory.self + ), + searchFactory: DIContainer.resolve( + type: DictionarySearchFactory.self + ), + notificationFactory: DIContainer.resolve( + type: DictionaryNotificationFactory.self + ), + loginFactory: DIContainer.resolve(type: LoginFactory.self), + userDefaultsRepository: DIContainer + .resolve( + type: DictionaryUserDefaultsRepository.self + ), + fetchProfileUseCase: DIContainer + .resolve( + type: FetchProfileUseCase.self + ) + ) + } + DIContainer.register(type: OnBoardingNotificationSheetFactory.self) { + OnBoardingNotificationSheetFactoryImpl( + authRepository: DIContainer.resolve( + type: AuthAPIRepository.self + ), + appCoordinator: { + DIContainer.resolve( + type: AppCoordinatorProtocol.self + ) + } + ) + } + DIContainer.register(type: OnBoardingInputFactory.self) { + OnBoardingInputFactoryImpl( + checkEmptyUseCase: DIContainer.resolve( + type: CheckEmptyLevelAndRoleUseCase.self + ), + checkValidLevelUseCase: DIContainer.resolve( + type: CheckValidLevelUseCase.self + ), + authRepository: + DIContainer + .resolve(type: AuthAPIRepository.self), + onBoardingNotificationFactory: DIContainer.resolve( + type: OnBoardingNotificationFactory.self + ), + appCoordinator: { + DIContainer.resolve(type: AppCoordinatorProtocol.self) + } + ) + } + DIContainer.register(type: OnBoardingQuestionFactory.self) { + OnBoardingQuestionFactoryImpl( + onBoardingInputFactory: DIContainer.resolve( + type: OnBoardingInputFactory.self + ) + ) + } + DIContainer.register(type: TermsAgreementFactory.self) { + TermsAgreementFactoryImpl( + onBoardingQuestionFactory: DIContainer.resolve( + type: OnBoardingQuestionFactory.self + ), + socialSignUpUseCase: + DIContainer + .resolve(type: SocialSignUpUseCase.self), + tokenRepository: + DIContainer + .resolve(type: TokenRepository.self) + ) + } + DIContainer.register(type: OnBoardingNotificationFactory.self) { + OnBoardingNotificationFactoryImpl( + onBoardingNotificationSheetFactory: DIContainer.resolve( + type: OnBoardingNotificationSheetFactory.self + ), + appCoordinator: { + DIContainer.resolve( + type: AppCoordinatorProtocol.self + ) + } + ) + } + DIContainer.register(type: BookmarkMainFactory.self) { + BookmarkMainFactoryImpl( + authRepository: + DIContainer + .resolve(type: BookmarkAuthRepository.self), + userDefaultsRepository: + DIContainer + .resolve(type: BookmarkUserDefaultsRepository.self), + onBoardingFactory: + DIContainer + .resolve(type: BookmarkOnBoardingFactory.self), + bookmarkListFactory: + DIContainer + .resolve(type: BookmarkListFactory.self), + collectionListFactory: + DIContainer + .resolve(type: CollectionListFactory.self), + searchFactory: + DIContainer + .resolve(type: DictionarySearchFactory.self), + notificationFactory: DIContainer.resolve( + type: DictionaryNotificationFactory.self + ), + loginFactory: DIContainer.resolve(type: LoginFactory.self) + ) + } + DIContainer.register(type: BookmarkOnBoardingFactory.self) { + BookmarkOnBoardingFactoryImpl() + } + DIContainer.register(type: BookmarkListFactory.self) { + BookmarkListFactoryImpl( + itemFilterFactory: DIContainer.resolve( + type: ItemFilterBottomSheetFactory.self + ), + monsterFilterFactory: DIContainer.resolve( + type: MonsterFilterBottomSheetFactory.self + ), + sortedFactory: DIContainer.resolve( + type: SortedBottomSheetFactory.self + ), + bookmarkModalFactory: DIContainer.resolve( + type: BookmarkModalFactory.self + ), + loginFactory: DIContainer.resolve(type: LoginFactory.self), + dictionaryDetailFactory: DIContainer.resolve( + type: DictionaryDetailFactory.self + ), + collectionEditFactory: DIContainer.resolve( + type: CollectionEditFactory.self + ), + authRepository: DIContainer + .resolve(type: BookmarkAuthRepository.self), + bookmarkRepository: DIContainer + .resolve(type: BookmarkRepository.self), + parseItemFilterResultUseCase: DIContainer.resolve( + type: ParseItemFilterResultUseCase.self + ) + ) + } + DIContainer.register(type: CollectionListFactory.self) { + CollectionListFactoryImpl( + collectionRepository: DIContainer.resolve( + type: CollectionRepository.self + ), + addCollectionFactory: DIContainer.resolve( + type: AddCollectionFactory.self + ), + collectionDetailFactory: DIContainer + .resolve(type: CollectionDetailFactory.self), + sortedBottomSheetFactory: + DIContainer + .resolve(type: SortedBottomSheetFactory.self) + ) + } + DIContainer.register(type: CollectionDetailFactory.self) { + CollectionDetailFactoryImpl( + bookmarkModalFactory: + DIContainer + .resolve(type: BookmarkModalFactory.self), + collectionSettingFactory: + DIContainer + .resolve(type: CollectionSettingFactory.self), + addCollectionFactory: + DIContainer + .resolve(type: AddCollectionFactory.self), + collectionEditFactory: + DIContainer + .resolve(type: CollectionEditFactory.self), + dictionaryDetailFactory: + DIContainer + .resolve(type: DictionaryDetailFactory.self), + collectionRepository: DIContainer + .resolve(type: CollectionRepository.self) + ) + } + DIContainer.register(type: CollectionSettingFactory.self) { + CollectionSettingFactoryImpl() + } + DIContainer.register(type: CollectionEditFactory.self) { + CollectionEditFactoryImpl( + bookmarkModalFactory: DIContainer.resolve( + type: BookmarkModalFactory.self + ) + ) + } + DIContainer.register(type: MyPageMainFactory.self) { + MyPageMainFactoryImpl( + loginFactory: DIContainer.resolve(type: LoginFactory.self), + setProfileFactory: + DIContainer + .resolve(type: SetProfileFactory.self), + customerSupportFactory: + DIContainer + .resolve(type: CustomerSupportFactory.self), + notificationSettingFactory: + DIContainer + .resolve(type: NotificationSettingFactory.self), + setCharacterFactory: + DIContainer + .resolve(type: SetCharacterFactory.self), + fetchProfileUseCase: DIContainer.resolve( + type: FetchProfileUseCase.self + ) + ) + } + DIContainer.register(type: CustomerSupportFactory.self) { + CustomerSupportBaseViewFactoryImpl( + policyFactory: DIContainer.resolve( + type: PolicyFactory.self + ), + alarmRepository: DIContainer.resolve( + type: AlarmRepository.self + ) + ) + } + DIContainer.register(type: SetProfileFactory.self) { + SetProfileFactoryImpl( + selectImageFactory: DIContainer.resolve( + type: SelectImageFactory.self + ), + checkNickNameUseCase: DIContainer.resolve( + type: CheckNickNameUseCase.self + ), + logoutUseCase: DIContainer.resolve(type: LogoutUseCase.self), + withdrawUseCase: DIContainer.resolve( + type: WithdrawUseCase.self + ), + fetchProfileUseCase: DIContainer.resolve( + type: FetchProfileUseCase.self + ), + myPageRepository: DIContainer + .resolve(type: MyPageRepository.self) + ) + } + DIContainer.register(type: SetCharacterFactory.self) { + SetCharacterFactoryImpl( + checkEmptyUseCase: + DIContainer + .resolve(type: CheckEmptyLevelAndRoleUseCase.self), + checkValidLevelUseCase: + DIContainer + .resolve(type: CheckValidLevelUseCase.self), + authRepository: DIContainer + .resolve(type: AuthAPIRepository.self), + myPageRepository: DIContainer + .resolve(type: MyPageRepository.self) + ) + } + DIContainer.register(type: SelectImageFactory.self) { + SelectImageFactoryImpl() + } + DIContainer.register(type: DetailOnBoardingFactory.self) { + DetailOnBoardingFactoryImpl() + } + DIContainer.register(type: PolicyFactory.self) { + PolicyFactoryImpl() + } + DIContainer.register(type: RecommendationMainFactory.self) { + RecommendationMainFactoryImpl( + repository: DIContainer.resolve(type: RecommendationRepository.self), + bookmarkRepository: DIContainer.resolve(type: BookmarkRepository.self), recommendationUserDefaultsRepository: DIContainer.resolve(type: RecommendationUserDefaultsRepository.self), + makeLoginVC: { + DIContainer.resolve(type: LoginFactory.self).make(exitRoute: .pop) + }, + makeCharacterSettingVC: { + DIContainer.resolve(type: SetCharacterFactory.self).make() + }, + makeSearchVC: { + DIContainer.resolve(type: DictionarySearchFactory.self).make() + }, + makeNotificationVC: { + DIContainer.resolve(type: DictionaryNotificationFactory.self).make() + }, + makeDetailVC: { mapId in + DIContainer.resolve(type: DictionaryDetailFactory.self).make( + type: .map, id: mapId, bookmarkRelay: nil, loginRelay: nil + ) + }, + makeBookmarkModalVC: { bookmarkIds, onComplete in + DIContainer.resolve(type: BookmarkModalFactory.self).make( + bookmarkIds: bookmarkIds, onComplete: onComplete + ) + } + ) + } + } +} diff --git a/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/ProviderAssembly.swift b/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/ProviderAssembly.swift new file mode 100644 index 00000000..3f5afac6 --- /dev/null +++ b/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/ProviderAssembly.swift @@ -0,0 +1,30 @@ +import MLSAuthFeature +import MLSAuthFeatureInterface +import MLSCore + +public enum ProviderAssembly { + public static func register() { + DIContainer.register(type: NetworkProvider.self) { + NetworkProviderImpl() + } + DIContainer.register( + type: SocialCredentialProvider.self, + name: "kakao" + ) { + KakaoCredentialProvider() + } + DIContainer.register( + type: SocialCredentialProvider.self, + name: "apple" + ) { + AppleCredentialProvider() + } + DIContainer.register(type: Interceptor.self, name: "tokenInterceptor") { + TokenInterceptor() + } + + DIContainer.register(type: Interceptor.self, name: "authInterceptor") { + AuthInterceptor(tokenRepository: DIContainer.resolve(type: TokenRepository.self), authRepository: { DIContainer.resolve(type: AuthAPIRepository.self) }) + } + } +} diff --git a/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/RepositoryAssembly.swift b/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/RepositoryAssembly.swift new file mode 100644 index 00000000..020b9ed6 --- /dev/null +++ b/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/RepositoryAssembly.swift @@ -0,0 +1,94 @@ +import MLSAppFeatureInterface +import MLSAuthFeature +import MLSAuthFeatureInterface +import MLSBookmarkFeature +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDictionaryFeature +import MLSDictionaryFeatureInterface +import MLSMyPageFeature +import MLSMyPageFeatureInterface +import MLSRecommendationFeature +import MLSRecommendationFeatureInterface + +public enum RepositoryAssembly { + public static func register() { + DIContainer.register(type: TokenRepository.self) { + KeyChainRepositoryImpl() + } + DIContainer.register(type: AuthAPIRepository.self) { + AuthAPIRepositoryImpl( + provider: DIContainer.resolve(type: NetworkProvider.self), + tokenInterceptor: DIContainer.resolve(type: Interceptor.self, name: "tokenInterceptor"), + authInterceptor: DIContainer.resolve(type: Interceptor.self, name: "authInterceptor") + ) + } + DIContainer.register(type: DictionaryDetailAPIRepository.self) { + DictionaryDetailAPIRepositoryImpl( + provider: DIContainer.resolve(type: NetworkProvider.self), + tokenInterceptor: DIContainer.resolve(type: Interceptor.self, name: "tokenInterceptor") + ) + } + DIContainer.register(type: DictionaryListAPIRepository.self) { + DictionaryListAPIRepositoryImpl( + provider: DIContainer.resolve(type: NetworkProvider.self), + tokenInterceptor: DIContainer.resolve(type: Interceptor.self, name: "tokenInterceptor") + ) + } + DIContainer.register(type: BookmarkRepository.self) { + BookmarkRepositoryImpl() + } + DIContainer.register(type: UserDefaultsRepository.self) { + UserDefaultsRepositoryImpl() + } + DIContainer.register(type: DictionaryUserDefaultsRepository.self) { + UserDefaultsRepositoryImpl() + } + DIContainer.register(type: AlarmRepository.self) { + AlarmRepositoryImpl( + provider: DIContainer.resolve(type: NetworkProvider.self), + interceptor: DIContainer + .resolve(type: Interceptor.self, name: "tokenInterceptor") + ) + } + DIContainer.register(type: CollectionRepository.self) { + CollectionRepositoryImpl() + } + DIContainer.register(type: RecommendationRepository.self) { + RecommendationRepositoryImpl() + } + DIContainer.register(type: RecentSearchRepository.self) { + RecentSearchRepositoryImpl() + } + DIContainer.register(type: NotificationPermissionRepository.self) { + NotificationPermissionRepositoryImpl() + } + DIContainer.register(type: MyPageRepository.self) { + MyPageRepositoryImpl( + provider: DIContainer.resolve(type: NetworkProvider.self), + interceptor: DIContainer + .resolve(type: Interceptor.self, name: "tokenInterceptor") + ) + } + DIContainer.register(type: BookmarkAuthRepository.self) { + BookmarkAuthRepositoryImpl( + tokenRepository: DIContainer + .resolve(type: TokenRepository.self), + authAPIRepository: DIContainer + .resolve(type: AuthAPIRepository.self) + ) + } + DIContainer.register(type: BookmarkUserDefaultsRepository.self) { + BookmarkUserDefaultsRepositoryImpl() + } + DIContainer.register(type: AppStoreRepositoryProtocol.self) { + AppStoreRepository() + } + DIContainer.register(type: UpdateSkipRepositoryProtocol.self) { + UpdateSkipRepository() + } + DIContainer.register(type: RecommendationUserDefaultsRepository.self) { + RecommendationUserDefaultsRepositoryImpl() + } + } +} diff --git a/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/UseCaseAssembly.swift b/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/UseCaseAssembly.swift new file mode 100644 index 00000000..65cffb41 --- /dev/null +++ b/MLS/MLSAppFeature/Sources/MLSAppFeature/Dependency/UseCaseAssembly.swift @@ -0,0 +1,93 @@ +import MLSAppFeatureInterface +import MLSAuthFeature +import MLSAuthFeatureInterface +import MLSCore +import MLSDictionaryFeature +import MLSDictionaryFeatureInterface +import MLSMyPageFeature +import MLSMyPageFeatureInterface + +public enum UseCaseAssembly { + public static func register() { + DIContainer.register(type: CheckEmptyLevelAndRoleUseCase.self) { + CheckEmptyLevelAndRoleUseCaseImpl() + } + DIContainer.register(type: CheckValidLevelUseCase.self) { + CheckValidLevelUseCaseImpl() + } + DIContainer.register(type: SocialLoginUseCase.self) { + SocialLoginUseCaseImpl( + authRepository: DIContainer.resolve( + type: AuthAPIRepository.self + ), + tokenRepository: DIContainer.resolve( + type: TokenRepository.self + ), + userDefaultsRepository: DIContainer.resolve( + type: UserDefaultsRepository.self) + ) + } + DIContainer.register(type: SocialSignUpUseCaseImpl.self) { + SocialSignUpUseCaseImpl( + authRepository: DIContainer.resolve(type: AuthAPIRepository.self), + tokenRepository: DIContainer.resolve(type: TokenRepository.self), + userDefaultsRepository: DIContainer.resolve(type: UserDefaultsRepository.self) + ) + } + DIContainer.register(type: CheckNotificationPermissionUseCase.self) { + CheckNotificationPermissionUseCaseImpl() + } + DIContainer.register(type: CheckLoginUseCase.self) { + CheckLoginUseCaseImpl( + authRepository: DIContainer.resolve( + type: AuthAPIRepository.self + ), + tokenRepository: DIContainer.resolve(type: TokenRepository.self) + ) + } + DIContainer.register(type: FetchProfileUseCase.self) { + FetchProfileUseCaseImpl( + repository: DIContainer.resolve(type: MyPageRepository.self) + ) + } + DIContainer.register(type: CheckNickNameUseCase.self) { + CheckNickNameUseCaseImpl() + } + DIContainer.register(type: LogoutUseCase.self) { + LogoutUseCaseImpl( + repository: DIContainer.resolve(type: TokenRepository.self) + ) + } + DIContainer.register(type: WithdrawUseCase.self) { + WithdrawUseCaseImpl( + authRepository: DIContainer.resolve( + type: AuthAPIRepository.self + ), + tokenRepository: DIContainer.resolve(type: TokenRepository.self) + ) + } + DIContainer.register(type: ParseItemFilterResultUseCase.self) { + ParseItemFilterResultUseCaseImpl() + } + DIContainer.register(type: FetchVisitDictionaryDetailUseCase.self) { + FetchVisitDictionaryDetailUseCaseImpl(repository: DIContainer.resolve(type: DictionaryUserDefaultsRepository.self)) + } + DIContainer.register(type: SocialSignUpUseCase.self) { + SocialSignUpUseCaseImpl( + authRepository: DIContainer + .resolve(type: AuthAPIRepository.self), + tokenRepository: DIContainer + .resolve(type: TokenRepository.self), + userDefaultsRepository: DIContainer + .resolve(type: UserDefaultsRepository.self) + ) + } + DIContainer.register(type: UpdateCheckerUseCaseProtocol.self) { + UpdateCheckerUseCase( + appID: AppInfo.appStoreID, + appStoreRepository: DIContainer.resolve(type: AppStoreRepositoryProtocol.self), + skipRepository: DIContainer.resolve(type: UpdateSkipRepositoryProtocol.self) + ) + } + } +} diff --git a/MLS/MLSAppFeature/Sources/MLSAppFeature/Launcher/AppLauncher.swift b/MLS/MLSAppFeature/Sources/MLSAppFeature/Launcher/AppLauncher.swift new file mode 100644 index 00000000..10e6ad59 --- /dev/null +++ b/MLS/MLSAppFeature/Sources/MLSAppFeature/Launcher/AppLauncher.swift @@ -0,0 +1,123 @@ +import UIKit + +import MLSAppFeatureInterface +import MLSAuthFeatureInterface +import MLSCore +import MLSDesignSystem + +import RxSwift + +@MainActor +public final class AppLauncher { + private let disposeBag = DisposeBag() + private var forceUpdateObserver: NSObjectProtocol? + + public init() {} + + public func launch(window: UIWindow?) { + DependencyAssembler.launch(window: window) + + let tokenRepository = DIContainer.resolve(type: TokenRepository.self) + let authRepository = DIContainer.resolve(type: AuthAPIRepository.self) + + switch tokenRepository.fetchToken(type: .refreshToken) { + case .success(let refreshToken): + #if DEBUG + print("refreshToken: \(refreshToken)") + #endif + authRepository.reissueToken(refreshToken: refreshToken) + .observe(on: MainScheduler.instance) + .subscribe( + onNext: { [weak self] _ in + Task { @MainActor in + let coordinator = DIContainer.resolve(type: AppCoordinatorProtocol.self) + coordinator.showMainTab() + self?.checkUpdateIfNeeded() + } + }, + onError: { [weak self] _ in + Task { @MainActor in + let coordinator = DIContainer.resolve(type: AppCoordinatorProtocol.self) + coordinator.showLogin(exitRoute: .home) + self?.checkUpdateIfNeeded() + } + } + ) + .disposed(by: disposeBag) + + case .failure: + let coordinator = DIContainer.resolve(type: AppCoordinatorProtocol.self) + coordinator.showLogin(exitRoute: .home) + checkUpdateIfNeeded() + } + } + + public func register() { + DependencyAssembler.register() + } +} + +// MARK: - Update Check +private extension AppLauncher { + func checkUpdateIfNeeded() { + guard + let versionString = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String, + let currentVersion = Version(versionString: versionString) + else { return } + + Task { @MainActor in + let useCase = DIContainer.resolve(type: UpdateCheckerUseCaseProtocol.self) + guard let status = try? await useCase.checkUpdate(currentVersion: currentVersion) else { return } + + switch status { + case .force: + showForceUpdateAlert() + if let existingObserver = forceUpdateObserver { + NotificationCenter.default.removeObserver(existingObserver) + } + forceUpdateObserver = NotificationCenter.default.addObserver( + forName: UIApplication.didBecomeActiveNotification, + object: nil, + queue: .main + ) { [weak self] _ in + Task { @MainActor in + self?.showForceUpdateAlert() + } + } + case .optional(let latestVersion): + GuideAlertFactory.show( + mainText: "앱 버전이 달라요.\n보다 좋은 서비스를 위해 업데이트 해주세요.", + ctaText: "업데이트 하기", + cancelText: "나중에", + ctaAction: { [weak self] in self?.openAppStore() }, + cancelAction: { useCase.skipUpdate(version: latestVersion) } + ) + case .none: + break + } + } + } + + func showForceUpdateAlert() { + GuideAlertFactory.show( + mainText: "앱 버전이 달라요.\n보다 좋은 서비스를 위해 업데이트 해주세요.", + ctaText: "업데이트 하기", + cancelText: nil, + ctaAction: { [weak self] in self?.openAppStore() } + ) + } + + func openAppStore() { + guard let url = URL(string: AppInfo.appStoreURL) else { + showForceUpdateAlert() + return + } + UIApplication.shared.open(url, options: [:]) { [weak self] success in + if !success { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + self?.showForceUpdateAlert() + } + } + } + } +} diff --git a/MLS/MLSAppFeature/Sources/MLSAppFeature/Launcher/TokenLauncher.swift b/MLS/MLSAppFeature/Sources/MLSAppFeature/Launcher/TokenLauncher.swift new file mode 100644 index 00000000..4af69946 --- /dev/null +++ b/MLS/MLSAppFeature/Sources/MLSAppFeature/Launcher/TokenLauncher.swift @@ -0,0 +1,50 @@ +import Foundation +import os + +import MLSAuthFeatureInterface +import MLSCore + +public final class TokenLauncher { + private let tokenRepository: TokenRepository + + public init(tokenRepository: TokenRepository = DIContainer.resolve(type: TokenRepository.self)) { + self.tokenRepository = tokenRepository + } + + public func didReceiveFCMToken(_ token: String) { + let dataDict: [String: String] = [ + "token": token + ] + + NotificationCenter.default.post( + name: Notification.Name("FCMToken"), + object: nil, + userInfo: dataDict + ) + + let saveResult = tokenRepository.saveToken( + type: .fcmToken, + value: token + ) + + switch saveResult { + case .success: + os_log("fcmToken Save Success: \(token)") + case .failure: + os_log("fcmToken Save Failure") + } + + if case .success(let accessToken) = + tokenRepository.fetchToken(type: .accessToken), + !accessToken.isEmpty { + _ = tokenRepository.saveToken( + type: .fcmToken, + value: token + ) + + os_log("Request to update FCM token on server") + } else { + os_log("Not logged in yet, skipping FCM update to server") + } + } +} diff --git a/MLS/MLSAppFeature/Sources/MLSAppFeatureInterface/Domain/Navigation/AppCoordinatorProtocol.swift b/MLS/MLSAppFeature/Sources/MLSAppFeatureInterface/Domain/Navigation/AppCoordinatorProtocol.swift new file mode 100644 index 00000000..6f04b060 --- /dev/null +++ b/MLS/MLSAppFeature/Sources/MLSAppFeatureInterface/Domain/Navigation/AppCoordinatorProtocol.swift @@ -0,0 +1,15 @@ +import MLSCore +import UIKit + +@MainActor +public protocol AppCoordinatorProtocol: AnyObject { + var window: UIWindow? { get set } + func showMainTab(selectedIndex: Int) + func showLogin(exitRoute: LoginExitRoute) +} + +public extension AppCoordinatorProtocol { + func showMainTab() { + showMainTab(selectedIndex: 0) + } +} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Interface/DictionaryTabControllable.swift b/MLS/MLSAppFeature/Sources/MLSAppFeatureInterface/Domain/Navigation/DictionaryTabControllable.swift similarity index 100% rename from MLS/Presentation/BaseFeature/BaseFeature/Interface/DictionaryTabControllable.swift rename to MLS/MLSAppFeature/Sources/MLSAppFeatureInterface/Domain/Navigation/DictionaryTabControllable.swift diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/DictionaryTabRegistry.swift b/MLS/MLSAppFeature/Sources/MLSAppFeatureInterface/Domain/Navigation/DictionaryTabRegistry.swift similarity index 96% rename from MLS/Presentation/BaseFeature/BaseFeature/Utills/DictionaryTabRegistry.swift rename to MLS/MLSAppFeature/Sources/MLSAppFeatureInterface/Domain/Navigation/DictionaryTabRegistry.swift index 467443c7..e3fef082 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/DictionaryTabRegistry.swift +++ b/MLS/MLSAppFeature/Sources/MLSAppFeatureInterface/Domain/Navigation/DictionaryTabRegistry.swift @@ -1,3 +1,4 @@ +@MainActor public enum DictionaryTabRegistry { private static weak var controller: DictionaryTabControllable? diff --git a/MLS/MLSAuthFeature/Package.swift b/MLS/MLSAuthFeature/Package.swift index 1e1db184..7dea5260 100644 --- a/MLS/MLSAuthFeature/Package.swift +++ b/MLS/MLSAuthFeature/Package.swift @@ -22,6 +22,7 @@ let package = Package( ) ], dependencies: [ + .package(path: "../MLSAppFeature"), .package(path: "../MLSCore"), .package(path: "../MLSDesignSystem"), .package(url: "https://github.com/ReactorKit/ReactorKit.git", from: "3.2.0"), @@ -46,6 +47,7 @@ let package = Package( name: "MLSAuthFeature", dependencies: [ "MLSAuthFeatureInterface", + .product(name: "MLSAppFeatureInterface", package: "MLSAppFeature"), .product(name: "MLSCore", package: "MLSCore"), .product(name: "MLSDesignSystem", package: "MLSDesignSystem"), .product(name: "ReactorKit", package: "ReactorKit"), diff --git a/MLS/Domain/Domain/Interceptor/AuthInterceptor.swift b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/AuthInterceptor.swift similarity index 97% rename from MLS/Domain/Domain/Interceptor/AuthInterceptor.swift rename to MLS/MLSAuthFeature/Sources/MLSAuthFeature/AuthInterceptor.swift index 26ce211a..8a9c5e66 100644 --- a/MLS/Domain/Domain/Interceptor/AuthInterceptor.swift +++ b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/AuthInterceptor.swift @@ -1,6 +1,7 @@ import Foundation -import DomainInterface +import MLSAuthFeatureInterface +import MLSCore public final class AuthInterceptor: Interceptor { private let tokenRepository: TokenRepository diff --git a/MLS/Data/Data/Repository/KeyChainRepositoryImpl.swift b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Data/Repositories/KeyChainRepositoryImpl.swift similarity index 99% rename from MLS/Data/Data/Repository/KeyChainRepositoryImpl.swift rename to MLS/MLSAuthFeature/Sources/MLSAuthFeature/Data/Repositories/KeyChainRepositoryImpl.swift index 287a8ec7..1e190abe 100644 --- a/MLS/Data/Data/Repository/KeyChainRepositoryImpl.swift +++ b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Data/Repositories/KeyChainRepositoryImpl.swift @@ -2,7 +2,7 @@ import Foundation import os import Security -import DomainInterface +import MLSAuthFeatureInterface import RxSwift diff --git a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Data/Repositories/UserDefaultsRepositoryImpl.swift b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Data/Repositories/UserDefaultsRepositoryImpl.swift new file mode 100644 index 00000000..5c771f2c --- /dev/null +++ b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Data/Repositories/UserDefaultsRepositoryImpl.swift @@ -0,0 +1,32 @@ +import Foundation + +import MLSAuthFeatureInterface + +import RxSwift + +public final class UserDefaultsRepositoryImpl: UserDefaultsRepository { + private let platformKey = "platformKey" + + public init() {} + + public func fetchPlatform() -> Observable { + return Observable.create { observer in + if let rawValue = UserDefaults.standard.string(forKey: self.platformKey), + let platform = LoginPlatform(rawValue: rawValue) { + observer.onNext(platform) + } else { + observer.onNext(nil) + } + observer.onCompleted() + return Disposables.create() + } + } + + public func savePlatform(platform: LoginPlatform) -> Completable { + return Completable.create { completable in + UserDefaults.standard.set(platform.rawValue, forKey: self.platformKey) + completable(.completed) + return Disposables.create() + } + } +} diff --git a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/Login/LoginFactoryImpl.swift b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/Login/LoginFactoryImpl.swift index 29d87721..5975d198 100644 --- a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/Login/LoginFactoryImpl.swift +++ b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/Login/LoginFactoryImpl.swift @@ -1,3 +1,4 @@ +import MLSAppFeatureInterface import MLSAuthFeatureInterface import MLSCore diff --git a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingInput/OnBoardingInputFactoryImpl.swift b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingInput/OnBoardingInputFactoryImpl.swift index e1e04104..ad1da9f3 100644 --- a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingInput/OnBoardingInputFactoryImpl.swift +++ b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingInput/OnBoardingInputFactoryImpl.swift @@ -1,3 +1,4 @@ +import MLSAppFeatureInterface import MLSAuthFeatureInterface import MLSCore diff --git a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingInput/OnBoardingInputViewController.swift b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingInput/OnBoardingInputViewController.swift index 0a2feebb..0608e966 100644 --- a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingInput/OnBoardingInputViewController.swift +++ b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingInput/OnBoardingInputViewController.swift @@ -1,5 +1,6 @@ import UIKit +import MLSAppFeatureInterface import MLSAuthFeatureInterface import MLSCore import MLSDesignSystem diff --git a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotification/OnBoardingNotificationFactoryImpl.swift b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotification/OnBoardingNotificationFactoryImpl.swift index bddcc1aa..bc846e0e 100644 --- a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotification/OnBoardingNotificationFactoryImpl.swift +++ b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotification/OnBoardingNotificationFactoryImpl.swift @@ -1,3 +1,4 @@ +import MLSAppFeatureInterface import MLSAuthFeatureInterface import MLSCore diff --git a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotification/OnBoardingNotificationViewController.swift b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotification/OnBoardingNotificationViewController.swift index 027544fd..f0f71c92 100644 --- a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotification/OnBoardingNotificationViewController.swift +++ b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotification/OnBoardingNotificationViewController.swift @@ -1,6 +1,7 @@ import UIKit import UserNotifications +import MLSAppFeatureInterface import MLSAuthFeatureInterface import MLSCore diff --git a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotificationSheet/OnBoardingNotificationSheetFactoryImpl.swift b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotificationSheet/OnBoardingNotificationSheetFactoryImpl.swift index 886ebcc4..81465827 100644 --- a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotificationSheet/OnBoardingNotificationSheetFactoryImpl.swift +++ b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotificationSheet/OnBoardingNotificationSheetFactoryImpl.swift @@ -1,3 +1,4 @@ +import MLSAppFeatureInterface import MLSAuthFeatureInterface import MLSCore import MLSDesignSystem diff --git a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotificationSheet/OnBoardingNotificationSheetViewController.swift b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotificationSheet/OnBoardingNotificationSheetViewController.swift index 971d5098..d7279549 100644 --- a/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotificationSheet/OnBoardingNotificationSheetViewController.swift +++ b/MLS/MLSAuthFeature/Sources/MLSAuthFeature/Presentation/OnBoardingNotificationSheet/OnBoardingNotificationSheetViewController.swift @@ -1,5 +1,6 @@ import UIKit +import MLSAppFeatureInterface import MLSAuthFeatureInterface import MLSCore import MLSDesignSystem diff --git a/MLS/MLSAuthFeature/Sources/MLSAuthFeatureInterface/AppCoordinatorProtocol.swift b/MLS/MLSAuthFeature/Sources/MLSAuthFeatureInterface/AppCoordinatorProtocol.swift deleted file mode 100644 index 24f9b2ca..00000000 --- a/MLS/MLSAuthFeature/Sources/MLSAuthFeatureInterface/AppCoordinatorProtocol.swift +++ /dev/null @@ -1,7 +0,0 @@ -import UIKit - -public protocol AppCoordinatorProtocol: AnyObject { - var window: UIWindow? { get set } - func showMainTab() - func showLogin(exitRoute: LoginExitRoute) -} diff --git a/MLS/MLSAuthFeatureExample/SceneDelegate.swift b/MLS/MLSAuthFeatureExample/SceneDelegate.swift index 0e253ecd..5a1752c3 100644 --- a/MLS/MLSAuthFeatureExample/SceneDelegate.swift +++ b/MLS/MLSAuthFeatureExample/SceneDelegate.swift @@ -1,5 +1,6 @@ import UIKit +import MLSAppFeatureInterface import MLSAuthFeature import MLSAuthFeatureInterface import MLSAuthFeatureTesting diff --git a/MLS/Core/Core.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MLS/MLSBookmarkFeature/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata similarity index 100% rename from MLS/Core/Core.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to MLS/MLSBookmarkFeature/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/MLS/MLSBookmarkFeature/Package.swift b/MLS/MLSBookmarkFeature/Package.swift new file mode 100644 index 00000000..884b025b --- /dev/null +++ b/MLS/MLSBookmarkFeature/Package.swift @@ -0,0 +1,87 @@ +// swift-tools-version: 6.2 +import PackageDescription + +let package = Package( + name: "MLSBookmarkFeature", + platforms: [.iOS(.v15)], + products: [ + .library( + name: "MLSBookmarkFeatureInterface", + targets: ["MLSBookmarkFeatureInterface"] + ), + .library( + name: "MLSBookmarkFeature", + targets: ["MLSBookmarkFeature"] + ), + .library( + name: "MLSBookmarkFeatureTesting", + targets: ["MLSBookmarkFeatureTesting"] + ) + ], + dependencies: [ + .package(path: "../MLSAppFeature"), + .package(path: "../MLSAuthFeature"), + .package(path: "../MLSCore"), + .package(path: "../MLSDesignSystem"), + .package(path: "../MLSDictionaryFeature"), + .package(url: "https://github.com/ReactorKit/ReactorKit.git", from: "3.2.0"), + .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "6.7.0"), + .package(url: "https://github.com/SnapKit/SnapKit.git", from: "5.7.1") + ], + targets: [ + // Interface 모듈 (팩토리/리포지토리 프로토콜 + 엔티티) + .target( + name: "MLSBookmarkFeatureInterface", + dependencies: [ + .product(name: "MLSCore", package: "MLSCore"), + .product(name: "MLSDesignSystem", package: "MLSDesignSystem"), + .product(name: "MLSDictionaryFeatureInterface", package: "MLSDictionaryFeature"), + .product(name: "RxSwift", package: "RxSwift"), + .product(name: "RxRelay", package: "RxSwift") + ], + swiftSettings: [.swiftLanguageMode(.v5)] + ), + // Feature 모듈 (Presentation + Domain + Data 구현체) + .target( + name: "MLSBookmarkFeature", + dependencies: [ + "MLSBookmarkFeatureInterface", + .product(name: "MLSAppFeatureInterface", package: "MLSAppFeature"), + .product(name: "MLSAuthFeatureInterface", package: "MLSAuthFeature"), + .product(name: "MLSCore", package: "MLSCore"), + .product(name: "MLSDesignSystem", package: "MLSDesignSystem"), + .product(name: "MLSDictionaryFeatureInterface", package: "MLSDictionaryFeature"), + .product(name: "ReactorKit", package: "ReactorKit"), + .product(name: "RxSwift", package: "RxSwift"), + .product(name: "RxCocoa", package: "RxSwift"), + .product(name: "RxRelay", package: "RxSwift"), + .product(name: "SnapKit", package: "SnapKit") + ], + swiftSettings: [.swiftLanguageMode(.v5)] + ), + // Testing 모듈 (Mock + Stub 객체) + .target( + name: "MLSBookmarkFeatureTesting", + dependencies: [ + "MLSBookmarkFeatureInterface", + .product(name: "MLSAuthFeatureInterface", package: "MLSAuthFeature"), + .product(name: "MLSDictionaryFeatureInterface", package: "MLSDictionaryFeature"), + .product(name: "MLSCore", package: "MLSCore"), + .product(name: "MLSDesignSystem", package: "MLSDesignSystem"), + .product(name: "RxSwift", package: "RxSwift"), + .product(name: "RxRelay", package: "RxSwift") + ], + swiftSettings: [.swiftLanguageMode(.v5)] + ), + // Tests 모듈 + .testTarget( + name: "MLSBookmarkFeatureTests", + dependencies: [ + "MLSBookmarkFeature", + "MLSBookmarkFeatureInterface", + "MLSBookmarkFeatureTesting", + .product(name: "RxBlocking", package: "RxSwift") + ] + ) + ] +) diff --git a/MLS/Data/Data/Network/DTO/BookmarkDTO/BookmarkDTO.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/DTOs/BookmarkDTO.swift similarity index 57% rename from MLS/Data/Data/Network/DTO/BookmarkDTO/BookmarkDTO.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/DTOs/BookmarkDTO.swift index 277fc720..c2b8b986 100644 --- a/MLS/Data/Data/Network/DTO/BookmarkDTO/BookmarkDTO.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/DTOs/BookmarkDTO.swift @@ -1,17 +1,16 @@ -import DomainInterface +import MLSBookmarkFeatureInterface +import MLSDictionaryFeatureInterface -public struct BookmarkDTO: Decodable { - public let bookmarkId: Int - public let originalId: Int - public let name: String - public let imageUrl: String - public let type: String - public let level: Int? +struct BookmarkDTO: Decodable { + let bookmarkId: Int + let originalId: Int + let name: String + let imageUrl: String + let type: String + let level: Int? - public func toDomain() -> BookmarkResponse? { - guard let type = DictionaryItemType(rawValue: type) else { - return nil - } + func toDomain() -> BookmarkResponse? { + guard let type = DictionaryItemType(rawValue: type) else { return nil } return BookmarkResponse( name: name, bookmarkId: bookmarkId, diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/DTOs/CollectionListResponseDTO.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/DTOs/CollectionListResponseDTO.swift new file mode 100644 index 00000000..b3494c56 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/DTOs/CollectionListResponseDTO.swift @@ -0,0 +1,17 @@ +import MLSBookmarkFeatureInterface + +struct CollectionListResponseDTO: Decodable { + let collectionId: Int + let name: String + let createdAt: [Int] + let recentBookmarks: [BookmarkDTO] + + func toDomain() -> CollectionResponse { + return CollectionResponse( + collectionId: collectionId, + name: name, + createdAt: createdAt, + recentBookmarks: recentBookmarks.toDomain() + ) + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/DTOs/SetBookmarkDTO.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/DTOs/SetBookmarkDTO.swift new file mode 100644 index 00000000..676b62bf --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/DTOs/SetBookmarkDTO.swift @@ -0,0 +1,9 @@ +struct SetBookmarkDTO: Decodable { + let bookmarkId: Int + let bookmarkType: String + let resourceId: Int + + func toDomain() -> Int { + return self.bookmarkId + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Endpoints/BookmarkEndPoint.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Endpoints/BookmarkEndPoint.swift new file mode 100644 index 00000000..ca79ae5d --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Endpoints/BookmarkEndPoint.swift @@ -0,0 +1,37 @@ +import MLSCore + +enum BookmarkEndPoint { + private static let base = "https://mapleland.2megabytes.me" + + static func setBookmark(body: Encodable) -> ResponsableEndPoint { + .init(baseURL: base, path: "/api/v1/bookmarks", method: .POST, body: body) + } + + static func deleteBookmark(bookmarkId: Int) -> ResponsableEndPoint { + .init(baseURL: base, path: "/api/v1/bookmarks/\(bookmarkId)", method: .DELETE) + } + + static func fetchBookmark(query: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { + .init(baseURL: base, path: "/api/v1/bookmarks", method: .GET, query: query) + } + + static func fetchMonsterBookmark(query: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { + .init(baseURL: base, path: "/api/v1/bookmarks/monsters", method: .GET, query: query) + } + + static func fetchNPCBookmark(query: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { + .init(baseURL: base, path: "/api/v1/bookmarks/npcs", method: .GET, query: query) + } + + static func fetchQuestBookmark(query: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { + .init(baseURL: base, path: "/api/v1/bookmarks/quests", method: .GET, query: query) + } + + static func fetchItemBookmark(query: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { + .init(baseURL: base, path: "/api/v1/bookmarks/items", method: .GET, query: query) + } + + static func fetchMapBookmark(query: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { + .init(baseURL: base, path: "/api/v1/bookmarks/maps", method: .GET, query: query) + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Endpoints/CollectionEndPoint.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Endpoints/CollectionEndPoint.swift new file mode 100644 index 00000000..a5821920 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Endpoints/CollectionEndPoint.swift @@ -0,0 +1,29 @@ +import MLSCore + +enum CollectionEndPoint { + private static let base = "https://mapleland.2megabytes.me" + + static func fetchCollectionList(query: Encodable) -> ResponsableEndPoint<[CollectionListResponseDTO]> { + .init(baseURL: base, path: "/api/v1/collections", method: .GET, query: query) + } + + static func createCollectionList(body: Encodable) -> EndPoint { + .init(baseURL: base, path: "/api/v1/collections", method: .POST, body: body) + } + + static func fetchCollection(id: Int) -> ResponsableEndPoint<[BookmarkDTO]> { + .init(baseURL: base, path: "/api/v1/collections/\(id)/bookmarks", method: .GET) + } + + static func setCollectionName(id: Int, body: Encodable) -> EndPoint { + .init(baseURL: base, path: "/api/v1/collections/\(id)", method: .PUT, body: body) + } + + static func deleteCollection(id: Int) -> EndPoint { + .init(baseURL: base, path: "/api/v1/collections/\(id)", method: .DELETE) + } + + static func addCollectionAndBookmark(body: Encodable) -> EndPoint { + .init(baseURL: base, path: "/api/v1/bookmark-collections", method: .POST, body: body) + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Repositories/BookmarkAuthRepositoryImpl.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Repositories/BookmarkAuthRepositoryImpl.swift new file mode 100644 index 00000000..35e6aa80 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Repositories/BookmarkAuthRepositoryImpl.swift @@ -0,0 +1,33 @@ +import MLSAuthFeatureInterface +import MLSBookmarkFeatureInterface +import RxSwift + +public final class BookmarkAuthRepositoryImpl: BookmarkAuthRepository { + private let tokenRepository: TokenRepository + private let authAPIRepository: AuthAPIRepository + + public init(tokenRepository: TokenRepository, authAPIRepository: AuthAPIRepository) { + self.tokenRepository = tokenRepository + self.authAPIRepository = authAPIRepository + } + + public func isLoggedIn() -> Observable { + switch tokenRepository.fetchToken(type: .refreshToken) { + case .success(let token): + guard !token.isEmpty else { return .just(false) } + return authAPIRepository.reissueToken(refreshToken: token) + .map { [weak self] response -> Bool in + guard let self else { return false } + let accessResult = self.tokenRepository.saveToken(type: .accessToken, value: response.accessToken) + let refreshResult = self.tokenRepository.saveToken(type: .refreshToken, value: response.refreshToken) + switch (accessResult, refreshResult) { + case (.success, .success): return true + default: return false + } + } + .catch { _ in .just(false) } + case .failure: + return .just(false) + } + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Repositories/BookmarkRepositoryImpl.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Repositories/BookmarkRepositoryImpl.swift new file mode 100644 index 00000000..5a97ece0 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Repositories/BookmarkRepositoryImpl.swift @@ -0,0 +1,88 @@ +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface + +import RxSwift + +public final class BookmarkRepositoryImpl: BookmarkRepository { + private let provider: NetworkProvider + private let interceptor: Interceptor + + public init() { + self.provider = NetworkProviderImpl() + self.interceptor = TokenInterceptor() + } + + public func setBookmark(resourceId: Int, type: DictionaryItemType) -> Observable { + let endpoint = BookmarkEndPoint.setBookmark(body: SetBookmarkBody(bookmarkType: type.rawValue, resourceId: resourceId)) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + .map { $0.toDomain() } + } + + public func deleteBookmark(bookmarkId: Int) -> Observable { + let endpoint = BookmarkEndPoint.deleteBookmark(bookmarkId: bookmarkId) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + .map { $0.toBookmarkDomain() } + } + + public func fetchBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + let endpoint = BookmarkEndPoint.fetchBookmark(query: SortQuery(sort: sort)) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + .map { $0.toDomain() } + } + + public func fetchMonsterBookmark(minLevel: Int?, maxLevel: Int?, sort: String?) -> Observable<[BookmarkResponse]> { + let endpoint = BookmarkEndPoint.fetchMonsterBookmark(query: MonsterQuery(minLevel: minLevel, maxLevel: maxLevel, sort: sort)) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + .map { $0.toDomain() } + } + + public func fetchNPCBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + let endpoint = BookmarkEndPoint.fetchNPCBookmark(query: SortQuery(sort: sort)) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + .map { $0.toDomain() } + } + + public func fetchQuestBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + let endpoint = BookmarkEndPoint.fetchQuestBookmark(query: SortQuery(sort: sort)) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + .map { $0.toDomain() } + } + + public func fetchItemBookmark(jobId: Int?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, sort: String?) -> Observable<[BookmarkResponse]> { + let endpoint = BookmarkEndPoint.fetchItemBookmark(query: ItemQuery(jobId: jobId, minLevel: minLevel, maxLevel: maxLevel, categoryIds: categoryIds, sort: sort)) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + .map { $0.toDomain() } + } + + public func fetchMapBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + let endpoint = BookmarkEndPoint.fetchMapBookmark(query: SortQuery(sort: sort)) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + .map { $0.toDomain() } + } +} + +private extension BookmarkRepositoryImpl { + struct SortQuery: Encodable { + let sort: String? + } + + struct SetBookmarkBody: Encodable { + let bookmarkType: String + let resourceId: Int + } + + struct MonsterQuery: Encodable { + let minLevel: Int? + let maxLevel: Int? + let sort: String? + } + + struct ItemQuery: Encodable { + let jobId: Int? + let minLevel: Int? + let maxLevel: Int? + let categoryIds: [Int]? + let sort: String? + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Repositories/BookmarkUserDefaultsRepositoryImpl.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Repositories/BookmarkUserDefaultsRepositoryImpl.swift new file mode 100644 index 00000000..20e30838 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Repositories/BookmarkUserDefaultsRepositoryImpl.swift @@ -0,0 +1,32 @@ +import Foundation + +import MLSBookmarkFeatureInterface + +import RxSwift + +public final class BookmarkUserDefaultsRepositoryImpl: BookmarkUserDefaultsRepository { + private let key = "bookmark_onboarding_visited" + + public init() {} + + public func hasVisitedOnboarding() -> Observable { + let hasVisited = UserDefaults.standard.bool(forKey: key) + if !hasVisited { + UserDefaults.standard.set(true, forKey: key) + return .just(false) + } + return .just(true) + } + + public func markOnboardingVisited() -> Completable { + return Completable.create { [weak self] observer in + guard let self else { + observer(.completed) + return Disposables.create() + } + UserDefaults.standard.set(true, forKey: self.key) + observer(.completed) + return Disposables.create() + } + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Repositories/CollectionRepositoryImpl.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Repositories/CollectionRepositoryImpl.swift new file mode 100644 index 00000000..57a7e926 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Data/Repositories/CollectionRepositoryImpl.swift @@ -0,0 +1,64 @@ +import MLSBookmarkFeatureInterface +import MLSCore +import RxSwift + +public final class CollectionRepositoryImpl: CollectionRepository { + private let provider: NetworkProvider + private let interceptor: Interceptor + + public init() { + self.provider = NetworkProviderImpl() + self.interceptor = TokenInterceptor() + } + + public func fetchCollectionList(sort: String?) -> Observable<[CollectionResponse]> { + let endpoint = CollectionEndPoint.fetchCollectionList(query: FetchListQuery(sort: sort)) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + .map { $0.map { $0.toDomain() } } + } + + public func createCollectionList(name: String) -> Completable { + let endpoint = CollectionEndPoint.createCollectionList(body: CreateBody(name: name)) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + } + + public func fetchCollection(id: Int) -> Observable<[BookmarkResponse]> { + let endpoint = CollectionEndPoint.fetchCollection(id: id) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + .map { $0.toDomain() } + } + + public func updateCollectionName(collectionId: Int, name: String) -> Completable { + let endpoint = CollectionEndPoint.setCollectionName(id: collectionId, body: UpdateNameBody(name: name)) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + } + + public func deleteCollection(collectionId: Int) -> Completable { + let endpoint = CollectionEndPoint.deleteCollection(id: collectionId) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + } + + public func addCollectionAndBookmark(collectionIds: [Int], bookmarkIds: [Int]) -> Completable { + let endpoint = CollectionEndPoint.addCollectionAndBookmark(body: AddBody(collectionIds: collectionIds, bookmarkIds: bookmarkIds)) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + } +} + +private extension CollectionRepositoryImpl { + struct FetchListQuery: Encodable { + let sort: String? + } + + struct CreateBody: Encodable { + let name: String + } + + struct UpdateNameBody: Encodable { + let name: String + } + + struct AddBody: Encodable { + let collectionIds: [Int] + let bookmarkIds: [Int] + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/AddCollection/AddCollectionFactoryImpl.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/AddCollection/AddCollectionFactoryImpl.swift new file mode 100644 index 00000000..70bc2ba6 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/AddCollection/AddCollectionFactoryImpl.swift @@ -0,0 +1,19 @@ +import MLSBookmarkFeatureInterface +import MLSCore + +public final class AddCollectionFactoryImpl: AddCollectionFactory { + private let collectionRepository: CollectionRepository + + public init(collectionRepository: CollectionRepository) { + self.collectionRepository = collectionRepository + } + + public func make(collection: CollectionResponse?) -> BaseViewController { + let viewController = AddCollectionViewController() + viewController.reactor = AddCollectionReactor( + collection: collection, + collectionRepository: collectionRepository + ) + return viewController + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/AddCollection/AddCollectionReactor.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/AddCollection/AddCollectionReactor.swift new file mode 100644 index 00000000..15b8f3a0 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/AddCollection/AddCollectionReactor.swift @@ -0,0 +1,80 @@ +import MLSBookmarkFeatureInterface +import ReactorKit +import RxSwift + +final class AddCollectionReactor: Reactor { + enum Route { + case dismiss + case dismissWithData + case createError + case updateError + } + + enum Action { + case inputTextChanged(String?) + case backButtonTapped + case completeButtonTapped + } + + enum Mutation { + case saveInput(String) + case setError(Bool) + case setButtonEnabled(Bool) + case toNavigate(Route) + } + + struct State { + @Pulse var route: Route? + var collection: CollectionResponse? + var inputText: String? + var isError: Bool = false + var isButtonEnabled: Bool = false + } + + var initialState: State + private let collectionRepository: CollectionRepository + + init(collection: CollectionResponse?, collectionRepository: CollectionRepository) { + self.initialState = State(collection: collection, inputText: collection?.name) + self.collectionRepository = collectionRepository + } + + func mutate(action: Action) -> Observable { + switch action { + case .inputTextChanged(let text): + let trimmed = text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" + return Observable.from([.setButtonEnabled(!trimmed.isEmpty), .saveInput(trimmed)]) + + case .backButtonTapped: + return .just(.toNavigate(.dismiss)) + + case .completeButtonTapped: + guard let text = currentState.inputText else { return .empty() } + let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines) + if trimmed.count > 18 { + return .just(.setError(true)) + } + if currentState.collection == nil { + return collectionRepository.createCollectionList(name: trimmed) + .andThen(.just(.toNavigate(.dismissWithData))) + .catch { _ in .just(.toNavigate(.createError)) } + } else { + guard let id = currentState.collection?.collectionId else { return .empty() } + return collectionRepository.updateCollectionName(collectionId: id, name: trimmed) + .andThen(.just(.toNavigate(.dismissWithData))) + .catch { _ in .just(.toNavigate(.updateError)) } + } + } + } + + func reduce(state: State, mutation: Mutation) -> State { + var newState = state + switch mutation { + case .saveInput(let text): newState.inputText = text + case .setError(let isError): newState.isError = isError + case .setButtonEnabled(let isEnabled): newState.isButtonEnabled = isEnabled + case .toNavigate(let route): newState.route = route + } + return newState + } +} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionView.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/AddCollection/AddCollectionView.swift similarity index 87% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionView.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/AddCollection/AddCollectionView.swift index 385f3acc..700a0830 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionView.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/AddCollection/AddCollectionView.swift @@ -1,13 +1,9 @@ -import UIKit - -import BookmarkFeatureInterface -import DesignSystem -import DomainInterface - +import MLSBookmarkFeatureInterface +import MLSDesignSystem import SnapKit +import UIKit -public final class AddCollectionView: UIView { - // MARK: - Type +final class AddCollectionView: UIView { enum Constant { static let radius: CGFloat = 8 static let titleTopMargin: CGFloat = 20 @@ -23,17 +19,15 @@ public final class AddCollectionView: UIView { static let buttonBottomMargin: CGFloat = 10 } - // MARK: - Properties - public var addButtonBottomConstraint: Constraint? + var addButtonBottomConstraint: Constraint? - // MARK: - Components private let titleLabel: UILabel = { let label = UILabel() label.attributedText = .makeStyledString(font: .h_xl_b, text: "컬렉션", alignment: .left) return label }() - public let backButton: UIButton = { + let backButton: UIButton = { let button = UIButton() button.setImage(DesignSystemAsset.image(named: "largeX"), for: .normal) return button @@ -58,7 +52,7 @@ public final class AddCollectionView: UIView { return view }() - private let nameLabel: UILabel = { + private var nameLabel: UILabel = { let label = UILabel() label.attributedText = .makeStyledString(font: .sub_m_sb, text: "컬렉션 이름 입력", alignment: .left) return label @@ -77,12 +71,12 @@ public final class AddCollectionView: UIView { private let iconView: UIImageView = { let view = UIImageView() - view.image = DesignSystemAsset.image(named: "bookmark")?.withRenderingMode(.alwaysTemplate) + view.image = DesignSystemAsset.image(named: "bookmark").withRenderingMode(.alwaysTemplate) view.tintColor = .neutral300 return view }() - public let inputTextField: UITextField = { + let inputTextField: UITextField = { let textField = UITextField() textField.backgroundColor = .clearMLS textField.tintColor = .primary300 @@ -96,23 +90,18 @@ public final class AddCollectionView: UIView { }() private let errorMessage = ErrorMessage(message: "폴더명은 18자 이하로 입력해주세요.") + let completeButton = CommonButton(style: .normal, title: "완료", disabledTitle: "완료") - public let completeButton = CommonButton(style: .normal, title: "완료", disabledTitle: "완료") - - // MARK: - init - public init() { + init() { super.init(frame: .zero) addViews() setupConstraints() } @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + required init?(coder: NSCoder) { fatalError() } } -// MARK: - SetUp private extension AddCollectionView { func addViews() { addSubview(titleLabel) @@ -175,7 +164,7 @@ extension AddCollectionView { } func updateTextField(text: String?) { - if let text = text { + if let text { inputTextField.attributedText = .makeStyledString(font: .b_s_sb, text: text, color: .textColor, alignment: .left) } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionViewController.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/AddCollection/AddCollectionViewController.swift similarity index 86% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionViewController.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/AddCollection/AddCollectionViewController.swift index fd32d227..a82dcf12 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionViewController.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/AddCollection/AddCollectionViewController.swift @@ -1,23 +1,16 @@ -import UIKit - -import BaseFeature -import BookmarkFeatureInterface -import DesignSystem - +import MLSCore +import MLSDesignSystem import ReactorKit -import RxKeyboard import RxSwift import SnapKit +import UIKit -public final class AddCollectionViewController: BaseViewController, View { - public typealias Reactor = AddCollectionModalReactor - - // MARK: - Properties - public var disposeBag = DisposeBag() +final class AddCollectionViewController: BaseViewController, View { + typealias Reactor = AddCollectionReactor - public let dismissed = PublishSubject() + var disposeBag = DisposeBag() + let dismissed = PublishSubject() - // MARK: - Components private let mainView = AddCollectionView() private let addCollectionContainer = UIView() private let dimmedBackgroundView: UIView = { @@ -29,38 +22,32 @@ public final class AddCollectionViewController: BaseViewController, View { return view }() - // MARK: - Init - override public init() { + override init() { super.init() modalPresentationStyle = .overFullScreen modalTransitionStyle = .crossDissolve } @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + required init?(coder: NSCoder) { fatalError() } - // MARK: - Lifecycle - override public func viewDidLoad() { + override func viewDidLoad() { super.viewDidLoad() setupViews() setupConstraints() setupGestures() } - override public func viewDidAppear(_ animated: Bool) { + override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) setupKeyboard() presentWithAnimation() } } -// MARK: - Setup private extension AddCollectionViewController { func setupViews() { view.backgroundColor = .clear - view.addSubview(dimmedBackgroundView) view.addSubview(addCollectionContainer) addCollectionContainer.addSubview(mainView) @@ -68,9 +55,7 @@ private extension AddCollectionViewController { addCollectionContainer.layer.cornerRadius = 16 addCollectionContainer.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] addCollectionContainer.backgroundColor = .whiteMLS - addCollectionContainer.isHidden = true - mainView.isHidden = false } func setupConstraints() { @@ -90,7 +75,6 @@ private extension AddCollectionViewController { func setupGestures() { let tapGesture = UITapGestureRecognizer() dimmedBackgroundView.addGestureRecognizer(tapGesture) - tapGesture.rx.event .withUnretained(self) .subscribe(onNext: { owner, _ in @@ -106,14 +90,8 @@ private extension AddCollectionViewController { } } -// MARK: - Bind extension AddCollectionViewController { - public func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindState(reactor: reactor) - } - - private func bindUserActions(reactor: Reactor) { + func bind(reactor: Reactor) { mainView.inputTextField.rx.text .distinctUntilChanged() .map { Reactor.Action.inputTextChanged($0) } @@ -129,9 +107,7 @@ extension AddCollectionViewController { .map { Reactor.Action.completeButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - } - private func bindState(reactor: Reactor) { reactor.state .map(\.inputText) .take(1) @@ -195,7 +171,6 @@ extension AddCollectionViewController { } } -// MARK: - Animation private extension AddCollectionViewController { func presentWithAnimation() { dimmedBackgroundView.alpha = 0 @@ -215,7 +190,6 @@ private extension AddCollectionViewController { func dismissWithAnimation(withData: Bool, completion: (() -> Void)? = nil) { mainView.endEditing(true) - UIView.animate(withDuration: 0.25, animations: { self.dimmedBackgroundView.alpha = 0 self.addCollectionContainer.transform = CGAffineTransform(translationX: 0, y: 400) @@ -225,7 +199,7 @@ private extension AddCollectionViewController { if withData { guard let text = self.mainView.inputTextField.text else { return } - self.dismissed.onNext((text)) + self.dismissed.onNext(text) self.dismissed.onCompleted() } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListFactoryImpl.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkList/BookmarkListFactoryImpl.swift similarity index 52% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListFactoryImpl.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkList/BookmarkListFactoryImpl.swift index edb8ceb6..742b6cfc 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListFactoryImpl.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkList/BookmarkListFactoryImpl.swift @@ -1,8 +1,7 @@ -import AuthFeatureInterface -import BaseFeature -import BookmarkFeatureInterface -import DictionaryFeatureInterface -import DomainInterface +import MLSAuthFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface public final class BookmarkListFactoryImpl: BookmarkListFactory { private let itemFilterFactory: ItemFilterBottomSheetFactory @@ -12,15 +11,8 @@ public final class BookmarkListFactoryImpl: BookmarkListFactory { private let loginFactory: LoginFactory private let dictionaryDetailFactory: DictionaryDetailFactory private let collectionEditFactory: CollectionEditFactory - - private let setBookmarkUseCase: SetBookmarkUseCase - private let fetchProfileUseCase: FetchProfileUseCase - private let fetchBookmarkUseCase: FetchBookmarkUseCase - private let fetchMonsterBookmarkUseCase: FetchMonsterBookmarkUseCase - private let fetchItemBookmarkUseCase: FetchItemBookmarkUseCase - private let fetchNPCBookmarkUseCase: FetchNPCBookmarkUseCase - private let fetchQuestBookmarkUseCase: FetchQuestBookmarkUseCase - private let fetchMapBookmarkUseCase: FetchMapBookmarkUseCase + private let authRepository: BookmarkAuthRepository + private let bookmarkRepository: BookmarkRepository private let parseItemFilterResultUseCase: ParseItemFilterResultUseCase public init( @@ -31,14 +23,8 @@ public final class BookmarkListFactoryImpl: BookmarkListFactory { loginFactory: LoginFactory, dictionaryDetailFactory: DictionaryDetailFactory, collectionEditFactory: CollectionEditFactory, - setBookmarkUseCase: SetBookmarkUseCase, - fetchProfileUseCase: FetchProfileUseCase, - fetchBookmarkUseCase: FetchBookmarkUseCase, - fetchMonsterBookmarkUseCase: FetchMonsterBookmarkUseCase, - fetchItemBookmarkUseCase: FetchItemBookmarkUseCase, - fetchNPCBookmarkUseCase: FetchNPCBookmarkUseCase, - fetchQuestBookmarkUseCase: FetchQuestBookmarkUseCase, - fetchMapBookmarkUseCase: FetchMapBookmarkUseCase, + authRepository: BookmarkAuthRepository, + bookmarkRepository: BookmarkRepository, parseItemFilterResultUseCase: ParseItemFilterResultUseCase ) { self.itemFilterFactory = itemFilterFactory @@ -48,28 +34,16 @@ public final class BookmarkListFactoryImpl: BookmarkListFactory { self.loginFactory = loginFactory self.dictionaryDetailFactory = dictionaryDetailFactory self.collectionEditFactory = collectionEditFactory - self.setBookmarkUseCase = setBookmarkUseCase - self.fetchProfileUseCase = fetchProfileUseCase - self.fetchBookmarkUseCase = fetchBookmarkUseCase - self.fetchNPCBookmarkUseCase = fetchNPCBookmarkUseCase - self.fetchMonsterBookmarkUseCase = fetchMonsterBookmarkUseCase - self.fetchItemBookmarkUseCase = fetchItemBookmarkUseCase - self.fetchQuestBookmarkUseCase = fetchQuestBookmarkUseCase - self.fetchMapBookmarkUseCase = fetchMapBookmarkUseCase + self.authRepository = authRepository + self.bookmarkRepository = bookmarkRepository self.parseItemFilterResultUseCase = parseItemFilterResultUseCase } public func make(type: DictionaryType, listType: DictionaryMainViewType) -> BaseViewController { let reactor = BookmarkListReactor( type: type, - fetchProfileUseCase: fetchProfileUseCase, - setBookmarkUseCase: setBookmarkUseCase, - fetchBookmarkUseCase: fetchBookmarkUseCase, - fetchMonsterBookmarkUseCase: fetchMonsterBookmarkUseCase, - fetchItemBookmarkUseCase: fetchItemBookmarkUseCase, - fetchNPCBookmarkUseCase: fetchNPCBookmarkUseCase, - fetchQuestBookmarkUseCase: fetchQuestBookmarkUseCase, - fetchMapBookmarkUseCase: fetchMapBookmarkUseCase, + authRepository: authRepository, + bookmarkRepository: bookmarkRepository, parseItemFilterResultUseCase: parseItemFilterResultUseCase ) let viewController = BookmarkListViewController( diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkList/BookmarkListReactor.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkList/BookmarkListReactor.swift new file mode 100644 index 00000000..0b15e0df --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkList/BookmarkListReactor.swift @@ -0,0 +1,239 @@ +import MLSBookmarkFeatureInterface +import MLSDictionaryFeatureInterface + +import ReactorKit +import RxSwift + +final class BookmarkListReactor: Reactor { + enum Route { + case none + case sort(DictionaryType) + case filter(DictionaryType) + case detail(DictionaryType, Int) + case dictionary + case login + case edit + case bookmarkError + } + + enum UIEvent { + case none + case add(BookmarkResponse) + case delete(BookmarkResponse) + case undo + case login + } + + enum ViewState: Equatable { + case loginWithData + case loginWithoutData + case logout + } + + enum Action { + case viewWillAppear + case toggleBookmark(Int) + case sortButtonTapped + case filterButtonTapped + case editButtonTapped + case fetchList + case sortOptionSelected(SortType) + case filterOptionSelected(startLevel: Int, endLevel: Int) + case undoLastDeletedBookmark + case dataTapped(Int) + case emptyButtonTapped + case itemFilterOptionSelected([(String, String)]) + case showLogin + } + + enum Mutation { + case setItems([BookmarkResponse]) + case setLoginState(Bool) + case setSort(SortType) + case setFilter(start: Int?, end: Int?) + case setLastDeletedBookmark(BookmarkResponse?) + case navigateTo(Route) + case setJobId([Int]) + case setCategoryId([Int]) + case setEvent(UIEvent) + } + + struct State { + @Pulse var uiEvent: UIEvent = .none + @Pulse var route: Route + var items: [BookmarkResponse] = [] + var type: DictionaryType + var isLogin: Bool + var jobId: [Int]? + var categoryIds: [Int]? + var sort: SortType? + var startLevel: Int? + var endLevel: Int? + var lastDeletedBookmark: BookmarkResponse? + var viewState: ViewState { + if !isLogin { return .logout } else if items.isEmpty { return .loginWithoutData } else { return .loginWithData } + } + } + + var initialState: State + + private let authRepository: BookmarkAuthRepository + private let bookmarkRepository: BookmarkRepository + private let parseItemFilterResultUseCase: ParseItemFilterResultUseCase + + init( + type: DictionaryType, + authRepository: BookmarkAuthRepository, + bookmarkRepository: BookmarkRepository, + parseItemFilterResultUseCase: ParseItemFilterResultUseCase + ) { + self.initialState = State(route: .none, type: type, isLogin: false) + self.authRepository = authRepository + self.bookmarkRepository = bookmarkRepository + self.parseItemFilterResultUseCase = parseItemFilterResultUseCase + } + + func mutate(action: Action) -> Observable { + switch action { + case .viewWillAppear: + return authRepository.isLoggedIn() + .flatMap { [weak self] isLogin -> Observable in + guard let self else { return .empty() } + if !isLogin { + return .just(.setLoginState(false)) + } else { + return Observable.concat([.just(.setLoginState(true)), self.fetchList()]) + } + } + + case let .toggleBookmark(id): + return handleToggle(id: id) + + case .sortButtonTapped: + return .just(.navigateTo(.sort(currentState.type))) + + case .filterButtonTapped: + return .just(.navigateTo(.filter(currentState.type))) + + case .fetchList: + guard currentState.isLogin else { return .empty() } + return fetchList() + + case let .sortOptionSelected(sort): + return Observable.concat([.just(.setSort(sort)), fetchList(sort: sort)]) + + case let .filterOptionSelected(startLevel, endLevel): + return Observable.concat([.just(.setFilter(start: startLevel, end: endLevel)), fetchList()]) + + case .undoLastDeletedBookmark: + return handleUndo() + + case let .dataTapped(index): + let item = currentState.items[index] + guard let type = item.type.toDictionaryType else { return .empty() } + return .just(.navigateTo(.detail(type, item.originalId))) + + case .emptyButtonTapped: + if currentState.viewState == .logout { + return .just(.navigateTo(.login)) + } else { + return .just(.navigateTo(.dictionary)) + } + + case .editButtonTapped: + return .just(.navigateTo(.edit)) + + case let .itemFilterOptionSelected(results): + let criteria = parseItemFilterResultUseCase.execute(results: results) + return Observable.concat([ + .just(.setJobId(criteria.jobIds)), + .just(.setFilter(start: criteria.startLevel, end: criteria.endLevel)), + .just(.setCategoryId(criteria.categoryIds)) + ]) + .concat(Observable.deferred { [weak self] in + guard let self else { return .empty() } + return self.fetchList() + }) + + case .showLogin: + return .just(.setEvent(.login)) + } + } + + func reduce(state: State, mutation: Mutation) -> State { + var newState = state + switch mutation { + case let .setItems(response): newState.items = response + case let .setLoginState(isLogin): newState.isLogin = isLogin + case let .setSort(sort): newState.sort = sort + case let .setFilter(start, end): + newState.startLevel = start + newState.endLevel = end + case let .setLastDeletedBookmark(item): newState.lastDeletedBookmark = item + case let .navigateTo(route): newState.route = route + case let .setJobId(ids): newState.jobId = ids + case let .setCategoryId(ids): newState.categoryIds = ids + case let .setEvent(event): newState.uiEvent = event + } + return newState + } +} + +private extension BookmarkListReactor { + func fetchList(sort: SortType? = nil) -> Observable { + let resolvedSort = (sort ?? currentState.sort)?.sortParameter + switch currentState.type { + case .total: + return bookmarkRepository.fetchBookmark(sort: resolvedSort).map { .setItems($0) } + case .monster: + return bookmarkRepository.fetchMonsterBookmark( + minLevel: currentState.startLevel ?? 1, + maxLevel: currentState.endLevel ?? 200, + sort: resolvedSort + ).map { .setItems($0) } + case .item: + return bookmarkRepository.fetchItemBookmark( + jobId: nil, + minLevel: currentState.startLevel, + maxLevel: currentState.endLevel, + categoryIds: nil, + sort: resolvedSort + ).map { .setItems($0) } + case .npc: + return bookmarkRepository.fetchNPCBookmark(sort: resolvedSort).map { .setItems($0) } + case .quest: + return bookmarkRepository.fetchQuestBookmark(sort: resolvedSort).map { .setItems($0) } + case .map: + return bookmarkRepository.fetchMapBookmark(sort: resolvedSort).map { .setItems($0) } + default: + return .empty() + } + } + + func handleToggle(id: Int) -> Observable { + guard let index = currentState.items.firstIndex(where: { $0.originalId == id }) else { + return .empty() + } + let targetItem = currentState.items[index] + return bookmarkRepository.deleteBookmark(bookmarkId: targetItem.bookmarkId) + .flatMap { [self] _ -> Observable in + return Observable.concat([ + .from([.setLastDeletedBookmark(targetItem), .setEvent(.delete(targetItem))]), + self.fetchList() + ]) + } + .catch { _ in .just(.navigateTo(.bookmarkError)) } + } + + func handleUndo() -> Observable { + guard let lastDeleted = currentState.lastDeletedBookmark else { return .empty() } + return bookmarkRepository.setBookmark(resourceId: lastDeleted.originalId, type: lastDeleted.type) + .flatMap { [self] _ -> Observable in + return Observable.concat([ + .from([.setLastDeletedBookmark(nil), .setEvent(.add(lastDeleted))]), + self.fetchList() + ]) + } + .catch { _ in .just(.navigateTo(.bookmarkError)) } + } +} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListView.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkList/BookmarkListView.swift similarity index 92% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListView.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkList/BookmarkListView.swift index fe335d9a..4c5300d4 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListView.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkList/BookmarkListView.swift @@ -1,12 +1,10 @@ +import MLSCore +import MLSDesignSystem import UIKit -import BaseFeature -import DesignSystem - final class BookmarkListView: BaseListView { let bookmarkEmptyView: DataEmptyView - // MARK: - Init init(isFilterHidden: Bool, bookmarkEmptyView: DataEmptyView) { let editButton = TextButton() let sortButton = BaseListView.makeSortButton(title: "가나다 순", tintColor: .textColor) diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkList/BookmarkListViewController.swift similarity index 65% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkList/BookmarkListViewController.swift index 853d175a..0bb75f3f 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkList/BookmarkListViewController.swift @@ -1,25 +1,24 @@ import UIKit -import AuthFeatureInterface -import BaseFeature -import BookmarkFeatureInterface -import DesignSystem -import DictionaryFeatureInterface -import DomainInterface +import MLSAppFeatureInterface +import MLSAuthFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface import ReactorKit import RxCocoa +import RxRelay import RxSwift -public final class BookmarkListViewController: BaseViewController, View { - public typealias Reactor = BookmarkListReactor +final class BookmarkListViewController: BaseViewController, View { + typealias Reactor = BookmarkListReactor - // MARK: - Properties - public var disposeBag = DisposeBag() + var disposeBag = DisposeBag() private let bookmarkChangeRelay = PublishRelay<(id: Int, newBookmarkId: Int?)>() - private var undoRelay = PublishRelay() - private var addCollectionRelay = PublishRelay() + private let undoRelay = PublishRelay() private let itemFilterFactory: ItemFilterBottomSheetFactory private let monsterFilterFactory: MonsterFilterBottomSheetFactory @@ -31,11 +30,10 @@ public final class BookmarkListViewController: BaseViewController, View { private var selectedSortIndex = 0 - // MARK: - Components private var mainView: BookmarkListView private var emptyView = DataEmptyView(type: .bookmark) - public init( + init( reactor: BookmarkListReactor, itemFilterFactory: ItemFilterBottomSheetFactory, monsterFilterFactory: MonsterFilterBottomSheetFactory, @@ -62,16 +60,14 @@ public final class BookmarkListViewController: BaseViewController, View { fatalError("init(coder:) has not been implemented") } - override public func viewDidLoad() { + override func viewDidLoad() { super.viewDidLoad() - addViews() setupConstraints() configureUI() } } -// MARK: - SetUp private extension BookmarkListViewController { func addViews() { view.addSubview(mainView) @@ -93,23 +89,17 @@ private extension BookmarkListViewController { func createListLayout() -> UICollectionViewLayout { guard let isHidden = reactor?.currentState.type.isBookmarkSortHidden else { return UICollectionViewLayout() } - let layoutFactory = LayoutFactory() let layout = CompositionalLayoutBuilder() - .section { _ in layoutFactory.getDictionaryListLayout(isFilterHidden: isHidden) } + .section { _ in LayoutFactory.getDictionaryListLayout(isFilterHidden: isHidden) } .build() layout.register(Neutral300DividerView.self, forDecorationViewOfKind: Neutral300DividerView.identifier) return layout } } -// MARK: - Bind extension BookmarkListViewController { - public func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindViewState(reactor: reactor) - } - - func bindUserActions(reactor: Reactor) { + func bind(reactor: Reactor) { + // User Actions rx.viewWillAppear .map { _ in Reactor.Action.viewWillAppear } .bind(to: reactor.action) @@ -134,9 +124,8 @@ extension BookmarkListViewController { .map { .emptyButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - } - func bindViewState(reactor: Reactor) { + // View State reactor.state .map(\.items) .distinctUntilChanged() @@ -156,11 +145,14 @@ extension BookmarkListViewController { .subscribe { owner, route in switch route { case .sort(let type): - let viewController = owner.sortedFactory.make(sortedOptions: type.bookmarkSortedFilter, selectedIndex: owner.selectedSortIndex) { index in + let viewController = owner.sortedFactory.make( + sortedOptions: type.bookmarkSortedFilter, + selectedIndex: owner.selectedSortIndex + ) { index in owner.selectedSortIndex = index let selectedFilter = reactor.currentState.type.bookmarkSortedFilter[index] reactor.action.onNext(.sortOptionSelected(selectedFilter)) - owner.mainView.selectSort(selectedType: selectedFilter) + owner.mainView.selectSort(selectedType: selectedFilter.rawValue) } owner.tabBarController?.presentModal(viewController) case .filter(let type): @@ -168,16 +160,14 @@ extension BookmarkListViewController { case .item: let viewController = owner.itemFilterFactory.make { results in reactor.action.onNext(.itemFilterOptionSelected(results)) - - if results.isEmpty { - owner.mainView.resetFilter() - } else { - owner.mainView.selectFilter() - } + if results.isEmpty { owner.mainView.resetFilter() } else { owner.mainView.selectFilter() } } owner.present(viewController, animated: true) case .monster: - let viewController = owner.monsterFilterFactory.make(startLevel: reactor.currentState.startLevel ?? 1, endLevel: reactor.currentState.endLevel ?? 200) { startLevel, endLevel in + let viewController = owner.monsterFilterFactory.make( + startLevel: reactor.currentState.startLevel ?? 1, + endLevel: reactor.currentState.endLevel ?? 200 + ) { startLevel, endLevel in owner.mainView.selectFilter() reactor.action.onNext(.filterOptionSelected(startLevel: startLevel, endLevel: endLevel)) } @@ -186,12 +176,11 @@ extension BookmarkListViewController { break } case .detail(let type, let id): - let viewcontroller = owner.dictionaryDetailFactory.make(type: type, id: id, bookmarkRelay: owner.bookmarkChangeRelay, loginRelay: nil) - owner.navigationController?.pushViewController(viewcontroller, animated: true) + let viewController = owner.dictionaryDetailFactory.make(type: type, id: id, bookmarkRelay: owner.bookmarkChangeRelay, loginRelay: nil) + owner.navigationController?.pushViewController(viewController, animated: true) case .login: - let viewcontroller = owner.loginFactory.make(exitRoute: .pop) - owner.navigationController?.pushViewController(viewcontroller, animated: true) - + let viewController = owner.loginFactory.make(exitRoute: .pop) + owner.navigationController?.pushViewController(viewController, animated: true) case .dictionary: if let tabBarController = owner.tabBarController as? BottomTabBarController { tabBarController.selectTab(index: 0) @@ -213,8 +202,8 @@ extension BookmarkListViewController { .distinctUntilChanged() .withUnretained(self) .bind(onNext: { owner, type in - owner.mainView.updateBookmarkFilter(type: type) - owner.mainView.updateFilter(sortType: type.bookmarkSortedFilter.first) + owner.mainView.updateBookmarkFilter(type: type.title) + owner.mainView.updateFilter(sortType: type.bookmarkSortedFilter.first?.rawValue) }) .disposed(by: disposeBag) @@ -238,53 +227,85 @@ extension BookmarkListViewController { } private func presentAddSnackBar(item: BookmarkResponse) { - SnackBarFactory.createSnackBar( - type: .normal, - imageUrl: item.imageUrl, - imageBackgroundColor: item.type.backgroundColor, - text: "아이템을 북마크에 추가했어요.", - buttonText: "컬렉션 추가", - buttonAction: { [weak self] in - self?.reactor?.state.map(\.items) - .compactMap { items in - items.first(where: { $0.originalId == item.originalId })?.bookmarkId - } - .take(1) - .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak self] bookmarkId in - guard let self else { return } - let vc = self.bookmarkModalFactory.make(bookmarkIds: [bookmarkId]) { isAdd in - if isAdd { - ToastFactory.createToast( - message: "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요." - ) - } - } - vc.modalPresentationStyle = .pageSheet - if let sheet = vc.sheetPresentationController { - sheet.detents = [.medium(), .large()] - sheet.prefersGrabberVisible = true - sheet.preferredCornerRadius = 16 + let backgroundColor = item.type.backgroundColor + let buttonAction: (() -> Void)? = { [weak self] in + self?.reactor?.state.map(\.items) + .compactMap { items in + items.first(where: { $0.originalId == item.originalId })?.bookmarkId + } + .take(1) + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] bookmarkId in + guard let self else { return } + let vc = self.bookmarkModalFactory.make(bookmarkIds: [bookmarkId]) { isAdd in + if isAdd { + ToastFactory.createToast(message: "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요.") } - self.present(vc, animated: true) - }) - .disposed(by: self?.disposeBag ?? DisposeBag()) + } + vc.modalPresentationStyle = .pageSheet + if let sheet = vc.sheetPresentationController { + sheet.detents = [.medium(), .large()] + sheet.prefersGrabberVisible = true + sheet.preferredCornerRadius = 16 + } + self.present(vc, animated: true) + }) + .disposed(by: self?.disposeBag ?? DisposeBag()) + } + if let urlString = item.imageUrl, let url = URL(string: urlString) { + ImageLoader.shared.loadImage(url: url) { image in + DispatchQueue.main.async { + SnackBarFactory.createSnackBar( + type: .normal, + image: image ?? UIImage(), + imageBackgroundColor: backgroundColor, + text: "아이템을 북마크에 추가했어요.", + buttonText: "컬렉션 추가", + buttonAction: buttonAction + ) + } } - ) + } else { + SnackBarFactory.createSnackBar( + type: .normal, + image: UIImage(), + imageBackgroundColor: backgroundColor, + text: "아이템을 북마크에 추가했어요.", + buttonText: "컬렉션 추가", + buttonAction: buttonAction + ) + } } private func presentDeleteSnackBar(item: BookmarkResponse) { - SnackBarFactory.createSnackBar( - type: .delete, - imageUrl: item.imageUrl, - imageBackgroundColor: item.type.backgroundColor, - text: "아이템을 북마크에서 삭제했어요.", - buttonText: "되돌리기", - buttonAction: { [weak self] in - self?.undoRelay.accept(()) - self?.reactor?.action.onNext(.undoLastDeletedBookmark) + let backgroundColor = item.type.backgroundColor + let buttonAction: (() -> Void)? = { [weak self] in + self?.undoRelay.accept(()) + self?.reactor?.action.onNext(.undoLastDeletedBookmark) + } + if let urlString = item.imageUrl, let url = URL(string: urlString) { + ImageLoader.shared.loadImage(url: url) { image in + DispatchQueue.main.async { + SnackBarFactory.createSnackBar( + type: .delete, + image: image ?? UIImage(), + imageBackgroundColor: backgroundColor, + text: "아이템을 북마크에서 삭제했어요.", + buttonText: "되돌리기", + buttonAction: buttonAction + ) + } } - ) + } else { + SnackBarFactory.createSnackBar( + type: .delete, + image: UIImage(), + imageBackgroundColor: backgroundColor, + text: "아이템을 북마크에서 삭제했어요.", + buttonText: "되돌리기", + buttonAction: buttonAction + ) + } } private func presentLoginGuide() { @@ -300,26 +321,18 @@ extension BookmarkListViewController { cancelAction: nil ) } - } -// MARK: - Delegate extension BookmarkListViewController: UICollectionViewDelegate, UICollectionViewDataSource { - public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { reactor?.currentState.items.count ?? 0 } - public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let state = reactor?.currentState else { return UICollectionViewCell() } - guard - let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: DictionaryListCell.identifier, - for: indexPath - ) as? DictionaryListCell - else { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DictionaryListCell.identifier, for: indexPath) as? DictionaryListCell else { return UICollectionViewCell() } - let item = state.items[indexPath.row] var subText: String? { [.item, .monster, .quest].contains(item.type) ? item.level.map { "Lv. \($0)" } : nil @@ -336,7 +349,6 @@ extension BookmarkListViewController: UICollectionViewDelegate, UICollectionView indexPath: indexPath, collectionView: collectionView, isMap: item.type == .map, - onBookmarkTapped: { [weak self] in guard let self else { return } guard self.reactor?.currentState.isLogin == true else { @@ -346,11 +358,10 @@ extension BookmarkListViewController: UICollectionViewDelegate, UICollectionView self.reactor?.action.onNext(.toggleBookmark(item.originalId)) } ) - return cell } - public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { reactor?.action.onNext(.dataTapped(indexPath.item)) } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkMain/BookmarkMainFactoryImpl.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkMain/BookmarkMainFactoryImpl.swift similarity index 61% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkMain/BookmarkMainFactoryImpl.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkMain/BookmarkMainFactoryImpl.swift index 8d1aaa9c..6eea4d8e 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkMain/BookmarkMainFactoryImpl.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkMain/BookmarkMainFactoryImpl.swift @@ -1,14 +1,13 @@ -import AuthFeatureInterface -import BaseFeature -import BookmarkFeatureInterface -import DictionaryFeatureInterface -import DomainInterface +import UIKit -public final class BookmarkMainFactoryImpl: BookmarkMainFactory { - private let setBookmarkUseCase: SetBookmarkUseCase - private let checkLoginUseCase: CheckLoginUseCase - private let fetchVisitBookmarkUseCase: FetchVisitBookmarkUseCase +import MLSAuthFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface +public final class BookmarkMainFactoryImpl: BookmarkMainFactory { + private let authRepository: BookmarkAuthRepository + private let userDefaultsRepository: BookmarkUserDefaultsRepository private let onBoardingFactory: BookmarkOnBoardingFactory private let bookmarkListFactory: BookmarkListFactory private let collectionListFactory: CollectionListFactory @@ -17,9 +16,8 @@ public final class BookmarkMainFactoryImpl: BookmarkMainFactory { private let loginFactory: LoginFactory public init( - setBookmarkUseCase: SetBookmarkUseCase, - checkLoginUseCase: CheckLoginUseCase, - fetchVisitBookmarkUseCase: FetchVisitBookmarkUseCase, + authRepository: BookmarkAuthRepository, + userDefaultsRepository: BookmarkUserDefaultsRepository, onBoardingFactory: BookmarkOnBoardingFactory, bookmarkListFactory: BookmarkListFactory, collectionListFactory: CollectionListFactory, @@ -27,9 +25,8 @@ public final class BookmarkMainFactoryImpl: BookmarkMainFactory { notificationFactory: DictionaryNotificationFactory, loginFactory: LoginFactory ) { - self.setBookmarkUseCase = setBookmarkUseCase - self.checkLoginUseCase = checkLoginUseCase - self.fetchVisitBookmarkUseCase = fetchVisitBookmarkUseCase + self.authRepository = authRepository + self.userDefaultsRepository = userDefaultsRepository self.onBoardingFactory = onBoardingFactory self.bookmarkListFactory = bookmarkListFactory self.collectionListFactory = collectionListFactory @@ -38,11 +35,13 @@ public final class BookmarkMainFactoryImpl: BookmarkMainFactory { self.loginFactory = loginFactory } - public func make() -> BaseViewController { + public func make(bottomInset: CGFloat = 64) -> BaseViewController { let reactor = BookmarkMainReactor( - setBookmarkUseCase: setBookmarkUseCase, checkLoginUseCase: checkLoginUseCase, fetchVisitBookmarkUseCase: fetchVisitBookmarkUseCase + authRepository: authRepository, + userDefaultsRepository: userDefaultsRepository ) - let viewController = BookmarkMainViewController( + return BookmarkMainViewController( + bottomInset: bottomInset, onBoardingFactory: onBoardingFactory, bookmarkListFactory: bookmarkListFactory, collectionListFactory: collectionListFactory, @@ -51,6 +50,5 @@ public final class BookmarkMainFactoryImpl: BookmarkMainFactory { loginFactory: loginFactory, reactor: reactor ) - return viewController } } diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkMain/BookmarkMainReactor.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkMain/BookmarkMainReactor.swift new file mode 100644 index 00000000..0384a091 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkMain/BookmarkMainReactor.swift @@ -0,0 +1,77 @@ +import MLSBookmarkFeatureInterface +import MLSDictionaryFeatureInterface + +import ReactorKit +import RxSwift + +final class BookmarkMainReactor: Reactor { + enum Route { + case none + case search + case onBoarding + case notification + case edit + case login + } + + enum Action { + case viewWillAppear + case searchButtonTapped + case notificationButtonTapped + case loginButtonTapped + } + + enum Mutation { + case navigateTo(Route) + case setLogin(Bool) + } + + struct State { + @Pulse var route: Route + let type = DictionaryMainViewType.bookmark + var sections: [String] { + return type.pageTabList.map { $0.title } + } + var isLogin = false + } + + var initialState: State + + private let authRepository: BookmarkAuthRepository + private let userDefaultsRepository: BookmarkUserDefaultsRepository + + init(authRepository: BookmarkAuthRepository, userDefaultsRepository: BookmarkUserDefaultsRepository) { + self.initialState = State(route: .none) + self.authRepository = authRepository + self.userDefaultsRepository = userDefaultsRepository + } + + func mutate(action: Action) -> Observable { + switch action { + case .viewWillAppear: + let onboardingMutation = userDefaultsRepository.hasVisitedOnboarding() + .flatMap { hasVisited -> Observable in + if hasVisited { return .empty() } else { return .just(.navigateTo(.onBoarding)) } + } + let loginMutation = authRepository.isLoggedIn().map { Mutation.setLogin($0) } + return .concat([onboardingMutation, loginMutation]) + case .searchButtonTapped: + return .just(.navigateTo(.search)) + case .notificationButtonTapped: + return .just(.navigateTo(.notification)) + case .loginButtonTapped: + return .just(.navigateTo(.login)) + } + } + + func reduce(state: State, mutation: Mutation) -> State { + var newState = state + switch mutation { + case let .navigateTo(route): + newState.route = route + case let .setLogin(isLogin): + newState.isLogin = isLogin + } + return newState + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkMain/BookmarkMainView.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkMain/BookmarkMainView.swift new file mode 100644 index 00000000..cd79055b --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkMain/BookmarkMainView.swift @@ -0,0 +1,88 @@ +import UIKit + +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface + +import SnapKit + +final class BookmarkMainView: UIView { + enum Constant { + static let topMargin: CGFloat = 20 + static let pageTabHeight: CGFloat = 40 + static let bottomTabHeight: CGFloat = 64 + } + + let headerView = Header(style: .main, title: "북마크") + + let tabCollectionView: UICollectionView = { + let layout = UICollectionViewLayout() + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.isScrollEnabled = false + return collectionView + }() + + let pageViewController = UIPageViewController( + transitionStyle: .scroll, + navigationOrientation: .horizontal + ) + + let emptyView = ToLoginView(type: .bookmark) + + init(type: DictionaryMainViewType, bottomInset: CGFloat = Constant.bottomTabHeight) { + super.init(frame: .zero) + setupBaseLayout(type: type, bottomInset: bottomInset) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +private extension BookmarkMainView { + func setupBaseLayout(type: DictionaryMainViewType, bottomInset: CGFloat) { + addSubview(headerView) + headerView.snp.makeConstraints { make in + make.top.equalTo(safeAreaLayoutGuide) + make.horizontalEdges.equalToSuperview() + } + + addSubview(tabCollectionView) + addSubview(pageViewController.view) + addSubview(emptyView) + + tabCollectionView.snp.makeConstraints { make in + make.top.equalTo(headerView.snp.bottom).offset(Constant.topMargin) + make.horizontalEdges.equalToSuperview() + make.height.equalTo(Constant.pageTabHeight) + } + + pageViewController.view.snp.makeConstraints { make in + make.top.equalTo(tabCollectionView.snp.bottom) + make.horizontalEdges.equalTo(safeAreaLayoutGuide) + make.bottom.equalToSuperview().inset(bottomInset) + } + + emptyView.snp.makeConstraints { make in + make.top.equalTo(headerView.snp.bottom).offset(Constant.topMargin) + make.horizontalEdges.equalToSuperview() + make.bottom.equalToSuperview().inset(bottomInset) + } + + tabCollectionView.isHidden = true + pageViewController.view.isHidden = true + emptyView.isHidden = false + } +} + +extension BookmarkMainView { + func updateLoginState(isLogin: Bool) { + tabCollectionView.isHidden = !isLogin + pageViewController.view.isHidden = !isLogin + emptyView.isHidden = isLogin + tabCollectionView.isUserInteractionEnabled = isLogin + pageViewController.view.isUserInteractionEnabled = isLogin + } +} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkMain/BookmarkMainViewController.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkMain/BookmarkMainViewController.swift similarity index 83% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkMain/BookmarkMainViewController.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkMain/BookmarkMainViewController.swift index d1fd9e50..85182557 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkMain/BookmarkMainViewController.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkMain/BookmarkMainViewController.swift @@ -1,36 +1,36 @@ import UIKit -import AuthFeatureInterface -import BaseFeature -import BookmarkFeatureInterface -import DictionaryFeatureInterface +import MLSAuthFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface import ReactorKit import RxCocoa import RxSwift import SnapKit -public final class BookmarkMainViewController: BaseViewController, View { - public typealias Reactor = BookmarkMainReactor +final class BookmarkMainViewController: BaseViewController, View { + typealias Reactor = BookmarkMainReactor - // MARK: - Properties - public var disposeBag = DisposeBag() + var disposeBag = DisposeBag() private let initialIndex: Int private lazy var currentPageIndex = BehaviorRelay(value: initialIndex) - private var onBoardingFactory: BookmarkOnBoardingFactory + private let onBoardingFactory: BookmarkOnBoardingFactory private let searchFactory: DictionarySearchFactory private let notificationFactory: DictionaryNotificationFactory private let loginFactory: LoginFactory private var viewControllers: [UIViewController] - private let mainView: BookmarkMainView private let underLineController = TabBarUnderlineController() - public init( + init( initialIndex: Int = 0, + bottomInset: CGFloat = BookmarkMainView.Constant.bottomTabHeight, onBoardingFactory: BookmarkOnBoardingFactory, bookmarkListFactory: BookmarkListFactory, collectionListFactory: CollectionListFactory, @@ -40,7 +40,7 @@ public final class BookmarkMainViewController: BaseViewController, View { reactor: BookmarkMainReactor ) { let type = reactor.currentState.type - self.mainView = BookmarkMainView(type: type) + self.mainView = BookmarkMainView(type: type, bottomInset: bottomInset) self.viewControllers = type.pageTabList.enumerated().map { index, tabType in if index == 1 { return collectionListFactory.make() @@ -64,7 +64,7 @@ public final class BookmarkMainViewController: BaseViewController, View { } // MARK: - Life Cycle -public extension BookmarkMainViewController { +extension BookmarkMainViewController { override func viewDidLoad() { super.viewDidLoad() addViews() @@ -84,7 +84,7 @@ private extension BookmarkMainViewController { func setupConstraints() { mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) + make.edges.equalToSuperview() } } @@ -111,14 +111,12 @@ private extension BookmarkMainViewController { func setInitialIndex() { let indexPath = IndexPath(item: initialIndex, section: 0) - mainView.pageViewController.setViewControllers( [viewControllers[initialIndex]], direction: .forward, animated: false, completion: nil ) - mainView.tabCollectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredHorizontally) DispatchQueue.main.async { [weak self] in self?.underLineController.setInitialIndicator() @@ -127,13 +125,8 @@ private extension BookmarkMainViewController { } // MARK: - Bind -public extension BookmarkMainViewController { +extension BookmarkMainViewController { func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindViewState(reactor: reactor) - } - - func bindUserActions(reactor: Reactor) { rx.viewWillAppear .map { _ in Reactor.Action.viewWillAppear } .bind(to: reactor.action) @@ -153,9 +146,7 @@ public extension BookmarkMainViewController { .map { Reactor.Action.loginButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - } - func bindViewState(reactor: Reactor) { rx.viewDidAppear .take(1) .flatMapLatest { _ in reactor.pulse(\.$route) } @@ -190,25 +181,24 @@ public extension BookmarkMainViewController { owner.underLineController.setHidden(hidden: !isLogin) } .disposed(by: disposeBag) - } } // MARK: - UIPageViewController DataSource & Delegate extension BookmarkMainViewController: UIPageViewControllerDataSource, UIPageViewControllerDelegate { - public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { + func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { guard let index = viewControllers.firstIndex(of: viewController) else { return nil } let previousIndex = index - 1 return previousIndex >= 0 ? viewControllers[previousIndex] : nil } - public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { + func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { guard let index = viewControllers.firstIndex(of: viewController) else { return nil } let nextIndex = index + 1 return nextIndex < viewControllers.count ? viewControllers[nextIndex] : nil } - public func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { + func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { if completed, let visibleViewController = pageViewController.viewControllers?.first, let newIndex = viewControllers.firstIndex(of: visibleViewController) { currentPageIndex.accept(newIndex) @@ -220,12 +210,12 @@ extension BookmarkMainViewController: UIPageViewControllerDataSource, UIPageView // MARK: - UICollectionView DataSource & Delegate extension BookmarkMainViewController: UICollectionViewDataSource, UICollectionViewDelegate { - public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { guard let reactor = reactor else { return 0 } return reactor.currentState.sections.count } - public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let reactor = reactor else { return UICollectionViewCell() } guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PageTabbarCell.identifier, for: indexPath) as? PageTabbarCell else { return UICollectionViewCell() @@ -236,21 +226,17 @@ extension BookmarkMainViewController: UICollectionViewDataSource, UICollectionVi return cell } - public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let newIndex = indexPath.row let oldIndex = currentPageIndex.value - guard newIndex != oldIndex else { return } - let direction: UIPageViewController.NavigationDirection = newIndex > oldIndex ? .forward : .reverse - mainView.pageViewController.setViewControllers( [viewControllers[newIndex]], direction: direction, animated: true, completion: nil ) - currentPageIndex.accept(newIndex) underLineController.animateIndicatorToSelectedItem() } diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkModal/BookmarkModalFactoryImpl.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkModal/BookmarkModalFactoryImpl.swift new file mode 100644 index 00000000..fdc51e48 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkModal/BookmarkModalFactoryImpl.swift @@ -0,0 +1,27 @@ +import MLSBookmarkFeatureInterface +import MLSCore + +public final class BookmarkModalFactoryImpl: BookmarkModalFactory { + private let addCollectionFactory: AddCollectionFactory + private let collectionRepository: CollectionRepository + + public init(addCollectionFactory: AddCollectionFactory, collectionRepository: CollectionRepository) { + self.addCollectionFactory = addCollectionFactory + self.collectionRepository = collectionRepository + } + + public func make(bookmarkIds: [Int]) -> BaseViewController { + let reactor = BookmarkModalReactor(bookmarkIds: bookmarkIds, collectionRepository: collectionRepository) + let viewController = BookmarkModalViewController(addCollectionFactory: addCollectionFactory) + viewController.reactor = reactor + return viewController + } + + public func make(bookmarkIds: [Int], onComplete: ((Bool) -> Void)?) -> BaseViewController { + let viewController = make(bookmarkIds: bookmarkIds) + if let vc = viewController as? BookmarkModalViewController { + vc.onCompleted = onComplete + } + return viewController + } +} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkModal/BookmarkModalReactor.swift similarity index 52% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkModal/BookmarkModalReactor.swift index 88011427..7e3d6ccb 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkModal/BookmarkModalReactor.swift @@ -1,11 +1,9 @@ -import BookmarkFeatureInterface -import DomainInterface - +import MLSBookmarkFeatureInterface import ReactorKit import RxSwift -public final class BookmarkModalReactor: Reactor { - public enum Route { +final class BookmarkModalReactor: Reactor { + enum Route { case none case dismiss case dismissWithData @@ -13,7 +11,7 @@ public final class BookmarkModalReactor: Reactor { case collectionError } - public enum Action { + enum Action { case backButtonTapped case addButtonTapped case completeAdding @@ -22,54 +20,47 @@ public final class BookmarkModalReactor: Reactor { case viewWillAppear } - public enum Mutation { - case navigatTo(Route) + enum Mutation { + case navigateTo(Route) case checkCollection([CollectionResponse]) case setCollection([CollectionResponse]) } - public struct State { + struct State { @Pulse var route: Route var bookmarkIds: [Int] var collections = [CollectionResponse]() var selectedItems = [CollectionResponse]() } - public var initialState: State - - private let disposeBag = DisposeBag() + var initialState: State - private let fetchCollectionListUseCase: FetchCollectionListUseCase - private let addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase + private let collectionRepository: CollectionRepository - public init(bookmarkIds: [Int], fetchCollectionListUseCase: FetchCollectionListUseCase, addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase) { + init(bookmarkIds: [Int], collectionRepository: CollectionRepository) { self.initialState = State(route: .none, bookmarkIds: bookmarkIds) - self.fetchCollectionListUseCase = fetchCollectionListUseCase - self.addCollectionAndBookmarkUseCase = addCollectionAndBookmarkUseCase + self.collectionRepository = collectionRepository } - public func mutate(action: Action) -> Observable { + func mutate(action: Action) -> Observable { switch action { case .viewWillAppear, .completeAdding: - return fetchCollectionListUseCase.execute(sort: nil) + return collectionRepository.fetchCollectionList(sort: nil) .map { .setCollection($0) } case .addButtonTapped: - return addCollectionAndBookmarkUseCase - .execute( - collectionIds: currentState.selectedItems.map { $0.collectionId }, - bookmarkIds: currentState.bookmarkIds - ) - .andThen(.just(.navigatTo(.dismissWithData))) - .catch { _ in - .just(.navigatTo(.collectionError)) - } + return collectionRepository.addCollectionAndBookmark( + collectionIds: currentState.selectedItems.map { $0.collectionId }, + bookmarkIds: currentState.bookmarkIds + ) + .andThen(.just(.navigateTo(.dismissWithData))) + .catch { _ in .just(.navigateTo(.collectionError)) } case .backButtonTapped: - return .just(.navigatTo(.dismiss)) + return .just(.navigateTo(.dismiss)) case .addCollectionTapped: - return .just(.navigatTo(.addCollection)) + return .just(.navigateTo(.addCollection)) case .selectItem(let id): var newItems = currentState.selectedItems @@ -82,10 +73,10 @@ public final class BookmarkModalReactor: Reactor { } } - public func reduce(state: State, mutation: Mutation) -> State { + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { - case .navigatTo(let route): + case .navigateTo(let route): newState.route = route case .checkCollection(let collections): newState.selectedItems = collections diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalView.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkModal/BookmarkModalView.swift similarity index 76% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalView.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkModal/BookmarkModalView.swift index 428e9326..8e05072c 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalView.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkModal/BookmarkModalView.swift @@ -1,44 +1,35 @@ -import UIKit - -import DesignSystem - +import MLSDesignSystem import SnapKit +import UIKit final class BookmarkModalView: UIView { - // MARK: - Type - enum Constant { + private enum Constant { static let buttonTopMargin: CGFloat = 12 static let buttonBottomMargin: CGFloat = 14 static let horizontalMargin: CGFloat = 16 - static let backButtonSize: CGFloat = 24 - static let titleTopMargin: CGFloat = 20 - static let titleBottomMargin: CGFloat = 12 } - // MARK: - Components private let titleLabel: UILabel = { let label = UILabel() label.attributedText = .makeStyledString(font: .h_xl_b, text: "컬렉션", alignment: .left) return label }() - public let backButton: UIButton = { + let backButton: UIButton = { let button = UIButton() button.setImage(DesignSystemAsset.image(named: "largeX"), for: .normal) return button }() - public let folderCollectionView: UICollectionView = { - let layout = UICollectionViewLayout() - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + let folderCollectionView: UICollectionView = { + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout()) return collectionView }() private let divider = DividerView() - public let addButtton = CommonButton(style: .normal, title: "", disabledTitle: "추가하기") + let addButtton = CommonButton(style: .normal, title: "", disabledTitle: "추가하기") - // MARK: - Init init() { super.init(frame: .zero) addViews() @@ -46,19 +37,16 @@ final class BookmarkModalView: UIView { } @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + required init?(coder: NSCoder) { fatalError() } } -// MARK: - SetUp private extension BookmarkModalView { func addViews() { addSubview(titleLabel) addSubview(backButton) addSubview(folderCollectionView) - addSubview(addButtton) addSubview(divider) + addSubview(addButtton) } func setupConstraints() { @@ -74,7 +62,7 @@ private extension BookmarkModalView { } folderCollectionView.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(Constant.titleBottomMargin) + make.top.equalTo(titleLabel.snp.bottom).offset(12) make.horizontalEdges.equalToSuperview() } @@ -91,7 +79,6 @@ private extension BookmarkModalView { } } -// MARK: - Methods extension BookmarkModalView { func setButtonTitle(count: Int) { if count == 0 { diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalViewController.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkModal/BookmarkModalViewController.swift similarity index 63% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalViewController.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkModal/BookmarkModalViewController.swift index ce1d4aa3..879688be 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalViewController.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkModal/BookmarkModalViewController.swift @@ -1,28 +1,22 @@ -import UIKit - -import BaseFeature -import BookmarkFeatureInterface -import DomainInterface - +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem import ReactorKit import RxCocoa import RxSwift import SnapKit +import UIKit -public final class BookmarkModalViewController: BaseViewController, View { - public typealias Reactor = BookmarkModalReactor +final class BookmarkModalViewController: BaseViewController, View { + typealias Reactor = BookmarkModalReactor - // MARK: - Properties - public var disposeBag = DisposeBag() - public var onCompleted: ((Bool) -> Void)? + var disposeBag = DisposeBag() + var onCompleted: ((Bool) -> Void)? private let addCollectionFactory: AddCollectionFactory - - // MARK: - Components private let mainView = BookmarkModalView() - // MARK: - Init - public init(addCollectionFactory: AddCollectionFactory) { + init(addCollectionFactory: AddCollectionFactory) { self.addCollectionFactory = addCollectionFactory super.init() } @@ -32,8 +26,7 @@ public final class BookmarkModalViewController: BaseViewController, View { fatalError("init(coder:) has not been implemented") } - // MARK: - Lifecycle - override public func viewDidLoad() { + override func viewDidLoad() { super.viewDidLoad() addViews() setupConstraints() @@ -41,7 +34,6 @@ public final class BookmarkModalViewController: BaseViewController, View { } } -// MARK: - Setup private extension BookmarkModalViewController { func addViews() { view.addSubview(mainView) @@ -57,27 +49,19 @@ private extension BookmarkModalViewController { mainView.folderCollectionView.collectionViewLayout = createListLayout() mainView.folderCollectionView.delegate = self mainView.folderCollectionView.dataSource = self - mainView.folderCollectionView.register(AddFolderCell.self, forCellWithReuseIdentifier: AddFolderCell.identifier) mainView.folderCollectionView.register(FolderCell.self, forCellWithReuseIdentifier: FolderCell.identifier) } func createListLayout() -> UICollectionViewLayout { - let layoutFactory = LayoutFactory() - return CompositionalLayoutBuilder() - .section { _ in layoutFactory.getCollectionModalLayout() } + CompositionalLayoutBuilder() + .section { _ in LayoutFactory.getCollectionModalLayout() } .build() } } -// MARK: - Bind extension BookmarkModalViewController { - public func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindViewState(reactor: reactor) - } - - private func bindUserActions(reactor: Reactor) { + func bind(reactor: Reactor) { rx.viewWillAppear .map { .viewWillAppear } .bind(to: reactor.action) @@ -92,9 +76,7 @@ extension BookmarkModalViewController { .map { Reactor.Action.addButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - } - private func bindViewState(reactor: Reactor) { reactor.state .map(\.collections) .observe(on: MainScheduler.instance) @@ -127,17 +109,14 @@ extension BookmarkModalViewController { owner.dismiss(animated: true) case .addCollection: let viewController = owner.addCollectionFactory.make(collection: nil) - - guard let viewController = viewController as? AddCollectionViewController else { return } - - viewController.dismissed - .withUnretained(self) - .subscribe { owner, _ in - owner.reactor?.action.onNext(.completeAdding) + guard let vc = viewController as? AddCollectionViewController else { return } + vc.dismissed + .withUnretained(owner) + .subscribe { o, _ in + o.reactor?.action.onNext(.completeAdding) } .disposed(by: owner.disposeBag) - - owner.present(viewController, animated: true) + owner.present(vc, animated: true) case .collectionError: ToastFactory.createToast(message: "컬렉션 저장에 실패했어요. 다시 시도해주세요.") default: @@ -148,29 +127,26 @@ extension BookmarkModalViewController { } } -// MARK: - UICollectionView Delegate extension BookmarkModalViewController: UICollectionViewDelegate, UICollectionViewDataSource { - public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { (reactor?.currentState.collections.count ?? 0) + 1 } - public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { if indexPath.row == 0 { return collectionView.dequeueReusableCell(withReuseIdentifier: AddFolderCell.identifier, for: indexPath) as? AddFolderCell ?? UICollectionViewCell() - } else { - guard let reactor = reactor else { return UICollectionViewCell() } - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: FolderCell.identifier, for: indexPath) as? FolderCell else { - return UICollectionViewCell() - } - let collection = reactor.currentState.collections[indexPath.row - 1] - let isSelected = reactor.currentState.selectedItems.contains(where: { $0.collectionId == collection.collectionId }) - cell.isChecked = isSelected - cell.inject(title: collection.name) - return cell } + guard let reactor, let cell = collectionView.dequeueReusableCell(withReuseIdentifier: FolderCell.identifier, for: indexPath) as? FolderCell else { + return UICollectionViewCell() + } + let collection = reactor.currentState.collections[indexPath.row - 1] + let isSelected = reactor.currentState.selectedItems.contains(where: { $0.collectionId == collection.collectionId }) + cell.isChecked = isSelected + cell.inject(title: collection.name) + return cell } - public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if indexPath.row == 0 { reactor?.action.onNext(.addCollectionTapped) } else { diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingFactoryImpl.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkOnBoarding/BookmarkOnBoardingFactoryImpl.swift similarity index 86% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingFactoryImpl.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkOnBoarding/BookmarkOnBoardingFactoryImpl.swift index 6d40fae3..17c87836 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingFactoryImpl.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkOnBoarding/BookmarkOnBoardingFactoryImpl.swift @@ -1,5 +1,5 @@ -import BaseFeature -import BookmarkFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore public final class BookmarkOnBoardingFactoryImpl: BookmarkOnBoardingFactory { public init() {} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingReactor.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkOnBoarding/BookmarkOnBoardingReactor.swift similarity index 65% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingReactor.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkOnBoarding/BookmarkOnBoardingReactor.swift index 76639b60..175172e0 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingReactor.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkOnBoarding/BookmarkOnBoardingReactor.swift @@ -1,35 +1,34 @@ import ReactorKit +import RxSwift -public final class BookmarkOnBoardingReactor: Reactor { - public enum Route { +final class BookmarkOnBoardingReactor: Reactor { + enum Route { case none case dismiss } - public enum Action { + enum Action { case nextButtonTapped case backButtonTapped } - public enum Mutation { + enum Mutation { case setStep(BookmarkOnBoardingView.OnBoardingIndexType) case dismiss } - public struct State { + struct State { @Pulse var route: Route = .none var step: BookmarkOnBoardingView.OnBoardingIndexType = .first } - public var initialState: State + var initialState: State - private let disposeBag = DisposeBag() - - public init() { + init() { self.initialState = State() } - public func mutate(action: Action) -> Observable { + func mutate(action: Action) -> Observable { switch action { case .nextButtonTapped: let next = currentState.step.next() @@ -38,14 +37,12 @@ public final class BookmarkOnBoardingReactor: Reactor { } else { return .just(.setStep(next)) } - case .backButtonTapped: - let prev = currentState.step.previous() - return .just(.setStep(prev)) + return .just(.setStep(currentState.step.previous())) } } - public func reduce(state: State, mutation: Mutation) -> State { + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { case .setStep(let step): diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingView.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkOnBoarding/BookmarkOnBoardingView.swift similarity index 84% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingView.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkOnBoarding/BookmarkOnBoardingView.swift index 44fa7c75..a2bb11fe 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingView.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkOnBoarding/BookmarkOnBoardingView.swift @@ -1,17 +1,22 @@ -import UIKit - -import DesignSystem - +import MLSDesignSystem import SnapKit +import UIKit -public final class BookmarkOnBoardingView: UIView { - // MARK: - Type - public enum OnBoardingIndexType: Int { +final class BookmarkOnBoardingView: UIView { + enum OnBoardingIndexType: Int { case first = 0 case second case end - var content: OnboardingContent? { + struct Content { + let imageName: String + let title: String + let description: String + let isBackButtonHidden: Bool + let buttonTitle: String + } + + var content: Content? { switch self { case .first: return .init( @@ -51,16 +56,9 @@ public final class BookmarkOnBoardingView: UIView { } } - struct OnboardingContent { - let imageName: String - let title: String - let description: String - let isBackButtonHidden: Bool - let buttonTitle: String - } - - enum Constant { + private enum Constant { static let imageSize: CGFloat = 220 + static let imageYOffset: CGFloat = -116 static let imageSpacing: CGFloat = 4 static let labelSpacing: CGFloat = 16 static let indicatorSpacing: CGFloat = 29 @@ -68,26 +66,21 @@ public final class BookmarkOnBoardingView: UIView { static let horizontalMargin: CGFloat = 16 static let backButtonTrailing: CGFloat = -28 static let backButtonBottom: CGFloat = 10 - static let backButtonSize: CGFloat = 24 - static let imageYOffset: CGFloat = -116 } - // MARK: - Components private let imageView = UIImageView() private let titleLabel = UILabel() private let descLabel = UILabel() - public let backButton: UIButton = { + let backButton: UIButton = { let button = UIButton() button.setImage(DesignSystemAsset.image(named: "arrowBack"), for: .normal) return button }() private let stepIndicator = StepIndicator(circleCount: 2) + let nextButton = CommonButton(style: .normal, title: "다음", disabledTitle: nil) - public let nextButton = CommonButton(style: .normal, title: "다음", disabledTitle: nil) - - // MARK: - Init init() { super.init(frame: .zero) addViews() @@ -96,12 +89,9 @@ public final class BookmarkOnBoardingView: UIView { } @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + required init?(coder: NSCoder) { fatalError() } } -// MARK: - SetUp private extension BookmarkOnBoardingView { func addViews() { addSubview(imageView) @@ -146,7 +136,7 @@ private extension BookmarkOnBoardingView { } } -public extension BookmarkOnBoardingView { +extension BookmarkOnBoardingView { func configureUI(type: OnBoardingIndexType) { guard let content = type.content else { return } imageView.image = DesignSystemAsset.image(named: content.imageName) diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingViewController.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkOnBoarding/BookmarkOnBoardingViewController.swift similarity index 59% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingViewController.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkOnBoarding/BookmarkOnBoardingViewController.swift index 101c9aad..0009f6d3 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingViewController.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/BookmarkOnBoarding/BookmarkOnBoardingViewController.swift @@ -1,63 +1,32 @@ -import UIKit - -import BaseFeature - +import MLSCore import ReactorKit import RxCocoa import RxSwift import SnapKit +import UIKit -public final class BookmarkOnBoardingViewController: BaseViewController, View { - public typealias Reactor = BookmarkOnBoardingReactor - - // MARK: - Properties - public var disposeBag = DisposeBag() +final class BookmarkOnBoardingViewController: BaseViewController, View { + typealias Reactor = BookmarkOnBoardingReactor - // MARK: - Components + var disposeBag = DisposeBag() private let mainView = BookmarkOnBoardingView() - // MARK: - Init - override public init() { + override init() { super.init() } @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + required init?(coder: NSCoder) { fatalError() } - // MARK: - Lifecycle - override public func viewDidLoad() { + override func viewDidLoad() { super.viewDidLoad() - addViews() - setupConstraints() - configureUI() - } -} - -// MARK: - Setup -private extension BookmarkOnBoardingViewController { - func addViews() { view.addSubview(mainView) - } - - func setupConstraints() { mainView.snp.makeConstraints { make in make.edges.equalTo(view.safeAreaLayoutGuide) } } - func configureUI() {} -} - -// MARK: - Bind -extension BookmarkOnBoardingViewController { - public func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindViewState(reactor: reactor) - } - - private func bindUserActions(reactor: Reactor) { + func bind(reactor: Reactor) { mainView.backButton.rx.tap .map { Reactor.Action.backButtonTapped } .bind(to: reactor.action) @@ -67,9 +36,7 @@ extension BookmarkOnBoardingViewController { .map { Reactor.Action.nextButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - } - private func bindViewState(reactor: Reactor) { rx.viewDidAppear .take(1) .flatMapLatest { _ in reactor.pulse(\.$route) } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailEmptyView.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailEmptyView.swift similarity index 74% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailEmptyView.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailEmptyView.swift index 15387f47..f6388220 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailEmptyView.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailEmptyView.swift @@ -1,20 +1,16 @@ -import UIKit - -import DesignSystem - +import MLSDesignSystem import SnapKit +import UIKit final class CollectionDetailEmptyView: UIView { - // MARK: - Type - enum Constant { + private enum Constant { static let imageSize: CGFloat = 220 static let textSpacing: CGFloat = 10 static let buttonSpacing: CGFloat = 24 static let buttonWidth: CGFloat = 186 } - // MARK: - Components - public let imageView: UIImageView = { + let imageView: UIImageView = { let view = UIImageView() view.image = DesignSystemAsset.image(named: "connectionError") return view @@ -29,36 +25,18 @@ final class CollectionDetailEmptyView: UIView { private let subLabel: UILabel = { let label = UILabel() label.attributedText = .makeStyledString(font: .b_s_r, text: "북마크해서 추가해보세요.", color: .neutral600) - label.numberOfLines = 2 return label }() - public let bookmarkButton = CommonButton(style: .normal, title: "북마크하러 가기", disabledTitle: nil) + let bookmarkButton = CommonButton(style: .normal, title: "북마크하러 가기", disabledTitle: nil) - // MARK: - Init init() { super.init(frame: .zero) - addViews() - setupConstraints() - configureUI() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension CollectionDetailEmptyView { - func addViews() { addSubview(imageView) addSubview(mainLabel) addSubview(subLabel) addSubview(bookmarkButton) - } - func setupConstraints() { imageView.snp.makeConstraints { make in make.centerX.equalToSuperview() make.size.equalTo(Constant.imageSize) @@ -79,9 +57,10 @@ private extension CollectionDetailEmptyView { make.width.equalTo(Constant.buttonWidth) make.centerX.equalToSuperview() } - } - func configureUI() { backgroundColor = .clearMLS } + + @available(*, unavailable) + required init?(coder: NSCoder) { fatalError() } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailFactoryImpl.swift similarity index 64% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailFactoryImpl.swift index 83341f3f..5b0e3ae3 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailFactoryImpl.swift @@ -1,22 +1,17 @@ -import BaseFeature -import BookmarkFeatureInterface -import DesignSystem -import DictionaryFeatureInterface -import DomainInterface - import RxSwift +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface + public final class CollectionDetailFactoryImpl: CollectionDetailFactory { private let bookmarkModalFactory: BookmarkModalFactory private let collectionSettingFactory: CollectionSettingFactory private let addCollectionFactory: AddCollectionFactory private let collectionEditFactory: CollectionEditFactory private let dictionaryDetailFactory: DictionaryDetailFactory - - private let setBookmarkUseCase: SetBookmarkUseCase - private let fetchCollectionUseCase: FetchCollectionUseCase - private let deleteCollectionUseCase: DeleteCollectionUseCase - private let addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase + private let collectionRepository: CollectionRepository public init( bookmarkModalFactory: BookmarkModalFactory, @@ -24,31 +19,21 @@ public final class CollectionDetailFactoryImpl: CollectionDetailFactory { addCollectionFactory: AddCollectionFactory, collectionEditFactory: CollectionEditFactory, dictionaryDetailFactory: DictionaryDetailFactory, - setBookmarkUseCase: SetBookmarkUseCase, - fetchCollectionUseCase: FetchCollectionUseCase, - deleteCollectionUseCase: DeleteCollectionUseCase, - addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase + collectionRepository: CollectionRepository ) { self.bookmarkModalFactory = bookmarkModalFactory self.collectionSettingFactory = collectionSettingFactory self.addCollectionFactory = addCollectionFactory self.collectionEditFactory = collectionEditFactory self.dictionaryDetailFactory = dictionaryDetailFactory - self.setBookmarkUseCase = setBookmarkUseCase - self.fetchCollectionUseCase = fetchCollectionUseCase - self.deleteCollectionUseCase = deleteCollectionUseCase - self.addCollectionAndBookmarkUseCase = addCollectionAndBookmarkUseCase + self.collectionRepository = collectionRepository } public func make(collection: CollectionResponse, onMoveToMain: (() -> Void)?) -> BaseViewController { let reactor = CollectionDetailReactor( collection: collection, - setBookmarkUseCase: setBookmarkUseCase, - fetchCollectionUseCase: fetchCollectionUseCase, - deleteCollectionUseCase: deleteCollectionUseCase, - addCollectionAndBookmarkUseCase: addCollectionAndBookmarkUseCase + collectionRepository: collectionRepository ) - let viewController = CollectionDetailViewController( reactor: reactor, bookmarkModalFactory: bookmarkModalFactory, @@ -59,7 +44,6 @@ public final class CollectionDetailFactoryImpl: CollectionDetailFactory { ) reactor.pulse(\.$route) - .compactMap { $0 } .observe(on: MainScheduler.instance) .bind(onNext: { [weak viewController] route in switch route { diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailReactor.swift similarity index 51% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailReactor.swift index 5dd270c7..f69ad8ce 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailReactor.swift @@ -1,10 +1,11 @@ -import BookmarkFeatureInterface -import DomainInterface +import MLSBookmarkFeatureInterface +import MLSDictionaryFeatureInterface import ReactorKit +import RxSwift -public final class CollectionDetailReactor: Reactor { - public enum Route { +final class CollectionDetailReactor: Reactor { + enum Route { case none case toMain case dismiss @@ -12,7 +13,7 @@ public final class CollectionDetailReactor: Reactor { case detail(DictionaryType, Int) } - public enum Action { + enum Action { case viewWillAppear case backButtonTapped case editButtonTapped @@ -24,61 +25,37 @@ public final class CollectionDetailReactor: Reactor { case deleteCollection } - public enum Mutation { + enum Mutation { case navigateTo(Route) case setItems([BookmarkResponse]) case setMenu(CollectionSettingMenu) case setName(String) - case setLastDeletedBookmark(BookmarkResponse?) } - public struct State { + struct State { @Pulse var route: Route @Pulse var collectionMenu: CollectionSettingMenu? - var sections: [String] { - return type.pageTabList.map { $0.title } - } - let type = DictionaryMainViewType.bookmark var collection: CollectionResponse - var lastDeletedBookmark: BookmarkResponse? } - // MARK: - Properties - private let setBookmarkUseCase: SetBookmarkUseCase - private let fetchCollectionUseCase: FetchCollectionUseCase - private let deleteCollectionUseCase: DeleteCollectionUseCase - private let addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase - - public var initialState: State + var initialState: State + private let collectionRepository: CollectionRepository - private let disposeBag = DisposeBag() - - public init( - collection: CollectionResponse, - setBookmarkUseCase: SetBookmarkUseCase, - fetchCollectionUseCase: FetchCollectionUseCase, - deleteCollectionUseCase: DeleteCollectionUseCase, - addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase - ) { + init(collection: CollectionResponse, collectionRepository: CollectionRepository) { self.initialState = State(route: .none, collection: collection) - self.setBookmarkUseCase = setBookmarkUseCase - self.fetchCollectionUseCase = fetchCollectionUseCase - self.deleteCollectionUseCase = deleteCollectionUseCase - self.addCollectionAndBookmarkUseCase = addCollectionAndBookmarkUseCase + self.collectionRepository = collectionRepository } - public func mutate(action: Action) -> Observable { + func mutate(action: Action) -> Observable { switch action { case .backButtonTapped: return .just(.navigateTo(.dismiss)) case .editButtonTapped: return .just(.navigateTo(.edit)) case .viewWillAppear: - return fetchCollectionUseCase.execute(id: currentState.collection.collectionId) + return collectionRepository.fetchCollection(id: currentState.collection.collectionId) .map { .setItems($0) } - case .addButtonTapped: - return .just(.navigateTo(.toMain)) - case .bookmarkButtonTapped: + case .addButtonTapped, .bookmarkButtonTapped: return .just(.navigateTo(.toMain)) case .selectSetting(let menu): return .just(.setMenu(menu)) @@ -89,12 +66,12 @@ public final class CollectionDetailReactor: Reactor { guard let type = item.type.toDictionaryType else { return .empty() } return .just(.navigateTo(.detail(type, item.originalId))) case .deleteCollection: - return deleteCollectionUseCase.execute(collectionId: currentState.collection.collectionId) + return collectionRepository.deleteCollection(collectionId: currentState.collection.collectionId) .andThen(.just(.navigateTo(.dismiss))) } } - public func reduce(state: State, mutation: Mutation) -> State { + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { case .setItems(let items): @@ -105,8 +82,6 @@ public final class CollectionDetailReactor: Reactor { newState.collectionMenu = menu case .setName(let name): newState.collection.name = name - case .setLastDeletedBookmark(let bookmark): - newState.lastDeletedBookmark = bookmark } return newState } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailView.swift similarity index 77% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailView.swift index 655275dc..b4b6a29b 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailView.swift @@ -1,35 +1,25 @@ -import UIKit - -import DesignSystem - +import MLSDesignSystem import SnapKit +import UIKit final class CollectionDetailView: UIView { - // MARK: - Type - enum Constant { + private enum Constant { static let topMargin: CGFloat = 12 static let collectionViewMargin: CGFloat = 24 } - // MARK: - Components - public let navigation: NavigationBar - - public let spacer: UIView = { + let navigation: NavigationBar + let spacer: UIView = { let view = UIView() view.backgroundColor = .whiteMLS return view }() - - public let listCollectionView: UICollectionView = { - let layout = UICollectionViewLayout() - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) - return collectionView + let listCollectionView: UICollectionView = { + UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout()) }() + let emptyContainerView = UIView() + let emptyView = CollectionDetailEmptyView() - public let emptyContainerView = UIView() - public let emptyView = CollectionDetailEmptyView() - - // MARK: - Init init(navTitle: String) { self.navigation = NavigationBar(type: .collection(navTitle)) super.init(frame: .zero) @@ -39,12 +29,9 @@ final class CollectionDetailView: UIView { } @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + required init?(coder: NSCoder) { fatalError() } } -// MARK: - SetUp private extension CollectionDetailView { func addViews() { addSubview(navigation) @@ -88,7 +75,6 @@ private extension CollectionDetailView { } } -// MARK: - Methods extension CollectionDetailView { func isEmptyData(isEmpty: Bool) { listCollectionView.isHidden = isEmpty diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailViewController.swift similarity index 70% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailViewController.swift index 9c11aa03..3473fb96 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionDetail/CollectionDetailViewController.swift @@ -1,20 +1,18 @@ import UIKit -import BaseFeature -import BookmarkFeatureInterface -import DictionaryFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface import ReactorKit import RxCocoa import RxSwift -public final class CollectionDetailViewController: BaseViewController, View { - public var modalHeight: CGFloat? +final class CollectionDetailViewController: BaseViewController, View { + typealias Reactor = CollectionDetailReactor - public typealias Reactor = CollectionDetailReactor - - // MARK: - Properties - public var disposeBag = DisposeBag() + var disposeBag = DisposeBag() private let bookmarkModalFactory: BookmarkModalFactory private let collectionSettingFactory: CollectionSettingFactory @@ -22,12 +20,9 @@ public final class CollectionDetailViewController: BaseViewController, View { private let collectionEditFactory: CollectionEditFactory private let dictionaryDetailFactory: DictionaryDetailFactory - private var selectedSortIndex = 0 - - // MARK: - Components private var mainView: CollectionDetailView - public init( + init( reactor: CollectionDetailReactor, bookmarkModalFactory: BookmarkModalFactory, collectionSettingFactory: CollectionSettingFactory, @@ -43,36 +38,23 @@ public final class CollectionDetailViewController: BaseViewController, View { self.dictionaryDetailFactory = dictionaryDetailFactory super.init() self.reactor = reactor - navigationController?.navigationBar.isHidden = true } @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + required init?(coder: NSCoder) { fatalError() } - override public func viewDidLoad() { + override func viewDidLoad() { super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - } -} - -// MARK: - SetUp -private extension CollectionDetailViewController { - func addViews() { view.addSubview(mainView) - } - - func setupConstraints() { mainView.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide) make.horizontalEdges.bottom.equalToSuperview() } + configureUI() } +} +private extension CollectionDetailViewController { func configureUI() { mainView.listCollectionView.collectionViewLayout = createListLayout() mainView.listCollectionView.delegate = self @@ -81,23 +63,16 @@ private extension CollectionDetailViewController { } func createListLayout() -> UICollectionViewLayout { - let layoutFactory = LayoutFactory() let layout = CompositionalLayoutBuilder() - .section { _ in layoutFactory.getDictionaryListLayout() } + .section { _ in LayoutFactory.getDictionaryListLayout() } .build() layout.register(Neutral300DividerView.self, forDecorationViewOfKind: Neutral300DividerView.identifier) return layout } } -// MARK: - Bind extension CollectionDetailViewController { - public func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindViewState(reactor: reactor) - } - - func bindUserActions(reactor: Reactor) { + func bind(reactor: Reactor) { rx.viewWillAppear .map { _ in Reactor.Action.viewWillAppear } .bind(to: reactor.action) @@ -122,9 +97,7 @@ extension CollectionDetailViewController { .map { Reactor.Action.addButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - } - func bindViewState(reactor: Reactor) { reactor.state .map(\.collection.recentBookmarks) .distinctUntilChanged() @@ -152,28 +125,23 @@ extension CollectionDetailViewController { .bind(onNext: { owner, menu in switch menu { case .editBookmark: - let viewController = owner.collectionEditFactory.make(bookmarks: reactor.currentState.collection.recentBookmarks) - owner.navigationController?.pushViewController(viewController, animated: true) + let vc = owner.collectionEditFactory.make(bookmarks: reactor.currentState.collection.recentBookmarks) + owner.navigationController?.pushViewController(vc, animated: true) case .editName: - let viewController = owner.addCollectionFactory.make(collection: reactor.currentState.collection) - - guard let viewController = viewController as? AddCollectionViewController else { return } - - viewController.dismissed + let vc = owner.addCollectionFactory.make(collection: reactor.currentState.collection) + guard let vc = vc as? AddCollectionViewController else { return } + vc.dismissed .subscribe { name in reactor.action.onNext(.changeName(name)) } .disposed(by: owner.disposeBag) - - owner.present(viewController, animated: true) + owner.present(vc, animated: true) case .delete: GuideAlertFactory.show( mainText: "컬렉션을 삭제하시겠어요?", ctaText: "삭제하기", cancelText: "취소", - ctaAction: { - reactor.action.onNext(.deleteCollection) - }, + ctaAction: { reactor.action.onNext(.deleteCollection) }, cancelAction: {} ) default: @@ -192,13 +160,13 @@ extension CollectionDetailViewController { case .dismiss: owner.navigationController?.popViewController(animated: true) case .edit: - let viewController = owner.collectionSettingFactory.make(setEditMenu: { menu in + let vc = owner.collectionSettingFactory.make(setEditMenu: { menu in owner.reactor?.action.onNext(.selectSetting(menu)) }) - owner.presentModal(viewController) + owner.presentModal(vc) case .detail(let type, let id): - let viewController = owner.dictionaryDetailFactory.make(type: type, id: id, bookmarkRelay: nil, loginRelay: nil) - owner.navigationController?.pushViewController(viewController, animated: true) + let vc = owner.dictionaryDetailFactory.make(type: type, id: id, bookmarkRelay: nil, loginRelay: nil) + owner.navigationController?.pushViewController(vc, animated: true) default: break } @@ -207,18 +175,14 @@ extension CollectionDetailViewController { } } -// MARK: - Delegate extension CollectionDetailViewController: UICollectionViewDelegate, UICollectionViewDataSource { - public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { reactor?.currentState.collection.recentBookmarks.count ?? 0 } - public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard - let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: DictionaryListCell.identifier, - for: indexPath - ) as? DictionaryListCell, + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DictionaryListCell.identifier, for: indexPath) as? DictionaryListCell, let item = reactor?.currentState.collection.recentBookmarks[indexPath.row] else { return UICollectionViewCell() @@ -237,12 +201,12 @@ extension CollectionDetailViewController: UICollectionViewDelegate, UICollection ), indexPath: indexPath, collectionView: collectionView, - onBookmarkTapped: { } + onBookmarkTapped: {} ) return cell } - public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { reactor?.action.onNext(.dataTapped(indexPath.row)) } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditFactoryImpl.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionEdit/CollectionEditFactoryImpl.swift similarity index 66% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditFactoryImpl.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionEdit/CollectionEditFactoryImpl.swift index 02533220..910c97b2 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditFactoryImpl.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionEdit/CollectionEditFactoryImpl.swift @@ -1,13 +1,10 @@ -import BaseFeature -import BookmarkFeatureInterface -import DomainInterface +import MLSBookmarkFeatureInterface +import MLSCore public final class CollectionEditFactoryImpl: CollectionEditFactory { - private let setBookmarkUseCase: SetBookmarkUseCase private let bookmarkModalFactory: BookmarkModalFactory - public init(setBookmarkUseCase: SetBookmarkUseCase, bookmarkModalFactory: BookmarkModalFactory) { - self.setBookmarkUseCase = setBookmarkUseCase + public init(bookmarkModalFactory: BookmarkModalFactory) { self.bookmarkModalFactory = bookmarkModalFactory } diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionEdit/CollectionEditReactor.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionEdit/CollectionEditReactor.swift new file mode 100644 index 00000000..f5c517b8 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionEdit/CollectionEditReactor.swift @@ -0,0 +1,63 @@ +import MLSBookmarkFeatureInterface +import ReactorKit +import RxSwift + +final class CollectionEditReactor: Reactor { + enum Route { + case none + case dismiss + case collectionList + } + + enum Action { + case backButtonTapped + case addCollectionButtonTapped + case itemTapped(Int) + } + + enum Mutation { + case navigateTo(Route) + case checkBookmarks([BookmarkResponse]) + } + + struct State { + @Pulse var route: Route + var bookmarks: [BookmarkResponse] + var selectedItems = [BookmarkResponse]() + } + + var initialState: State + + init(bookmarks: [BookmarkResponse]) { + self.initialState = State(route: .none, bookmarks: bookmarks) + } + + func mutate(action: Action) -> Observable { + switch action { + case .backButtonTapped: + return .just(.navigateTo(.dismiss)) + case .addCollectionButtonTapped: + return .just(.navigateTo(.collectionList)) + case .itemTapped(let index): + let item = currentState.bookmarks[index] + var newItems = currentState.selectedItems + if let idx = newItems.firstIndex(where: { $0.originalId == item.originalId }) { + newItems.remove(at: idx) + } else { + newItems.append(item) + } + return .just(.checkBookmarks(newItems)) + } + } + + func reduce(state: State, mutation: Mutation) -> State { + var newState = state + switch mutation { + case .navigateTo(let route): + newState.route = route + case .checkBookmarks(let bookmarks): + newState.selectedItems = bookmarks + } + return newState + } +} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditView.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionEdit/CollectionEditView.swift similarity index 72% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditView.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionEdit/CollectionEditView.swift index 878c03e4..13c43a62 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditView.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionEdit/CollectionEditView.swift @@ -1,12 +1,9 @@ -import UIKit - -import DesignSystem - +import MLSDesignSystem import SnapKit +import UIKit final class CollectionEditView: UIView { - // MARK: - Type - enum Constant { + private enum Constant { static let imageViewSize: CGFloat = 44 static let iconSize: CGFloat = 24 static let horizontalMargin: CGFloat = 16 @@ -14,21 +11,17 @@ final class CollectionEditView: UIView { static let bottomMargin: CGFloat = 14 } - // MARK: - Components - private lazy var imageView: UIView = { + private lazy var headerView: UIView = { let view = UIView() - view.addSubview(cancelButton) - cancelButton.snp.makeConstraints { make in make.center.equalToSuperview() make.size.equalTo(Constant.iconSize) } - return view }() - public let cancelButton: UIButton = { + let cancelButton: UIButton = { let button = UIButton() button.setImage(DesignSystemAsset.image(named: "largeX"), for: .normal) return button @@ -40,34 +33,28 @@ final class CollectionEditView: UIView { return label }() - public let listCollectionView: UICollectionView = { - let layout = UICollectionViewLayout() - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) - return collectionView + let listCollectionView: UICollectionView = { + UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout()) }() private let divider = DividerView() + let addButtton = CommonButton(style: .normal, title: "컬렉션에 추가하기", disabledTitle: nil) - public let addButtton = CommonButton(style: .normal, title: "컬렉션에 추가하기", disabledTitle: nil) - - // MARK: - Init init() { super.init(frame: .zero) addViews() setupConstraints() - configureUI() + backgroundColor = .whiteMLS + listCollectionView.backgroundColor = .neutral100 } @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + required init?(coder: NSCoder) { fatalError() } } -// MARK: - SetUp private extension CollectionEditView { func addViews() { - addSubview(imageView) + addSubview(headerView) addSubview(titleLabel) addSubview(listCollectionView) addSubview(divider) @@ -75,7 +62,7 @@ private extension CollectionEditView { } func setupConstraints() { - imageView.snp.makeConstraints { make in + headerView.snp.makeConstraints { make in make.top.equalTo(safeAreaLayoutGuide) make.leading.equalToSuperview() make.size.equalTo(Constant.imageViewSize) @@ -83,11 +70,11 @@ private extension CollectionEditView { titleLabel.snp.makeConstraints { make in make.centerX.equalToSuperview() - make.centerY.equalTo(imageView) + make.centerY.equalTo(headerView) } listCollectionView.snp.makeConstraints { make in - make.top.equalTo(imageView.snp.bottom) + make.top.equalTo(headerView.snp.bottom) make.horizontalEdges.equalToSuperview() } @@ -102,9 +89,4 @@ private extension CollectionEditView { make.bottom.equalToSuperview().inset(Constant.bottomMargin) } } - - func configureUI() { - backgroundColor = .whiteMLS - listCollectionView.backgroundColor = .neutral100 - } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionEdit/CollectionEditViewController.swift similarity index 63% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionEdit/CollectionEditViewController.swift index af476175..eb836cde 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionEdit/CollectionEditViewController.swift @@ -1,54 +1,37 @@ -import UIKit - -import BaseFeature -import BookmarkFeatureInterface - +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem import ReactorKit import RxCocoa import RxSwift +import UIKit -public final class CollectionEditViewController: BaseViewController, View { - public typealias Reactor = CollectionEditReactor - - // MARK: - Properties - public var disposeBag = DisposeBag() +final class CollectionEditViewController: BaseViewController, View { + typealias Reactor = CollectionEditReactor + var disposeBag = DisposeBag() private let bookmarkModalFactory: BookmarkModalFactory - - // MARK: - Components private var mainView = CollectionEditView() - public init(bookmarkModalFactory: BookmarkModalFactory) { + init(bookmarkModalFactory: BookmarkModalFactory) { self.bookmarkModalFactory = bookmarkModalFactory super.init() } @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + required init?(coder: NSCoder) { fatalError() } - override public func viewDidLoad() { + override func viewDidLoad() { super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - } -} - -// MARK: - SetUp -private extension CollectionEditViewController { - func addViews() { view.addSubview(mainView) - } - - func setupConstraints() { mainView.snp.makeConstraints { make in make.edges.equalTo(view.safeAreaLayoutGuide) } + configureUI() } +} +private extension CollectionEditViewController { func configureUI() { mainView.listCollectionView.collectionViewLayout = createListLayout() mainView.listCollectionView.delegate = self @@ -57,22 +40,14 @@ private extension CollectionEditViewController { } func createListLayout() -> UICollectionViewLayout { - let layoutFactory = LayoutFactory() - let layout = CompositionalLayoutBuilder() - .section { _ in layoutFactory.getCollectionListEditLayout() } + CompositionalLayoutBuilder() + .section { _ in LayoutFactory.getCollectionListEditLayout() } .build() - return layout } } -// MARK: - Bind extension CollectionEditViewController { - public func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindViewState(reactor: reactor) - } - - func bindUserActions(reactor: Reactor) { + func bind(reactor: Reactor) { mainView.cancelButton.rx.tap .map { Reactor.Action.backButtonTapped } .bind(to: reactor.action) @@ -82,20 +57,6 @@ extension CollectionEditViewController { .map { Reactor.Action.addCollectionButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - } - - func bindViewState(reactor: Reactor) { - rx.viewDidAppear - .take(1) - .flatMapLatest { _ in reactor.pulse(\.$route) } - .withUnretained(self) - .subscribe { _, route in - switch route { - default: - break - } - } - .disposed(by: disposeBag) reactor.state .map(\.bookmarks) @@ -126,13 +87,15 @@ extension CollectionEditViewController { switch route { case .dismiss: owner.navigationController?.popViewController(animated: true) - case .collcectionList: - let viewController = owner.bookmarkModalFactory.make(bookmarkIds: reactor.currentState.selectedItems.map { $0.bookmarkId }) { isSave in + case .collectionList: + let vc = owner.bookmarkModalFactory.make( + bookmarkIds: reactor.currentState.selectedItems.map { $0.bookmarkId } + ) { isSave in if isSave { owner.navigationController?.popToRootViewController(animated: true) } } - owner.present(viewController, animated: true) + owner.present(vc, animated: true) default: break } @@ -141,18 +104,14 @@ extension CollectionEditViewController { } } -// MARK: - Delegate extension CollectionEditViewController: UICollectionViewDelegate, UICollectionViewDataSource { - public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { reactor?.currentState.bookmarks.count ?? 0 } - public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard - let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: DictionaryListCell.identifier, - for: indexPath - ) as? DictionaryListCell, + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DictionaryListCell.identifier, for: indexPath) as? DictionaryListCell, let item = reactor?.currentState.bookmarks[indexPath.row] else { return UICollectionViewCell() @@ -161,7 +120,6 @@ extension CollectionEditViewController: UICollectionViewDelegate, UICollectionVi var subText: String? { [.item, .monster, .quest].contains(item.type) ? item.level.map { "Lv. \($0)" } : nil } - cell.inject( type: .checkbox, input: DictionaryListCell.Input( @@ -180,7 +138,7 @@ extension CollectionEditViewController: UICollectionViewDelegate, UICollectionVi return cell } - public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { reactor?.action.onNext(.itemTapped(indexPath.row)) } } diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListCell.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListCell.swift new file mode 100644 index 00000000..5fcffca1 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListCell.swift @@ -0,0 +1,51 @@ +import MLSCore +import MLSDesignSystem +import SnapKit +import UIKit + +final class CollectionListCell: UICollectionViewCell { + let cellView = CollectionList() + + override init(frame: CGRect) { + super.init(frame: frame) + contentView.addSubview(cellView) + cellView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + + @available(*, unavailable) + required init?(coder: NSCoder) { fatalError() } +} + +extension CollectionListCell { + struct Input { + let title: String + let count: Int + let images: [String?] + } + + func inject(input: Input) { + loadImages(from: input.images) { [weak self] images in + self?.cellView.setImages(images: images) + } + cellView.setTitle(text: input.title) + cellView.setSubtitle(text: "\(input.count)개") + } +} + +@MainActor +private func loadImages(from urls: [String?], completion: @escaping ([UIImage?]) -> Void) { + var results = [UIImage?](repeating: nil, count: urls.count) + let group = DispatchGroup() + for (index, urlString) in urls.enumerated() { + group.enter() + ImageLoader.shared.loadImage(stringURL: urlString) { image in + results[index] = image + group.leave() + } + } + group.notify(queue: .main) { + completion(results) + } +} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListEmptyView.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListEmptyView.swift similarity index 72% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListEmptyView.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListEmptyView.swift index e50a1ee4..1bfc5706 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListEmptyView.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListEmptyView.swift @@ -1,19 +1,15 @@ -import UIKit - -import DesignSystem - +import MLSDesignSystem import SnapKit +import UIKit final class CollectionListEmptyView: UIView { - // MARK: - Type - enum Constant { + private enum Constant { static let topInset: CGFloat = 60 static let imageSize: CGFloat = 220 static let textSpacing: CGFloat = 10 } - // MARK: - Components - public let imageView: UIImageView = { + let imageView: UIImageView = { let view = UIImageView() view.image = DesignSystemAsset.image(named: "fabHint") return view @@ -27,34 +23,17 @@ final class CollectionListEmptyView: UIView { private let subLabel: UILabel = { let label = UILabel() - label.attributedText = .makeStyledString(font: .cp_s_r, text: "컬렉션을 만들면 북마크한 리스트틀\n내 취향대로 정리할 수 있어요.", color: .neutral600) + label.attributedText = .makeStyledString(font: .cp_s_r, text: "컬렉션을 만들면 북마크한 리스트를\n내 취향대로 정리할 수 있어요.", color: .neutral600) label.numberOfLines = 2 return label }() - // MARK: - Init init() { super.init(frame: .zero) - addViews() - setupConstraints() - configureUI() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension CollectionListEmptyView { - func addViews() { addSubview(imageView) addSubview(mainLabel) addSubview(subLabel) - } - func setupConstraints() { imageView.snp.makeConstraints { make in make.top.equalToSuperview().inset(Constant.topInset) make.centerX.equalToSuperview() @@ -72,7 +51,6 @@ private extension CollectionListEmptyView { } } - func configureUI() { -// backgroundColor = .clearMLS - } + @available(*, unavailable) + required init?(coder: NSCoder) { fatalError() } } diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListFactoryImpl.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListFactoryImpl.swift new file mode 100644 index 00000000..ace20f08 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListFactoryImpl.swift @@ -0,0 +1,33 @@ +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface + +public final class CollectionListFactoryImpl: CollectionListFactory { + private let collectionRepository: CollectionRepository + private let addCollectionFactory: AddCollectionFactory + private let collectionDetailFactory: CollectionDetailFactory + private let sortedBottomSheetFactory: SortedBottomSheetFactory + + public init( + collectionRepository: CollectionRepository, + addCollectionFactory: AddCollectionFactory, + collectionDetailFactory: CollectionDetailFactory, + sortedBottomSheetFactory: SortedBottomSheetFactory + ) { + self.collectionRepository = collectionRepository + self.addCollectionFactory = addCollectionFactory + self.collectionDetailFactory = collectionDetailFactory + self.sortedBottomSheetFactory = sortedBottomSheetFactory + } + + public func make() -> BaseViewController { + let reactor = CollectionListReactor(collectionRepository: collectionRepository) + let viewController = CollectionListViewController( + addCollectionFactory: addCollectionFactory, + detailFactory: collectionDetailFactory, + sortedBottomSheetFactory: sortedBottomSheetFactory + ) + viewController.reactor = reactor + return viewController + } +} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListReactor.swift similarity index 57% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListReactor.swift index d621baf3..2193306c 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListReactor.swift @@ -1,18 +1,17 @@ -import UIKit - -import BookmarkFeatureInterface -import DomainInterface +import MLSBookmarkFeatureInterface +import MLSDictionaryFeatureInterface import ReactorKit +import RxSwift -public final class CollectionListReactor: Reactor { - public enum Route { +final class CollectionListReactor: Reactor { + enum Route { case none case detail(CollectionResponse) case sortFilter } - public enum Action { + enum Action { case itemTapped(Int) case viewWillAppear case completeAdding @@ -20,50 +19,48 @@ public final class CollectionListReactor: Reactor { case sortOptionSelected(SortType) } - public enum Mutation { + enum Mutation { case navigateTo(Route) case setListData([CollectionResponse]) case setSort(SortType) } - public struct State { + struct State { @Pulse var route: Route var collectionList: [CollectionResponse] var sortFilter: [SortType] = [.korean, .latest] var selectedSort: SortType? } - // MARK: - Properties - public var initialState: State - private let disposeBag = DisposeBag() - - private let fetchCollectionListUseCase: FetchCollectionListUseCase + var initialState: State + private let collectionRepository: CollectionRepository - public init(fetchCollectionListUseCase: FetchCollectionListUseCase) { - self.fetchCollectionListUseCase = fetchCollectionListUseCase + init(collectionRepository: CollectionRepository) { + self.collectionRepository = collectionRepository self.initialState = State(route: .none, collectionList: []) } - public func mutate(action: Action) -> Observable { + func mutate(action: Action) -> Observable { switch action { case .viewWillAppear: - return fetchCollectionListUseCase.execute(sort: currentState.selectedSort).map { .setListData($0) } + return collectionRepository.fetchCollectionList(sort: currentState.selectedSort?.sortParameter) + .map { .setListData($0) } case .itemTapped(let index): return .just(.navigateTo(.detail(currentState.collectionList[index]))) case .completeAdding: - return fetchCollectionListUseCase.execute(sort: currentState.selectedSort) - .map {.setListData($0)} + return collectionRepository.fetchCollectionList(sort: currentState.selectedSort?.sortParameter) + .map { .setListData($0) } case .sortButtonTapped: return .just(.navigateTo(.sortFilter)) case .sortOptionSelected(let sort): return Observable.concat([ .just(.setSort(sort)), - fetchCollectionListUseCase.execute(sort: currentState.selectedSort).map { .setListData($0) } + collectionRepository.fetchCollectionList(sort: sort.sortParameter).map { .setListData($0) } ]) } } - public func reduce(state: State, mutation: Mutation) -> State { + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { case .setListData(let data): diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListView.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListView.swift similarity index 79% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListView.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListView.swift index 48c57b5f..c70cf340 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListView.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListView.swift @@ -1,13 +1,13 @@ import UIKit -import DesignSystem -import DomainInterface +import MLSBookmarkFeatureInterface +import MLSDesignSystem +import MLSDictionaryFeatureInterface import SnapKit final class CollectionListView: UIView { - // MARK: - Type - enum Constant { + private enum Constant { static let stackViewSpacing: CGFloat = 12 static let topMargin: CGFloat = 12 static let filterHeight: CGFloat = 32 @@ -15,10 +15,8 @@ final class CollectionListView: UIView { static let nonFilterTopMargin: CGFloat = 20 } - // MARK: - Components - public let listCollectionView: UICollectionView = { - let layout = UICollectionViewLayout() - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + let listCollectionView: UICollectionView = { + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout()) return collectionView }() @@ -30,22 +28,21 @@ final class CollectionListView: UIView { return view }() - public lazy var sortButton: UIButton = { + lazy var sortButton: UIButton = { let button = UIButton() button.setAttributedTitle(.makeStyledString(font: .b_s_r, text: "최신 순", color: .textColor), for: .normal) - button.setImage(DesignSystemAsset.image(named: "lineArrowDown")?.withRenderingMode(.alwaysTemplate), for: .normal) + button.setImage(DesignSystemAsset.image(named: "lineArrowDown").withRenderingMode(.alwaysTemplate), for: .normal) button.tintColor = .textColor button.semanticContentAttribute = .forceRightToLeft return button }() - public let emptyView: CollectionListEmptyView = { + let emptyView: CollectionListEmptyView = { let view = CollectionListEmptyView() view.isHidden = true return view }() - // MARK: - Init init() { super.init(frame: .zero) addViews() @@ -54,12 +51,9 @@ final class CollectionListView: UIView { } @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + required init?(coder: NSCoder) { fatalError() } } -// MARK: - SetUp private extension CollectionListView { func addViews() { addSubview(filterStackView) @@ -79,8 +73,7 @@ private extension CollectionListView { listCollectionView.snp.makeConstraints { make in make.top.equalTo(filterStackView.snp.bottom).offset(Constant.topMargin) - make.horizontalEdges.equalToSuperview() - make.bottom.equalToSuperview() + make.horizontalEdges.bottom.equalToSuperview() } emptyView.snp.makeConstraints { make in @@ -118,11 +111,6 @@ extension CollectionListView { } } - func selectFilter(selectedType: SortType) { - sortButton.setAttributedTitle(.makeStyledString(font: .b_s_r, text: selectedType.rawValue, color: .primary700), for: .normal) - sortButton.tintColor = .primary700 - } - func selectSort(selectedType: SortType) { sortButton.setAttributedTitle(.makeStyledString(font: .b_s_r, text: selectedType.rawValue, color: .primary700), for: .normal) sortButton.tintColor = .primary700 diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListViewController.swift similarity index 65% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListViewController.swift index f1175a4b..c16c95f4 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionList/CollectionListViewController.swift @@ -1,29 +1,31 @@ import UIKit -import BaseFeature -import BookmarkFeatureInterface -import DesignSystem -import DictionaryFeatureInterface -import DomainInterface +import MLSAppFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface import ReactorKit import RxSwift -public final class CollectionListViewController: BaseViewController, View { - public typealias Reactor = CollectionListReactor +final class CollectionListViewController: BaseViewController, View { + typealias Reactor = CollectionListReactor - // MARK: - Properties - public var disposeBag = DisposeBag() + var disposeBag = DisposeBag() private var selectedSortIndex = 0 private let addCollectionFactory: AddCollectionFactory private let detailFactory: CollectionDetailFactory private let sortedBottomSheetFactory: SortedBottomSheetFactory - // MARK: - Components private var mainView = CollectionListView() - public init(addCollectionFactory: AddCollectionFactory, detailFactory: CollectionDetailFactory, sortedBottomSheetFactory: SortedBottomSheetFactory) { + init( + addCollectionFactory: AddCollectionFactory, + detailFactory: CollectionDetailFactory, + sortedBottomSheetFactory: SortedBottomSheetFactory + ) { self.addCollectionFactory = addCollectionFactory self.detailFactory = detailFactory self.sortedBottomSheetFactory = sortedBottomSheetFactory @@ -31,20 +33,16 @@ public final class CollectionListViewController: BaseViewController, View { } @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + required init?(coder: NSCoder) { fatalError() } - override public func viewDidLoad() { + override func viewDidLoad() { super.viewDidLoad() - addViews() setupConstraints() configureUI() } } -// MARK: - SetUp private extension CollectionListViewController { func addViews() { view.addSubview(mainView) @@ -63,38 +61,28 @@ private extension CollectionListViewController { mainView.listCollectionView.register(CollectionListCell.self, forCellWithReuseIdentifier: CollectionListCell.identifier) addFloatingButton { [weak self] in - guard let self = self else { return } + guard let self else { return } let viewController = self.addCollectionFactory.make(collection: nil) - - guard let viewController = viewController as? AddCollectionViewController else { return } - viewController.dismissed + guard let vc = viewController as? AddCollectionViewController else { return } + vc.dismissed .withUnretained(self) .subscribe { owner, _ in owner.reactor?.action.onNext(.completeAdding) } - .disposed(by: disposeBag) - - self.present(viewController, animated: true) + .disposed(by: self.disposeBag) + self.present(vc, animated: true) } } func createListLayout() -> UICollectionViewLayout { - let layoutFactory = LayoutFactory() - let layout = CompositionalLayoutBuilder() - .section { _ in layoutFactory.getCollectionListLayout() } + CompositionalLayoutBuilder() + .section { _ in LayoutFactory.getCollectionListLayout() } .build() - return layout } } -// MARK: - Bind extension CollectionListViewController { - public func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindViewState(reactor: reactor) - } - - func bindUserActions(reactor: Reactor) { + func bind(reactor: Reactor) { rx.viewWillAppear .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) @@ -104,9 +92,7 @@ extension CollectionListViewController { .map { .sortButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - } - func bindViewState(reactor: Reactor) { rx.viewDidAppear .take(1) .flatMapLatest { _ in reactor.pulse(\.$route) } @@ -123,7 +109,10 @@ extension CollectionListViewController { }) owner.tabBarController?.navigationController?.pushViewController(viewController, animated: true) case .sortFilter: - let viewController = owner.sortedBottomSheetFactory.make(sortedOptions: reactor.currentState.sortFilter, selectedIndex: owner.selectedSortIndex) { index in + let viewController = owner.sortedBottomSheetFactory.make( + sortedOptions: reactor.currentState.sortFilter, + selectedIndex: owner.selectedSortIndex + ) { index in owner.selectedSortIndex = index let selectedFilter = reactor.currentState.sortFilter[index] reactor.action.onNext(.sortOptionSelected(selectedFilter)) @@ -148,27 +137,27 @@ extension CollectionListViewController { } } -// MARK: - Delegate extension CollectionListViewController: UICollectionViewDelegate, UICollectionViewDataSource { - public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { reactor?.currentState.collectionList.count ?? 0 } - public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard - let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: CollectionListCell.identifier, - for: indexPath - ) as? CollectionListCell, + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionListCell.identifier, for: indexPath) as? CollectionListCell, let item = reactor?.currentState.collectionList[indexPath.row] else { return UICollectionViewCell() } - cell.inject(input: CollectionListCell.Input(title: item.name, count: item.recentBookmarks.count, images: item.recentBookmarks.map { $0.imageUrl })) + cell.inject(input: CollectionListCell.Input( + title: item.name, + count: item.recentBookmarks.count, + images: item.recentBookmarks.map { $0.imageUrl } + )) return cell } - public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { reactor?.action.onNext(.itemTapped(indexPath.row)) } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionSettingSheet/CollectionSettingFactoryImpl.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionSettingSheet/CollectionSettingFactoryImpl.swift similarity index 69% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionSettingSheet/CollectionSettingFactoryImpl.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionSettingSheet/CollectionSettingFactoryImpl.swift index 5a66eea4..f35ac896 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionSettingSheet/CollectionSettingFactoryImpl.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionSettingSheet/CollectionSettingFactoryImpl.swift @@ -1,7 +1,8 @@ -import BaseFeature -import BookmarkFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem -public struct CollectionSettingFactoryImpl: CollectionSettingFactory { +public final class CollectionSettingFactoryImpl: CollectionSettingFactory { public init() {} public func make(setEditMenu: ((CollectionSettingMenu) -> Void)?) -> BaseViewController & ModalPresentable { diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionSettingSheet/CollectionSettingReactor.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionSettingSheet/CollectionSettingReactor.swift new file mode 100644 index 00000000..ebe9a5a9 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionSettingSheet/CollectionSettingReactor.swift @@ -0,0 +1,55 @@ +import MLSBookmarkFeatureInterface +import ReactorKit +import RxSwift + +final class CollectionSettingReactor: Reactor { + enum Route { + case none + case dismiss + case dismissWithMenu(CollectionSettingMenu) + } + + enum Action { + case cancelButtonTapped + case editBookmarkButtonTapped + case editNameButtonTapped + case deleteButtonTapped + } + + enum Mutation { + case navigateTo(Route) + } + + struct State { + @Pulse var route: Route = .none + var menu = CollectionSettingMenu.allCases + } + + var initialState: State + + init() { + self.initialState = State() + } + + func mutate(action: Action) -> Observable { + switch action { + case .cancelButtonTapped: + return .just(.navigateTo(.dismiss)) + case .editBookmarkButtonTapped: + return .just(.navigateTo(.dismissWithMenu(.editBookmark))) + case .editNameButtonTapped: + return .just(.navigateTo(.dismissWithMenu(.editName))) + case .deleteButtonTapped: + return .just(.navigateTo(.dismissWithMenu(.delete))) + } + } + + func reduce(state: State, mutation: Mutation) -> State { + var newState = state + switch mutation { + case .navigateTo(let route): + newState.route = route + } + return newState + } +} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionSettingSheet/CollectionSettingView.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionSettingSheet/CollectionSettingView.swift similarity index 57% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionSettingSheet/CollectionSettingView.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionSettingSheet/CollectionSettingView.swift index b7bb8d95..061934c4 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionSettingSheet/CollectionSettingView.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionSettingSheet/CollectionSettingView.swift @@ -1,51 +1,29 @@ -import UIKit - -import DesignSystem - +import MLSDesignSystem import SnapKit +import UIKit final class CollectionSettingView: UIView { private enum Constant { static let topInset: CGFloat = 16 static let tableViewInset: CGFloat = 14 - static let tableViewHeight: CGFloat = 284 } - // MARK: - Properties let header: Header = { - let header = Header(style: .filter, title: "컬렉션") - return header + Header(style: .filter, title: "컬렉션") }() - public let menuTableView: UITableView = { + let menuTableView: UITableView = { let view = UITableView() view.isScrollEnabled = false view.separatorStyle = .none return view }() - // MARK: - init init() { super.init(frame: .zero) - - addViews() - setupConstraints() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension CollectionSettingView { - func addViews() { addSubview(header) addSubview(menuTableView) - } - func setupConstraints() { header.snp.makeConstraints { make in make.top.equalToSuperview().inset(Constant.topInset) make.horizontalEdges.equalToSuperview() @@ -53,8 +31,10 @@ private extension CollectionSettingView { menuTableView.snp.makeConstraints { make in make.top.equalTo(header.snp.bottom).offset(Constant.tableViewInset) - make.horizontalEdges.equalToSuperview() - make.bottom.equalToSuperview() + make.horizontalEdges.bottom.equalToSuperview() } } + + @available(*, unavailable) + required init?(coder: NSCoder) { fatalError() } } diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionSettingSheet/CollectionSettingViewController.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionSettingSheet/CollectionSettingViewController.swift new file mode 100644 index 00000000..bdf3b951 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/CollectionSettingSheet/CollectionSettingViewController.swift @@ -0,0 +1,92 @@ +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem +import ReactorKit +import RxCocoa +import RxSwift +import SnapKit +import UIKit + +final class CollectionSettingViewController: BaseViewController, ModalPresentable, View { + var modalHeight: CGFloat? = 284 + + typealias Reactor = CollectionSettingReactor + + var disposeBag = DisposeBag() + var setMenu: ((CollectionSettingMenu) -> Void)? + + private var mainView = CollectionSettingView() + + override func viewDidLoad() { + super.viewDidLoad() + view.addSubview(mainView) + mainView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + mainView.menuTableView.delegate = self + mainView.menuTableView.dataSource = self + } + + func bind(reactor: Reactor) { + mainView.header.firstIconButton.rx.tap + .map { Reactor.Action.cancelButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + rx.viewDidAppear + .take(1) + .flatMapLatest { _ in reactor.pulse(\.$route) } + .withUnretained(self) + .subscribe { owner, route in + switch route { + case .dismiss: + owner.dismissCurrentModal() + case .dismissWithMenu(let menu): + owner.setMenu?(menu) + owner.dismissCurrentModal() + default: + break + } + } + .disposed(by: disposeBag) + } +} + +extension CollectionSettingViewController: UITableViewDelegate, UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + reactor?.currentState.menu.count ?? 0 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = UITableViewCell() + guard let item = reactor?.currentState.menu[indexPath.row] else { return cell } + cell.textLabel?.textAlignment = .center + cell.textLabel?.autoresizingMask = [.flexibleWidth, .flexibleHeight] + cell.textLabel?.attributedText = .makeStyledString(font: .b_m_r, text: item.title, color: item.titleColor) + if indexPath.row < (reactor?.currentState.menu.count ?? 0) - 1 { + let divider = UIView() + divider.backgroundColor = .lightGray.withAlphaComponent(0.3) + cell.contentView.addSubview(divider) + divider.snp.makeConstraints { make in + make.horizontalEdges.bottom.equalToSuperview() + make.height.equalTo(1) + } + } + cell.selectionStyle = .none + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + guard let item = reactor?.currentState.menu[indexPath.row] else { return } + switch item { + case .editBookmark: reactor?.action.onNext(.editBookmarkButtonTapped) + case .editName: reactor?.action.onNext(.editNameButtonTapped) + case .delete: reactor?.action.onNext(.deleteButtonTapped) + case .cancel: reactor?.action.onNext(.cancelButtonTapped) + } + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + 54 + } +} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/AddFolderCell.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/Common/AddFolderCell.swift similarity index 73% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/AddFolderCell.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/Common/AddFolderCell.swift index 5632d0ad..f9b35472 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/AddFolderCell.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/Common/AddFolderCell.swift @@ -1,12 +1,8 @@ -import UIKit - -import DesignSystem - -import RxSwift +import MLSDesignSystem import SnapKit +import UIKit -public class AddFolderCell: UICollectionViewCell { - // MARK: - Type +final class AddFolderCell: UICollectionViewCell { private enum Constant { static let iconInset: CGFloat = 8 static let radius: CGFloat = 8 @@ -14,7 +10,6 @@ public class AddFolderCell: UICollectionViewCell { static let buttonSize: CGFloat = 40 } - // MARK: - Components private lazy var addIconView: UIView = { let view = UIView() view.layer.cornerRadius = Constant.radius @@ -29,7 +24,7 @@ public class AddFolderCell: UICollectionViewCell { private let iconView: UIImageView = { let view = UIImageView() - view.image = DesignSystemAsset.image(named: "addIcon")?.withRenderingMode(.alwaysTemplate) + view.image = DesignSystemAsset.image(named: "addIcon").withRenderingMode(.alwaysTemplate) view.tintColor = .whiteMLS return view }() @@ -46,41 +41,27 @@ public class AddFolderCell: UICollectionViewCell { return view }() - private var disposeBag = DisposeBag() - override init(frame: CGRect) { super.init(frame: frame) - addViews() - setupConstraints() } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension AddFolderCell { - func addViews() { contentView.addSubview(addIconView) contentView.addSubview(titleLabel) contentView.addSubview(divider) - } - func setupConstraints() { addIconView.snp.makeConstraints { make in make.leading.verticalEdges.equalToSuperview().inset(Constant.margin) make.size.equalTo(Constant.buttonSize) } - titleLabel.snp.makeConstraints { make in make.leading.equalTo(addIconView.snp.trailing).offset(Constant.margin) make.centerY.equalToSuperview() make.trailing.equalToSuperview().inset(Constant.margin) } - divider.snp.makeConstraints { make in - make.height.equalTo(1) - make.horizontalEdges.bottom.equalToSuperview() - } + make.height.equalTo(1) + make.horizontalEdges.bottom.equalToSuperview() + } } + + @available(*, unavailable) + required init?(coder: NSCoder) { fatalError() } } diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/Common/DictionaryListCell.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/Common/DictionaryListCell.swift new file mode 100644 index 00000000..5e02d5af --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/Common/DictionaryListCell.swift @@ -0,0 +1,93 @@ +import UIKit + +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface + +import SnapKit + +final class DictionaryListCell: UICollectionViewCell { + private var onBookmarkTapped: (() -> Void)? + + let cellView = CardList() + + override init(frame: CGRect) { + super.init(frame: frame) + contentView.addSubview(cellView) + cellView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + + @available(*, unavailable) + required init?(coder: NSCoder) { fatalError() } + + override func prepareForReuse() { + super.prepareForReuse() + onBookmarkTapped = nil + cellView.onIconTapped = nil + cellView.setMainText(text: "") + cellView.setSubText(text: nil) + cellView.setSelected(isSelected: false) + } +} + +extension DictionaryListCell { + struct Input { + let type: DictionaryItemType + let mainText: String + let subText: String? + let imageUrl: String + let isBookmarked: Bool + } + + func inject( + type: CardList.CardListType, + input: Input, + indexPath: IndexPath, + collectionView: UICollectionView, + isMap: Bool = false, + onBookmarkTapped: @escaping () -> Void + ) { + cellView.setType(type: type) + cellView.setImage(image: UIImage(), backgroundColor: input.type.backgroundColor) + + if let url = URL(string: input.imageUrl) { + ImageLoader.shared.loadImage(url: url) { [weak self] image in + guard let self else { return } + if let currentIndex = collectionView.indexPath(for: self), currentIndex == indexPath { + if isMap { + self.cellView.setMapImage(image: image ?? UIImage(), backgroundColor: input.type.backgroundColor) + } else { + self.cellView.setImage(image: image ?? UIImage(), backgroundColor: input.type.backgroundColor) + } + } + } + } + + cellView.setMainText(text: input.mainText) + cellView.setSubText(text: input.subText) + cellView.setSelected(isSelected: input.isBookmarked) + self.onBookmarkTapped = onBookmarkTapped + cellView.onIconTapped = { [weak self] in + self?.onBookmarkTapped?() + } + } + + func updateBookmarkState(isBookmarked: Bool) { + cellView.setSelected(isSelected: isBookmarked) + } +} + +extension DictionaryItemType { + var backgroundColor: UIColor { + switch self { + case .item: .listItem + case .monster: .listMonster + case .map: .listMap + case .npc: .listNPC + case .quest: .listQuest + } + } +} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/FolderCell.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/Common/FolderCell.swift similarity index 70% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/FolderCell.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/Common/FolderCell.swift index 73e28adb..e159eca3 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/FolderCell.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/Common/FolderCell.swift @@ -1,11 +1,8 @@ -import UIKit - -import DesignSystem - -import RxSwift +import MLSDesignSystem import SnapKit +import UIKit -public class FolderCell: UICollectionViewCell { +final class FolderCell: UICollectionViewCell { private enum Constant { static let iconInset: CGFloat = 8 static let radius: CGFloat = 8 @@ -14,7 +11,7 @@ public class FolderCell: UICollectionViewCell { static let buttonSize: CGFloat = 40 } - private lazy var imageView: UIView = { + private lazy var imageContainerView: UIView = { let view = UIView() view.layer.cornerRadius = Constant.radius view.backgroundColor = .neutral200 @@ -28,7 +25,7 @@ public class FolderCell: UICollectionViewCell { private let iconView: UIImageView = { let view = UIImageView() - view.image = DesignSystemAsset.image(named: "bookmark")?.withRenderingMode(.alwaysTemplate) + view.image = DesignSystemAsset.image(named: "bookmark").withRenderingMode(.alwaysTemplate) view.tintColor = .neutral300 return view }() @@ -40,9 +37,9 @@ public class FolderCell: UICollectionViewCell { return label }() - public let checkBoxButton: UIButton = { + let checkBoxButton: UIButton = { let button = UIButton() - button.setImage(DesignSystemAsset.image(named: "checkSquareFill")?.withRenderingMode(.alwaysTemplate), for: .normal) + button.setImage(DesignSystemAsset.image(named: "checkSquareFill").withRenderingMode(.alwaysTemplate), for: .normal) button.tintColor = .neutral300 button.isUserInteractionEnabled = false return button @@ -54,59 +51,42 @@ public class FolderCell: UICollectionViewCell { return view }() - public var isChecked: Bool = false { - didSet { - checkBoxButton.tintColor = isChecked ? .primary700 : .neutral300 - } - } + var isChecked: Bool = false { + didSet { + checkBoxButton.tintColor = isChecked ? .primary700 : .neutral300 + } + } override init(frame: CGRect) { super.init(frame: frame) - addViews() - setupConstraints() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension FolderCell { - func addViews() { - contentView.addSubview(imageView) + contentView.addSubview(imageContainerView) contentView.addSubview(titleLabel) contentView.addSubview(checkBoxButton) contentView.addSubview(divider) - } - func setupConstraints() { - imageView.snp.makeConstraints { make in + imageContainerView.snp.makeConstraints { make in make.leading.verticalEdges.equalToSuperview().inset(Constant.margin) make.size.equalTo(Constant.buttonSize) } - titleLabel.snp.makeConstraints { make in - make.leading.equalTo(imageView.snp.trailing).offset(Constant.margin) + make.leading.equalTo(imageContainerView.snp.trailing).offset(Constant.margin) make.centerY.equalToSuperview() } - checkBoxButton.snp.makeConstraints { make in make.leading.equalTo(titleLabel.snp.trailing).offset(Constant.margin) make.centerY.equalToSuperview() make.trailing.equalToSuperview().inset(Constant.margin) make.size.equalTo(Constant.iconSize) } - divider.snp.makeConstraints { make in make.height.equalTo(1) make.horizontalEdges.bottom.equalToSuperview() } } -} -public extension FolderCell { + @available(*, unavailable) + required init?(coder: NSCoder) { fatalError() } + func inject(title: String?) { titleLabel.attributedText = .makeStyledString(font: .b_m_r, text: title, alignment: .left) } diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/Common/PageTabbarCell.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/Common/PageTabbarCell.swift new file mode 100644 index 00000000..7a8e1d04 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeature/Presentation/Common/PageTabbarCell.swift @@ -0,0 +1,38 @@ +import MLSDesignSystem +import SnapKit +import UIKit + +final class PageTabbarCell: UICollectionViewCell { + private let titleLabel: UILabel = { + let label = UILabel() + label.font = .b_m_r + label.textColor = .neutral600 + label.numberOfLines = 1 + label.adjustsFontSizeToFitWidth = true + label.minimumScaleFactor = 0.8 + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + contentView.addSubview(titleLabel) + titleLabel.snp.makeConstraints { make in + make.horizontalEdges.equalToSuperview() + make.centerY.equalToSuperview() + } + } + + @available(*, unavailable) + required init?(coder: NSCoder) { fatalError() } + + override var isSelected: Bool { + didSet { + titleLabel.font = isSelected ? .sub_m_b : .b_m_r + titleLabel.textColor = isSelected ? .textColor : .neutral600 + } + } + + func inject(title: String?) { + titleLabel.text = title + } +} diff --git a/MLS/Domain/DomainInterface/Entity/Bookmark/BookmarkResponse.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Entities/BookmarkResponse.swift similarity index 93% rename from MLS/Domain/DomainInterface/Entity/Bookmark/BookmarkResponse.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Entities/BookmarkResponse.swift index e9934cd5..24cb421e 100644 --- a/MLS/Domain/DomainInterface/Entity/Bookmark/BookmarkResponse.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Entities/BookmarkResponse.swift @@ -1,3 +1,5 @@ +import MLSDictionaryFeatureInterface + public struct BookmarkResponse: Equatable { public let name: String public let bookmarkId: Int diff --git a/MLS/Domain/DomainInterface/Entity/Collection/CollectionResponse.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Entities/CollectionResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/Collection/CollectionResponse.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Entities/CollectionResponse.swift diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Entities/CollectionSettingMenu.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Entities/CollectionSettingMenu.swift new file mode 100644 index 00000000..c02404cc --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Entities/CollectionSettingMenu.swift @@ -0,0 +1,24 @@ +import UIKit + +public enum CollectionSettingMenu: CaseIterable { + case editBookmark + case editName + case delete + case cancel + + public var title: String { + switch self { + case .editBookmark: return "컬렉션 북마크 수정" + case .editName: return "컬렉션 이름 수정" + case .delete: return "컬렉션 삭제" + case .cancel: return "취소" + } + } + + public var titleColor: UIColor { + switch self { + case .delete: return .red + default: return .black + } + } +} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/AddCollectionFactory.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/AddCollectionFactory.swift similarity index 72% rename from MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/AddCollectionFactory.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/AddCollectionFactory.swift index de84a042..029875dd 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/AddCollectionFactory.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/AddCollectionFactory.swift @@ -1,5 +1,4 @@ -import BaseFeature -import DomainInterface +import MLSCore public protocol AddCollectionFactory { func make(collection: CollectionResponse?) -> BaseViewController diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkListFactory.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/BookmarkListFactory.swift similarity index 71% rename from MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkListFactory.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/BookmarkListFactory.swift index 712c2201..3ca92bd4 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkListFactory.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/BookmarkListFactory.swift @@ -1,5 +1,5 @@ -import BaseFeature -import DomainInterface +import MLSCore +import MLSDictionaryFeatureInterface public protocol BookmarkListFactory { func make(type: DictionaryType, listType: DictionaryMainViewType) -> BaseViewController diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/BookmarkMainFactory.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/BookmarkMainFactory.swift new file mode 100644 index 00000000..f1e7b23a --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/BookmarkMainFactory.swift @@ -0,0 +1,7 @@ +import MLSCore + +import UIKit + +public protocol BookmarkMainFactory { + func make(bottomInset: CGFloat) -> BaseViewController +} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/BookmarkModalFactory.swift similarity index 81% rename from MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/BookmarkModalFactory.swift index 8c96281c..b26528a5 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/BookmarkModalFactory.swift @@ -1,5 +1,4 @@ -import BaseFeature -import DomainInterface +import MLSCore public protocol BookmarkModalFactory { func make(bookmarkIds: [Int]) -> BaseViewController diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkOnBoardingFactory.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/BookmarkOnBoardingFactory.swift similarity index 81% rename from MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkOnBoardingFactory.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/BookmarkOnBoardingFactory.swift index 665830ec..71aa08ff 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkOnBoardingFactory.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/BookmarkOnBoardingFactory.swift @@ -1,4 +1,4 @@ -import BaseFeature +import MLSCore public protocol BookmarkOnBoardingFactory { func make() -> BaseViewController diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionDetailFactory.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/CollectionDetailFactory.swift similarity index 77% rename from MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionDetailFactory.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/CollectionDetailFactory.swift index 67d08357..3d6a3f5c 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionDetailFactory.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/CollectionDetailFactory.swift @@ -1,5 +1,4 @@ -import BaseFeature -import DomainInterface +import MLSCore public protocol CollectionDetailFactory { func make(collection: CollectionResponse, onMoveToMain: (() -> Void)?) -> BaseViewController diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionEditFactory.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/CollectionEditFactory.swift similarity index 72% rename from MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionEditFactory.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/CollectionEditFactory.swift index affdca0b..e2986af9 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionEditFactory.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/CollectionEditFactory.swift @@ -1,5 +1,4 @@ -import BaseFeature -import DomainInterface +import MLSCore public protocol CollectionEditFactory { func make(bookmarks: [BookmarkResponse]) -> BaseViewController diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionListFactory.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/CollectionListFactory.swift similarity index 81% rename from MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionListFactory.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/CollectionListFactory.swift index 142b498d..6217a045 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionListFactory.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/CollectionListFactory.swift @@ -1,4 +1,4 @@ -import BaseFeature +import MLSCore public protocol CollectionListFactory { func make() -> BaseViewController diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionSettingFactory.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/CollectionSettingFactory.swift similarity index 79% rename from MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionSettingFactory.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/CollectionSettingFactory.swift index 934ed890..549fcafb 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionSettingFactory.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Factories/CollectionSettingFactory.swift @@ -1,4 +1,5 @@ -import BaseFeature +import MLSCore +import MLSDesignSystem public protocol CollectionSettingFactory { func make(setEditMenu: ((CollectionSettingMenu) -> Void)?) -> BaseViewController & ModalPresentable diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Repositories/BookmarkAuthRepository.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Repositories/BookmarkAuthRepository.swift new file mode 100644 index 00000000..35a32e84 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Repositories/BookmarkAuthRepository.swift @@ -0,0 +1,5 @@ +import RxSwift + +public protocol BookmarkAuthRepository { + func isLoggedIn() -> Observable +} diff --git a/MLS/Domain/DomainInterface/Repository/BookmarkRepository.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Repositories/BookmarkRepository.swift similarity index 87% rename from MLS/Domain/DomainInterface/Repository/BookmarkRepository.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Repositories/BookmarkRepository.swift index 7730602d..ed51aad8 100644 --- a/MLS/Domain/DomainInterface/Repository/BookmarkRepository.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Repositories/BookmarkRepository.swift @@ -1,21 +1,14 @@ -import Foundation +import MLSDictionaryFeatureInterface import RxSwift public protocol BookmarkRepository { - func setBookmark(bookmarkId: Int, type: DictionaryItemType) -> Observable - + func setBookmark(resourceId: Int, type: DictionaryItemType) -> Observable func deleteBookmark(bookmarkId: Int) -> Observable - func fetchBookmark(sort: String?) -> Observable<[BookmarkResponse]> - func fetchMonsterBookmark(minLevel: Int?, maxLevel: Int?, sort: String?) -> Observable<[BookmarkResponse]> - func fetchNPCBookmark(sort: String?) -> Observable<[BookmarkResponse]> - func fetchQuestBookmark(sort: String?) -> Observable<[BookmarkResponse]> - func fetchItemBookmark(jobId: Int?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, sort: String?) -> Observable<[BookmarkResponse]> - func fetchMapBookmark(sort: String?) -> Observable<[BookmarkResponse]> } diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Repositories/BookmarkUserDefaultsRepository.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Repositories/BookmarkUserDefaultsRepository.swift new file mode 100644 index 00000000..099d0d7e --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Repositories/BookmarkUserDefaultsRepository.swift @@ -0,0 +1,6 @@ +import RxSwift + +public protocol BookmarkUserDefaultsRepository { + func hasVisitedOnboarding() -> Observable + func markOnboardingVisited() -> Completable +} diff --git a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Repositories/CollectionRepository.swift similarity index 62% rename from MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift rename to MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Repositories/CollectionRepository.swift index a6033351..d839bb4b 100644 --- a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureInterface/Repositories/CollectionRepository.swift @@ -1,18 +1,10 @@ -import Foundation - import RxSwift -public protocol CollectionAPIRepository { - // 컬렉션 목록 조회 +public protocol CollectionRepository { func fetchCollectionList(sort: String?) -> Observable<[CollectionResponse]> - // 컬렉션 목록 추가 func createCollectionList(name: String) -> Completable - // 컬렉션 상세 조회 - func fetchCollectionUseCase(id: Int) -> Observable<[BookmarkResponse]> - + func fetchCollection(id: Int) -> Observable<[BookmarkResponse]> func updateCollectionName(collectionId: Int, name: String) -> Completable - func deleteCollection(collectionId: Int) -> Completable - func addCollectionAndBookmark(collectionIds: [Int], bookmarkIds: [Int]) -> Completable } diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockBookmarkAuthRepository.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockBookmarkAuthRepository.swift new file mode 100644 index 00000000..f7bef002 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockBookmarkAuthRepository.swift @@ -0,0 +1,12 @@ +import MLSBookmarkFeatureInterface +import RxSwift + +public final class MockBookmarkAuthRepository: BookmarkAuthRepository { + public var isLoggedInResult: Observable = .just(true) + + public init() {} + + public func isLoggedIn() -> Observable { + isLoggedInResult + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockBookmarkRepository.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockBookmarkRepository.swift new file mode 100644 index 00000000..4bae38a3 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockBookmarkRepository.swift @@ -0,0 +1,49 @@ +import MLSBookmarkFeatureInterface +import MLSDictionaryFeatureInterface + +import RxSwift + +public final class MockBookmarkRepository: BookmarkRepository { + public var setBookmarkResult: Observable = .just(0) + public var deleteBookmarkResult: Observable = .just(nil) + public var fetchBookmarkResult: Observable<[BookmarkResponse]> = .just([]) + public var fetchMonsterBookmarkResult: Observable<[BookmarkResponse]> = .just([]) + public var fetchItemBookmarkResult: Observable<[BookmarkResponse]> = .just([]) + public var fetchNPCBookmarkResult: Observable<[BookmarkResponse]> = .just([]) + public var fetchQuestBookmarkResult: Observable<[BookmarkResponse]> = .just([]) + public var fetchMapBookmarkResult: Observable<[BookmarkResponse]> = .just([]) + + public init() {} + + public func setBookmark(resourceId: Int, type: DictionaryItemType) -> Observable { + setBookmarkResult + } + + public func deleteBookmark(bookmarkId: Int) -> Observable { + deleteBookmarkResult + } + + public func fetchBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + fetchBookmarkResult + } + + public func fetchMonsterBookmark(minLevel: Int?, maxLevel: Int?, sort: String?) -> Observable<[BookmarkResponse]> { + fetchMonsterBookmarkResult + } + + public func fetchItemBookmark(jobId: Int?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, sort: String?) -> Observable<[BookmarkResponse]> { + fetchItemBookmarkResult + } + + public func fetchNPCBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + fetchNPCBookmarkResult + } + + public func fetchQuestBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + fetchQuestBookmarkResult + } + + public func fetchMapBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + fetchMapBookmarkResult + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockBookmarkUserDefaultsRepository.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockBookmarkUserDefaultsRepository.swift new file mode 100644 index 00000000..4a4e29f7 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockBookmarkUserDefaultsRepository.swift @@ -0,0 +1,17 @@ +import MLSBookmarkFeatureInterface +import RxSwift + +public final class MockBookmarkUserDefaultsRepository: BookmarkUserDefaultsRepository { + public var hasVisitedResult: Observable = .just(false) + public var markVisitedResult: Completable = .empty() + + public init() {} + + public func hasVisitedOnboarding() -> Observable { + hasVisitedResult + } + + public func markOnboardingVisited() -> Completable { + markVisitedResult + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockCollectionRepository.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockCollectionRepository.swift new file mode 100644 index 00000000..65a76757 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockCollectionRepository.swift @@ -0,0 +1,37 @@ +import MLSBookmarkFeatureInterface +import RxSwift + +public final class MockCollectionRepository: CollectionRepository { + public var fetchCollectionListResult: Observable<[CollectionResponse]> = .just([]) + public var createCollectionListResult: Completable = .empty() + public var fetchCollectionResult: Observable<[BookmarkResponse]> = .just([]) + public var updateCollectionNameResult: Completable = .empty() + public var deleteCollectionResult: Completable = .empty() + public var addCollectionAndBookmarkResult: Completable = .empty() + + public init() {} + + public func fetchCollectionList(sort: String?) -> Observable<[CollectionResponse]> { + fetchCollectionListResult + } + + public func createCollectionList(name: String) -> Completable { + createCollectionListResult + } + + public func fetchCollection(id: Int) -> Observable<[BookmarkResponse]> { + fetchCollectionResult + } + + public func updateCollectionName(collectionId: Int, name: String) -> Completable { + updateCollectionNameResult + } + + public func deleteCollection(collectionId: Int) -> Completable { + deleteCollectionResult + } + + public func addCollectionAndBookmark(collectionIds: [Int], bookmarkIds: [Int]) -> Completable { + addCollectionAndBookmarkResult + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockParseItemFilterResultUseCase.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockParseItemFilterResultUseCase.swift new file mode 100644 index 00000000..f3bc6635 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/MockParseItemFilterResultUseCase.swift @@ -0,0 +1,12 @@ +import MLSBookmarkFeatureInterface +import MLSDictionaryFeatureInterface + +public final class MockParseItemFilterResultUseCase: ParseItemFilterResultUseCase { + public var result: ItemFilterCriteria = ItemFilterCriteria(jobIds: [], startLevel: nil, endLevel: nil, categoryIds: []) + + public init() {} + + public func execute(results: [(String, String)]) -> ItemFilterCriteria { + result + } +} diff --git a/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/Stubs.swift b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/Stubs.swift new file mode 100644 index 00000000..284c63db --- /dev/null +++ b/MLS/MLSBookmarkFeature/Sources/MLSBookmarkFeatureTesting/Stubs.swift @@ -0,0 +1,77 @@ +import UIKit + +import MLSAuthFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface + +import RxRelay + +// MARK: - Stub Modal ViewController + +public final class StubModalViewController: BaseViewController, ModalPresentable { + public var modalHeight: CGFloat? +} + +// MARK: - Login + +public final class StubLoginFactory: LoginFactory { + public init() {} + public func make(exitRoute: LoginExitRoute, onLoginCompleted: (() -> Void)?) -> BaseViewController { + BaseViewController() + } +} + +// MARK: - Dictionary External Factories + +public final class StubDictionarySearchFactory: DictionarySearchFactory { + public init() {} + public func make() -> BaseViewController { BaseViewController() } +} + +public final class StubDictionaryNotificationFactory: DictionaryNotificationFactory { + public init() {} + public func make() -> BaseViewController { BaseViewController() } +} + +public final class StubDictionaryDetailFactory: DictionaryDetailFactory { + public init() {} + public func make( + type: DictionaryType, + id: Int, + bookmarkRelay: PublishRelay<(id: Int, newBookmarkId: Int?)>?, + loginRelay: PublishRelay? + ) -> BaseViewController { + BaseViewController() + } +} + +public final class StubSortedBottomSheetFactory: SortedBottomSheetFactory { + public init() {} + public func make( + sortedOptions: [SortType], + selectedIndex: Int, + onSelectedIndex: @escaping (Int) -> Void + ) -> BaseViewController & ModalPresentable { + StubModalViewController() + } +} + +public final class StubItemFilterBottomSheetFactory: ItemFilterBottomSheetFactory { + public init() {} + public func make(onFilterSelected: @escaping ([(String, String)]) -> Void) -> BaseViewController { + BaseViewController() + } +} + +public final class StubMonsterFilterBottomSheetFactory: MonsterFilterBottomSheetFactory { + public init() {} + public func make( + startLevel: Int, + endLevel: Int, + onFilterSelected: @escaping (Int, Int) -> Void + ) -> BaseViewController & ModalPresentable { + StubModalViewController() + } +} diff --git a/MLS/MLSBookmarkFeature/Tests/MLSBookmarkFeatureTests/BookmarkListReactorTests.swift b/MLS/MLSBookmarkFeature/Tests/MLSBookmarkFeatureTests/BookmarkListReactorTests.swift new file mode 100644 index 00000000..a568d224 --- /dev/null +++ b/MLS/MLSBookmarkFeature/Tests/MLSBookmarkFeatureTests/BookmarkListReactorTests.swift @@ -0,0 +1,79 @@ +@testable import MLSBookmarkFeature +import MLSBookmarkFeatureInterface +import MLSBookmarkFeatureTesting +import RxBlocking +import XCTest + +final class BookmarkListReactorTests: XCTestCase { + var authRepository: MockBookmarkAuthRepository! + var bookmarkRepository: MockBookmarkRepository! + var parseUseCase: MockParseItemFilterResultUseCase! + + override func setUp() { + super.setUp() + authRepository = MockBookmarkAuthRepository() + bookmarkRepository = MockBookmarkRepository() + parseUseCase = MockParseItemFilterResultUseCase() + } + + func test_viewWillAppear_whenLoggedIn_fetchesItems() throws { + let expectedItems = [ + BookmarkResponse(name: "테스트", bookmarkId: 1, originalId: 100, imageUrl: nil, type: .item, level: nil) + ] + authRepository.isLoggedInResult = .just(true) + bookmarkRepository.fetchBookmarkResult = .just(expectedItems) + + let reactor = BookmarkListReactor( + type: .total, + authRepository: authRepository, + bookmarkRepository: bookmarkRepository, + parseItemFilterResultUseCase: parseUseCase + ) + + reactor.action.onNext(.viewWillAppear) + + let items = try reactor.state.map(\.items).toBlocking(timeout: 1).first() + XCTAssertEqual(items, expectedItems) + } + + func test_viewWillAppear_whenLoggedOut_itemsEmpty() throws { + authRepository.isLoggedInResult = .just(false) + + let reactor = BookmarkListReactor( + type: .total, + authRepository: authRepository, + bookmarkRepository: bookmarkRepository, + parseItemFilterResultUseCase: parseUseCase + ) + + reactor.action.onNext(.viewWillAppear) + + let isLogin = try reactor.state.map(\.isLogin).toBlocking(timeout: 1).first() + XCTAssertEqual(isLogin, false) + XCTAssertTrue(reactor.currentState.items.isEmpty) + } + + func test_toggleBookmark_removesItemAndEmitsDeleteEvent() throws { + let item = BookmarkResponse(name: "테스트", bookmarkId: 1, originalId: 100, imageUrl: nil, type: .item, level: nil) + authRepository.isLoggedInResult = .just(true) + bookmarkRepository.fetchBookmarkResult = .just([item]) + bookmarkRepository.deleteBookmarkResult = .empty() + + let reactor = BookmarkListReactor( + type: .total, + authRepository: authRepository, + bookmarkRepository: bookmarkRepository, + parseItemFilterResultUseCase: parseUseCase + ) + + reactor.action.onNext(.viewWillAppear) + reactor.action.onNext(.toggleBookmark(100)) + + let event = try reactor.state.map(\.uiEvent).toBlocking(timeout: 1).first() + if case .delete(let deletedItem) = event { + XCTAssertEqual(deletedItem.originalId, 100) + } else { + XCTFail("Expected delete event") + } + } +} diff --git a/MLS/MLSBookmarkFeatureExample/AppDelegate.swift b/MLS/MLSBookmarkFeatureExample/AppDelegate.swift new file mode 100644 index 00000000..eb30b59f --- /dev/null +++ b/MLS/MLSBookmarkFeatureExample/AppDelegate.swift @@ -0,0 +1,16 @@ +import MLSDesignSystem +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + FontManager.registerFonts() + return true + } + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {} +} diff --git a/MLS/Presentation/AuthFeature/AuthFeatureDemo/Assets.xcassets/AccentColor.colorset/Contents.json b/MLS/MLSBookmarkFeatureExample/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from MLS/Presentation/AuthFeature/AuthFeatureDemo/Assets.xcassets/AccentColor.colorset/Contents.json rename to MLS/MLSBookmarkFeatureExample/Assets.xcassets/AccentColor.colorset/Contents.json diff --git "a/MLS/MLSBookmarkFeatureExample/Assets.xcassets/AppIcon.appiconset/ChatGPT Image 2026\353\205\204 5\354\233\224 29\354\235\274 \354\230\244\354\240\204 10_33_07 (1).png" "b/MLS/MLSBookmarkFeatureExample/Assets.xcassets/AppIcon.appiconset/ChatGPT Image 2026\353\205\204 5\354\233\224 29\354\235\274 \354\230\244\354\240\204 10_33_07 (1).png" new file mode 100644 index 00000000..0e39b28a Binary files /dev/null and "b/MLS/MLSBookmarkFeatureExample/Assets.xcassets/AppIcon.appiconset/ChatGPT Image 2026\353\205\204 5\354\233\224 29\354\235\274 \354\230\244\354\240\204 10_33_07 (1).png" differ diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/MLS/MLSBookmarkFeatureExample/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 88% rename from MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/AppIcon.appiconset/Contents.json rename to MLS/MLSBookmarkFeatureExample/Assets.xcassets/AppIcon.appiconset/Contents.json index 5aca7cac..6834c3e4 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/MLS/MLSBookmarkFeatureExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "appIcon.png", + "filename" : "ChatGPT Image 2026년 5월 29일 오전 10_33_07 (1).png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/MLS/Presentation/AuthFeature/AuthFeatureDemo/Assets.xcassets/Contents.json b/MLS/MLSBookmarkFeatureExample/Assets.xcassets/Contents.json similarity index 100% rename from MLS/Presentation/AuthFeature/AuthFeatureDemo/Assets.xcassets/Contents.json rename to MLS/MLSBookmarkFeatureExample/Assets.xcassets/Contents.json diff --git a/MLS/Presentation/AuthFeature/AuthFeatureDemo/Base.lproj/LaunchScreen.storyboard b/MLS/MLSBookmarkFeatureExample/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from MLS/Presentation/AuthFeature/AuthFeatureDemo/Base.lproj/LaunchScreen.storyboard rename to MLS/MLSBookmarkFeatureExample/Base.lproj/LaunchScreen.storyboard diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Info.plist b/MLS/MLSBookmarkFeatureExample/Info.plist similarity index 90% rename from MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Info.plist rename to MLS/MLSBookmarkFeatureExample/Info.plist index 7c5f0be1..bc7411a1 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Info.plist +++ b/MLS/MLSBookmarkFeatureExample/Info.plist @@ -2,8 +2,8 @@ - ITSAppUsesNonExemptEncryption - + UILaunchStoryboardName + LaunchScreen UIApplicationSceneManifest UIApplicationSupportsMultipleScenes diff --git a/MLS/MLSBookmarkFeatureExample/MenuViewController.swift b/MLS/MLSBookmarkFeatureExample/MenuViewController.swift new file mode 100644 index 00000000..dd7fcc80 --- /dev/null +++ b/MLS/MLSBookmarkFeatureExample/MenuViewController.swift @@ -0,0 +1,133 @@ +import UIKit + +import MLSBookmarkFeature +import MLSBookmarkFeatureInterface +import MLSBookmarkFeatureTesting +import MLSCore +import MLSDictionaryFeatureInterface + +final class MenuViewController: UITableViewController { + + private struct MenuItem { + let title: String + let subtitle: String + let action: (UINavigationController) -> Void + } + + private lazy var items: [MenuItem] = [ + MenuItem( + title: "BookmarkMain", + subtitle: "북마크 메인 (탭 + 페이지)" + ) { nav in + let factory = DIContainer.resolve(type: BookmarkMainFactory.self) + let vc = factory.make(bottomInset: 0) + let child = UINavigationController(rootViewController: vc) + child.navigationBar.isHidden = true + child.modalPresentationStyle = .fullScreen + nav.present(child, animated: true) + }, + MenuItem( + title: "BookmarkList – 전체", + subtitle: "전체 북마크 리스트" + ) { nav in + let factory = DIContainer.resolve(type: BookmarkListFactory.self) + let vc = factory.make(type: .total, listType: .bookmark) + nav.pushViewController(vc, animated: true) + }, + MenuItem( + title: "BookmarkList – 아이템", + subtitle: "아이템 북마크 리스트 (필터 포함)" + ) { nav in + let factory = DIContainer.resolve(type: BookmarkListFactory.self) + let vc = factory.make(type: .item, listType: .bookmark) + nav.pushViewController(vc, animated: true) + }, + MenuItem( + title: "BookmarkList – 몬스터", + subtitle: "몬스터 북마크 리스트 (필터 포함)" + ) { nav in + let factory = DIContainer.resolve(type: BookmarkListFactory.self) + let vc = factory.make(type: .monster, listType: .bookmark) + nav.pushViewController(vc, animated: true) + }, + MenuItem( + title: "CollectionList", + subtitle: "컬렉션 목록" + ) { nav in + let factory = DIContainer.resolve(type: CollectionListFactory.self) + let vc = factory.make() + nav.pushViewController(vc, animated: true) + }, + MenuItem( + title: "CollectionDetail", + subtitle: "컬렉션 상세 (빈 컬렉션)" + ) { nav in + let factory = DIContainer.resolve(type: CollectionDetailFactory.self) + let stub = CollectionResponse( + collectionId: 1, + name: "내 컬렉션", + createdAt: [2024, 1, 1], + recentBookmarks: [] + ) + let vc = factory.make(collection: stub, onMoveToMain: nil) + nav.pushViewController(vc, animated: true) + }, + MenuItem( + title: "BookmarkModal", + subtitle: "컬렉션에 북마크 추가 모달" + ) { nav in + let factory = DIContainer.resolve(type: BookmarkModalFactory.self) + let vc = factory.make(bookmarkIds: [1, 2, 3]) + vc.modalPresentationStyle = .pageSheet + if let sheet = vc.sheetPresentationController { + sheet.detents = [.medium(), .large()] + sheet.prefersGrabberVisible = true + } + nav.present(vc, animated: true) + }, + MenuItem( + title: "AddCollection", + subtitle: "새 컬렉션 추가 모달" + ) { nav in + let factory = DIContainer.resolve(type: AddCollectionFactory.self) + let vc = factory.make(collection: nil) + nav.present(vc, animated: true) + }, + MenuItem( + title: "BookmarkOnBoarding", + subtitle: "온보딩 화면" + ) { nav in + let factory = DIContainer.resolve(type: BookmarkOnBoardingFactory.self) + let vc = factory.make() + vc.modalPresentationStyle = .fullScreen + nav.present(vc, animated: true) + } + ] + + override func viewDidLoad() { + super.viewDidLoad() + title = "Bookmark Feature" + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + items.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + let item = items[indexPath.row] + var config = cell.defaultContentConfiguration() + config.text = item.title + config.secondaryText = item.subtitle + cell.contentConfiguration = config + cell.accessoryType = .disclosureIndicator + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + guard let nav = navigationController else { return } + items[indexPath.row].action(nav) + } +} diff --git a/MLS/MLSBookmarkFeatureExample/SceneDelegate.swift b/MLS/MLSBookmarkFeatureExample/SceneDelegate.swift new file mode 100644 index 00000000..aefd3329 --- /dev/null +++ b/MLS/MLSBookmarkFeatureExample/SceneDelegate.swift @@ -0,0 +1,86 @@ +import MLSBookmarkFeature +import MLSBookmarkFeatureInterface +import MLSBookmarkFeatureTesting +import MLSCore +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + guard let windowScene = (scene as? UIWindowScene) else { return } + + registerDependencies() + + let window = UIWindow(windowScene: windowScene) + window.backgroundColor = .white + self.window = window + + let menuVC = MenuViewController() + let nav = UINavigationController(rootViewController: menuVC) + window.rootViewController = nav + window.makeKeyAndVisible() + } + + private func registerDependencies() { + let mockBookmarkRepository = MockBookmarkRepository() + let mockCollectionRepository = MockCollectionRepository() + let mockAuthRepository = MockBookmarkAuthRepository() + let mockUserDefaultsRepository = MockBookmarkUserDefaultsRepository() + let mockParseUseCase = MockParseItemFilterResultUseCase() + + let addCollectionFactory = AddCollectionFactoryImpl(collectionRepository: mockCollectionRepository) + let bookmarkModalFactory = BookmarkModalFactoryImpl( + addCollectionFactory: addCollectionFactory, + collectionRepository: mockCollectionRepository + ) + let collectionSettingFactory = CollectionSettingFactoryImpl() + let collectionEditFactory = CollectionEditFactoryImpl(bookmarkModalFactory: bookmarkModalFactory) + let collectionDetailFactory = CollectionDetailFactoryImpl( + bookmarkModalFactory: bookmarkModalFactory, + collectionSettingFactory: collectionSettingFactory, + addCollectionFactory: addCollectionFactory, + collectionEditFactory: collectionEditFactory, + dictionaryDetailFactory: StubDictionaryDetailFactory(), + collectionRepository: mockCollectionRepository + ) + let collectionListFactory = CollectionListFactoryImpl( + collectionRepository: mockCollectionRepository, + addCollectionFactory: addCollectionFactory, + collectionDetailFactory: collectionDetailFactory, + sortedBottomSheetFactory: StubSortedBottomSheetFactory() + ) + let bookmarkListFactory = BookmarkListFactoryImpl( + itemFilterFactory: StubItemFilterBottomSheetFactory(), + monsterFilterFactory: StubMonsterFilterBottomSheetFactory(), + sortedFactory: StubSortedBottomSheetFactory(), + bookmarkModalFactory: bookmarkModalFactory, + loginFactory: StubLoginFactory(), + dictionaryDetailFactory: StubDictionaryDetailFactory(), + collectionEditFactory: collectionEditFactory, + authRepository: mockAuthRepository, + bookmarkRepository: mockBookmarkRepository, + parseItemFilterResultUseCase: mockParseUseCase + ) + let onBoardingFactory = BookmarkOnBoardingFactoryImpl() + + DIContainer.register(type: BookmarkMainFactory.self) { + BookmarkMainFactoryImpl( + authRepository: mockAuthRepository, + userDefaultsRepository: mockUserDefaultsRepository, + onBoardingFactory: onBoardingFactory, + bookmarkListFactory: bookmarkListFactory, + collectionListFactory: collectionListFactory, + searchFactory: StubDictionarySearchFactory(), + notificationFactory: StubDictionaryNotificationFactory(), + loginFactory: StubLoginFactory() + ) + } + DIContainer.register(type: BookmarkListFactory.self) { bookmarkListFactory } + DIContainer.register(type: CollectionListFactory.self) { collectionListFactory } + DIContainer.register(type: CollectionDetailFactory.self) { collectionDetailFactory } + DIContainer.register(type: BookmarkModalFactory.self) { bookmarkModalFactory } + DIContainer.register(type: AddCollectionFactory.self) { addCollectionFactory } + DIContainer.register(type: BookmarkOnBoardingFactory.self) { onBoardingFactory } + } +} diff --git a/MLS/MLSBookmarkFeatureExample/Stubs.swift b/MLS/MLSBookmarkFeatureExample/Stubs.swift new file mode 100644 index 00000000..c1474e27 --- /dev/null +++ b/MLS/MLSBookmarkFeatureExample/Stubs.swift @@ -0,0 +1 @@ +// Stubs are defined in MLSBookmarkFeatureTesting diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Shared/WebViewController.swift b/MLS/MLSCore/Sources/MLSCore/BaseController/WebViewController.swift similarity index 100% rename from MLS/Presentation/BaseFeature/BaseFeature/Shared/WebViewController.swift rename to MLS/MLSCore/Sources/MLSCore/BaseController/WebViewController.swift diff --git a/MLS/MLSCore/Sources/MLSCore/Extension/String+.swift b/MLS/MLSCore/Sources/MLSCore/Extension/String+.swift new file mode 100644 index 00000000..72f0b89c --- /dev/null +++ b/MLS/MLSCore/Sources/MLSCore/Extension/String+.swift @@ -0,0 +1,36 @@ +import Foundation + +public extension String { + func isOnlyKorean() -> Bool { + return !self.isEmpty && + self.allSatisfy { char in + char.unicodeScalars.allSatisfy { scalar in + switch scalar.value { + case 0x30...0x39: + return true + case 0xac00...0xd7a3: + return true + case 0x3131...0x3163: + return true + default: + return false + } + } + } + } + + func toDisplayDateString() -> String { + let inputFormatter = DateFormatter() + inputFormatter.locale = Locale(identifier: "ko_KR") + inputFormatter.timeZone = TimeZone(identifier: "Asia/Seoul") + inputFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" + + guard let date = inputFormatter.date(from: self) else { return self } + + let outputFormatter = DateFormatter() + outputFormatter.locale = Locale(identifier: "ko_KR") + outputFormatter.dateFormat = "yyyy.MM.dd HH:mm" + + return outputFormatter.string(from: date) + } +} diff --git a/MLS/MLSCore/Sources/MLSCore/ImageLoader/ImageLoader.swift b/MLS/MLSCore/Sources/MLSCore/ImageLoader/ImageLoader.swift index 3e2bbc3e..c26f3b9c 100644 --- a/MLS/MLSCore/Sources/MLSCore/ImageLoader/ImageLoader.swift +++ b/MLS/MLSCore/Sources/MLSCore/ImageLoader/ImageLoader.swift @@ -34,7 +34,7 @@ public final class ImageLoader: @unchecked Sendable { private init() {} - public func loadImage(stringURL: String?, defaultImage: UIImage? = nil, completion: @escaping @Sendable (UIImage?) -> Void) { + @MainActor public func loadImage(stringURL: String?, defaultImage: UIImage? = nil, completion: @MainActor @escaping @Sendable (UIImage?) -> Void) { guard let stringURL, let url = URL(string: stringURL), ["http", "https"].contains(url.scheme?.lowercased() ?? "") @@ -52,7 +52,7 @@ public final class ImageLoader: @unchecked Sendable { /// - stringURL: 이미지 URL 문자열 /// - defaultImage: 로드 실패 시 반환할 기본 이미지 /// - completion: 로드 완료 후 호출되는 클로저 - public func loadImage(url: URL?, defaultImage: UIImage? = nil, completion: @escaping @Sendable (UIImage?) -> Void) { + @MainActor public func loadImage(url: URL?, defaultImage: UIImage? = nil, completion: @MainActor @escaping @Sendable (UIImage?) -> Void) { loadImage(url: url) { result in DispatchQueue.main.async { switch result { diff --git a/MLS/Data/Data/Network/DTO/APIDefaultResponseDTO.swift b/MLS/MLSCore/Sources/MLSCore/Network/APIDefaultResponseDTO.swift similarity index 89% rename from MLS/Data/Data/Network/DTO/APIDefaultResponseDTO.swift rename to MLS/MLSCore/Sources/MLSCore/Network/APIDefaultResponseDTO.swift index b190ce1f..8fd6e60b 100644 --- a/MLS/Data/Data/Network/DTO/APIDefaultResponseDTO.swift +++ b/MLS/MLSCore/Sources/MLSCore/Network/APIDefaultResponseDTO.swift @@ -1,7 +1,5 @@ import Foundation -import DomainInterface - public struct APIDefaultResponseDTO: Decodable { public let success: Bool public let code: String? diff --git a/MLS/Data/Data/Network/DTO/EmptyResponseDTO.swift b/MLS/MLSCore/Sources/MLSCore/Network/EmptyResponseDTO.swift similarity index 61% rename from MLS/Data/Data/Network/DTO/EmptyResponseDTO.swift rename to MLS/MLSCore/Sources/MLSCore/Network/EmptyResponseDTO.swift index 2f92153f..7dc2a765 100644 --- a/MLS/Data/Data/Network/DTO/EmptyResponseDTO.swift +++ b/MLS/MLSCore/Sources/MLSCore/Network/EmptyResponseDTO.swift @@ -1,5 +1,5 @@ public struct EmptyResponseDTO: Decodable { - func toBookmarkDomain() -> Int? { + public func toBookmarkDomain() -> Int? { return nil } } diff --git a/MLS/MLSCore/Sources/MLSCore/Network/NetworkProviderImpl.swift b/MLS/MLSCore/Sources/MLSCore/Network/NetworkProviderImpl.swift index 38b7be28..70b8060f 100644 --- a/MLS/MLSCore/Sources/MLSCore/Network/NetworkProviderImpl.swift +++ b/MLS/MLSCore/Sources/MLSCore/Network/NetworkProviderImpl.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift -public final class NetworkProviderImpl: NetworkProvider { +public final class NetworkProviderImpl: NetworkProvider, Loggable { private let session: URLSession @@ -17,32 +17,40 @@ public final class NetworkProviderImpl: NetworkProvider { public func requestData(endPoint: T, interceptor: Interceptor?) -> Observable { return Observable.create { [weak self] observer in - print("🚀 requestData: 요청 시작 - \(endPoint)") + self?.logDebug("Core requestData: 요청 시작 - \(endPoint)") self?.sendRequest(endPoint: endPoint, interceptor: interceptor, completion: { result in switch result { case .success(let data): - print("✅ requestData: 응답 수신") + self?.logDebug("Core requestData: 응답 수신") if let data = data { - print("📦 requestData: 응답 데이터 있음 - \(String(data: data, encoding: .utf8) ?? "디코딩 실패")") + self?.logDebug("Core requestData: 응답 데이터 있음 - \(String(data: data, encoding: .utf8) ?? "디코딩 실패")") do { - let decoded = try JSONDecoder().decode(T.Response.self, from: data) - print("🎯 requestData: 디코딩 성공 - \(decoded)") - observer.onNext(decoded) + let decoded = try JSONDecoder().decode(APIDefaultResponseDTO.self, from: data) + self?.logDebug("Core requestData: 디코딩 성공 - \(decoded)") + if let decodedData = decoded.data { + observer.onNext(decodedData) + } else { + if T.Response.self == EmptyResponseDTO.self { + observer.onNext(EmptyResponseDTO() as! T.Response) + } else { + observer.onError(NetworkError.invalidResponse) + } + } observer.onCompleted() } catch { - print("❌ requestData: 디코딩 실패 - \(error)") + self?.logError("Core requestData: 디코딩 실패 - \(error)") observer.onError(NetworkError.decodeError(error)) } } else { - print("⚠️ requestData: 응답 데이터 없음") + self?.logWarning("Core requestData: 응답 데이터 없음") observer.onError(NetworkError.noData) } case .failure(let error): - print("🔥 requestData: 네트워크 실패 - \(error)") + self?.logError("🔥 requestData: 네트워크 실패 - \(error)") observer.onError(error) } }) @@ -53,7 +61,7 @@ public final class NetworkProviderImpl: NetworkProvider { errors .enumerated() .flatMap { attempt, error -> Observable in - print("🔁 requestData: 재시도 \(attempt + 1)회 - 에러: \(error)") + self.logWarning("🔁 requestData: 재시도 \(attempt + 1)회 - 에러: \(error)") if attempt < self.retryAttempt, let networkError = error as? NetworkError, networkError == .retry { return Observable.just(()) } else { @@ -94,10 +102,6 @@ public final class NetworkProviderImpl: NetworkProvider { } private extension NetworkProviderImpl { - /// 엔드 포인트를 이용하여 요청을 보내기 위한 함수 - /// - Parameters: - /// - endPoint: 요청을 위한 엔드포인트 객체 - /// - completion: 응답 결과 func sendRequest(endPoint: T, interceptor: Interceptor?, completion: @escaping (Result) -> Void) { do { var request = try endPoint.getUrlRequest() @@ -113,7 +117,7 @@ private extension NetworkProviderImpl { completion(.success(data)) case .failure(let error): completion(.failure(error)) - print("API 통신에러 \(error)") + logError("API 통신에러 \(error)") } } task.resume() @@ -122,12 +126,6 @@ private extension NetworkProviderImpl { } } - /// 통신간의 유효성 검사를 위한 함수 - /// - Parameters: - /// - data: 통신 결과로 돌려받은 데이터 - /// - response: 상태코드를 포함한 통신 응답 - /// - error: 통신간에 발생한 에러 - /// - Returns: 유효성검사 결과에 따른 데이터와 에러 func checkValidation( data: Data?, response: URLResponse?, @@ -135,7 +133,6 @@ private extension NetworkProviderImpl { interceptor: Interceptor? ) -> Result { - // 1️⃣ 네트워크 레벨 에러 먼저 체크 if let error { if let urlError = error as? URLError, urlError.code == .unsupportedURL { return .failure(.urlRequest(error)) @@ -143,14 +140,11 @@ private extension NetworkProviderImpl { return .failure(.network(error)) } - // 2️⃣ HTTP 응답 객체 확인 guard let httpResponse = response as? HTTPURLResponse else { return .failure(.httpError) } - // 3️⃣ 상태 코드 기반 검사 guard (200 ... 299).contains(httpResponse.statusCode) else { - // ❗️여기서만 인터셉터 개입 if let interceptor = interceptor, interceptor.retry(data: data, response: response, error: error) { return .failure(.retry) @@ -160,7 +154,6 @@ private extension NetworkProviderImpl { return .failure(.statusError(httpResponse.statusCode, errorMessage)) } - // ✅ 성공 응답 return .success(data) } } diff --git a/MLS/MLSCore/Sources/MLSCore/Network/TokenInterceptor.swift b/MLS/MLSCore/Sources/MLSCore/Network/TokenInterceptor.swift new file mode 100644 index 00000000..9df5df11 --- /dev/null +++ b/MLS/MLSCore/Sources/MLSCore/Network/TokenInterceptor.swift @@ -0,0 +1,32 @@ +import Foundation +import Security + +public final class TokenInterceptor: Interceptor { + + public init() {} + + public func adapt(_ request: URLRequest) -> URLRequest { + guard let token = fetchAccessToken() else { return request } + var adapted = request + adapted.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") + return adapted + } + + public func retry(data: Data?, response: URLResponse?, error: Error?) -> Bool { + return false + } + + private func fetchAccessToken() -> String? { + let query: NSDictionary = [ + kSecClass: kSecClassGenericPassword, + kSecAttrService: "keyChain", + kSecAttrAccount: "accessToken", + kSecReturnData: true, + kSecMatchLimit: kSecMatchLimitOne + ] + var ref: AnyObject? + guard SecItemCopyMatching(query, &ref) == errSecSuccess, + let data = ref as? Data else { return nil } + return String(data: data, encoding: .utf8) + } +} diff --git a/MLS/MLSAuthFeature/Sources/MLSAuthFeatureInterface/Entities/LoginExitRoute.swift b/MLS/MLSCore/Sources/MLSCore/Utils/LoginExitRoute.swift similarity index 100% rename from MLS/MLSAuthFeature/Sources/MLSAuthFeatureInterface/Entities/LoginExitRoute.swift rename to MLS/MLSCore/Sources/MLSCore/Utils/LoginExitRoute.swift index 597ae3e7..ea927460 100644 --- a/MLS/MLSAuthFeature/Sources/MLSAuthFeatureInterface/Entities/LoginExitRoute.swift +++ b/MLS/MLSCore/Sources/MLSCore/Utils/LoginExitRoute.swift @@ -1,4 +1,4 @@ public enum LoginExitRoute { - case home case pop + case home } diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/CardList.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/CardList.swift index f461d117..73c3199e 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/CardList.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/CardList.swift @@ -47,6 +47,9 @@ public final class CardList: UIView { static let iconSize: CGFloat = 24 static let mapImageSize: CGFloat = 40 static let tagHeight: CGFloat = 24 + static let tagWidth: CGFloat = 45 + static let tagHorizontalInset: CGFloat = 10 + static let tagVerticalInset: CGFloat = 4 } // MARK: - Properties @@ -78,7 +81,7 @@ public final class CardList: UIView { public let imageView = ItemImageView(image: nil, cornerRadius: Constant.imageRadius, inset: Constant.imageInset, backgroundColor: .listMap) private lazy var textLabelStackView: UIStackView = { - let view = UIStackView(arrangedSubviews: [rankTag, mainTextLabel, subTextLabel]) + let view = UIStackView(arrangedSubviews: [rankContainer, mainTextLabel, subTextLabel]) view.axis = .vertical view.spacing = Constant.stackViewSpacing view.alignment = .leading @@ -129,7 +132,21 @@ public final class CardList: UIView { private let badge = Badge(style: .currentQuest) - private let rankTag = TagChip(style: .text, text: "순위") + private let rankContainer = { + let view = UIView() + view.backgroundColor = .primary50 + view.layer.cornerRadius = 12 + view.clipsToBounds = true + return view + }() + + private let rankTag = { + let label = UILabel() + label.textAlignment = .center + label.font = .korFont(style: .semiBold, size: 14) + label.textColor = .primary700 + return label + }() public init() { super.init(frame: .zero) @@ -153,6 +170,7 @@ private extension CardList { addSubview(iconButton) addSubview(dropInfoStack) addSubview(badge) + rankContainer.addSubview(rankTag) } func setupConstraints() { @@ -177,8 +195,14 @@ private extension CardList { make.trailing.equalToSuperview().inset(Constant.cardTrailingInset) } - rankTag.snp.makeConstraints { make in + rankContainer.snp.makeConstraints { make in make.height.equalTo(Constant.tagHeight) + make.width.equalTo(Constant.tagWidth) + } + + rankTag.snp.makeConstraints { make in + make.horizontalEdges.equalToSuperview().inset(Constant.tagHorizontalInset) + make.verticalEdges.equalToSuperview().inset(Constant.tagVerticalInset) } iconButton.snp.makeConstraints { make in @@ -250,29 +274,30 @@ public extension CardList { iconButton.isHidden = true dropInfoStack.isHidden = true badge.isHidden = true - rankTag.isHidden = true + rankContainer.isHidden = true case .detailStackText: iconButton.isHidden = true dropInfoStack.isHidden = false badge.isHidden = true - rankTag.isHidden = true + rankContainer.isHidden = true case .detailStackBadge(let type): iconButton.isHidden = true dropInfoStack.isHidden = false badge.isHidden = false - rankTag.isHidden = true + rankContainer.isHidden = true badge.update(style: type) case .recommended(let rank): - iconButton.isHidden = true + iconButton.isHidden = false dropInfoStack.isHidden = true subTextLabel.isHidden = true badge.isHidden = true - rankTag.isHidden = false + rankContainer.isHidden = false rankTag.text = "\(rank)위" default: iconButton.isHidden = false dropInfoStack.isHidden = true badge.isHidden = true + rankContainer.isHidden = true } } diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/DictionaryDetailListView.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/DictionaryDetailListView.swift index a3b9c42e..0c0c16a9 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/DictionaryDetailListView.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/DictionaryDetailListView.swift @@ -76,7 +76,7 @@ private extension DictionaryDetailListView { func makeButton(label: UILabel) -> UIButton { let button = UIButton() - let icon = UIImageView(image: UIImage(named: "rightArrow")) + let icon = UIImageView(image: DesignSystemAsset.image(named: "rightArrow")) button.addSubview(label) button.addSubview(icon) diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/DropDownBox/DropDownBox.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/DropDownBox/DropDownBox.swift index 7be4f3f8..7f885298 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/DropDownBox/DropDownBox.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/DropDownBox/DropDownBox.swift @@ -44,7 +44,7 @@ public final class DropDownBox: UIStackView { tableView.layer.cornerRadius = 8 tableView.layer.borderColor = UIColor.neutral300.cgColor tableView.separatorStyle = .none - tableView.isScrollEnabled = false + tableView.isScrollEnabled = true tableView.contentInset = UIEdgeInsets(top: 4, left: 0, bottom: 4, right: 0) tableView.register(DropDownBoxCell.self, forCellReuseIdentifier: "DropDownCell") return tableView @@ -124,7 +124,8 @@ private extension DropDownBox { isExpanded.toggle() tableView.isHidden = !isExpanded iconButton.setImage(isExpanded ? DesignSystemAsset.image(named: "arrowDropUp") : DesignSystemAsset.image(named: "arrowDropdown"), for: .normal) - let height = CGFloat(items.count) * 44 + tableView.contentInset.top + tableView.contentInset.bottom + let visibleCount = min(items.count, 4) + let height = CGFloat(visibleCount) * 44 + tableView.contentInset.top + tableView.contentInset.bottom tableViewHeightConstraint?.update(offset: isExpanded ? height : 0) } @@ -169,6 +170,17 @@ extension DropDownBox: UITableViewDataSource, UITableViewDelegate { } } +// MARK: - Public Methods +public extension DropDownBox { + func selectItem(id: Int) { + guard let index = items.firstIndex(where: { $0.id == id }) else { return } + selectedIndex = index + inputBox.textField.attributedText = .makeStyledString( + font: .b_m_r, text: items[index].name, alignment: .left, lineHeight: 1.0 + ) + } +} + // MARK: Model extension DropDownBox { public struct Item { diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/ErrorMessage.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/ErrorMessage.swift index 973bb586..d4a3dca1 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/ErrorMessage.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/ErrorMessage.swift @@ -15,7 +15,7 @@ public final class ErrorMessage: UIView { // MARK: - Properties private let iconView: UIImageView = { let view = UIImageView() - view.image = UIImage(named: "error") + view.image = DesignSystemAsset.image(named: "error") return view }() diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/FloatingActionButton.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/FloatingActionButton.swift index 841459a8..ee884ebf 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/FloatingActionButton.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/FloatingActionButton.swift @@ -22,7 +22,7 @@ public final class FloatingActionButton: UIButton { // MARK: - SetUp private extension FloatingActionButton { func configureUI() { - setImage(UIImage(named: "fab"), for: .normal) + setImage(DesignSystemAsset.image(named: "fab"), for: .normal) layer.cornerRadius = 24 clipsToBounds = true addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/GuideAlert.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/GuideAlert.swift index 13e1f05e..11571cbb 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/GuideAlert.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/GuideAlert.swift @@ -18,7 +18,7 @@ public class GuideAlert: UIView { // MARK: - Components private let warningIconView: UIImageView = { let view = UIImageView() - view.image = UIImage(named: "warning") + view.image = DesignSystemAsset.image(named: "warning") return view }() @@ -34,7 +34,7 @@ public class GuideAlert: UIView { // MARK: - init public init(mainText: String, ctaText: String, cancelText: String?, ctaRatio: Double = 0.7) { - mainTextLabel.attributedText = .makeStyledString(font: .sub_l_b, text: mainText) + mainTextLabel.attributedText = .makeStyledString(font: .sub_m_sb, text: mainText) self.ctaButton = CommonButton(style: .normal, title: ctaText, disabledTitle: nil) self.cancelButton = cancelText.map { CommonButton(style: .border, title: $0, disabledTitle: nil) } mainTextLabel.numberOfLines = 0 diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Tabbar/BottomTabBar.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Tabbar/BottomTabBar.swift index 03dc2c5d..832a69db 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Tabbar/BottomTabBar.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Tabbar/BottomTabBar.swift @@ -6,6 +6,11 @@ import SnapKit public struct TabItem { var title: String var icon: UIImage + + public init(title: String, icon: UIImage) { + self.title = title + self.icon = icon + } } internal final class BottomTabBar: UIStackView { diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Tabbar/BottomTabBarController.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Tabbar/BottomTabBarController.swift index 0a3ebd03..06be2ab5 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Tabbar/BottomTabBarController.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Tabbar/BottomTabBarController.swift @@ -6,21 +6,40 @@ public final class BottomTabBarController: UITabBarController { // MARK: - Type private enum Constant { static let horizontalInset: CGFloat = 24 + static let commentBottomMargin: CGFloat = -8 + static let commentWidth: CGFloat = 152 + static let commentHeight: CGFloat = 28 + static let buttonSize: CGFloat = 64 + static let arrowInset: CGFloat = 16 + @MainActor static let spacing: CGFloat = (UIScreen.main.bounds.width - (horizontalInset * 2) - (buttonSize * 4)) / 3 + @MainActor static let commentLeadingOffset: CGFloat = horizontalInset + buttonSize + spacing + (buttonSize / 2) - arrowInset } // MARK: - Components private let divider = DividerView() private let tabItems: [TabItem] private let customTabBar: BottomTabBar + private let tabBarBackground: UIView = { + let view = UIView() + view.backgroundColor = .systemBackground + return view + }() + private let recommendationCommentView: UIImageView = { + let view = UIImageView() + view.image = DesignSystemAsset.image(named: "recommendationComment") + view.isHidden = true + return view + }() // MARK: - Init - public init(viewControllers: [UIViewController], initialIndex: Int = 0) { - tabItems = [ + public init(viewControllers: [UIViewController], tabItems: [TabItem]? = nil, initialIndex: Int = 0) { + let resolvedItems = tabItems ?? [ TabItem(title: "도감", icon: DesignSystemAsset.image(named: "dictionary")), TabItem(title: "북마크", icon: DesignSystemAsset.image(named: "bookmarkList")), TabItem(title: "MY", icon: DesignSystemAsset.image(named: "mypage")) ] - customTabBar = BottomTabBar(tabItems: tabItems, selectedIndex: initialIndex) + self.tabItems = resolvedItems + customTabBar = BottomTabBar(tabItems: resolvedItems, selectedIndex: initialIndex) super.init(nibName: nil, bundle: nil) configureUI(controllers: viewControllers) selectedIndex = initialIndex @@ -41,8 +60,10 @@ public final class BottomTabBarController: UITabBarController { // MARK: - SetUp private extension BottomTabBarController { func addViews() { + view.addSubview(tabBarBackground) view.addSubview(customTabBar) view.addSubview(divider) + view.addSubview(recommendationCommentView) } func setupConstraints() { @@ -55,15 +76,30 @@ private extension BottomTabBarController { make.horizontalEdges.equalToSuperview().inset(Constant.horizontalInset) make.bottom.equalTo(view.safeAreaLayoutGuide) } + + tabBarBackground.snp.makeConstraints { make in + make.horizontalEdges.bottom.equalToSuperview() + make.top.equalTo(divider.snp.top) + } + + recommendationCommentView.snp.makeConstraints { make in + make.bottom.equalTo(customTabBar.snp.top).offset(Constant.commentBottomMargin) + make.leading.equalTo(view.snp.leading).offset(Constant.commentLeadingOffset) + make.width.equalTo(Constant.commentWidth) + make.height.equalTo(Constant.commentHeight) + } } func configureUI(controllers: [UIViewController]) { viewControllers = controllers.map { - if $0 is UINavigationController { - return $0 + let nav: UINavigationController + if let existing = $0 as? UINavigationController { + nav = existing } else { - return UINavigationController(rootViewController: $0) + nav = UINavigationController(rootViewController: $0) } + nav.delegate = self + return nav } tabBar.isHidden = true @@ -76,6 +112,17 @@ private extension BottomTabBarController { } } +extension BottomTabBarController: UINavigationControllerDelegate { + public func navigationController( + _ navigationController: UINavigationController, + willShow viewController: UIViewController, + animated: Bool + ) { + let isRoot = navigationController.viewControllers.count == 1 + setHidden(hidden: !isRoot, animated: isRoot && animated) + } +} + public extension BottomTabBarController { func setHidden(hidden: Bool, animated: Bool = false) { guard customTabBar.isHidden != hidden else { return } @@ -84,15 +131,19 @@ public extension BottomTabBarController { UIView.animate(withDuration: 0.3) { self.customTabBar.alpha = hidden ? 0 : 1 self.divider.alpha = hidden ? 0 : 1 + self.tabBarBackground.alpha = hidden ? 0 : 1 } completion: { _ in self.customTabBar.isHidden = hidden self.divider.isHidden = hidden + self.tabBarBackground.isHidden = hidden } } else { customTabBar.isHidden = hidden customTabBar.alpha = hidden ? 0 : 1 divider.isHidden = hidden divider.alpha = hidden ? 0 : 1 + tabBarBackground.isHidden = hidden + tabBarBackground.alpha = hidden ? 0 : 1 } } @@ -102,4 +153,8 @@ public extension BottomTabBarController { customTabBar.selectTab(index: index) } } + + func checkRecommendationFirstLaunch(isFirst: Bool) { + recommendationCommentView.isHidden = isFirst + } } diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/TextButton.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/TextButton.swift index 3b05ec06..29f54a58 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/TextButton.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/TextButton.swift @@ -16,7 +16,7 @@ public final class TextButton: UIButton { // MARK: - Properties public let iconView: UIImageView = { let view = UIImageView() - view.image = UIImage(named: "edit")?.withRenderingMode(.alwaysTemplate) + view.image = DesignSystemAsset.image(named: "edit").withRenderingMode(.alwaysTemplate) view.tintColor = .neutral700 return view }() diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Toast.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Toast.swift index a889b4c6..fdc07250 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Toast.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Toast.swift @@ -4,8 +4,8 @@ import SnapKit public final class Toast: UIView { private enum Constant { - static let verticalEdgesInset: CGFloat = 16 static let horizontalEdges: CGFloat = 16 + static let height: CGFloat = 44 static let cornerRadius: CGFloat = 8 } @@ -37,23 +37,24 @@ public final class Toast: UIView { private extension Toast { func addViews() { addSubview(self.toastContentView) - toastContentView.addSubview(self.label) + self.toastContentView.addSubview(self.label) } func setupConstraints() { - toastContentView.snp.makeConstraints { make in + self.toastContentView.snp.makeConstraints { make in make.edges.equalToSuperview() + make.height.equalTo(Constant.height) } - label.snp.makeConstraints { make in - make.verticalEdges.equalToSuperview().inset(Constant.verticalEdgesInset) + self.label.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(Constant.horizontalEdges) + make.centerY.equalToSuperview() } } func configureUI(message: String?) { layer.cornerRadius = Constant.cornerRadius clipsToBounds = true - label.attributedText = .makeStyledString(font: .b_s_r, text: message, color: .whiteMLS) + self.label.attributedText = .makeStyledString(font: .b_s_r, text: message, color: .whiteMLS) } } diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Tooltip/TooltipView.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Tooltip/TooltipView.swift index 4eddb2ed..1b1ff343 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Tooltip/TooltipView.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Components/Tooltip/TooltipView.swift @@ -29,6 +29,7 @@ final class TooltipView: UIView { init(text: String, tooltipPosition: TooltipPosition) { self.tooltipPosition = tooltipPosition super.init(frame: .zero) + translatesAutoresizingMaskIntoConstraints = false addViews() setupConstraints() configureUI(text: text) @@ -136,5 +137,11 @@ extension TooltipView { shapeLayer.frame = bounds shapeLayer.path = bubblePath.cgPath shapeLayer.fillColor = UIColor.whiteMLS.cgColor + + shapeLayer.shadowPath = bubblePath.cgPath + shapeLayer.shadowColor = UIColor.black.withAlphaComponent(0.15).cgColor + shapeLayer.shadowOpacity = 1 + shapeLayer.shadowRadius = 20 + shapeLayer.shadowOffset = .zero } } diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/CheckBoxButtonListSmallCell.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CheckBoxButtonListSmallCell.swift similarity index 98% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/CheckBoxButtonListSmallCell.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CheckBoxButtonListSmallCell.swift index ed43d99c..b657c4ff 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/CheckBoxButtonListSmallCell.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CheckBoxButtonListSmallCell.swift @@ -1,7 +1,5 @@ import UIKit -import DesignSystem - import RxSwift import SnapKit diff --git a/MLS/MLSCore/Sources/MLSCore/CompositionalLayoutBuilder/CompositionalLayoutBuilder.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/CompositionalLayoutBuilder.swift similarity index 100% rename from MLS/MLSCore/Sources/MLSCore/CompositionalLayoutBuilder/CompositionalLayoutBuilder.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/CompositionalLayoutBuilder.swift diff --git a/MLS/MLSCore/Sources/MLSCore/CompositionalLayoutBuilder/CompositionalSectionBuilder.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/CompositionalSectionBuilder.swift similarity index 100% rename from MLS/MLSCore/Sources/MLSCore/CompositionalLayoutBuilder/CompositionalSectionBuilder.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/CompositionalSectionBuilder.swift diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/CompositionalLayoutBuilder/LayoutFactory.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/LayoutFactory.swift similarity index 92% rename from MLS/Presentation/BaseFeature/BaseFeature/Utills/CompositionalLayoutBuilder/LayoutFactory.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/LayoutFactory.swift index a79c95f7..a2c3a549 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/CompositionalLayoutBuilder/LayoutFactory.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/LayoutFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public class LayoutFactory { public init() {} @@ -36,7 +37,7 @@ public class LayoutFactory { .contentInsets(.init(top: 12, leading: 16, bottom: 32, trailing: 16)) } - public func getDictionaryListLayout(isFilterHidden: Bool = true) -> CompositionalSectionBuilder { + public static func getDictionaryListLayout(isFilterHidden: Bool = true) -> CompositionalSectionBuilder { return CompositionalSectionBuilder() .item(width: .fractionalWidth(1.0), height: .absolute(104)) .group(.horizontal, width: .fractionalWidth(1.0), height: .absolute(104)) @@ -45,7 +46,7 @@ public class LayoutFactory { .contentInsets(.init(top: isFilterHidden ? 20 : 0, leading: 16, bottom: 0, trailing: 16)) } - public func getTagChipLayout() -> CompositionalSectionBuilder { + public static func getTagChipLayout() -> CompositionalSectionBuilder { return CompositionalSectionBuilder() .item(width: .estimated(70), height: .estimated(32)) .group(.horizontal, width: .estimated(70), height: .estimated(32)) @@ -56,7 +57,7 @@ public class LayoutFactory { .contentInsets(.init(top: 24, leading: 16, bottom: 24, trailing: 16)) } - public func getDecorationSection() -> CompositionalSectionBuilder { + public static func getDecorationSection() -> CompositionalSectionBuilder { return CompositionalSectionBuilder() .item(width: .fractionalWidth(1.0), height: .absolute(1)) .group(.vertical, width: .fractionalWidth(1.0), height: .absolute(10)) @@ -65,7 +66,7 @@ public class LayoutFactory { .contentInsets(.init(top: 5, leading: 0, bottom: 5, trailing: 0)) } - public func getPopularResultLayout() -> CompositionalSectionBuilder { + public static func getPopularResultLayout() -> CompositionalSectionBuilder { return CompositionalSectionBuilder() .item(width: .fractionalWidth(1.0), height: .estimated(40)) .group(.horizontal, width: .fractionalWidth(1.0), height: .estimated(40), count: 2) @@ -83,7 +84,7 @@ public class LayoutFactory { .contentInsets(.init(top: 0, leading: 16, bottom: 0, trailing: 16)) } - public func getCollectionModalLayout() -> CompositionalSectionBuilder { + public static func getCollectionModalLayout() -> CompositionalSectionBuilder { return CompositionalSectionBuilder() .item(width: .fractionalWidth(1.0), height: .absolute(72)) .group(.vertical, width: .fractionalWidth(1.0), height: .absolute(72)) @@ -91,7 +92,7 @@ public class LayoutFactory { .interGroupSpacing(1) } - public func getCollectionListLayout() -> CompositionalSectionBuilder { + public static func getCollectionListLayout() -> CompositionalSectionBuilder { return CompositionalSectionBuilder() .item(width: .fractionalWidth(1.0), height: .absolute(96)) .group(.vertical, width: .fractionalWidth(1.0), height: .absolute(96)) @@ -100,7 +101,7 @@ public class LayoutFactory { .contentInsets(.init(top: 0, leading: 16, bottom: 0, trailing: 16)) } - public func getCollectionListEditLayout() -> CompositionalSectionBuilder { + public static func getCollectionListEditLayout() -> CompositionalSectionBuilder { return CompositionalSectionBuilder() .item(width: .fractionalWidth(1.0), height: .absolute(104)) .group(.vertical, width: .fractionalWidth(1.0), height: .absolute(104)) diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/DescriptionBackgroundView.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/DescriptionBackgroundView.swift similarity index 98% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/DescriptionBackgroundView.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/DescriptionBackgroundView.swift index 87cd1f98..c6846274 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/DescriptionBackgroundView.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/DescriptionBackgroundView.swift @@ -1,4 +1,3 @@ -import DesignSystem import UIKit final public class DescriptionBackgroundView: UICollectionReusableView { diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/Neutral100BackgroundView.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/Neutral100BackgroundView.swift similarity index 97% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/Neutral100BackgroundView.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/Neutral100BackgroundView.swift index e65412fc..f25a4d51 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/Neutral100BackgroundView.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/Neutral100BackgroundView.swift @@ -1,4 +1,3 @@ -import DesignSystem import UIKit final public class Neutral100BackgroundView: UICollectionReusableView { diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/Neutral200DividerView.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/Neutral200DividerView.swift similarity index 95% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/Neutral200DividerView.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/Neutral200DividerView.swift index 914f1c1b..7c9ea6bf 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/Neutral200DividerView.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/Neutral200DividerView.swift @@ -1,7 +1,5 @@ import UIKit -import DesignSystem - final public class Neutral200DividerView: UICollectionReusableView { override init(frame: CGRect) { super.init(frame: frame) diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/Neutral300DividerView.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/Neutral300DividerView.swift similarity index 95% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/Neutral300DividerView.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/Neutral300DividerView.swift index 551557c0..49911c86 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/Neutral300DividerView.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/Neutral300DividerView.swift @@ -1,7 +1,5 @@ import UIKit -import DesignSystem - final public class Neutral300DividerView: UICollectionReusableView { override init(frame: CGRect) { super.init(frame: frame) diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/PopularSearchHeaderView.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/PopularSearchHeaderView.swift similarity index 99% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/PopularSearchHeaderView.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/PopularSearchHeaderView.swift index bf633d0d..5db2bcbb 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/PopularSearchHeaderView.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/PopularSearchHeaderView.swift @@ -1,7 +1,5 @@ import UIKit -import DesignSystem - import SnapKit public final class PopularSearchHeaderView: UICollectionReusableView { diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/RecentSearchHeaderView.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/RecentSearchHeaderView.swift similarity index 98% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/RecentSearchHeaderView.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/RecentSearchHeaderView.swift index 878213a8..e0ac47ae 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/RecentSearchHeaderView.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/RecentSearchHeaderView.swift @@ -1,7 +1,7 @@ -import DesignSystem -import SnapKit import UIKit +import SnapKit + public final class RecentSearchHeaderView: UICollectionReusableView { // MARK: - Components private let titleLabel = UILabel() diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/SearchDividerView.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/SearchDividerView.swift similarity index 97% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/SearchDividerView.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/SearchDividerView.swift index 63e4b8c4..013dc110 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/SearchDividerView.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/SearchDividerView.swift @@ -1,7 +1,5 @@ import UIKit -import DesignSystem - import SnapKit final public class SearchDividerView: UICollectionReusableView { diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/SettingBackgroundView.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/SettingBackgroundView.swift similarity index 98% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/SettingBackgroundView.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/SettingBackgroundView.swift index 6f8dc1e4..55ad6e60 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/SettingBackgroundView.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/SettingBackgroundView.swift @@ -1,4 +1,3 @@ -import DesignSystem import UIKit final public class SettingBackgroundView: UICollectionReusableView { diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/SubTitleBoldHeaderView.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/SubTitleBoldHeaderView.swift similarity index 97% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/SubTitleBoldHeaderView.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/SubTitleBoldHeaderView.swift index ea34e22b..f16f0468 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/SubTitleBoldHeaderView.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/SubTitleBoldHeaderView.swift @@ -1,7 +1,5 @@ import UIKit -import DesignSystem - import SnapKit final public class SubTitleBoldHeaderView: UICollectionReusableView { diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/SupportBackgroundView.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/SupportBackgroundView.swift similarity index 98% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/SupportBackgroundView.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/SupportBackgroundView.swift index 2a5d5153..9ab057e7 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/Views/SupportBackgroundView.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/CompositionalLayoutBuilder/Views/SupportBackgroundView.swift @@ -1,4 +1,3 @@ -import DesignSystem import UIKit final public class SupportBackgroundView: UICollectionReusableView { diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/Factory/GuideAlertFactory.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/Factory/GuideAlertFactory.swift index a19c01ca..4df58a32 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/Factory/GuideAlertFactory.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/Factory/GuideAlertFactory.swift @@ -16,9 +16,10 @@ public enum GuideAlertFactory { ctaText: String, cancelText: String? = nil, ctaAction: @escaping () -> Void, - cancelAction: (() -> Void)? = nil + cancelAction: (() -> Void)? = nil, + ctaRatio: Double = 0.7 ) { - let alert = GuideAlert(mainText: mainText, ctaText: ctaText, cancelText: cancelText) + let alert = GuideAlert(mainText: mainText, ctaText: ctaText, cancelText: cancelText, ctaRatio: ctaRatio) presentAlert(alert: alert, ctaAction: ctaAction, cancelAction: cancelAction) } diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/Factory/TooltipFactory.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/Factory/TooltipFactory.swift index 55c86e10..5f6e56d7 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/Factory/TooltipFactory.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/Factory/TooltipFactory.swift @@ -51,10 +51,6 @@ public extension TooltipFactory { let frame = anchorView.convert(anchorView.bounds, to: window) - tooltip.frame.origin = CGPoint(x: 0, y: 0) - tooltip.setNeedsLayout() - tooltip.layoutIfNeeded() - let tooltipSize = tooltip.systemLayoutSizeFitting( UIView.layoutFittingCompressedSize ) @@ -84,12 +80,10 @@ public extension TooltipFactory { y = frame.maxY + 8 } - tooltip.frame = CGRect( - x: x, - y: y, - width: tooltipSize.width, - height: tooltipSize.height - ) + tooltip.snp.makeConstraints { make in + make.leading.equalToSuperview().offset(x) + make.top.equalToSuperview().offset(y) + } tooltip.alpha = 0 UIView.animate(withDuration: 0.25) { diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/PageTabbarCell.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/PageTabbarCell.swift similarity index 98% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/PageTabbarCell.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/PageTabbarCell.swift index 87a4941b..d26591f8 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/PageTabbarCell.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/PageTabbarCell.swift @@ -1,7 +1,5 @@ import UIKit -import DesignSystem - import SnapKit public class PageTabbarCell: UICollectionViewCell { diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/TapButtonCell.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/TapButtonCell.swift similarity index 97% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/TapButtonCell.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/TapButtonCell.swift index ef26dbb3..15a9c49a 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/TapButtonCell.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/TapButtonCell.swift @@ -1,7 +1,5 @@ import UIKit -import DesignSystem - import SnapKit public class TapButtonCell: UICollectionViewCell { diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/ToLoginView.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/ToLoginView.swift index 3cad3a23..f911f0d5 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/ToLoginView.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Layouts/ToLoginView.swift @@ -36,8 +36,18 @@ public final class ToLoginView: UIView { // MARK: - Components public let imageView = UIImageView() - private let mainLabel = UILabel() - private let subLabel = UILabel() + + private let mainLabel: UILabel = { + let label = UILabel() + label.numberOfLines = 0 + return label + }() + + private let subLabel: UILabel = { + let label = UILabel() + label.numberOfLines = 0 + return label + }() public let button = CommonButton() diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/favorite.imageset/Contents.json b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/favorite.imageset/Contents.json index 33e62908..b1b0879a 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/favorite.imageset/Contents.json +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/favorite.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "favorite_true.svg", + "filename" : "favoriteFill.svg", "idiom" : "universal", "scale" : "1x" }, diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/favorite.imageset/favoriteFill.svg b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/favorite.imageset/favoriteFill.svg new file mode 100644 index 00000000..1d3af65e --- /dev/null +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/favorite.imageset/favoriteFill.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/favorite.imageset/favorite_true.svg b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/favorite.imageset/favorite_true.svg deleted file mode 100644 index d47569a5..00000000 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/favorite.imageset/favorite_true.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideAlert.imageset/Contents.json b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/image/recommendationComment.imageset/Contents.json similarity index 84% rename from MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideAlert.imageset/Contents.json rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/image/recommendationComment.imageset/Contents.json index 7911bc12..97b8a027 100644 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideAlert.imageset/Contents.json +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/image/recommendationComment.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "guideAlert.png", + "filename" : "recommendationComment.png", "idiom" : "universal", "scale" : "1x" }, diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/image/recommendationComment.imageset/recommendationComment.png b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/image/recommendationComment.imageset/recommendationComment.png new file mode 100644 index 00000000..bc409637 Binary files /dev/null and b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Resources/Image.xcassets/image/recommendationComment.imageset/recommendationComment.png differ diff --git a/MLS/MLSCore/Sources/MLSCore/Extension/UICollectionReusableView+.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Utills/DesignExtensions/UICollectionReusableView+.swift similarity index 100% rename from MLS/MLSCore/Sources/MLSCore/Extension/UICollectionReusableView+.swift rename to MLS/MLSDesignSystem/Sources/MLSDesignSystem/Utills/DesignExtensions/UICollectionReusableView+.swift diff --git a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Utills/DesignSystemAsset.swift b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Utills/DesignSystemAsset.swift index 9ac04ad1..f6a15335 100644 --- a/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Utills/DesignSystemAsset.swift +++ b/MLS/MLSDesignSystem/Sources/MLSDesignSystem/Utills/DesignSystemAsset.swift @@ -5,9 +5,10 @@ public enum DesignSystemAsset { public static func image(named name: String) -> UIImage { guard let image = UIImage(named: name, in: .module, compatibleWith: nil) else { - fatalError("❌ Image not found: \(name)") - } - return image + print("❌ Image not found: \(name)") + return UIImage() + } + return image } } diff --git a/MLS/MLSDictionaryFeature/.gitignore b/MLS/MLSDictionaryFeature/.gitignore new file mode 100644 index 00000000..0023a534 --- /dev/null +++ b/MLS/MLSDictionaryFeature/.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/MLS/MLSDictionaryFeature/Package.swift b/MLS/MLSDictionaryFeature/Package.swift new file mode 100644 index 00000000..65ccfc58 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Package.swift @@ -0,0 +1,93 @@ +// swift-tools-version: 6.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "MLSDictionaryFeature", + platforms: [.iOS(.v15)], + products: [ + .library( + name: "MLSDictionaryFeatureInterface", + targets: ["MLSDictionaryFeatureInterface"] + ), + .library( + name: "MLSDictionaryFeature", + targets: ["MLSDictionaryFeature"] + ), + .library( + name: "MLSDictionaryFeatureTesting", + targets: ["MLSDictionaryFeatureTesting"] + ) + ], + dependencies: [ + .package(path: "../MLSAppFeature"), + .package(path: "../MLSAuthFeature"), + .package(path: "../MLSBookmarkFeature"), + .package(path: "../MLSCore"), + .package(path: "../MLSDesignSystem"), + .package(path: "../MLSMyPageFeature"), + .package(url: "https://github.com/ReactorKit/ReactorKit.git", from: "3.2.0"), + .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "6.7.0"), + .package(url: "https://github.com/RxSwiftCommunity/RxKeyboard.git", from: "2.0.0"), + .package(url: "https://github.com/SnapKit/SnapKit.git", from: "5.7.1") + ], + targets: [ + // Interface + .target( + name: "MLSDictionaryFeatureInterface", + dependencies: [ + .product(name: "MLSCore", package: "MLSCore"), + .product(name: "MLSDesignSystem", package: "MLSDesignSystem"), + .product(name: "RxSwift", package: "RxSwift") + ], + swiftSettings: [.swiftLanguageMode(.v5)] + ), + // Feature + .target( + name: "MLSDictionaryFeature", + dependencies: [ + "MLSDictionaryFeatureInterface", + .product(name: "MLSAppFeatureInterface", package: "MLSAppFeature"), + .product(name: "MLSAuthFeatureInterface", package: "MLSAuthFeature"), + .product(name: "MLSBookmarkFeatureInterface", package: "MLSBookmarkFeature"), + .product(name: "MLSCore", package: "MLSCore"), + .product(name: "MLSDesignSystem", package: "MLSDesignSystem"), + .product(name: "MLSMyPageFeatureInterface", package: "MLSMyPageFeature"), + .product(name: "ReactorKit", package: "ReactorKit"), + .product(name: "RxSwift", package: "RxSwift"), + .product(name: "RxCocoa", package: "RxSwift"), + .product(name: "RxRelay", package: "RxSwift"), + .product(name: "RxKeyboard", package: "RxKeyboard"), + .product(name: "SnapKit", package: "SnapKit") + ], + swiftSettings: [.swiftLanguageMode(.v5)] + ), + // Mock + .target( + name: "MLSDictionaryFeatureTesting", + dependencies: [ + "MLSDictionaryFeatureInterface", + .product(name: "MLSAppFeatureInterface", package: "MLSAppFeature"), + .product(name: "MLSAuthFeatureInterface", package: "MLSAuthFeature"), + .product(name: "MLSBookmarkFeatureInterface", package: "MLSBookmarkFeature"), + .product(name: "MLSMyPageFeatureInterface", package: "MLSMyPageFeature"), + .product(name: "MLSCore", package: "MLSCore"), + .product(name: "RxSwift", package: "RxSwift") + ], + swiftSettings: [.swiftLanguageMode(.v5)] + ), + // Tests + .testTarget( + name: "MLSDictionaryFeatureTests", + dependencies: [ + "MLSDictionaryFeature", + "MLSDictionaryFeatureInterface", + "MLSDictionaryFeatureTesting", + .product(name: "MLSAuthFeatureInterface", package: "MLSAuthFeature"), + .product(name: "MLSAuthFeatureTesting", package: "MLSAuthFeature"), + .product(name: "RxBlocking", package: "RxSwift") + ], + ) + ] +) diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryAllDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryAllDTO.swift new file mode 100644 index 00000000..8373b1f6 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryAllDTO.swift @@ -0,0 +1,18 @@ +public struct DictionaryAllDTO: DictionaryDTOProtocol { + public let originalId: Int + public let name: String + public let imageUrl: String? + public let level: Int? + public let type: String + public let bookmarkId: Int? + public var id: Int { originalId } + + public init(originalId: Int, name: String, imageUrl: String?, level: Int?, type: String, bookmarkId: Int?) { + self.originalId = originalId + self.name = name + self.imageUrl = imageUrl + self.level = level + self.type = type + self.bookmarkId = bookmarkId + } +} diff --git a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryDTOProtocol.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDTOProtocol.swift similarity index 95% rename from MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryDTOProtocol.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDTOProtocol.swift index ff3457a2..7fba9deb 100644 --- a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryDTOProtocol.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDTOProtocol.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public protocol DictionaryDTOProtocol: Decodable { var id: Int { get } diff --git a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Item/DictionaryDetailItemDropMonsterResponseDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailItemDropMonsterResponseDTO.swift similarity index 92% rename from MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Item/DictionaryDetailItemDropMonsterResponseDTO.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailItemDropMonsterResponseDTO.swift index f92a5868..893f89cd 100644 --- a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Item/DictionaryDetailItemDropMonsterResponseDTO.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailItemDropMonsterResponseDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public struct DictionaryDetailItemDropMonsterResponseDTO: Decodable { public let monsterId: Int? diff --git a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Item/DictionaryDetailItemResponseDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailItemResponseDTO.swift similarity index 97% rename from MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Item/DictionaryDetailItemResponseDTO.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailItemResponseDTO.swift index 2cf32564..3917ef98 100644 --- a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Item/DictionaryDetailItemResponseDTO.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailItemResponseDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public struct DictionaryDetailItemResponseDTO: Decodable { public let itemId: Int diff --git a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Map/DictionaryDetailMapNpcResponseDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMapNpcResponseDTO.swift similarity index 91% rename from MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Map/DictionaryDetailMapNpcResponseDTO.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMapNpcResponseDTO.swift index 0d3a5469..c4d436be 100644 --- a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Map/DictionaryDetailMapNpcResponseDTO.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMapNpcResponseDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public struct DictionaryDetailMapNpcResponseDTO: Decodable { public let npcId: Int? diff --git a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Map/DictionaryDetailMapResponseDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMapResponseDTO.swift similarity index 94% rename from MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Map/DictionaryDetailMapResponseDTO.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMapResponseDTO.swift index 653158e9..18ba133c 100644 --- a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Map/DictionaryDetailMapResponseDTO.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMapResponseDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public struct DictionaryDetailMapResponseDTO: Decodable { public let mapId: Int diff --git a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Map/DictionaryDetailMapSpawnMonsterResponseDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMapSpawnMonsterResponseDTO.swift similarity index 92% rename from MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Map/DictionaryDetailMapSpawnMonsterResponseDTO.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMapSpawnMonsterResponseDTO.swift index a0b7c376..dd427ea2 100644 --- a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Map/DictionaryDetailMapSpawnMonsterResponseDTO.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMapSpawnMonsterResponseDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public struct DictionaryDetailMapSpawnMonsterResponseDTO: Decodable { public let monsterId: Int? diff --git a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Monster/DictionaryDetailMonsterDropItemResponseDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMonsterDropItemResponseDTO.swift similarity index 92% rename from MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Monster/DictionaryDetailMonsterDropItemResponseDTO.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMonsterDropItemResponseDTO.swift index 13e85308..5e58f98e 100644 --- a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Monster/DictionaryDetailMonsterDropItemResponseDTO.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMonsterDropItemResponseDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public struct DictionaryDetailMonsterDropItemResponseDTO: Decodable { public let itemId: Int diff --git a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Monster/DictionaryDetailMonsterMapResponseDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMonsterMapResponseDTO.swift similarity index 93% rename from MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Monster/DictionaryDetailMonsterMapResponseDTO.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMonsterMapResponseDTO.swift index e34e5920..538e3fba 100644 --- a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Monster/DictionaryDetailMonsterMapResponseDTO.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMonsterMapResponseDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public struct DictionaryDetailMonsterMapResponseDTO: Decodable { public let mapId: Int diff --git a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Monster/DictionaryDetailMonsterResponseDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMonsterResponseDTO.swift similarity index 97% rename from MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Monster/DictionaryDetailMonsterResponseDTO.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMonsterResponseDTO.swift index b094b332..b8e0ffa1 100644 --- a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Monster/DictionaryDetailMonsterResponseDTO.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailMonsterResponseDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public struct DictionaryDetailMonsterResponseDTO: Decodable { public let monsterId: Int diff --git a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Npc/DictionaryDetailNpcQuestResponseDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailNpcQuestResponseDTO.swift similarity index 93% rename from MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Npc/DictionaryDetailNpcQuestResponseDTO.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailNpcQuestResponseDTO.swift index 582eb0e2..7ab0e6e6 100644 --- a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Npc/DictionaryDetailNpcQuestResponseDTO.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailNpcQuestResponseDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public struct DictionaryDetailNpcQuestResponseDTO: Decodable { public let questId: Int diff --git a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Npc/DictionaryDetailNpcResponseDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailNpcResponseDTO.swift similarity index 92% rename from MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Npc/DictionaryDetailNpcResponseDTO.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailNpcResponseDTO.swift index ad3b8027..8e5963fa 100644 --- a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Npc/DictionaryDetailNpcResponseDTO.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailNpcResponseDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public struct DictionaryDetailNpcResponseDTO: Decodable { public let npcId: Int diff --git a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Quest/DictionaryDetailQuestLinkedQuestsResponseDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailQuestLinkedQuestsResponseDTO.swift similarity index 90% rename from MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Quest/DictionaryDetailQuestLinkedQuestsResponseDTO.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailQuestLinkedQuestsResponseDTO.swift index b34302f7..298ea608 100644 --- a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Quest/DictionaryDetailQuestLinkedQuestsResponseDTO.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailQuestLinkedQuestsResponseDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public struct DictionaryDetailQuestLinkedQuestsResponseDTO: Decodable { public let previousQuests: [Quest]? diff --git a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Quest/DictionaryDetailQuestResponseDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailQuestResponseDTO.swift similarity index 97% rename from MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Quest/DictionaryDetailQuestResponseDTO.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailQuestResponseDTO.swift index e79a4294..73de6515 100644 --- a/MLS/Data/Data/Network/DTO/DictionaryDetailDTO/Quest/DictionaryDetailQuestResponseDTO.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryDetailQuestResponseDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public struct DictionaryDetailQuestResponseDTO: Decodable { public let questId: Int diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryItemDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryItemDTO.swift new file mode 100644 index 00000000..27fd745b --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryItemDTO.swift @@ -0,0 +1,18 @@ +public struct DictionaryItemDTO: DictionaryDTOProtocol { + public let itemId: Int + public let name: String + public let imageUrl: String? + public let level: Int? + public let type: String + public let bookmarkId: Int? + public var id: Int { itemId } + + public init(itemId: Int, name: String, imageUrl: String?, level: Int?, type: String, bookmarkId: Int?) { + self.itemId = itemId + self.name = name + self.imageUrl = imageUrl + self.level = level + self.type = type + self.bookmarkId = bookmarkId + } +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryMapDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryMapDTO.swift new file mode 100644 index 00000000..7f3d3782 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryMapDTO.swift @@ -0,0 +1,18 @@ +public struct DictionaryMapDTO: DictionaryDTOProtocol { + public let mapId: Int + public let name: String + public let imageUrl: String? + public let level: Int? + public let type: String + public let bookmarkId: Int? + public var id: Int { mapId } + + public init(mapId: Int, name: String, imageUrl: String?, level: Int?, type: String, bookmarkId: Int?) { + self.mapId = mapId + self.name = name + self.imageUrl = imageUrl + self.level = level + self.type = type + self.bookmarkId = bookmarkId + } +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryMonsterDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryMonsterDTO.swift new file mode 100644 index 00000000..fb4b2f44 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryMonsterDTO.swift @@ -0,0 +1,18 @@ +public struct DictionaryMonsterDTO: DictionaryDTOProtocol { + public let monsterId: Int + public let name: String + public let imageUrl: String? + public let level: Int? + public let type: String + public let bookmarkId: Int? + public var id: Int { monsterId } + + public init(monsterId: Int, name: String, imageUrl: String?, level: Int?, type: String, bookmarkId: Int?) { + self.monsterId = monsterId + self.name = name + self.imageUrl = imageUrl + self.level = level + self.type = type + self.bookmarkId = bookmarkId + } +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryNPCDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryNPCDTO.swift new file mode 100644 index 00000000..710eb6c0 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryNPCDTO.swift @@ -0,0 +1,18 @@ +public struct DictionaryNPCDTO: DictionaryDTOProtocol { + public let npcId: Int + public let name: String + public let imageUrl: String? + public let level: Int? + public let type: String + public let bookmarkId: Int? + public var id: Int { npcId } + + public init(npcId: Int, name: String, imageUrl: String?, level: Int?, type: String, bookmarkId: Int?) { + self.npcId = npcId + self.name = name + self.imageUrl = imageUrl + self.level = level + self.type = type + self.bookmarkId = bookmarkId + } +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryQuestDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryQuestDTO.swift new file mode 100644 index 00000000..704f061a --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/DictionaryQuestDTO.swift @@ -0,0 +1,18 @@ +public struct DictionaryQuestDTO: DictionaryDTOProtocol { + public let questId: Int + public let name: String + public let imageUrl: String? + public let level: Int? + public let type: String + public let bookmarkId: Int? + public var id: Int { questId } + + public init(questId: Int, name: String, imageUrl: String?, level: Int?, type: String, bookmarkId: Int?) { + self.questId = questId + self.name = name + self.imageUrl = imageUrl + self.level = level + self.type = type + self.bookmarkId = bookmarkId + } +} diff --git a/MLS/Data/Data/Network/DTO/PagedListResponseDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/PagedListResponseDTO.swift similarity index 50% rename from MLS/Data/Data/Network/DTO/PagedListResponseDTO.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/PagedListResponseDTO.swift index d16b2813..7e683f46 100644 --- a/MLS/Data/Data/Network/DTO/PagedListResponseDTO.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/PagedListResponseDTO.swift @@ -1,13 +1,19 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public struct PagedListResponseDTO: Decodable { public let totalPages: Int public let totalElements: Int public let content: [Item] + + public init(totalPages: Int, totalElements: Int, content: [Item]) { + self.totalPages = totalPages + self.totalElements = totalElements + self.content = content + } } -extension PagedListResponseDTO where Item: DictionaryDTOProtocol { - public func toDomain() -> DictionaryMainResponse { +public extension PagedListResponseDTO where Item: DictionaryDTOProtocol { + func toDomain() -> DictionaryMainResponse { DictionaryMainResponse( totalPages: totalPages, totalElements: totalElements, diff --git a/MLS/Data/Data/Network/DTO/SearchCountDTO/SearchCountDTO.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/SearchCountDTO.swift similarity index 83% rename from MLS/Data/Data/Network/DTO/SearchCountDTO/SearchCountDTO.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/SearchCountDTO.swift index 885b5795..b8ba702d 100644 --- a/MLS/Data/Data/Network/DTO/SearchCountDTO/SearchCountDTO.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/DTOs/SearchCountDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public struct SearchCountDTO: Decodable { public let counts: Int? diff --git a/MLS/Data/Data/Network/Endpoints/DictionaryDetailEndPoint.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Endpoints/DictionaryDetailEndPoint.swift similarity index 98% rename from MLS/Data/Data/Network/Endpoints/DictionaryDetailEndPoint.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Endpoints/DictionaryDetailEndPoint.swift index d71fd9c4..a49282fb 100644 --- a/MLS/Data/Data/Network/Endpoints/DictionaryDetailEndPoint.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Endpoints/DictionaryDetailEndPoint.swift @@ -1,7 +1,4 @@ -import Foundation -import UIKit - -import DomainInterface +import MLSCore public enum DictionaryDetailEndPoint { static let base = "https://mapleland.2megabytes.me" diff --git a/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Endpoints/DictionaryListEndPoint.swift similarity index 98% rename from MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Endpoints/DictionaryListEndPoint.swift index 76002ae1..47a81015 100644 --- a/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Endpoints/DictionaryListEndPoint.swift @@ -1,7 +1,8 @@ import Foundation import UIKit -import DomainInterface +import MLSCore +import MLSDictionaryFeatureInterface public enum DictionaryListEndPoint { static let base = "https://mapleland.2megabytes.me" diff --git a/MLS/Data/Data/Repository/DictionaryDetailAPIRepositoryImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Repositories/DictionaryDetailAPIRepositoryImpl.swift similarity index 98% rename from MLS/Data/Data/Repository/DictionaryDetailAPIRepositoryImpl.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Repositories/DictionaryDetailAPIRepositoryImpl.swift index 83f4faf0..6156fc49 100644 --- a/MLS/Data/Data/Repository/DictionaryDetailAPIRepositoryImpl.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Repositories/DictionaryDetailAPIRepositoryImpl.swift @@ -1,4 +1,5 @@ -import DomainInterface +import MLSCore +import MLSDictionaryFeatureInterface import RxSwift diff --git a/MLS/Data/Data/Repository/DictionaryListAPIRepositoryImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Repositories/DictionaryListAPIRepositoryImpl.swift similarity index 98% rename from MLS/Data/Data/Repository/DictionaryListAPIRepositoryImpl.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Repositories/DictionaryListAPIRepositoryImpl.swift index 15c454a9..a65f2b8a 100644 --- a/MLS/Data/Data/Repository/DictionaryListAPIRepositoryImpl.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Repositories/DictionaryListAPIRepositoryImpl.swift @@ -1,4 +1,5 @@ -import DomainInterface +import MLSCore +import MLSDictionaryFeatureInterface import RxSwift diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Repositories/RecentSearchRepositoryImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Repositories/RecentSearchRepositoryImpl.swift new file mode 100644 index 00000000..ac77a231 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Repositories/RecentSearchRepositoryImpl.swift @@ -0,0 +1,70 @@ +import Foundation + +import MLSDictionaryFeatureInterface + +import RxSwift + +public final class RecentSearchRepositoryImpl: RecentSearchRepository { + private let recentSearchkey = "recentSearch" + + public init() {} + + public func fetchRecentSearch() -> Observable<[String]> { + return Observable.create { observer in + let current = UserDefaults.standard.stringArray(forKey: self.recentSearchkey) ?? [] + observer.onNext(current) + observer.onCompleted() + return Disposables.create() + } + } + + public func addRecentSearch(keyword: String) -> Completable { + return Completable.create { [recentSearchkey] completable in + let trimmedKeyword = keyword.trimmingCharacters(in: .whitespacesAndNewlines) + + guard !trimmedKeyword.isEmpty else { + completable(.completed) + return Disposables.create() + } + + var current = UserDefaults.standard.stringArray(forKey: recentSearchkey) ?? [] + + current.removeAll(where: { $0 == trimmedKeyword }) + current.insert(trimmedKeyword, at: 0) + + UserDefaults.standard.set(current, forKey: recentSearchkey) + completable(.completed) + return Disposables.create() + } + } + + public func removeRecentSearch(keyword: String) -> Completable { + return Completable.create { completable in + var current = UserDefaults.standard.stringArray(forKey: self.recentSearchkey) ?? [] + + // 해당 키워드 제거 + current.removeAll { $0 == keyword } + + // 다시 저장 + UserDefaults.standard.set(current, forKey: self.recentSearchkey) + + completable(.completed) + return Disposables.create() + } + } + + public func removeAllSearch() -> Completable { + return Completable.create { completable in + var current = UserDefaults.standard.stringArray(forKey: self.recentSearchkey) ?? [] + + // 해당 키워드 제거 + current.removeAll() + + // 다시 저장 + UserDefaults.standard.set(current, forKey: self.recentSearchkey) + + completable(.completed) + return Disposables.create() + } + } +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Repositories/UserDefaultsRepositoryImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Repositories/UserDefaultsRepositoryImpl.swift new file mode 100644 index 00000000..ef50a367 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Data/Repositories/UserDefaultsRepositoryImpl.swift @@ -0,0 +1,34 @@ +import Foundation + +import MLSDictionaryFeatureInterface + +import RxSwift + +public final class UserDefaultsRepositoryImpl: DictionaryUserDefaultsRepository { + private let dictionaryDetailkey = "dictionaryDetailkey" + private let recommendationkey = "recommendationkey" + + public init() {} + + public func fetchDictionaryDetail() -> Observable { + return Observable.create { observer in + let hasVisited = UserDefaults.standard.bool(forKey: self.dictionaryDetailkey) + observer.onNext(hasVisited) + observer.onCompleted() + return Disposables.create() + } + } + + public func saveDictionaryDetail() -> Completable { + return Completable.create { completable in + UserDefaults.standard.set(true, forKey: self.dictionaryDetailkey) + completable(.completed) + return Disposables.create() + } + } + + public func checkFirstLaunchRecommendation() -> Observable { + let hasVisited = UserDefaults.standard.bool(forKey: recommendationkey) + return .just(hasVisited) + } +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Domain/Usecases/CheckLoginUseCaseImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Domain/Usecases/CheckLoginUseCaseImpl.swift new file mode 100644 index 00000000..83dac045 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Domain/Usecases/CheckLoginUseCaseImpl.swift @@ -0,0 +1,49 @@ +import MLSAuthFeatureInterface +import MLSDictionaryFeatureInterface + +import RxRelay +import RxSwift + +public final class CheckLoginUseCaseImpl: CheckLoginUseCase { + private let authRepository: AuthAPIRepository + private let tokenRepository: TokenRepository + + public init(authRepository: AuthAPIRepository, tokenRepository: TokenRepository) { + self.authRepository = authRepository + self.tokenRepository = tokenRepository + } + + public func execute() -> Observable { + return Observable.deferred { [weak self] in + guard let self else { return .just(false) } + switch self.tokenRepository.fetchToken(type: .refreshToken) { + case .success(let token): + guard !token.isEmpty else { return .just(false) } + + return self.authRepository.reissueToken(refreshToken: token) + .map { [weak self] response in + guard let self else { return false } + + let accessResult = self.tokenRepository.saveToken(type: .accessToken, value: response.accessToken) + let refreshResult = self.tokenRepository.saveToken(type: .refreshToken, value: response.refreshToken) + + switch (accessResult, refreshResult) { + case (.success, .success): + return true + case (.failure(let error), _), + (_, .failure(let error)): + print("Token 저장 실패:", error.localizedDescription) + return false + } + } + .catch { error in + print("reissueToken 실패:", error.localizedDescription) + return .just(false) + } + + case .failure: + return .just(false) + } + } + } +} diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/CheckNotificationPermissionUseCaseImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Domain/Usecases/CheckNotificationPermissionUseCaseImpl.swift similarity index 94% rename from MLS/Domain/Domain/UseCaseImpl/AuthAPI/CheckNotificationPermissionUseCaseImpl.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Domain/Usecases/CheckNotificationPermissionUseCaseImpl.swift index 72deffc0..b9ed8015 100644 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/CheckNotificationPermissionUseCaseImpl.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Domain/Usecases/CheckNotificationPermissionUseCaseImpl.swift @@ -1,6 +1,6 @@ import UserNotifications -import DomainInterface +import MLSDictionaryFeatureInterface import RxSwift diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchVisitDictionaryDetailUseCaseImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Domain/Usecases/FetchVisitDictionaryDetailUseCaseImpl.swift similarity index 77% rename from MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchVisitDictionaryDetailUseCaseImpl.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Domain/Usecases/FetchVisitDictionaryDetailUseCaseImpl.swift index 72ec9a53..5b6dcc62 100644 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryDetail/FetchVisitDictionaryDetailUseCaseImpl.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Domain/Usecases/FetchVisitDictionaryDetailUseCaseImpl.swift @@ -1,11 +1,11 @@ -import DomainInterface import Foundation +import MLSDictionaryFeatureInterface import RxSwift public class FetchVisitDictionaryDetailUseCaseImpl: FetchVisitDictionaryDetailUseCase { - var repository: UserDefaultsRepository - public init(repository: UserDefaultsRepository) { + private let repository: DictionaryUserDefaultsRepository + public init(repository: DictionaryUserDefaultsRepository) { self.repository = repository } diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/ParseItemFilterResultUseCaseImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Domain/Usecases/ParseItemFilterResultUseCaseImpl.swift similarity index 85% rename from MLS/Domain/Domain/UseCaseImpl/DictionaryList/ParseItemFilterResultUseCaseImpl.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Domain/Usecases/ParseItemFilterResultUseCaseImpl.swift index 8bb273d4..db6fdba9 100644 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/ParseItemFilterResultUseCaseImpl.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Domain/Usecases/ParseItemFilterResultUseCaseImpl.swift @@ -1,21 +1,21 @@ -import DomainInterface +import MLSDictionaryFeatureInterface public final class ParseItemFilterResultUseCaseImpl: ParseItemFilterResultUseCase { private let jobIdMap: [String: Int] = [ - "전사": 100, "마법사": 200, "도적": 400, "궁수": 300 + "전사": 100, "마법사": 200, "도적": 400, "궁수": 300, "해적": 500 ] private let categoryIdMap: [String: Int] = [ "한손검": 7, "한손도끼": 8, "한손둔기": 9, "단검": 10, "완드": 12, "스태프": 13, "두손검": 14, "두손도끼": 15, "두손둔기": 16, "창": 17, - "폴암": 18, "활": 19, "석궁": 20, "아대": 21, "모자": 24, "상의": 25, + "폴암": 18, "활": 19, "석궁": 20, "아대": 21, "너클": 22, "건": 23, "모자": 24, "상의": 25, "하의": 26, "전신갑옷": 27, "신발": 28, "장갑": 29, "방패": 31, "망토": 30, "얼굴장식": 32, "눈장식": 33, "귀고리": 34, "반지": 35, "펜던트": 36, - "벨트": 37, "어깨장식": 38, "화살": 81, "표창": 83, + "벨트": 37, "어깨장식": 38, "화살": 81, "불릿": 82, "표창": 83, "한손검주문서": 50, "한손도끼주문서": 51, "한손둔기주문서": 52, "단검주문서": 53, "완드주문서": 55, "스태프주문서": 56, "두손검주문서": 57, "두손도끼주문서": 58, "두손둔기주문서": 59, "창주문서": 60, "폴암주문서": 61, - "활주문서": 62, "석궁주문서": 63, "아대주문서": 64, "투구주문서": 67, "상의주문서": 68, "하의주문서": 69, "전신갑옷주문서": 70, "신발주문서": 71, "장갑주문서": 72, "망토주문서": 73, "방패주문서": 74, + "활주문서": 62, "석궁주문서": 63, "아대주문서": 64, "너클주문서": 65, "건주문서": 66, "투구주문서": 67, "상의주문서": 68, "하의주문서": 69, "전신갑옷주문서": 70, "신발주문서": 71, "장갑주문서": 72, "망토주문서": 73, "방패주문서": 74, "귀장식주문서": 78, "펫장비주문서": 75, "연성서주문서": 76, "귀환주문서": 77, "마스터리북": 42, "스킬북": 43, "소비": 44, "설치": 45, "이동수단": 46 ] diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Shared/BaseListView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/BaseListView.swift similarity index 97% rename from MLS/Presentation/BaseFeature/BaseFeature/Shared/BaseListView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/BaseListView.swift index bfad802d..ffaa01a6 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/Shared/BaseListView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/BaseListView.swift @@ -1,7 +1,7 @@ import UIKit -import DesignSystem -import DomainInterface +import MLSDesignSystem +import MLSDictionaryFeatureInterface import SnapKit @@ -139,7 +139,7 @@ public extension BaseListView { static func makeSortButton(title: String, tintColor: UIColor) -> UIButton { let button = UIButton() button.setAttributedTitle(.makeStyledString(font: .b_s_r, text: title), for: .normal) - button.setImage(DesignSystemAsset.image(named: "lineArrowDown")?.withRenderingMode(.alwaysTemplate), for: .normal) + button.setImage(DesignSystemAsset.image(named: "lineArrowDown").withRenderingMode(.alwaysTemplate), for: .normal) button.tintColor = tintColor button.setTitleColor(tintColor, for: .normal) button.semanticContentAttribute = .forceRightToLeft @@ -149,7 +149,7 @@ public extension BaseListView { static func makeFilterButton(title: String, tintColor: UIColor) -> UIButton { let button = UIButton() button.setAttributedTitle(.makeStyledString(font: .b_s_r, text: title), for: .normal) - button.setImage(DesignSystemAsset.image(named: "filter")?.withRenderingMode(.alwaysTemplate), for: .normal) + button.setImage(DesignSystemAsset.image(named: "filter").withRenderingMode(.alwaysTemplate), for: .normal) button.tintColor = tintColor button.setTitleColor(tintColor, for: .normal) button.semanticContentAttribute = .forceRightToLeft diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/DictionaryDetailBaseView.swift similarity index 98% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/DictionaryDetailBaseView.swift index eb3e4c90..6edd1233 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/DictionaryDetailBaseView.swift @@ -1,7 +1,7 @@ import UIKit -import DesignSystem -import DomainInterface +import MLSDesignSystem +import MLSDictionaryFeatureInterface import SnapKit @@ -52,7 +52,7 @@ class DictionaryDetailBaseView: UIView { button .setImage( DesignSystemAsset - .image(named: "arrowBack")? + .image(named: "arrowBack") .withRenderingMode(.alwaysTemplate) .resizableImage( withCapInsets: UIEdgeInsets( @@ -76,7 +76,7 @@ class DictionaryDetailBaseView: UIView { button .setImage( DesignSystemAsset - .image(named: "dictionary")? + .image(named: "dictionary") .withRenderingMode(.alwaysTemplate) .resizableImage( withCapInsets: UIEdgeInsets( @@ -98,7 +98,7 @@ class DictionaryDetailBaseView: UIView { button .setImage( DesignSystemAsset - .image(named: "errorBlack")? + .image(named: "errorBlack") .withRenderingMode(.alwaysTemplate) .resizableImage( withCapInsets: UIEdgeInsets( diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/DictionaryDetailBaseViewController.swift similarity index 85% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/DictionaryDetailBaseViewController.swift index 84d5dfcb..f4e0ae5e 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/DictionaryDetailBaseViewController.swift @@ -1,11 +1,11 @@ import UIKit -import AuthFeatureInterface -import BaseFeature -import BookmarkFeatureInterface -import DesignSystem -import DictionaryFeatureInterface -import DomainInterface +import MLSAppFeatureInterface +import MLSAuthFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface import RxCocoa import RxSwift @@ -20,21 +20,20 @@ class DictionaryDetailBaseViewController: BaseViewController { var loginRelay: PublishRelay? /// 각 탭에 해당하는 콘텐츠 뷰들을 담는 배열 - public var contentViews: [UIView] = [] { - didSet { - let index = currentTabIndex ?? 0 - guard index < contentViews.count else { return } - mainView.setTabView(index: index, contentViews: contentViews) - } - } + private(set) lazy var contentViews: [UIView] = { + Array( + repeating: UIView(), + count: type.detailTypes.count + ) + }() /// 현재 보여지고 있는 뷰의 인덱스 private var currentTabIndex: Int? let bookmarkModalFactory: BookmarkModalFactory let loginFactory: LoginFactory - public let dictionaryDetailFactory: DictionaryDetailFactory private let detailOnBoardingFactory: DetailOnBoardingFactory + weak var dictionaryDetailFactory: DictionaryDetailFactory? private let appCoordinator: AppCoordinatorProtocol private let fetchVisitDictionaryDetailUseCase: FetchVisitDictionaryDetailUseCase @@ -49,7 +48,6 @@ class DictionaryDetailBaseViewController: BaseViewController { type: DictionaryItemType, bookmarkModalFactory: BookmarkModalFactory, loginFactory: LoginFactory, - dictionaryDetailFactory: DictionaryDetailFactory, detailOnBoardingFactory: DetailOnBoardingFactory, appCoordinator: AppCoordinatorProtocol, fetchVisitDictionaryDetailUseCase: FetchVisitDictionaryDetailUseCase, @@ -60,7 +58,6 @@ class DictionaryDetailBaseViewController: BaseViewController { self.bookmarkModalFactory = bookmarkModalFactory self.loginFactory = loginFactory self.appCoordinator = appCoordinator - self.dictionaryDetailFactory = dictionaryDetailFactory self.detailOnBoardingFactory = detailOnBoardingFactory self.fetchVisitDictionaryDetailUseCase = fetchVisitDictionaryDetailUseCase mainView.titleLabel.attributedText = .makeStyledString(font: .sub_m_b, text: type.detailTitle) @@ -81,6 +78,7 @@ class DictionaryDetailBaseViewController: BaseViewController { setupConstraints() configureUI() bind() // 액션 바인딩 +// configureContentView() setupMenu(type.detailTypes) } @@ -105,6 +103,32 @@ class DictionaryDetailBaseViewController: BaseViewController { open func undoBookmark() { assertionFailure("Subclass should override undoBookmark()") } + +// func configureContentView() { +// contentViews = Array( +// repeating: UIView(), +// count: type.detailTypes.count +// ) +// } + + func index(of detailType: DetailType) -> Int? { + type.detailTypes.firstIndex { $0 == detailType } + } + + func setContentView(view: UIView, detailType: DetailType) { + guard let index = index(of: detailType) else { + assertionFailure("detailType not found") + return + } + contentViews[index] = view + + if currentTabIndex == index { + mainView.setTabView( + index: index, + contentViews: contentViews + ) + } + } } // MARK: - SetUp @@ -182,7 +206,7 @@ extension DictionaryDetailBaseViewController { func makeTagsRow(_ tags: Effectiveness) { mainView.tagsVerticalStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } - let maxWidth = UIScreen.main.bounds.width - DictionaryDetailBaseView.Constant.horizontalInset // 좌우 여백 고려 (16 * 2) + let maxWidth = view.bounds.width - DictionaryDetailBaseView.Constant.horizontalInset // 좌우 여백 고려 (16 * 2) // 좌우 여백 고려 (16 * 2) let tagSpacing: CGFloat = DictionaryDetailBaseView.Constant.tagVerticalSpacing var tagsRowStackView = mainView.createHorizontalStackView() @@ -273,8 +297,6 @@ extension DictionaryDetailBaseViewController { // 인덱스 유효성 검사 guard index < contentViews.count else { return } - // 현재 뷰가 같다면 변경 안함 - if currentTabIndex == index { return } // 각 탭에 맞는 뷰 설정 mainView.setTabView(index: index, contentViews: contentViews) currentTabIndex = index @@ -314,7 +336,7 @@ private extension DictionaryDetailBaseViewController { .observe(on: MainScheduler.instance) .withUnretained(self) .bind { owner, _ in - owner.appCoordinator.showMainTab() + owner.appCoordinator.showMainTab(selectedIndex: 1) } .disposed(by: disposeBag) @@ -368,29 +390,35 @@ extension DictionaryDetailBaseViewController { extension DictionaryDetailBaseViewController { func presentAddSnackBar(bookmarkId: Int?, imageUrl: String?, background: UIColor) { guard let bookmarkId else { return } - SnackBarFactory.createSnackBar( - type: .normal, - imageUrl: imageUrl, - imageBackgroundColor: background, - text: "아이템을 북마크에 추가했어요.", - buttonText: "컬렉션 추가", - buttonAction: { [weak self] in - self?.presentCollectionModal(bookmarkId: bookmarkId) - } - ) + ImageLoader.shared.loadImage(stringURL: imageUrl) { image in + guard let image = image else { return } + SnackBarFactory.createSnackBar( + type: .normal, + image: image, + imageBackgroundColor: background, + text: "아이템을 북마크에 추가했어요.", + buttonText: "컬렉션 추가", + buttonAction: { [weak self] in + self?.presentCollectionModal(bookmarkId: bookmarkId) + } + ) + } } func presentDeleteSnackBar(imageUrl: String?, background: UIColor) { - SnackBarFactory.createSnackBar( - type: .delete, - imageUrl: imageUrl, - imageBackgroundColor: background, - text: "아이템을 북마크에서 삭제했어요.", - buttonText: "되돌리기", - buttonAction: { [weak self] in - self?.undoBookmark() - } - ) + ImageLoader.shared.loadImage(stringURL: imageUrl) { image in + guard let image = image else { return } + SnackBarFactory.createSnackBar( + type: .delete, + image: image, + imageBackgroundColor: background, + text: "아이템을 북마크에서 삭제했어요.", + buttonText: "되돌리기", + buttonAction: { [weak self] in + self?.undoBookmark() + } + ) + } } private func presentCollectionModal(bookmarkId: Int) { @@ -405,7 +433,7 @@ extension DictionaryDetailBaseViewController { sheet.prefersGrabberVisible = true sheet.preferredCornerRadius = 16 } - self.present(viewController, animated: true) + present(viewController, animated: true) } func presentLoginGuide() { diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/DictionaryDetailFactoryImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/DictionaryDetailFactoryImpl.swift new file mode 100644 index 00000000..4ad2587d --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/DictionaryDetailFactoryImpl.swift @@ -0,0 +1,233 @@ +import MLSAppFeatureInterface +import MLSAuthFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface + +import RxCocoa + +public final class DictionaryDetailFactoryImpl: DictionaryDetailFactory { + private let loginFactory: () -> LoginFactory + private let bookmarkModalFactory: BookmarkModalFactory + private let detailOnBoardingFactory: DetailOnBoardingFactory + private let appCoordinator: () -> AppCoordinatorProtocol + private let dictionaryDetailAPIRepository: DictionaryDetailAPIRepository + private let bookmarkRepository: BookmarkRepository + private let checkLoginUseCase: CheckLoginUseCase + private let fetchVisitDictionaryDetailUseCase: FetchVisitDictionaryDetailUseCase + + public init( + loginFactory: @escaping () -> LoginFactory, + bookmarkModalFactory: BookmarkModalFactory, + detailOnBoardingFactory: DetailOnBoardingFactory, + appCoordinator: @escaping () -> AppCoordinatorProtocol, + dictionaryDetailAPIRepository: DictionaryDetailAPIRepository, + bookmarkRepository: BookmarkRepository, + checkLoginUseCase: CheckLoginUseCase, + fetchVisitDictionaryDetailUseCase: FetchVisitDictionaryDetailUseCase + ) { + self.loginFactory = loginFactory + self.bookmarkModalFactory = bookmarkModalFactory + self.detailOnBoardingFactory = detailOnBoardingFactory + self.appCoordinator = appCoordinator + self.dictionaryDetailAPIRepository = dictionaryDetailAPIRepository + self.bookmarkRepository = bookmarkRepository + self.checkLoginUseCase = checkLoginUseCase + self.fetchVisitDictionaryDetailUseCase = fetchVisitDictionaryDetailUseCase + } + + public func make( + type: DictionaryType, + id: Int, + bookmarkRelay: PublishRelay<(id: Int, newBookmarkId: Int?)>?, + loginRelay: PublishRelay? + ) -> BaseViewController { + let viewController: BaseViewController + + switch type { + case .total, .collection: + viewController = BaseViewController() + + case .item: + viewController = makeItemViewController( + id: id, + bookmarkRelay: bookmarkRelay, + loginRelay: loginRelay + ) + + case .monster: + viewController = makeMonsterViewController( + id: id, + bookmarkRelay: bookmarkRelay, + loginRelay: loginRelay + ) + + case .map: + viewController = makeMapViewController( + id: id, + bookmarkRelay: bookmarkRelay, + loginRelay: loginRelay + ) + + case .npc: + viewController = makeNpcViewController( + id: id, + bookmarkRelay: bookmarkRelay, + loginRelay: loginRelay + ) + + case .quest: + viewController = makeQuestViewController( + id: id, + bookmarkRelay: bookmarkRelay, + loginRelay: loginRelay + ) + } + + // 하단 탭바 히든 + viewController.isBottomTabbarHidden = true + return viewController + } +} + +private extension DictionaryDetailFactoryImpl { + func makeItemViewController( + id: Int, + bookmarkRelay: PublishRelay<(id: Int, newBookmarkId: Int?)>?, + loginRelay: PublishRelay? + ) -> BaseViewController { + let viewController = ItemDictionaryDetailViewController( + type: .item, + bookmarkModalFactory: bookmarkModalFactory, + loginFactory: loginFactory(), + detailOnBoardingFactory: detailOnBoardingFactory, + appCoordinator: appCoordinator(), + fetchVisitDictionaryDetailUseCase: fetchVisitDictionaryDetailUseCase, + bookmarkRelay: bookmarkRelay, + loginRelay: loginRelay + ) + + viewController.dictionaryDetailFactory = self + + viewController.reactor = ItemDictionaryDetailReactor( + dictionaryDetailAPIRepository: dictionaryDetailAPIRepository, + bookmarkRepository: bookmarkRepository, + checkLoginUseCase: checkLoginUseCase, + id: id + ) + + return viewController + } + + func makeMonsterViewController( + id: Int, + bookmarkRelay: PublishRelay<(id: Int, newBookmarkId: Int?)>?, + loginRelay: PublishRelay? + ) -> BaseViewController { + let viewController = MonsterDictionaryDetailViewController( + type: .monster, + bookmarkModalFactory: bookmarkModalFactory, + loginFactory: loginFactory(), + detailOnBoardingFactory: detailOnBoardingFactory, + appCoordinator: appCoordinator(), + fetchVisitDictionaryDetailUseCase: fetchVisitDictionaryDetailUseCase, + bookmarkRelay: bookmarkRelay, + loginRelay: loginRelay + ) + + viewController.dictionaryDetailFactory = self + + viewController.reactor = MonsterDictionaryDetailReactor( + dictionaryDetailAPIRepository: dictionaryDetailAPIRepository, + bookmarkRepository: bookmarkRepository, + checkLoginUseCase: checkLoginUseCase, + id: id + ) + + return viewController + } + + func makeMapViewController( + id: Int, + bookmarkRelay: PublishRelay<(id: Int, newBookmarkId: Int?)>?, + loginRelay: PublishRelay? + ) -> BaseViewController { + let viewController = MapDictionaryDetailViewController( + type: .map, + bookmarkModalFactory: bookmarkModalFactory, + loginFactory: loginFactory(), + detailOnBoardingFactory: detailOnBoardingFactory, + appCoordinator: appCoordinator(), + fetchVisitDictionaryDetailUseCase: fetchVisitDictionaryDetailUseCase, + bookmarkRelay: bookmarkRelay, + loginRelay: loginRelay + ) + + viewController.dictionaryDetailFactory = self + + viewController.reactor = MapDictionaryDetailReactor( + dictionaryDetailAPIRepository: dictionaryDetailAPIRepository, + bookmarkRepository: bookmarkRepository, + checkLoginUseCase: checkLoginUseCase, + id: id + ) + + return viewController + } + + func makeNpcViewController( + id: Int, + bookmarkRelay: PublishRelay<(id: Int, newBookmarkId: Int?)>?, + loginRelay: PublishRelay? + ) -> BaseViewController { + let viewController = NpcDictionaryDetailViewController( + type: .npc, + bookmarkModalFactory: bookmarkModalFactory, + loginFactory: loginFactory(), + detailOnBoardingFactory: detailOnBoardingFactory, + appCoordinator: appCoordinator(), + fetchVisitDictionaryDetailUseCase: fetchVisitDictionaryDetailUseCase, + bookmarkRelay: bookmarkRelay, + loginRelay: loginRelay + ) + + viewController.dictionaryDetailFactory = self + + viewController.reactor = NpcDictionaryDetailReactor( + dictionaryDetailAPIRepository: dictionaryDetailAPIRepository, + bookmarkRepository: bookmarkRepository, + checkLoginUseCase: checkLoginUseCase, + id: id + ) + + return viewController + } + + func makeQuestViewController( + id: Int, + bookmarkRelay: PublishRelay<(id: Int, newBookmarkId: Int?)>?, + loginRelay: PublishRelay? + ) -> BaseViewController { + let viewController = QuestDictionaryDetailViewController( + type: .quest, + bookmarkModalFactory: bookmarkModalFactory, + loginFactory: loginFactory(), + detailOnBoardingFactory: detailOnBoardingFactory, + appCoordinator: appCoordinator(), + fetchVisitDictionaryDetailUseCase: fetchVisitDictionaryDetailUseCase, + bookmarkRelay: bookmarkRelay, + loginRelay: loginRelay + ) + + viewController.dictionaryDetailFactory = self + + viewController.reactor = QuestDictionaryDetailReactor( + dictionaryDetailAPIRepository: dictionaryDetailAPIRepository, + bookmarkRepository: bookmarkRepository, + checkLoginUseCase: checkLoginUseCase, + id: id + ) + + return viewController + } +} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailReactor.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Item/ItemDictionaryDetailReactor.swift similarity index 56% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailReactor.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Item/ItemDictionaryDetailReactor.swift index b9e31a13..7f690b70 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailReactor.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Item/ItemDictionaryDetailReactor.swift @@ -1,4 +1,5 @@ -import DomainInterface +import MLSBookmarkFeatureInterface +import MLSDictionaryFeatureInterface import ReactorKit @@ -31,7 +32,7 @@ public final class ItemDictionaryDetailReactor: Reactor { // MARK: Mutation public enum Mutation { - case navigatTo(Route) + case navigateTo(Route) case setDetailData(DictionaryDetailItemResponse) case setDetailDropMonsterData([DictionaryDetailItemDropMonsterResponse]) case setLoginState(Bool) @@ -52,23 +53,20 @@ public final class ItemDictionaryDetailReactor: Reactor { public var initialState: State private let disposeBag = DisposeBag() - private let dictionaryDetailItemUseCase: FetchDictionaryDetailItemUseCase - private let dictionaryDetailItemDropMonsterUseCase: FetchDictionaryDetailItemDropMonsterUseCase + private let dictionaryDetailAPIRepository: DictionaryDetailAPIRepository + private let bookmarkRepository: BookmarkRepository private let checkLoginUseCase: CheckLoginUseCase - private let setBookmarkUseCase: SetBookmarkUseCase // MARK: Init public init( - dictionaryDetailItemUseCase: FetchDictionaryDetailItemUseCase, - dictionaryDetailItemDropMonsterUseCase: FetchDictionaryDetailItemDropMonsterUseCase, + dictionaryDetailAPIRepository: DictionaryDetailAPIRepository, + bookmarkRepository: BookmarkRepository, checkLoginUseCase: CheckLoginUseCase, - setBookmarkUseCase: SetBookmarkUseCase, id: Int ) { - self.dictionaryDetailItemUseCase = dictionaryDetailItemUseCase - self.dictionaryDetailItemDropMonsterUseCase = dictionaryDetailItemDropMonsterUseCase + self.dictionaryDetailAPIRepository = dictionaryDetailAPIRepository + self.bookmarkRepository = bookmarkRepository self.checkLoginUseCase = checkLoginUseCase - self.setBookmarkUseCase = setBookmarkUseCase self.initialState = .init( itemDetailInfo: DictionaryDetailItemResponse( itemId: 0, nameKr: nil, nameEn: nil, descriptionText: nil, @@ -86,15 +84,15 @@ public final class ItemDictionaryDetailReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .filterButtonTapped: - return .just(.navigatTo(.filter(currentState.type))) + return .just(.navigateTo(.filter(currentState.type))) case .viewWillAppear: return .merge([ checkLoginUseCase.execute().map { .setLoginState($0) }, - dictionaryDetailItemUseCase.execute(id: currentState.id).map { .setDetailData($0) }, - dictionaryDetailItemDropMonsterUseCase.execute(id: currentState.id, sort: nil).map { .setDetailDropMonsterData($0) } + dictionaryDetailAPIRepository.fetchItemDetail(id: currentState.id).map { .setDetailData($0) }, + dictionaryDetailAPIRepository.fetchItemDetailDropMonster(id: currentState.id, sort: nil).map { .setDetailDropMonsterData($0) } ]) case let .selectFilter(type): - return dictionaryDetailItemDropMonsterUseCase.execute(id: currentState.id, sort: type.sortParameter).map { .setDetailDropMonsterData($0) } + return dictionaryDetailAPIRepository.fetchItemDetailDropMonster(id: currentState.id, sort: type.sortParameter).map { .setDetailDropMonsterData($0) } case .toggleBookmark: return handleToggleBookmark() @@ -104,7 +102,7 @@ public final class ItemDictionaryDetailReactor: Reactor { case .dataTapped(let index): guard let id = currentState.monsters[index].monsterId else { return .empty() } - return .just(.navigatTo(.detail(id))) + return .just(.navigateTo(.detail(id))) } } @@ -118,7 +116,7 @@ public final class ItemDictionaryDetailReactor: Reactor { newState.monsters = data case let .setLoginState(isLogin): newState.isLogin = isLogin - case .navigatTo(let route): + case .navigateTo(let route): newState.route = route case let .setEvent(event): newState.event = event @@ -133,47 +131,55 @@ private extension ItemDictionaryDetailReactor { let isSelected = item.bookmarkId != nil guard let type = currentState.type.toItemType else { return .empty() } - return setBookmarkUseCase.execute( - bookmarkId: isSelected ? item.bookmarkId ?? item.itemId : item.itemId, - isBookmark: isSelected ? .delete : .set(type) - ) - .flatMap { [weak self] newBookmarkId -> Observable in - guard let self else { return .empty() } + let bookmarkObservable: Observable - item.bookmarkId = newBookmarkId - let event: UIEvent = isSelected ? .delete(item) : .add(item) - let eventMutation = Observable.just(Mutation.setEvent(event)) + if isSelected, let bookmarkId = item.bookmarkId { + bookmarkObservable = bookmarkRepository + .deleteBookmark(bookmarkId: bookmarkId) + } else { + bookmarkObservable = bookmarkRepository + .setBookmark(resourceId: item.itemId, type: type) + .map { Optional($0) } + } - let refresh = self.dictionaryDetailItemUseCase.execute(id: self.currentState.id) - .map { Mutation.setDetailData($0) } + return bookmarkObservable + .flatMap { [weak self] newBookmarkId -> Observable in + guard let self else { return .empty() } - return .concat([eventMutation, refresh]) - } - .catch { _ in - .just(.navigatTo(.bookmarkError)) - } + item.bookmarkId = newBookmarkId + let event: UIEvent = isSelected ? .delete(item) : .add(item) + let eventMutation = Observable.just(Mutation.setEvent(event)) + + let refresh = self.dictionaryDetailAPIRepository + .fetchItemDetail(id: self.currentState.id) + .map { Mutation.setDetailData($0) } + + return .concat([eventMutation, refresh]) + } + .catch { _ in + .just(.navigateTo(.bookmarkError)) + } } func handleUndoLastDeletedBookmark() -> Observable { var item = currentState.itemDetailInfo guard let type = currentState.type.toItemType else { return .empty() } - return setBookmarkUseCase.execute( - bookmarkId: item.itemId, - isBookmark: .set(type) - ) - .flatMap { [weak self] newBookmarkId -> Observable in - guard let self else { return .empty() } - - item.bookmarkId = newBookmarkId - let eventMutation = Observable.just(Mutation.setEvent(.add(item))) - let refresh = self.dictionaryDetailItemUseCase.execute(id: self.currentState.id) - .map { Mutation.setDetailData($0) } - - return .concat([eventMutation, refresh]) - } - .catch { _ in - .just(.navigatTo(.bookmarkError)) - } + return bookmarkRepository + .setBookmark(resourceId: item.itemId, type: type) + .flatMap { [weak self] newBookmarkId -> Observable in + guard let self else { return .empty() } + + item.bookmarkId = newBookmarkId + let eventMutation = Observable.just(Mutation.setEvent(.add(item))) + let refresh = self.dictionaryDetailAPIRepository + .fetchItemDetail(id: self.currentState.id) + .map { Mutation.setDetailData($0) } + + return .concat([eventMutation, refresh]) + } + .catch { _ in + .just(.navigateTo(.bookmarkError)) + } } } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift similarity index 95% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift index c4efe2c5..8bafb730 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift @@ -1,9 +1,7 @@ import UIKit -import BaseFeature -import DesignSystem -import DictionaryFeatureInterface -import DomainInterface +import MLSDesignSystem +import MLSDictionaryFeatureInterface import ReactorKit @@ -45,11 +43,11 @@ private extension ItemDictionaryDetailViewController { guard let reactor = reactor else { return } let infos = reactor.currentState.itemDetailInfo - contentViews.append(detailInfoView) + detailInfoView.reset() // descriptionText detailInfoView.descriptionLabel.text = infos.descriptionText ?? "" + setContentView(view: detailInfoView, detailType: .normal) - detailInfoView.reset() if let npcPrice = infos.npcPrice { detailInfoView.addInfo(mainText: "상점판매가", subText: "\(npcPrice.formatted()) 메소") } @@ -155,11 +153,11 @@ private extension ItemDictionaryDetailViewController { let monsters = reactor.currentState.monsters monsterCardView.reset() - contentViews.append(monsterCardView) if monsters.isEmpty { - contentViews[1] = DetailEmptyView(type: .dropMonsterWithText) + setContentView(view: DetailEmptyView(type: .dropMonsterWithText), detailType: .dropMonsterWithText) } else { - contentViews[1] = monsterCardView + setContentView(view: monsterCardView + , detailType: .dropMonsterWithText) for monster in monsters { monsterCardView.inject(input: DetailStackCardView.Input(type: .dropMonsterWithText, imageUrl: monster.imageUrl ?? "", mainText: monster.monsterName, additionalText: "\(monster.dropRate ?? 0)%") ) @@ -234,7 +232,7 @@ extension ItemDictionaryDetailViewController { } owner.tabBarController?.presentModal(viewController, hideTabBar: true) case let .detail(id): - let viewController = owner.dictionaryDetailFactory.make(type: .monster, id: id, bookmarkRelay: owner.bookmarkRelay, loginRelay: owner.loginRelay) + guard let viewController = owner.dictionaryDetailFactory?.make(type: .monster, id: id, bookmarkRelay: owner.bookmarkRelay, loginRelay: owner.loginRelay) else { return } owner.navigationController?.pushViewController(viewController, animated: true) case .bookmarkError: ToastFactory.createToast(message: "북마크 요청에 실패했어요. 다시 시도해주세요.") diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailReactor.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Map/MapDictionaryDetailReactor.swift similarity index 58% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailReactor.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Map/MapDictionaryDetailReactor.swift index 8ed34e3c..7ef80027 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailReactor.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Map/MapDictionaryDetailReactor.swift @@ -1,4 +1,5 @@ -import DomainInterface +import MLSBookmarkFeatureInterface +import MLSDictionaryFeatureInterface import ReactorKit @@ -29,7 +30,7 @@ public final class MapDictionaryDetailReactor: Reactor { } public enum Mutation { - case navigatTo(Route) + case navigateTo(Route) case setDetailData(DictionaryDetailMapResponse) case setDetailSpawnMonsters([DictionaryDetailMapSpawnMonsterResponse]) case setDetailNpc([DictionaryDetailMapNpcResponse]) @@ -39,11 +40,9 @@ public final class MapDictionaryDetailReactor: Reactor { case setEvent(UIEvent) } - public let dictionaryDetailMapUseCase: FetchDictionaryDetailMapUseCase - public let dictionaryDetailMapSpawnMonsterUseCase: FetchDictionaryDetailMapSpawnMonsterUseCase - public let dictionaryDetailMapNpcUseCase: FetchDictionaryDetailMapNpcUseCase + public let dictionaryDetailAPIRepository: DictionaryDetailAPIRepository + private let bookmarkRepository: BookmarkRepository private let checkLoginUseCase: CheckLoginUseCase - private let setBookmarkUseCase: SetBookmarkUseCase public struct State { @Pulse var event: UIEvent = .none @@ -64,11 +63,9 @@ public final class MapDictionaryDetailReactor: Reactor { private let disposBag = DisposeBag() public init( - dictionaryDetailMapUseCase: FetchDictionaryDetailMapUseCase, - dictionaryDetailMapSpawnMonsterUseCase: FetchDictionaryDetailMapSpawnMonsterUseCase, - dictionaryDetailMapNpcUseCase: FetchDictionaryDetailMapNpcUseCase, + dictionaryDetailAPIRepository: DictionaryDetailAPIRepository, + bookmarkRepository: BookmarkRepository, checkLoginUseCase: CheckLoginUseCase, - setBookmarkUseCase: SetBookmarkUseCase, id: Int ) { initialState = State( @@ -89,23 +86,21 @@ public final class MapDictionaryDetailReactor: Reactor { id: id ) - self.dictionaryDetailMapUseCase = dictionaryDetailMapUseCase - self.dictionaryDetailMapSpawnMonsterUseCase = dictionaryDetailMapSpawnMonsterUseCase - self.dictionaryDetailMapNpcUseCase = dictionaryDetailMapNpcUseCase + self.dictionaryDetailAPIRepository = dictionaryDetailAPIRepository + self.bookmarkRepository = bookmarkRepository self.checkLoginUseCase = checkLoginUseCase - self.setBookmarkUseCase = setBookmarkUseCase } public func mutate(action: Action) -> Observable { switch action { case .monsterFilterButtonTapped: - return Observable.just(.navigatTo(.filter(currentState.monsterFilter))) + return Observable.just(.navigateTo(.filter(currentState.monsterFilter))) case .viewWillAppear: return .merge([ checkLoginUseCase.execute().map { .setLoginState($0) }, - dictionaryDetailMapUseCase.execute(id: currentState.id).map {.setDetailData($0)}, - dictionaryDetailMapSpawnMonsterUseCase.execute(id: currentState.id, sort: nil).map {.setDetailSpawnMonsters($0)}, - dictionaryDetailMapNpcUseCase.execute(id: currentState.id).map {.setDetailNpc($0)} + dictionaryDetailAPIRepository.fetchMapDetail(id: currentState.id).map {.setDetailData($0)}, + dictionaryDetailAPIRepository.fetchMapDetailSpawnMonster(id: currentState.id, sort: nil).map {.setDetailSpawnMonsters($0)}, + dictionaryDetailAPIRepository.fetchMapDetailNpc(id: currentState.id).map {.setDetailNpc($0)} ]) case .toggleBookmark: return handleToggleBookmark() @@ -114,15 +109,15 @@ public final class MapDictionaryDetailReactor: Reactor { return handleUndoLastDeletedBookmark() case let .selectFilter(type): - return dictionaryDetailMapSpawnMonsterUseCase.execute(id: currentState.id, sort: type.sortParameter).map { .setDetailSpawnMonsters($0) } + return dictionaryDetailAPIRepository.fetchMapDetailSpawnMonster(id: currentState.id, sort: type.sortParameter).map { .setDetailSpawnMonsters($0) } case .monsterTapped(index: let index): guard let id = currentState.spawnMonsters[index].monsterId else { return .empty() } - return .just(.navigatTo(.detail(type: .monster, id: id))) + return .just(.navigateTo(.detail(type: .monster, id: id))) case .npcTapped(index: let index): guard let id = currentState.npcs[index].npcId else { return .empty() } - return .just(.navigatTo(.detail(type: .npc, id: id))) + return .just(.navigateTo(.detail(type: .npc, id: id))) } } @@ -130,7 +125,7 @@ public final class MapDictionaryDetailReactor: Reactor { var newState = state switch mutation { - case .navigatTo(let route): + case .navigateTo(let route): newState.route = route case let .setDetailData(data): newState.mapDetailInfo = data @@ -157,47 +152,55 @@ private extension MapDictionaryDetailReactor { let isSelected = map.bookmarkId != nil guard let type = currentState.type.toItemType else { return .empty() } - return setBookmarkUseCase.execute( - bookmarkId: isSelected ? map.bookmarkId ?? map.mapId : map.mapId, - isBookmark: isSelected ? .delete : .set(type) - ) - .flatMap { [weak self] newBookmarkId -> Observable in - guard let self else { return .empty() } + let bookmarkObservable: Observable - map.bookmarkId = newBookmarkId - let event: UIEvent = isSelected ? .delete(map) : .add(map) - let eventMutation = Observable.just(Mutation.setEvent(event)) + if isSelected, let bookmarkId = map.bookmarkId { + bookmarkObservable = bookmarkRepository + .deleteBookmark(bookmarkId: bookmarkId) + } else { + bookmarkObservable = bookmarkRepository + .setBookmark(resourceId: map.mapId, type: type) + .map { Optional($0) } + } - let refresh = self.dictionaryDetailMapUseCase.execute(id: self.currentState.id) - .map { Mutation.setDetailData($0) } + return bookmarkObservable + .flatMap { [weak self] newBookmarkId -> Observable in + guard let self else { return .empty() } - return .concat([eventMutation, refresh]) - } - .catch { _ in - .just(.navigatTo(.bookmarkError)) - } + map.bookmarkId = newBookmarkId + let event: UIEvent = isSelected ? .delete(map) : .add(map) + let eventMutation = Observable.just(Mutation.setEvent(event)) + + let refresh = self.dictionaryDetailAPIRepository + .fetchMapDetail(id: self.currentState.id) + .map { Mutation.setDetailData($0) } + + return .concat([eventMutation, refresh]) + } + .catch { _ in + .just(.navigateTo(.bookmarkError)) + } } func handleUndoLastDeletedBookmark() -> Observable { var map = currentState.mapDetailInfo guard let type = currentState.type.toItemType else { return .empty() } - return setBookmarkUseCase.execute( - bookmarkId: map.mapId, - isBookmark: .set(type) - ) - .flatMap { [weak self] newBookmarkId -> Observable in - guard let self else { return .empty() } - - map.bookmarkId = newBookmarkId - let eventMutation = Observable.just(Mutation.setEvent(.add(map))) - let refresh = self.dictionaryDetailMapUseCase.execute(id: self.currentState.id) - .map { Mutation.setDetailData($0) } - - return .concat([eventMutation, refresh]) - } - .catch { _ in - .just(.navigatTo(.bookmarkError)) - } + return bookmarkRepository + .setBookmark(resourceId: map.mapId, type: type) + .flatMap { [weak self] newBookmarkId -> Observable in + guard let self else { return .empty() } + + map.bookmarkId = newBookmarkId + let eventMutation = Observable.just(Mutation.setEvent(.add(map))) + let refresh = self.dictionaryDetailAPIRepository + .fetchMapDetail(id: self.currentState.id) + .map { Mutation.setDetailData($0) } + + return .concat([eventMutation, refresh]) + } + .catch { _ in + .just(.navigateTo(.bookmarkError)) + } } } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Map/MapDictionaryDetailViewController.swift similarity index 90% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Map/MapDictionaryDetailViewController.swift index 62ab5cc7..1631e825 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Map/MapDictionaryDetailViewController.swift @@ -1,9 +1,7 @@ import UIKit -import BaseFeature -import DesignSystem -import DictionaryFeatureInterface -import DomainInterface +import MLSDesignSystem +import MLSDictionaryFeatureInterface import ReactorKit import RxCocoa @@ -55,13 +53,11 @@ private extension MapDictionaryDetailViewController { func setUpMapView() { guard let reactor = reactor else { return } - - contentViews.append(mapInfoView) if let mapUrl = reactor.currentState.mapDetailInfo.mapUrl, !mapUrl.isEmpty { - mapInfoView.setUpMapView(imageUrl: reactor.currentState.mapDetailInfo.mapUrl) - contentViews[0] = mapInfoView + mapInfoView.setUpMapView(imageUrl: mapUrl) + setContentView(view: mapInfoView, detailType: .mapInfo) } else { - contentViews[0] = DetailEmptyView(type: .mapInfo) + setContentView(view: DetailEmptyView(type: .mapInfo), detailType: .mapInfo) } } @@ -72,11 +68,10 @@ private extension MapDictionaryDetailViewController { appearMonsterView.reset() let monsters = reactor.currentState.spawnMonsters - contentViews.append(appearMonsterView) if monsters.isEmpty { - contentViews[1] = DetailEmptyView(type: .appearMonsterWithText) + setContentView(view: DetailEmptyView(type: .appearMonsterWithText), detailType: .appearMonsterWithText) } else { - contentViews[1] = appearMonsterView + setContentView(view: appearMonsterView, detailType: .appearMonsterWithText) for monster in monsters { appearMonsterView.inject(input: DetailStackCardView.Input(type: .appearMonsterWithText, imageUrl: monster.imageUrl ?? "", mainText: monster.monsterName, subText: "Lv.\(monster.level ?? 0)", additionalText: { if let count = monster.maxSpawnCount { @@ -94,11 +89,10 @@ private extension MapDictionaryDetailViewController { let npcs = reactor.currentState.npcs appearNpcView.reset() - contentViews.append(appearNpcView) if npcs.isEmpty { - contentViews[2] = DetailEmptyView(type: .appearNPC) + setContentView(view: DetailEmptyView(type: .appearNPC), detailType: .appearNPC) } else { - contentViews[2] = appearNpcView + setContentView(view: appearNpcView, detailType: .appearNPC) for npc in npcs { appearNpcView.inject(input: DetailStackCardView.Input(type: .appearNPC, imageUrl: npc.iconUrl ?? "", mainText: npc.npcName)) } @@ -118,7 +112,7 @@ private extension MapDictionaryDetailViewController { let viewController = PinchMapViewController(imageUrl: url) viewController.modalPresentationStyle = .overFullScreen owner.isBottomTabbarHidden = true - self.present(viewController, animated: true) + owner.present(viewController, animated: true) }) .disposed(by: disposeBag) } @@ -200,7 +194,7 @@ extension MapDictionaryDetailViewController { } owner.tabBarController?.presentModal(viewController, hideTabBar: true) case .detail(let type, let id): - let viewController = owner.dictionaryDetailFactory.make(type: type, id: id, bookmarkRelay: owner.bookmarkRelay, loginRelay: owner.loginRelay) + guard let viewController = owner.dictionaryDetailFactory?.make(type: type, id: id, bookmarkRelay: owner.bookmarkRelay, loginRelay: owner.loginRelay) else { return } owner.navigationController?.pushViewController(viewController, animated: true) case .bookmarkError: ToastFactory.createToast(message: "북마크 요청에 실패했어요. 다시 시도해주세요.") diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailReactor.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Monster/MonsterDictionaryDetailReactor.swift similarity index 58% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailReactor.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Monster/MonsterDictionaryDetailReactor.swift index 0f798445..4df54eb5 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailReactor.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Monster/MonsterDictionaryDetailReactor.swift @@ -1,4 +1,5 @@ -import DomainInterface +import MLSBookmarkFeatureInterface +import MLSDictionaryFeatureInterface import ReactorKit @@ -36,7 +37,7 @@ public final class MonsterDictionaryDetailReactor: Reactor { // MARK: - Mutation public enum Mutation { - case navigatTo(Route) + case navigateTo(Route) case setDetailData(DictionaryDetailMonsterResponse) case setDetailDropItemData([DictionaryDetailMonsterDropItemResponse]) case setDetailMapData([DictionaryDetailMonsterMapResponse]) @@ -76,47 +77,41 @@ public final class MonsterDictionaryDetailReactor: Reactor { // MARK: - Properties private let disposeBag = DisposeBag() - private let dictionaryDetailMonsterUseCase: FetchDictionaryDetailMonsterUseCase - private let dictionaryDetailMonsterDropItemUseCase: FetchDictionaryDetailMonsterItemsUseCase - private let dictionaryDetailMonsterMapUseCase: FetchDictionaryDetailMonsterMapUseCase + private let dictionaryDetailAPIRepository: DictionaryDetailAPIRepository + private let bookmarkRepository: BookmarkRepository private let checkLoginUseCase: CheckLoginUseCase - private let setBookmarkUseCase: SetBookmarkUseCase public var initialState: State // MARK: - Init public init( - dictionaryDetailMonsterUseCase: FetchDictionaryDetailMonsterUseCase, - dictionaryDetailMonsterDropItemUseCase: FetchDictionaryDetailMonsterItemsUseCase, - dictionaryDetailMonsterMapUseCase: FetchDictionaryDetailMonsterMapUseCase, + dictionaryDetailAPIRepository: DictionaryDetailAPIRepository, + bookmarkRepository: BookmarkRepository, checkLoginUseCase: CheckLoginUseCase, - setBookmarkUseCase: SetBookmarkUseCase, id: Int ) { self.initialState = State(type: .monster, id: id) - self.dictionaryDetailMonsterUseCase = dictionaryDetailMonsterUseCase - self.dictionaryDetailMonsterDropItemUseCase = dictionaryDetailMonsterDropItemUseCase - self.dictionaryDetailMonsterMapUseCase = dictionaryDetailMonsterMapUseCase + self.dictionaryDetailAPIRepository = dictionaryDetailAPIRepository + self.bookmarkRepository = bookmarkRepository self.checkLoginUseCase = checkLoginUseCase - self.setBookmarkUseCase = setBookmarkUseCase } // MARK: - Mutate public func mutate(action: Action) -> Observable { switch action { case let .filterButtonTapped(type): - return .just(.navigatTo(.filter(type: type, sort: type == .map ? currentState.mapFilter : currentState.itemFilter))) + return .just(.navigateTo(.filter(type: type, sort: type == .map ? currentState.mapFilter : currentState.itemFilter))) case .viewWillAppear: return .merge([ checkLoginUseCase.execute().map { .setLoginState($0) }, - dictionaryDetailMonsterUseCase.execute(id: currentState.id).map { .setDetailData($0) }, - dictionaryDetailMonsterDropItemUseCase.execute(id: currentState.id, sort: nil).map { .setDetailDropItemData($0) }, - dictionaryDetailMonsterMapUseCase.execute(id: currentState.id).map { .setDetailMapData($0) } + dictionaryDetailAPIRepository.fetchMonsterDetail(id: currentState.id).map { .setDetailData($0) }, + dictionaryDetailAPIRepository.fetchMonsterDetailDropItem(id: currentState.id, sort: nil).map { .setDetailDropItemData($0) }, + dictionaryDetailAPIRepository.fetchMonsterDetailMap(id: currentState.id).map { .setDetailMapData($0) } ]) case let .selectFilter(type): - return dictionaryDetailMonsterDropItemUseCase.execute(id: currentState.id, sort: type.sortParameter).map { .setDetailDropItemData($0) } + return dictionaryDetailAPIRepository.fetchMonsterDetailDropItem(id: currentState.id, sort: type.sortParameter).map { .setDetailDropItemData($0) } case .toggleBookmark: return handleToggleBookmark() @@ -124,11 +119,25 @@ public final class MonsterDictionaryDetailReactor: Reactor { case .undoLastDeletedBookmark: return handleUndoLastDeletedBookmark() - case .itemTapped(index: let index): - return .just(.navigatTo(.detail(type: .item, id: currentState.dropItems[index].itemId))) - - case .mapTapped(index: let index): - return .just(.navigatTo(.detail(type: .map, id: currentState.spawnMaps[index].mapId))) + case let .itemTapped(index): + guard currentState.dropItems.indices.contains(index) else { + return .empty() + } + + return .just( + .navigateTo(.detail(type: .item, id: currentState.dropItems[index].itemId) + ) + ) + + case let .mapTapped(index): + guard currentState.spawnMaps.indices.contains(index) else { + return .empty() + } + + return .just( + .navigateTo(.detail(type: .map, id: currentState.spawnMaps[index].mapId) + ) + ) } } @@ -137,7 +146,7 @@ public final class MonsterDictionaryDetailReactor: Reactor { var newState = state switch mutation { - case let .navigatTo(route): + case let .navigateTo(route): newState.route = route case let .setDetailData(data): @@ -179,47 +188,55 @@ private extension MonsterDictionaryDetailReactor { let isSelected = monster.bookmarkId != nil guard let type = currentState.type.toItemType else { return .empty() } - return setBookmarkUseCase.execute( - bookmarkId: isSelected ? monster.bookmarkId ?? monster.monsterId : monster.monsterId, - isBookmark: isSelected ? .delete : .set(type) - ) - .flatMap { [weak self] newBookmarkId -> Observable in - guard let self else { return .empty() } + let bookmarkObservable: Observable + + if isSelected, let bookmarkId = monster.bookmarkId { + bookmarkObservable = bookmarkRepository + .deleteBookmark(bookmarkId: bookmarkId) + } else { + bookmarkObservable = bookmarkRepository + .setBookmark(resourceId: monster.monsterId, type: type) + .map { Optional($0) } + } - monster.bookmarkId = newBookmarkId - let event: UIEvent = isSelected ? .delete(monster) : .add(monster) - let eventMutation = Observable.just(Mutation.setEvent(event)) + return bookmarkObservable + .flatMap { [weak self] newBookmarkId -> Observable in + guard let self else { return .empty() } - let refresh = self.dictionaryDetailMonsterUseCase.execute(id: self.currentState.id) - .map { Mutation.setDetailData($0) } + monster.bookmarkId = newBookmarkId + let event: UIEvent = isSelected ? .delete(monster) : .add(monster) + let eventMutation = Observable.just(Mutation.setEvent(event)) - return .concat([eventMutation, refresh]) - } - .catch { _ in - .just(.navigatTo(.bookmarkError)) - } + let refresh = self.dictionaryDetailAPIRepository + .fetchMonsterDetail(id: self.currentState.id) + .map { Mutation.setDetailData($0) } + + return .concat([eventMutation, refresh]) + } + .catch { _ in + .just(.navigateTo(.bookmarkError)) + } } func handleUndoLastDeletedBookmark() -> Observable { var monster = currentState.monsterDetailInfo guard let type = currentState.type.toItemType else { return .empty() } - return setBookmarkUseCase.execute( - bookmarkId: monster.monsterId, - isBookmark: .set(type) - ) - .flatMap { [weak self] newBookmarkId -> Observable in - guard let self else { return .empty() } - - monster.bookmarkId = newBookmarkId - let eventMutation = Observable.just(Mutation.setEvent(.add(monster))) - let refresh = self.dictionaryDetailMonsterUseCase.execute(id: self.currentState.id) - .map { Mutation.setDetailData($0) } - - return .concat([eventMutation, refresh]) - } - .catch { _ in - .just(.navigatTo(.bookmarkError)) - } + return bookmarkRepository + .setBookmark(resourceId: monster.monsterId, type: type) + .flatMap { [weak self] newBookmarkId -> Observable in + guard let self else { return .empty() } + + monster.bookmarkId = newBookmarkId + let eventMutation = Observable.just(Mutation.setEvent(.add(monster))) + let refresh = self.dictionaryDetailAPIRepository + .fetchMonsterDetail(id: self.currentState.id) + .map { Mutation.setDetailData($0) } + + return .concat([eventMutation, refresh]) + } + .catch { _ in + .just(.navigateTo(.bookmarkError)) + } } } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift similarity index 93% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift index 2e4e7ca7..17576ad2 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift @@ -1,9 +1,7 @@ import UIKit -import BaseFeature -import DesignSystem -import DictionaryFeatureInterface -import DomainInterface +import MLSDesignSystem +import MLSDictionaryFeatureInterface import ReactorKit import RxCocoa @@ -55,7 +53,7 @@ private extension MonsterDictionaryDetailViewController { guard let reactor = reactor else { return } let infos = reactor.currentState.infos - contentViews.append(detailView) + setContentView(view: detailView, detailType: .normal) for info in infos { detailView.addInfo(mainText: info.name, subText: info.desc) @@ -70,11 +68,10 @@ private extension MonsterDictionaryDetailViewController { let maps = reactor.currentState.spawnMaps appearMapView.reset() - contentViews.append(appearMapView) if maps.isEmpty { - contentViews[1] = DetailEmptyView(type: .appearMap) + setContentView(view: DetailEmptyView(type: .appearMap), detailType: .appearMapWithText) } else { - contentViews[1] = appearMapView + setContentView(view: appearMapView, detailType: .appearMapWithText) for map in maps { appearMapView.inject(input: DetailStackCardView @@ -104,13 +101,12 @@ private extension MonsterDictionaryDetailViewController { let items = reactor.currentState.dropItems dropItemView.reset() - contentViews.append(dropItemView) // 드롭아이템 if items.isEmpty { // 드롭 아이템 - contentViews[2] = DetailEmptyView(type: .dropItemWithText) + setContentView(view: DetailEmptyView(type: .dropItemWithText), detailType: .dropItemWithText) } else { - contentViews[2] = dropItemView + setContentView(view: dropItemView, detailType: .dropItemWithText) for item in items { dropItemView .inject( @@ -221,7 +217,7 @@ extension MonsterDictionaryDetailViewController { } owner.tabBarController?.presentModal(viewController, hideTabBar: true) case let .detail(type: type, id: id): - let viewController = owner.dictionaryDetailFactory.make(type: type, id: id, bookmarkRelay: owner.bookmarkRelay, loginRelay: owner.loginRelay) + guard let viewController = owner.dictionaryDetailFactory?.make(type: type, id: id, bookmarkRelay: owner.bookmarkRelay, loginRelay: owner.loginRelay) else { return } owner.navigationController?.pushViewController(viewController, animated: true) case .bookmarkError: ToastFactory.createToast(message: "북마크 요청에 실패했어요. 다시 시도해주세요.") diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailReactor.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/NPC/NpcDictionaryDetailReactor.swift similarity index 56% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailReactor.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/NPC/NpcDictionaryDetailReactor.swift index 1857182c..a3a3ebcb 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailReactor.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/NPC/NpcDictionaryDetailReactor.swift @@ -1,4 +1,5 @@ -import DomainInterface +import MLSBookmarkFeatureInterface +import MLSDictionaryFeatureInterface import ReactorKit @@ -31,7 +32,7 @@ public final class NpcDictionaryDetailReactor: Reactor { // MARK: - Mutation public enum Mutation { - case navigatTo(Route) + case navigateTo(Route) case setDetailData(DictionaryDetailNpcResponse) case setDetailMaps([DictionaryDetailMonsterMapResponse]) case setDetailQuests([DictionaryDetailNpcQuestResponse]) @@ -57,29 +58,23 @@ public final class NpcDictionaryDetailReactor: Reactor { } // MARK: - UseCases - private let dictionaryDetailNpcUseCase: FetchDictionaryDetailNpcUseCase - private let dictionaryDetailNpcQuestUseCase: FetchDictionaryDetailNpcQuestUseCase - private let dictionaryDetailNpcMapUseCase: FetchDictionaryDetailNpcMapUseCase + private let dictionaryDetailAPIRepository: DictionaryDetailAPIRepository + private let bookmarkRepository: BookmarkRepository private let checkLoginUseCase: CheckLoginUseCase - private let setBookmarkUseCase: SetBookmarkUseCase public var initialState: State private let disposeBag = DisposeBag() // MARK: - Init public init( - dictionaryDetailNpcUseCase: FetchDictionaryDetailNpcUseCase, - dictionaryDetailNpcQuestUseCase: FetchDictionaryDetailNpcQuestUseCase, - dictionaryDetailNpcMapUseCase: FetchDictionaryDetailNpcMapUseCase, + dictionaryDetailAPIRepository: DictionaryDetailAPIRepository, + bookmarkRepository: BookmarkRepository, checkLoginUseCase: CheckLoginUseCase, - setBookmarkUseCase: SetBookmarkUseCase, id: Int ) { - self.dictionaryDetailNpcUseCase = dictionaryDetailNpcUseCase - self.dictionaryDetailNpcQuestUseCase = dictionaryDetailNpcQuestUseCase - self.dictionaryDetailNpcMapUseCase = dictionaryDetailNpcMapUseCase + self.dictionaryDetailAPIRepository = dictionaryDetailAPIRepository + self.bookmarkRepository = bookmarkRepository self.checkLoginUseCase = checkLoginUseCase - self.setBookmarkUseCase = setBookmarkUseCase self.initialState = State( npcDetailInfo: DictionaryDetailNpcResponse( npcId: 0, nameKr: "", nameEn: "", iconUrlDetail: nil, bookmarkId: nil @@ -94,16 +89,16 @@ public final class NpcDictionaryDetailReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .filterButtonTapped: - return .just(.navigatTo(.filter(currentState.questFilter))) + return .just(.navigateTo(.filter(currentState.questFilter))) case .viewWillAppear: return .merge([ checkLoginUseCase.execute().map { .setLoginState($0) }, - dictionaryDetailNpcUseCase.execute(id: currentState.id).map { .setDetailData($0) }, - dictionaryDetailNpcMapUseCase.execute(id: currentState.id).map { .setDetailMaps($0) }, - dictionaryDetailNpcQuestUseCase.execute(id: currentState.id, sort: nil).map { .setDetailQuests($0) } + dictionaryDetailAPIRepository.fetchNpcDetail(id: currentState.id).map { .setDetailData($0) }, + dictionaryDetailAPIRepository.fetchNpcDetailMap(id: currentState.id).map { .setDetailMaps($0) }, + dictionaryDetailAPIRepository.fetchNpcDetailQuest(id: currentState.id, sort: nil).map { .setDetailQuests($0) } ]) case let .selectFilter(type): - return dictionaryDetailNpcQuestUseCase.execute(id: currentState.id, sort: type.sortParameter).map { .setDetailQuests($0) } + return dictionaryDetailAPIRepository.fetchNpcDetailQuest(id: currentState.id, sort: type.sortParameter).map { .setDetailQuests($0) } case .toggleBookmark: return handleToggleBookmark() @@ -112,10 +107,10 @@ public final class NpcDictionaryDetailReactor: Reactor { return handleUndoLastDeletedBookmark() case .mapTapped(index: let index): - return .just(.navigatTo(.detail(type: .map, id: currentState.maps[index].mapId))) + return .just(.navigateTo(.detail(type: .map, id: currentState.maps[index].mapId))) case .questTapped(index: let index): - return .just(.navigatTo(.detail(type: .quest, id: currentState.quests[index].questId))) + return .just(.navigateTo(.detail(type: .quest, id: currentState.quests[index].questId))) } } @@ -123,7 +118,7 @@ public final class NpcDictionaryDetailReactor: Reactor { public func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { - case .navigatTo(let route): + case .navigateTo(let route): newState.route = route case let .setDetailData(data): newState.npcDetailInfo = data @@ -148,47 +143,55 @@ private extension NpcDictionaryDetailReactor { let isSelected = npc.bookmarkId != nil guard let type = currentState.type.toItemType else { return .empty() } - return setBookmarkUseCase.execute( - bookmarkId: isSelected ? npc.bookmarkId ?? npc.npcId : npc.npcId, - isBookmark: isSelected ? .delete : .set(type) - ) - .flatMap { [weak self] newBookmarkId -> Observable in - guard let self else { return .empty() } + let bookmarkObservable: Observable - npc.bookmarkId = newBookmarkId - let event: UIEvent = isSelected ? .delete(npc) : .add(npc) - let eventMutation = Observable.just(Mutation.setEvent(event)) + if isSelected, let bookmarkId = npc.bookmarkId { + bookmarkObservable = bookmarkRepository + .deleteBookmark(bookmarkId: bookmarkId) + } else { + bookmarkObservable = bookmarkRepository + .setBookmark(resourceId: npc.npcId, type: type) + .map { Optional($0) } + } - let refresh = self.dictionaryDetailNpcUseCase.execute(id: self.currentState.id) - .map { Mutation.setDetailData($0) } + return bookmarkObservable + .flatMap { [weak self] newBookmarkId -> Observable in + guard let self else { return .empty() } - return .concat([eventMutation, refresh]) - } - .catch { _ in - .just(.navigatTo(.bookmarkError)) - } + npc.bookmarkId = newBookmarkId + let event: UIEvent = isSelected ? .delete(npc) : .add(npc) + let eventMutation = Observable.just(Mutation.setEvent(event)) + + let refresh = self.dictionaryDetailAPIRepository + .fetchNpcDetail(id: self.currentState.id) + .map { Mutation.setDetailData($0) } + + return .concat([eventMutation, refresh]) + } + .catch { _ in + .just(.navigateTo(.bookmarkError)) + } } func handleUndoLastDeletedBookmark() -> Observable { var npc = currentState.npcDetailInfo guard let type = currentState.type.toItemType else { return .empty() } - return setBookmarkUseCase.execute( - bookmarkId: npc.npcId, - isBookmark: .set(type) - ) - .flatMap { [weak self] newBookmarkId -> Observable in - guard let self else { return .empty() } - - npc.bookmarkId = newBookmarkId - let eventMutation = Observable.just(Mutation.setEvent(.add(npc))) - let refresh = self.dictionaryDetailNpcUseCase.execute(id: self.currentState.id) - .map { Mutation.setDetailData($0) } - - return .concat([eventMutation, refresh]) - } - .catch { _ in - .just(.navigatTo(.bookmarkError)) - } + return bookmarkRepository + .setBookmark(resourceId: npc.npcId, type: type) + .flatMap { [weak self] newBookmarkId -> Observable in + guard let self else { return .empty() } + + npc.bookmarkId = newBookmarkId + let eventMutation = Observable.just(Mutation.setEvent(.add(npc))) + let refresh = self.dictionaryDetailAPIRepository + .fetchNpcDetail(id: self.currentState.id) + .map { Mutation.setDetailData($0) } + + return .concat([eventMutation, refresh]) + } + .catch { _ in + .just(.navigateTo(.bookmarkError)) + } } } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/NPC/NpcDictionaryDetailViewController.swift similarity index 92% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/NPC/NpcDictionaryDetailViewController.swift index 04d448ef..6efedf8d 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/NPC/NpcDictionaryDetailViewController.swift @@ -1,9 +1,7 @@ import UIKit -import BaseFeature -import DesignSystem -import DictionaryFeatureInterface -import DomainInterface +import MLSDesignSystem +import MLSDictionaryFeatureInterface import ReactorKit import RxCocoa @@ -53,12 +51,11 @@ private extension NpcDictionaryDetailViewController { let maps = reactor.currentState.maps appearMapView.reset() - contentViews.append(appearMapView) if maps.isEmpty { // 출현맵 - contentViews[0] = DetailEmptyView(type: .appearMap) + } else { - contentViews[0] = appearMapView + setContentView(view: appearMapView, detailType: .appearMap) for map in maps { appearMapView.inject(input: DetailStackCardView.Input( type: .appearMap, @@ -78,12 +75,11 @@ private extension NpcDictionaryDetailViewController { let quests = reactor.currentState.quests questView.reset() - contentViews.append(questView) if quests.isEmpty { // 퀘스트 - contentViews[1] = DetailEmptyView(type: .quest) + setContentView(view: DetailEmptyView(type: .quest), detailType: .quest) } else { - contentViews[1] = questView + setContentView(view: questView, detailType: .quest) for quest in quests { questView.inject(input: DetailStackCardView.Input( type: .quest, @@ -142,7 +138,7 @@ extension NpcDictionaryDetailViewController { } owner.tabBarController?.presentModal(viewController, hideTabBar: true) case .detail(type: let type, id: let id): - let viewController = owner.dictionaryDetailFactory.make(type: type, id: id, bookmarkRelay: owner.bookmarkRelay, loginRelay: owner.loginRelay) + guard let viewController = owner.dictionaryDetailFactory?.make(type: type, id: id, bookmarkRelay: owner.bookmarkRelay, loginRelay: owner.loginRelay) else { return } owner.navigationController?.pushViewController(viewController, animated: true) case .bookmarkError: ToastFactory.createToast(message: "북마크 요청에 실패했어요. 다시 시도해주세요.") diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/OnBoarding/DetailOnBoardingFactoryImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/OnBoarding/DetailOnBoardingFactoryImpl.swift similarity index 87% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/OnBoarding/DetailOnBoardingFactoryImpl.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/OnBoarding/DetailOnBoardingFactoryImpl.swift index 6c0482f5..3adab8e7 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/OnBoarding/DetailOnBoardingFactoryImpl.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/OnBoarding/DetailOnBoardingFactoryImpl.swift @@ -1,5 +1,5 @@ -import BaseFeature -import DictionaryFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface public final class DetailOnBoardingFactoryImpl: DetailOnBoardingFactory { public init() {} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/OnBoarding/DetailOnBoardingReactor.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/OnBoarding/DetailOnBoardingReactor.swift similarity index 96% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/OnBoarding/DetailOnBoardingReactor.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/OnBoarding/DetailOnBoardingReactor.swift index c473e635..29a2c8ae 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/OnBoarding/DetailOnBoardingReactor.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/OnBoarding/DetailOnBoardingReactor.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface import ReactorKit diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/OnBoarding/DetailOnBoardingView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/OnBoarding/DetailOnBoardingView.swift similarity index 99% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/OnBoarding/DetailOnBoardingView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/OnBoarding/DetailOnBoardingView.swift index 798bcbc8..803d0b89 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/OnBoarding/DetailOnBoardingView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/OnBoarding/DetailOnBoardingView.swift @@ -1,7 +1,6 @@ import UIKit -import DesignSystem -import DomainInterface +import MLSDesignSystem import SnapKit diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/OnBoarding/DetailOnBoardingViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/OnBoarding/DetailOnBoardingViewController.swift similarity index 97% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/OnBoarding/DetailOnBoardingViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/OnBoarding/DetailOnBoardingViewController.swift index 1f1f1552..909e1b92 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/OnBoarding/DetailOnBoardingViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/OnBoarding/DetailOnBoardingViewController.swift @@ -1,7 +1,7 @@ import UIKit -import BaseFeature -import DictionaryFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface import ReactorKit import RxCocoa diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailReactor.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Quest/QuestDictionaryDetailReactor.swift similarity index 67% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailReactor.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Quest/QuestDictionaryDetailReactor.swift index 8e35deb5..c3a91523 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailReactor.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Quest/QuestDictionaryDetailReactor.swift @@ -1,4 +1,5 @@ -import DomainInterface +import MLSBookmarkFeatureInterface +import MLSDictionaryFeatureInterface import ReactorKit @@ -37,7 +38,7 @@ public final class QuestDictionaryDetailReactor: Reactor { } public enum Mutation { - case navigatTo(Route) + case navigateTo(Route) case setDetailData(DictionaryDetailQuestResponse) case setLinkedQuests(DictionaryDetailQuestLinkedQuestsResponse) case setLoginState(Bool) @@ -57,25 +58,22 @@ public final class QuestDictionaryDetailReactor: Reactor { var lastDeletedBookmark: DictionaryDetailQuestResponse? } - private let dictionaryDetailQuestUseCase: FetchDictionaryDetailQuestUseCase - private let dictionaryDetailQuestLinkedQuestUseCase: FetchDictionaryDetailQuestLinkedQuestsUseCase + private let dictionaryDetailAPIRepository: DictionaryDetailAPIRepository + private let bookmarkRepository: BookmarkRepository private let checkLoginUseCase: CheckLoginUseCase - private let setBookmarkUseCase: SetBookmarkUseCase public var initialState: State private let disposeBag = DisposeBag() public init( - dictionaryDetailQuestUseCase: FetchDictionaryDetailQuestUseCase, - dictionaryDetailQuestLinkedQuestUseCase: FetchDictionaryDetailQuestLinkedQuestsUseCase, + dictionaryDetailAPIRepository: DictionaryDetailAPIRepository, + bookmarkRepository: BookmarkRepository, checkLoginUseCase: CheckLoginUseCase, - setBookmarkUseCase: SetBookmarkUseCase, id: Int ) { - self.dictionaryDetailQuestUseCase = dictionaryDetailQuestUseCase - self.dictionaryDetailQuestLinkedQuestUseCase = dictionaryDetailQuestLinkedQuestUseCase + self.dictionaryDetailAPIRepository = dictionaryDetailAPIRepository + self.bookmarkRepository = bookmarkRepository self.checkLoginUseCase = checkLoginUseCase - self.setBookmarkUseCase = setBookmarkUseCase self.initialState = .init( id: id, detailInfo: .init( @@ -107,8 +105,8 @@ public final class QuestDictionaryDetailReactor: Reactor { case .viewWillAppear: return .merge([ checkLoginUseCase.execute().map { .setLoginState($0) }, - dictionaryDetailQuestUseCase.execute(id: currentState.id).map { .setDetailData($0) }, - dictionaryDetailQuestLinkedQuestUseCase.execute(id: currentState.id).map { .setLinkedQuests($0) } + dictionaryDetailAPIRepository.fetchQuestDetail(id: currentState.id).map { .setDetailData($0) }, + dictionaryDetailAPIRepository.fetchQuestDetailLinkedQuestsDetail(id: currentState.id).map { .setLinkedQuests($0) } ]) case .toggleBookmark: @@ -121,10 +119,10 @@ public final class QuestDictionaryDetailReactor: Reactor { let tappedQuestInfo = currentState.totalQuest[index] guard let id = tappedQuestInfo.quest.questId, tappedQuestInfo.type != .current else { return .empty() } - return .just(.navigatTo(.detail(type: .quest, id: id))) + return .just(.navigateTo(.detail(type: .quest, id: id))) case let .infoTapped(type: type, id: id): - return .just(.navigatTo(.detail(type: type, id: id))) + return .just(.navigateTo(.detail(type: type, id: id))) } } @@ -147,7 +145,7 @@ public final class QuestDictionaryDetailReactor: Reactor { newState.isLogin = isLogin case let .setLastDeletedBookmark(data): newState.lastDeletedBookmark = data - case let .navigatTo(route): + case let .navigateTo(route): newState.route = route case let .setEvent(event): newState.event = event @@ -192,47 +190,55 @@ private extension QuestDictionaryDetailReactor { let isSelected = quest.bookmarkId != nil guard let type = currentState.type.toItemType else { return .empty() } - return setBookmarkUseCase.execute( - bookmarkId: isSelected ? quest.bookmarkId ?? quest.questId : quest.questId, - isBookmark: isSelected ? .delete : .set(type) - ) - .flatMap { [weak self] newBookmarkId -> Observable in - guard let self else { return .empty() } + let bookmarkObservable: Observable - quest.bookmarkId = newBookmarkId - let event: UIEvent = isSelected ? .delete(quest) : .add(quest) - let eventMutation = Observable.just(Mutation.setEvent(event)) + if isSelected, let bookmarkId = quest.bookmarkId { + bookmarkObservable = bookmarkRepository + .deleteBookmark(bookmarkId: bookmarkId) + } else { + bookmarkObservable = bookmarkRepository + .setBookmark(resourceId: quest.questId, type: type) + .map { Optional($0) } + } - let refresh = self.dictionaryDetailQuestUseCase.execute(id: self.currentState.id) - .map { Mutation.setDetailData($0) } + return bookmarkObservable + .flatMap { [weak self] newBookmarkId -> Observable in + guard let self else { return .empty() } - return .concat([eventMutation, refresh]) - } - .catch { _ in - .just(.navigatTo(.bookmarkError)) - } + quest.bookmarkId = newBookmarkId + let event: UIEvent = isSelected ? .delete(quest) : .add(quest) + let eventMutation = Observable.just(Mutation.setEvent(event)) + + let refresh = self.dictionaryDetailAPIRepository + .fetchQuestDetail(id: self.currentState.id) + .map { Mutation.setDetailData($0) } + + return .concat([eventMutation, refresh]) + } + .catch { _ in + .just(.navigateTo(.bookmarkError)) + } } func handleUndoLastDeletedBookmark() -> Observable { var quest = currentState.detailInfo guard let type = currentState.type.toItemType else { return .empty() } - return setBookmarkUseCase.execute( - bookmarkId: quest.questId, - isBookmark: .set(type) - ) - .flatMap { [weak self] newBookmarkId -> Observable in - guard let self else { return .empty() } - - quest.bookmarkId = newBookmarkId - let eventMutation = Observable.just(Mutation.setEvent(.add(quest))) - let refresh = self.dictionaryDetailQuestUseCase.execute(id: self.currentState.id) - .map { Mutation.setDetailData($0) } - - return .concat([eventMutation, refresh]) - } - .catch { _ in - .just(.navigatTo(.bookmarkError)) - } + return bookmarkRepository + .setBookmark(resourceId: quest.questId, type: type) + .flatMap { [weak self] newBookmarkId -> Observable in + guard let self else { return .empty() } + + quest.bookmarkId = newBookmarkId + let eventMutation = Observable.just(Mutation.setEvent(.add(quest))) + let refresh = self.dictionaryDetailAPIRepository + .fetchQuestDetail(id: self.currentState.id) + .map { Mutation.setDetailData($0) } + + return .concat([eventMutation, refresh]) + } + .catch { _ in + .just(.navigateTo(.bookmarkError)) + } } } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Quest/QuestDictionaryDetailViewController.swift similarity index 93% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Quest/QuestDictionaryDetailViewController.swift index 25e88982..1fc038be 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/Quest/QuestDictionaryDetailViewController.swift @@ -1,8 +1,7 @@ import UIKit -import BaseFeature -import DesignSystem -import DomainInterface +import MLSDesignSystem +import MLSDictionaryFeatureInterface import ReactorKit import RxCocoa @@ -47,11 +46,10 @@ private extension QuestDictionaryDetailViewController { let rewardInfos = reactor.currentState.detailInfo.reward let rewardItemInfos = reactor.currentState.detailInfo.rewardItems - contentViews.append(detailInfoView) // 뭘로 빈페이지 보여줄지 정하지.. detailInfoView.reset() if !(detailInfos.startNpcName == nil) { - contentViews.append(detailInfoView) + setContentView(view: detailInfoView, detailType: .normal) // 완료조건 추가 if let requirements = detailInfos.requirements { for requirement in requirements { @@ -120,7 +118,7 @@ private extension QuestDictionaryDetailViewController { } } else { - contentViews.append(DetailEmptyView(type: .normal)) + setContentView(view: DetailEmptyView(type: .normal), detailType: .normal) } } @@ -129,12 +127,11 @@ private extension QuestDictionaryDetailViewController { let quests = reactor.currentState.totalQuest linkedQuestView.reset() - contentViews.append(linkedQuestView) if quests.isEmpty { - contentViews[1] = DetailEmptyView(type: .quest) + setContentView(view: DetailEmptyView(type: .quest), detailType: .linkedQuest) } else { - contentViews[1] = linkedQuestView + setContentView(view: linkedQuestView, detailType: .linkedQuest) for data in quests { linkedQuestView.inject( @@ -200,7 +197,7 @@ extension QuestDictionaryDetailViewController { .subscribe { owner, route in switch route { case let .detail(type, id): - let viewController = owner.dictionaryDetailFactory.make(type: type, id: id, bookmarkRelay: owner.bookmarkRelay, loginRelay: owner.loginRelay) + guard let viewController = owner.dictionaryDetailFactory?.make(type: type, id: id, bookmarkRelay: owner.bookmarkRelay, loginRelay: owner.loginRelay) else { return } owner.navigationController?.pushViewController(viewController, animated: true) case .bookmarkError: ToastFactory.createToast(message: "북마크 요청에 실패했어요. 다시 시도해주세요.") diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailEmptyView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/DetailEmptyView.swift similarity index 95% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailEmptyView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/DetailEmptyView.swift index 20d4cee5..eba14097 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailEmptyView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/DetailEmptyView.swift @@ -1,7 +1,7 @@ -import DomainInterface import UIKit -import DesignSystem +import MLSDesignSystem +import MLSDictionaryFeatureInterface import SnapKit diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailStackCardView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/DetailStackCardView.swift similarity index 98% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailStackCardView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/DetailStackCardView.swift index 3d567b67..9f89211e 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailStackCardView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/DetailStackCardView.swift @@ -1,11 +1,13 @@ -import BaseFeature -import DesignSystem -import DomainInterface +import UIKit + +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface + import RxCocoa import RxGesture import RxSwift import SnapKit -import UIKit final class DetailStackCardView: UIStackView { // MARK: - Type @@ -47,7 +49,7 @@ final class DetailStackCardView: UIStackView { for: .normal ) button.setImage( - DesignSystemAsset.image(named: "dropDown")?.withRenderingMode( + DesignSystemAsset.image(named: "dropDown").withRenderingMode( .alwaysTemplate ), for: .normal diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailStackInfoView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/DetailStackInfoView.swift similarity index 99% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailStackInfoView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/DetailStackInfoView.swift index 00670a7f..fff25013 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailStackInfoView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/DetailStackInfoView.swift @@ -1,7 +1,7 @@ import UIKit -import DesignSystem -import DomainInterface +import MLSDesignSystem +import MLSDictionaryFeatureInterface import RxGesture import RxSwift diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailStackMapView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/DetailStackMapView.swift similarity index 98% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailStackMapView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/DetailStackMapView.swift index 3fd3b0f6..55b310a0 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailStackMapView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/DetailStackMapView.swift @@ -1,7 +1,8 @@ -import BaseFeature -import DesignSystem import UIKit +import MLSCore +import MLSDesignSystem + final class DetailStackMapView: UIStackView { // MARK: - Type private enum Constant { diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/PinchMapView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/PinchMapView.swift similarity index 96% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/PinchMapView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/PinchMapView.swift index 4ffa6d00..fba6614c 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/PinchMapView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/PinchMapView.swift @@ -1,7 +1,7 @@ import UIKit -import BaseFeature -import DesignSystem +import MLSCore +import MLSDesignSystem final class PinchMapView: UIView { // MARK: - Type @@ -34,7 +34,7 @@ final class PinchMapView: UIView { let backButton: UIButton = { let button = UIButton() - button.setImage(DesignSystemAsset.image(named: "largeX")?.withRenderingMode(.alwaysTemplate), for: .normal) + button.setImage(DesignSystemAsset.image(named: "largeX").withRenderingMode(.alwaysTemplate), for: .normal) button.tintColor = .whiteMLS return button }() diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/PinchMapViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/PinchMapViewController.swift similarity index 98% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/PinchMapViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/PinchMapViewController.swift index 745e6893..baa60757 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/PinchMapViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryDetail/SectionStackView/PinchMapViewController.swift @@ -1,6 +1,6 @@ import UIKit -import BaseFeature +import MLSCore import RxCocoa import RxSwift diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListFactoryImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryList/DictionaryListFactoryImpl.swift similarity index 52% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListFactoryImpl.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryList/DictionaryListFactoryImpl.swift index 02884710..a0851372 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListFactoryImpl.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryList/DictionaryListFactoryImpl.swift @@ -1,19 +1,14 @@ -import AuthFeatureInterface -import BaseFeature -import BookmarkFeatureInterface -import DictionaryFeatureInterface -import DomainInterface +import MLSAuthFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface public final class DictionaryListFactoryImpl: DictionaryMainListFactory { private let checkLoginUseCase: CheckLoginUseCase - private let dictionaryAllListItemUseCase: FetchDictionaryAllListUseCase - private let dictionaryMapListItemUseCase: FetchDictionaryMapListUseCase - private let dictionaryItemListItemUseCase: FetchDictionaryItemListUseCase - private let dictionaryQuestListItemUseCase: FetchDictionaryQuestListUseCase - private let dictionaryNpcListItemUseCase: FetchDictionaryNpcListUseCase - private let dictionaryListItemUseCase: FetchDictionaryMonsterListUseCase + private let bookmarkRepository: BookmarkRepository private let parseItemFilterResultUseCase: ParseItemFilterResultUseCase - private let setBookmarkUseCase: SetBookmarkUseCase + + private let dictionaryListAPIRepository: DictionaryListAPIRepository private let itemFilterFactory: ItemFilterBottomSheetFactory private let monsterFilterFactory: MonsterFilterBottomSheetFactory @@ -24,14 +19,9 @@ public final class DictionaryListFactoryImpl: DictionaryMainListFactory { public init( checkLoginUseCase: CheckLoginUseCase, - dictionaryAllListItemUseCase: FetchDictionaryAllListUseCase, - dictionaryMapListItemUseCase: FetchDictionaryMapListUseCase, - dictionaryItemListItemUseCase: FetchDictionaryItemListUseCase, - dictionaryQuestListItemUseCase: FetchDictionaryQuestListUseCase, - dictionaryNpcListItemUseCase: FetchDictionaryNpcListUseCase, - dictionaryListItemUseCase: FetchDictionaryMonsterListUseCase, - setBookmarkUseCase: SetBookmarkUseCase, + bookmarkRepository: BookmarkRepository, parseItemFilterResultUseCase: ParseItemFilterResultUseCase, + dictionaryListAPIRepository: DictionaryListAPIRepository, itemFilterFactory: ItemFilterBottomSheetFactory, monsterFilterFactory: MonsterFilterBottomSheetFactory, sortedFactory: SortedBottomSheetFactory, @@ -40,14 +30,9 @@ public final class DictionaryListFactoryImpl: DictionaryMainListFactory { loginFactory: @escaping () -> LoginFactory ) { self.checkLoginUseCase = checkLoginUseCase - self.dictionaryAllListItemUseCase = dictionaryAllListItemUseCase - self.dictionaryMapListItemUseCase = dictionaryMapListItemUseCase - self.dictionaryItemListItemUseCase = dictionaryItemListItemUseCase - self.dictionaryQuestListItemUseCase = dictionaryQuestListItemUseCase - self.dictionaryNpcListItemUseCase = dictionaryNpcListItemUseCase - self.dictionaryListItemUseCase = dictionaryListItemUseCase - self.setBookmarkUseCase = setBookmarkUseCase + self.bookmarkRepository = bookmarkRepository self.parseItemFilterResultUseCase = parseItemFilterResultUseCase + self.dictionaryListAPIRepository = dictionaryListAPIRepository self.itemFilterFactory = itemFilterFactory self.monsterFilterFactory = monsterFilterFactory self.sortedFactory = sortedFactory @@ -60,14 +45,9 @@ public final class DictionaryListFactoryImpl: DictionaryMainListFactory { let reactor = DictionaryListReactor( type: type, keyword: keyword, + dictionaryListAPIRepository: dictionaryListAPIRepository, checkLoginUseCase: checkLoginUseCase, - dictionaryAllListUseCase: dictionaryAllListItemUseCase, - dictionaryMapListUseCase: dictionaryMapListItemUseCase, - dictionaryItemListUseCase: dictionaryItemListItemUseCase, - dictionaryQuestListUseCase: dictionaryQuestListItemUseCase, - dictionaryNpcListUseCase: dictionaryNpcListItemUseCase, - dictionaryListUseCase: dictionaryListItemUseCase, - setBookmarkUseCase: setBookmarkUseCase, + bookmarkRepository: bookmarkRepository, parseItemFilterResultUseCase: parseItemFilterResultUseCase ) let viewController = DictionaryListViewController( diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryList/DictionaryListReactor.swift similarity index 76% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryList/DictionaryListReactor.swift index 7be1f4a9..bca71c61 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryList/DictionaryListReactor.swift @@ -1,5 +1,6 @@ -import BaseFeature -import DomainInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface import ReactorKit import RxSwift @@ -39,7 +40,7 @@ public final class DictionaryListReactor: Reactor { // MARK: - Mutation public enum Mutation { - case navigatTo(Route) + case navigateTo(Route) case setListItem(DictionaryMainResponse, updateBookmarkOnly: Bool = false) case setSort(String) case setFilter(start: Int?, end: Int?) @@ -56,7 +57,7 @@ public final class DictionaryListReactor: Reactor { // MARK: - State public struct State { - @Pulse var uiEvent: UIEvent = .none + @Pulse public var uiEvent: UIEvent = .none @Pulse var route: Route public var listItems: [DictionaryMainItemResponse] = [] public var type: DictionaryType @@ -74,20 +75,15 @@ public final class DictionaryListReactor: Reactor { var isLogin: Bool var lastDeletedBookmark: DictionaryMainItemResponse? var isBookmarkUpdateOnly = false - var isFirstFetch = true + public var isFirstFetch = true } public var initialState: State // MARK: - UseCases + private let dictionaryListAPIRepository: DictionaryListAPIRepository private let checkLoginUseCase: CheckLoginUseCase - private let dictionaryAllListUseCase: FetchDictionaryAllListUseCase - private let dictionaryMapListUseCase: FetchDictionaryMapListUseCase - private let dictionaryItemListUseCase: FetchDictionaryItemListUseCase - private let dictionaryQuestListUseCase: FetchDictionaryQuestListUseCase - private let dictionaryNpcListUseCase: FetchDictionaryNpcListUseCase - private let dictionaryListUseCase: FetchDictionaryMonsterListUseCase - private let setBookmarkUseCase: SetBookmarkUseCase + private let bookmarkRepository: BookmarkRepository private let parseItemFilterResultUseCase: ParseItemFilterResultUseCase private let disposeBag = DisposeBag() @@ -96,25 +92,15 @@ public final class DictionaryListReactor: Reactor { public init( type: DictionaryType, keyword: String?, + dictionaryListAPIRepository: DictionaryListAPIRepository, checkLoginUseCase: CheckLoginUseCase, - dictionaryAllListUseCase: FetchDictionaryAllListUseCase, - dictionaryMapListUseCase: FetchDictionaryMapListUseCase, - dictionaryItemListUseCase: FetchDictionaryItemListUseCase, - dictionaryQuestListUseCase: FetchDictionaryQuestListUseCase, - dictionaryNpcListUseCase: FetchDictionaryNpcListUseCase, - dictionaryListUseCase: FetchDictionaryMonsterListUseCase, - setBookmarkUseCase: SetBookmarkUseCase, + bookmarkRepository: BookmarkRepository, parseItemFilterResultUseCase: ParseItemFilterResultUseCase ) { self.initialState = State(route: .none, type: type, keyword: keyword, isLogin: false) + self.dictionaryListAPIRepository = dictionaryListAPIRepository self.checkLoginUseCase = checkLoginUseCase - self.dictionaryAllListUseCase = dictionaryAllListUseCase - self.dictionaryMapListUseCase = dictionaryMapListUseCase - self.dictionaryItemListUseCase = dictionaryItemListUseCase - self.dictionaryQuestListUseCase = dictionaryQuestListUseCase - self.dictionaryNpcListUseCase = dictionaryNpcListUseCase - self.dictionaryListUseCase = dictionaryListUseCase - self.setBookmarkUseCase = setBookmarkUseCase + self.bookmarkRepository = bookmarkRepository self.parseItemFilterResultUseCase = parseItemFilterResultUseCase } @@ -124,9 +110,9 @@ public final class DictionaryListReactor: Reactor { case .viewWillAppear: return handleViewWillAppear() case .sortButtonTapped: - return .just(.navigatTo(.sort(currentState.type))) + return .just(.navigateTo(.sort(currentState.type))) case .filterButtonTapped: - return .just(.navigatTo(.filter(currentState.type))) + return .just(.navigateTo(.filter(currentState.type))) case let .sortOptionSelected(sort): return handleSortOptionSelected(sort: sort) case let .filterOptionSelected(startLevel, endLevel): @@ -152,7 +138,7 @@ public final class DictionaryListReactor: Reactor { public func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { - case let .navigatTo(route): + case let .navigateTo(route): newState.route = route case let .setListItem(items, updateBookmarkOnly): newState.isBookmarkUpdateOnly = updateBookmarkOnly @@ -211,19 +197,16 @@ private extension DictionaryListReactor { switch currentState.type { case .monster: - response = dictionaryListUseCase.execute( - type: .monster, - query: DictionaryListQuery( + response = dictionaryListAPIRepository.fetchMonsterList( keyword: currentState.keyword ?? "", + minLevel: startLevel, + maxLevel: endLevel, page: currentState.currentPage, size: 20, - sort: sort, - minLevel: startLevel, - maxLevel: endLevel + sort: sort ) - ) case .item: - response = dictionaryItemListUseCase.execute( + response = dictionaryListAPIRepository.fetchItemList( keyword: currentState.keyword ?? "", jobId: currentState.jobId, minLevel: currentState.startLevel, @@ -234,28 +217,28 @@ private extension DictionaryListReactor { sort: sort ) case .map: - response = dictionaryMapListUseCase.execute( + response = dictionaryListAPIRepository.fetchMapList( keyword: currentState.keyword ?? "", page: currentState.currentPage, size: 20, sort: sort ?? "ASC" ) case .npc: - response = dictionaryNpcListUseCase.execute( + response = dictionaryListAPIRepository.fetchNpcList( keyword: currentState.keyword ?? "", page: currentState.currentPage, size: 20, sort: sort ?? "ASC" ) case .quest: - response = dictionaryQuestListUseCase.execute( + response = dictionaryListAPIRepository.fetchQuestList( keyword: currentState.keyword ?? "", page: currentState.currentPage, size: 20, sort: sort ?? "ASC" ) case .total: - response = dictionaryAllListUseCase.execute( + response = dictionaryListAPIRepository.fetchAllList( keyword: currentState.keyword ?? "", page: currentState.currentPage ) @@ -300,22 +283,28 @@ private extension DictionaryListReactor { let targetItem = currentState.listItems[index] let isSelected = targetItem.bookmarkId != nil - return setBookmarkUseCase.execute( - bookmarkId: isSelected ? targetItem.bookmarkId ?? targetItem.id : targetItem.id, - isBookmark: isSelected ? .delete : .set(targetItem.type) - ) - .flatMap { newBookmarkId -> Observable in - let lastItem = Mutation.setLastDeletedBookmark(targetItem) - - let event: UIEvent = isSelected ? .delete(targetItem) : .add(targetItem) - let eventMutation = Mutation.setEvent(event) + let bookmarkObservable: Observable - let updateMutation = Mutation.updateBookmarkId(id: id, newBookmarkId: newBookmarkId) - return .from([lastItem, updateMutation, eventMutation]) - } - .catch { _ in - .just(.navigatTo(.bookmarkError)) + if isSelected, let bookmarkId = targetItem.bookmarkId { + bookmarkObservable = bookmarkRepository + .deleteBookmark(bookmarkId: bookmarkId) + } else { + bookmarkObservable = bookmarkRepository + .setBookmark(resourceId: targetItem.id, type: targetItem.type) + .map { Optional($0) } } + + return bookmarkObservable + .flatMap { newBookmarkId -> Observable in + let lastItem = Mutation.setLastDeletedBookmark(targetItem) + let event: UIEvent = isSelected ? .delete(targetItem) : .add(targetItem) + let eventMutation = Mutation.setEvent(event) + let updateMutation = Mutation.updateBookmarkId(id: id, newBookmarkId: newBookmarkId) + return .from([lastItem, updateMutation, eventMutation]) + } + .catch { _ in + .just(.navigateTo(.bookmarkError)) + } } func handleUpdateBookmark(id: Int, newBookmarkId: Int?) -> Observable { @@ -355,22 +344,18 @@ private extension DictionaryListReactor { func handleUndoLastDeletedBookmark() -> Observable { guard let lastDeleted = currentState.lastDeletedBookmark else { return .empty() } - return setBookmarkUseCase.execute( - bookmarkId: lastDeleted.id, - isBookmark: .set(lastDeleted.type) - ) - .flatMap { newBookmarkId -> Observable in - let lastItem = Mutation.setLastDeletedBookmark(nil) - - let event: UIEvent = .add(lastDeleted) - let eventMutation = Mutation.setEvent(event) - - let updateMutation = Mutation.updateBookmarkId(id: lastDeleted.id, newBookmarkId: newBookmarkId) - return .from([lastItem, updateMutation, eventMutation]) - } - .catch { _ in - .just(.navigatTo(.bookmarkError)) - } + return bookmarkRepository + .setBookmark(resourceId: lastDeleted.id, type: lastDeleted.type) + .flatMap { newBookmarkId -> Observable in + let lastItem = Mutation.setLastDeletedBookmark(nil) + let event: UIEvent = .add(lastDeleted) + let eventMutation = Mutation.setEvent(event) + let updateMutation = Mutation.updateBookmarkId(id: lastDeleted.id, newBookmarkId: newBookmarkId) + return .from([lastItem, updateMutation, eventMutation]) + } + .catch { _ in + .just(.navigateTo(.bookmarkError)) + } } func handleItemFilterOptionSelected(results: [(String, String)]) -> Observable { diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryList/DictionaryListView.swift similarity index 94% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryList/DictionaryListView.swift index d32a7174..b133cc46 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryList/DictionaryListView.swift @@ -1,7 +1,6 @@ import UIKit -import BaseFeature -import DesignSystem +import MLSDesignSystem final class DictionaryListView: BaseListView { // MARK: - Init diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryList/DictionaryListViewController.swift similarity index 83% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryList/DictionaryListViewController.swift index c89a9695..e4707c30 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryList/DictionaryListViewController.swift @@ -1,12 +1,14 @@ -import AuthFeatureInterface -import BaseFeature -import BookmarkFeatureInterface -import DictionaryFeatureInterface -import DomainInterface +import UIKit + +import MLSAuthFeatureInterface +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface + import ReactorKit import RxCocoa import RxSwift -import UIKit public final class DictionaryListViewController: BaseViewController, View { public typealias Reactor = DictionaryListReactor @@ -88,9 +90,8 @@ private extension DictionaryListViewController { func createListLayout() -> UICollectionViewLayout { guard let isHidden = reactor?.currentState.type.isSortHidden else { return UICollectionViewLayout() } - let layoutFactory = LayoutFactory() let layout = CompositionalLayoutBuilder() - .section { _ in layoutFactory.getDictionaryListLayout(isFilterHidden: isHidden) } + .section { _ in LayoutFactory.getDictionaryListLayout(isFilterHidden: isHidden) } .build() layout.register( Neutral300DividerView.self, @@ -254,52 +255,68 @@ extension DictionaryListViewController { } private func presentAddSnackBar(item: DictionaryMainItemResponse) { - SnackBarFactory.createSnackBar( - type: .normal, - imageUrl: item.imageUrl, - imageBackgroundColor: item.type.backgroundColor, - text: "아이템을 북마크에 추가했어요.", - buttonText: "컬렉션 추가", - buttonAction: { [weak self] in - self?.reactor?.state.map(\.listItems) - .compactMap { list in - list.first(where: { $0.id == item.id })?.bookmarkId - } - .take(1) - .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak self] bookmarkId in - guard let self else { return } - let vc = self.bookmarkModalFactory.make(bookmarkIds: [bookmarkId]) { isAdd in - if isAdd { - ToastFactory.createToast( - message: "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요." - ) - } + ImageLoader.shared.loadImage(stringURL: item.imageUrl) { image in + guard let image = image else { return } + + SnackBarFactory.createSnackBar( + type: .normal, + image: image, + imageBackgroundColor: item.type.backgroundColor, + text: "아이템을 북마크에 추가했어요.", + buttonText: "컬렉션 추가", + buttonAction: { [weak self] in + guard let self else { return } + + self.reactor?.state.map(\.listItems) + .compactMap { list in + list.first(where: { $0.id == item.id })?.bookmarkId } - vc.modalPresentationStyle = .pageSheet - if let sheet = vc.sheetPresentationController { - sheet.detents = [.medium(), .large()] - sheet.prefersGrabberVisible = true - sheet.preferredCornerRadius = 16 - } - self.present(vc, animated: true) - }) - .disposed(by: self?.disposeBag ?? DisposeBag()) - } - ) + .take(1) + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] bookmarkId in + guard let self else { return } + + let vc = self.bookmarkModalFactory.make( + bookmarkIds: [bookmarkId] + ) { isAdd in + if isAdd { + ToastFactory.createToast( + message: "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요." + ) + } + } + + vc.modalPresentationStyle = .pageSheet + + if let sheet = vc.sheetPresentationController { + sheet.detents = [.medium(), .large()] + sheet.prefersGrabberVisible = true + sheet.preferredCornerRadius = 16 + } + + self.present(vc, animated: true) + }) + .disposed(by: self.disposeBag) + } + ) + } } private func presentDeleteSnackBar(item: DictionaryMainItemResponse) { - SnackBarFactory.createSnackBar( - type: .delete, - imageUrl: item.imageUrl, - imageBackgroundColor: item.type.backgroundColor, - text: "아이템을 북마크에서 삭제했어요.", - buttonText: "되돌리기", - buttonAction: { [weak self] in - self?.reactor?.action.onNext(.undoLastDeletedBookmark) - } - ) + ImageLoader.shared.loadImage(stringURL: item.imageUrl) { image in + guard let image = image else { return } + + SnackBarFactory.createSnackBar( + type: .delete, + image: image, + imageBackgroundColor: item.type.backgroundColor, + text: "아이템을 북마크에서 삭제했어요.", + buttonText: "되돌리기", + buttonAction: { [weak self] in + self?.reactor?.action.onNext(.undoLastDeletedBookmark) + } + ) + } } private func presentLoginGuide() { diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/DictionaryListCell.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryListCell.swift similarity index 89% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/DictionaryListCell.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryListCell.swift index 33361c55..a08f74fd 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/DictionaryListCell.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryListCell.swift @@ -1,7 +1,8 @@ import UIKit -import DesignSystem -import DomainInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface public final class DictionaryListCell: UICollectionViewCell { // MARK: - Properties @@ -102,20 +103,3 @@ public extension DictionaryListCell { cellView.setSelected(isSelected: isBookmarked) } } - -public extension DictionaryItemType { - var backgroundColor: UIColor { - switch self { - case .item: - .listItem - case .monster: - .listMonster - case .map: - .listMap - case .npc: - .listNPC - case .quest: - .listQuest - } - } -} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainReactor.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryMain/DictionaryMainReactor.swift similarity index 68% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainReactor.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryMain/DictionaryMainReactor.swift index 49a8a7fa..09c2c0cb 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainReactor.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryMain/DictionaryMainReactor.swift @@ -1,6 +1,7 @@ import ReactorKit -import DomainInterface +import MLSDictionaryFeatureInterface +import MLSMyPageFeatureInterface public final class DictionaryMainReactor: Reactor { // MARK: - Reactor @@ -22,6 +23,7 @@ public final class DictionaryMainReactor: Reactor { case navigateTo(Route) case setLogin(Bool) case setCurrentTab(oldIndex: Int, newIndex: Int) + case setIsFirstRecommendationLaunch(Bool) } public struct State { @@ -30,20 +32,24 @@ public final class DictionaryMainReactor: Reactor { var sections: [String] { return type.pageTabList.map { $0.title } } + var isLogin = false var currentPageIndex = 0 var oldPageIndex = 0 + var isFirstRecommendationLaunch = false } // MARK: - properties public var initialState: State var disposeBag = DisposeBag() + private let userDefaultsRepository: DictionaryUserDefaultsRepository private let fetchProfileUseCase: FetchProfileUseCase // MARK: - init - public init(fetchProfileUseCase: FetchProfileUseCase) { + public init(userDefaultsRepository: DictionaryUserDefaultsRepository, fetchProfileUseCase: FetchProfileUseCase) { self.initialState = State() + self.userDefaultsRepository = userDefaultsRepository self.fetchProfileUseCase = fetchProfileUseCase } @@ -51,9 +57,18 @@ public final class DictionaryMainReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .viewWillAppear: - return fetchProfileUseCase.execute() - .map { .setLogin($0 != nil) } + let firstRecommendation = userDefaultsRepository + .checkFirstLaunchRecommendation() + .map(Mutation.setIsFirstRecommendationLaunch) + + let fetchProfile = fetchProfileUseCase.execute() + .map { Mutation.setLogin($0 != nil) } .catchAndReturn(.setLogin(false)) + + return Observable.merge( + firstRecommendation, + fetchProfile + ) case .searchButtonTapped: return .just(.navigateTo(.search)) case .notificationButtonTapped: @@ -68,13 +83,15 @@ public final class DictionaryMainReactor: Reactor { var newState = state switch mutation { - case .navigateTo(let route): + case let .navigateTo(route): newState.route = route case let .setLogin(isLogin): newState.isLogin = isLogin case let .setCurrentTab(oldIndex, newIndex): newState.oldPageIndex = oldIndex newState.currentPageIndex = newIndex + case let .setIsFirstRecommendationLaunch(isFirst): + newState.isFirstRecommendationLaunch = isFirst } return newState diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryMain/DictionaryMainView.swift similarity index 97% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryMain/DictionaryMainView.swift index f7feb203..edc88343 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryMain/DictionaryMainView.swift @@ -1,8 +1,7 @@ import UIKit -import DesignSystem -import DictionaryFeatureInterface -import DomainInterface +import MLSDesignSystem +import MLSDictionaryFeatureInterface import SnapKit diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryMain/DictionaryMainViewController.swift similarity index 94% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryMain/DictionaryMainViewController.swift index c7abe68b..663068cf 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryMain/DictionaryMainViewController.swift @@ -1,10 +1,10 @@ import UIKit -import AuthFeatureInterface -import BaseFeature -import DesignSystem -import DictionaryFeatureInterface -import DomainInterface +import MLSAppFeatureInterface +import MLSAuthFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface import ReactorKit import RxCocoa @@ -193,6 +193,16 @@ public extension DictionaryMainViewController { owner.moveToTab(oldIndex: oldIndex, newIndex: newIndex) }) .disposed(by: disposeBag) + + reactor.state + .observe(on: MainScheduler.instance) + .map { $0.isFirstRecommendationLaunch } + .withUnretained(self) + .subscribe { owner, isFirst in + (owner.tabBarController as? BottomTabBarController)? + .checkRecommendationFirstLaunch(isFirst: isFirst) + } + .disposed(by: disposeBag) } } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainViewFactoryImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryMain/DictionaryMainViewFactoryImpl.swift similarity index 68% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainViewFactoryImpl.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryMain/DictionaryMainViewFactoryImpl.swift index 798c5e6f..8cd5fa49 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainViewFactoryImpl.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryMain/DictionaryMainViewFactoryImpl.swift @@ -1,25 +1,27 @@ -import AuthFeatureInterface -import BaseFeature -import DictionaryFeatureInterface -import DomainInterface +import MLSAuthFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface +import MLSMyPageFeatureInterface public final class DictionaryMainViewFactoryImpl: DictionaryMainViewFactory { private let dictionaryMainListFactory: DictionaryMainListFactory private let searchFactory: DictionarySearchFactory private let notificationFactory: DictionaryNotificationFactory private let loginFactory: LoginFactory + private let userDefaultsRepository: DictionaryUserDefaultsRepository private let fetchProfileUseCase: FetchProfileUseCase - public init(dictionaryMainListFactory: DictionaryMainListFactory, searchFactory: DictionarySearchFactory, notificationFactory: DictionaryNotificationFactory, loginFactory: LoginFactory, fetchProfileUseCase: FetchProfileUseCase) { + public init(dictionaryMainListFactory: DictionaryMainListFactory, searchFactory: DictionarySearchFactory, notificationFactory: DictionaryNotificationFactory, loginFactory: LoginFactory, userDefaultsRepository: DictionaryUserDefaultsRepository, fetchProfileUseCase: FetchProfileUseCase) { self.dictionaryMainListFactory = dictionaryMainListFactory self.searchFactory = searchFactory self.notificationFactory = notificationFactory self.loginFactory = loginFactory + self.userDefaultsRepository = userDefaultsRepository self.fetchProfileUseCase = fetchProfileUseCase } public func make() -> BaseViewController { - let reactor = DictionaryMainReactor(fetchProfileUseCase: fetchProfileUseCase) + let reactor = DictionaryMainReactor(userDefaultsRepository: userDefaultsRepository, fetchProfileUseCase: fetchProfileUseCase) let viewController = DictionaryMainViewController(dictionaryMainListFactory: dictionaryMainListFactory, searchFactory: searchFactory, notificationFactory: notificationFactory, loginFactory: loginFactory, reactor: reactor, ) viewController.isBottomTabbarHidden = false viewController.reactor = reactor diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/DictionaryNotificationFactoryImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/DictionaryNotificationFactoryImpl.swift similarity index 60% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/DictionaryNotificationFactoryImpl.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/DictionaryNotificationFactoryImpl.swift index 2543eac8..e0943f2d 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/DictionaryNotificationFactoryImpl.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/DictionaryNotificationFactoryImpl.swift @@ -1,32 +1,28 @@ -import BaseFeature -import DictionaryFeatureInterface -import DomainInterface -import MyPageFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface +import MLSMyPageFeatureInterface public final class DictionaryNotificationFactoryImpl: DictionaryNotificationFactory { private let notificationSettingFactory: NotificationSettingFactory - private let fetchAllAlarmUseCase: FetchAllAlarmUseCase private let fetchProfileUseCase: FetchProfileUseCase private let checkNotificationPermissionUseCase: CheckNotificationPermissionUseCase - private let setReadUseCase: SetReadUseCase + private let alarmRepository: AlarmRepository public init( notificationSettingFactory: NotificationSettingFactory, - fetchAllAlarmUseCase: FetchAllAlarmUseCase, fetchProfileUseCase: FetchProfileUseCase, checkNotificationPermissionUseCase: CheckNotificationPermissionUseCase, - setReadUseCase: SetReadUseCase + alarmRepository: AlarmRepository ) { self.notificationSettingFactory = notificationSettingFactory - self.fetchAllAlarmUseCase = fetchAllAlarmUseCase self.fetchProfileUseCase = fetchProfileUseCase self.checkNotificationPermissionUseCase = checkNotificationPermissionUseCase - self.setReadUseCase = setReadUseCase + self.alarmRepository = alarmRepository } public func make() -> BaseViewController { - let reactor = DictionaryNotificationReactor(fetchAllAlarmUseCase: fetchAllAlarmUseCase, fetchProfileUseCase: fetchProfileUseCase, checkNotificationPermissionUseCase: checkNotificationPermissionUseCase, setReadUseCase: setReadUseCase) + let reactor = DictionaryNotificationReactor(fetchProfileUseCase: fetchProfileUseCase, checkNotificationPermissionUseCase: checkNotificationPermissionUseCase, alarmRepository: alarmRepository) let viewController = DictionaryNotificationViewController(notificationSettingFactory: notificationSettingFactory) viewController.reactor = reactor return viewController diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/DictionaryNotificationReactor.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/DictionaryNotificationReactor.swift similarity index 87% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/DictionaryNotificationReactor.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/DictionaryNotificationReactor.swift index 20e8e42c..16e7dbde 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/DictionaryNotificationReactor.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/DictionaryNotificationReactor.swift @@ -1,4 +1,6 @@ -import DomainInterface +import MLSDictionaryFeatureInterface +import MLSMyPageFeatureInterface + import ReactorKit import RxSwift @@ -40,18 +42,16 @@ public final class DictionaryNotificationReactor: Reactor { // MARK: - Properties public var initialState: State private let disposeBag = DisposeBag() - private let fetchAllAlarmUseCase: FetchAllAlarmUseCase private let fetchProfileUseCase: FetchProfileUseCase private let checkNotificationPermissionUseCase: CheckNotificationPermissionUseCase - private let setReadUseCase: SetReadUseCase + private let alarmRepository: AlarmRepository // MARK: - Init - public init(fetchAllAlarmUseCase: FetchAllAlarmUseCase, fetchProfileUseCase: FetchProfileUseCase, checkNotificationPermissionUseCase: CheckNotificationPermissionUseCase, setReadUseCase: SetReadUseCase) { + public init(fetchProfileUseCase: FetchProfileUseCase, checkNotificationPermissionUseCase: CheckNotificationPermissionUseCase, alarmRepository: AlarmRepository) { self.initialState = State() - self.fetchAllAlarmUseCase = fetchAllAlarmUseCase self.fetchProfileUseCase = fetchProfileUseCase self.checkNotificationPermissionUseCase = checkNotificationPermissionUseCase - self.setReadUseCase = setReadUseCase + self.alarmRepository = alarmRepository } // MARK: - Mutate @@ -63,7 +63,7 @@ public final class DictionaryNotificationReactor: Reactor { let notificationStream: Observable = Observable.concat([ Observable.just(.setLoading(true)), - fetchAllAlarmUseCase.execute(id: nil, pageSize: 20) + alarmRepository.fetchAll(cursor: nil, pageSize: 20) .map { paged in Mutation.setNotifications(paged.items, hasMore: paged.hasMore, reset: true) }, @@ -82,7 +82,7 @@ public final class DictionaryNotificationReactor: Reactor { return Observable.concat([ Observable.just(.setLoading(true)), - fetchAllAlarmUseCase.execute(id: cursor, pageSize: 20) + alarmRepository.fetchAll(cursor: cursor, pageSize: 20) .map { paged in Mutation.setNotifications(paged.items, hasMore: paged.hasMore, reset: false) }, @@ -98,7 +98,7 @@ public final class DictionaryNotificationReactor: Reactor { case let .notificationTapped(index): let notification = currentState.notifications[index] - return setReadUseCase.execute(alarmLink: notification.link) + return alarmRepository.setRead(alarmLink: notification.link) .andThen( Observable.concat([ .just(.checkAlarm(link: notification.link)), diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/DictionaryNotificationView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/DictionaryNotificationView.swift similarity index 99% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/DictionaryNotificationView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/DictionaryNotificationView.swift index 49602afe..fa0d0c8e 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/DictionaryNotificationView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/DictionaryNotificationView.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import SnapKit diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/DictionaryNotificationViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/DictionaryNotificationViewController.swift similarity index 97% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/DictionaryNotificationViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/DictionaryNotificationViewController.swift index 0e1538d7..5924139c 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/DictionaryNotificationViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/DictionaryNotificationViewController.swift @@ -1,8 +1,9 @@ -import MyPageFeatureInterface import UIKit -import BaseFeature -import DictionaryFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface +import MLSMyPageFeatureInterface import ReactorKit import RxCocoa @@ -174,6 +175,7 @@ extension DictionaryNotificationViewController: UICollectionViewDelegate, UIColl let frameHeight = scrollView.frame.size.height if offsetY > contentHeight - frameHeight - 100 { + lastPagingTime = now reactor.action.onNext(.loadMore) } } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/NotificationEmptyView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/NotificationEmptyView.swift similarity index 99% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/NotificationEmptyView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/NotificationEmptyView.swift index 660383eb..c7dd036d 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryNotification/NotificationEmptyView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotification/NotificationEmptyView.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import SnapKit diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/DictionaryNotificationCell.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotificationCell.swift similarity index 99% rename from MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/DictionaryNotificationCell.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotificationCell.swift index db03aa1d..2327dafa 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/DictionaryNotificationCell.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionaryNotificationCell.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import RxSwift import SnapKit diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/DictionarySearchFactoryImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/DictionarySearchFactoryImpl.swift new file mode 100644 index 00000000..5aaf9d8c --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/DictionarySearchFactoryImpl.swift @@ -0,0 +1,19 @@ +import MLSCore +import MLSDictionaryFeatureInterface + +public final class DictionarySearchFactoryImpl: DictionarySearchFactory { + private let recentSearchRepository: RecentSearchRepository + private let searchResultFactory: DictionarySearchResultFactory + + public init(recentSearchRepository: RecentSearchRepository, searchResultFactory: DictionarySearchResultFactory) { + self.recentSearchRepository = recentSearchRepository + self.searchResultFactory = searchResultFactory + } + + public func make() -> BaseViewController { + let reactor = DictionarySearchReactor(recentSearchRepository: recentSearchRepository) + let viewController = DictionarySearchViewController(searchResultFactory: searchResultFactory) + viewController.reactor = reactor + return viewController + } +} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchReactor.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/DictionarySearchReactor.swift similarity index 82% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchReactor.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/DictionarySearchReactor.swift index 98e3b950..ff9be5f9 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchReactor.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/DictionarySearchReactor.swift @@ -1,5 +1,7 @@ -import DomainInterface import Foundation + +import MLSDictionaryFeatureInterface + import ReactorKit struct PopularItem { @@ -38,16 +40,14 @@ public final class DictionarySearchReactor: Reactor { let popularResult: [PopularItem] } - public let recentSearchAddUseCase: RecentSearchAddUseCase - public let recentSearchRemoveUseCase: RecentSearchRemoveUseCase - public let recentSearchFetchUseCase: RecentSearchFetchUseCase + public let recentSearchRepository: RecentSearchRepository // MARK: - properties public var initialState: State var disposeBag = DisposeBag() // MARK: - init - public init(recentSearchAddUseCase: RecentSearchAddUseCase, recentSearchRemoveUseCase: RecentSearchRemoveUseCase, recentSearchFetchUseCase: RecentSearchFetchUseCase) { + public init(recentSearchRepository: RecentSearchRepository) { // TODO: 인기 검색어 추후 개발 let items = [ PopularItem(rank: 1, name: "주니어 예티"), @@ -73,9 +73,7 @@ public final class DictionarySearchReactor: Reactor { let newItems = grid.flatMap { $0.compactMap { $0 } } - self.recentSearchAddUseCase = recentSearchAddUseCase - self.recentSearchRemoveUseCase = recentSearchRemoveUseCase - self.recentSearchFetchUseCase = recentSearchFetchUseCase + self.recentSearchRepository = recentSearchRepository let savedRecentResult: [String] = [] @@ -90,11 +88,11 @@ public final class DictionarySearchReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .viewWillAppear: - return recentSearchFetchUseCase.fetch().map { Mutation.setRecentList($0) } + return recentSearchRepository.fetchRecentSearch().map { Mutation.setRecentList($0) } case .backButtonTapped: return Observable.just(.navigateTo(.dismiss)) case .searchButtonTapped(let keyword): - return recentSearchAddUseCase.add(keyword: keyword) + return recentSearchRepository.addRecentSearch(keyword: keyword) .andThen( currentState.recentResult.contains(keyword) ? .just(.navigateTo(.search(keyword))) @@ -104,12 +102,12 @@ public final class DictionarySearchReactor: Reactor { ]) ) case .cancelRecentButtonTapped(let keyword): - return recentSearchRemoveUseCase.remove(keyword: keyword) + return recentSearchRepository.removeRecentSearch(keyword: keyword) .andThen(.just(.deleteItem(keyword))) case .recentButtonTapped(let keyword): return Observable.just(.navigateTo(.search(keyword))) case .deleteAllButtonTapped: - return recentSearchRemoveUseCase.removeAll() + return recentSearchRepository.removeAllSearch() .andThen(.just(.deleteAllItems)) } } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/DictionarySearchView.swift similarity index 98% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/DictionarySearchView.swift index 2e2f3f29..86022b03 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/DictionarySearchView.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import RxSwift import SnapKit diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/DictionarySearchViewController.swift similarity index 96% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/DictionarySearchViewController.swift index 9d826ca9..9f515a9a 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/DictionarySearchViewController.swift @@ -1,9 +1,8 @@ -import DictionaryFeatureInterface import UIKit -import BaseFeature -import DesignSystem -import DomainInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface import ReactorKit import RxCocoa @@ -79,8 +78,6 @@ private extension DictionarySearchViewController { } func createLayout() -> UICollectionViewLayout { - let layoutFactory = LayoutFactory() - let layout = UICollectionViewCompositionalLayout { [weak self] sectionIndex, _ in guard self != nil else { return NSCollectionLayoutSection( @@ -93,11 +90,11 @@ private extension DictionarySearchViewController { switch sectionIndex { case 0: - return layoutFactory.getTagChipLayout().build() + return LayoutFactory.getTagChipLayout().build() case 1: - return layoutFactory.getDecorationSection().build() + return LayoutFactory.getDecorationSection().build() case 2: - return layoutFactory.getPopularResultLayout().build() + return LayoutFactory.getPopularResultLayout().build() default: return nil } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/EmptyRecentCell.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/EmptyRecentCell.swift similarity index 100% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/EmptyRecentCell.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/EmptyRecentCell.swift diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/PopularResultCell.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/PopularResultCell.swift similarity index 97% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/PopularResultCell.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/PopularResultCell.swift index af6a3155..d7fbf5d0 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/PopularResultCell.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/PopularResultCell.swift @@ -1,7 +1,6 @@ import UIKit -import DesignSystem -import DomainInterface +import MLSDesignSystem final class PopularResultCell: UICollectionViewCell { // MARK: - Type diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/TagChipCell.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/TagChipCell.swift similarity index 98% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/TagChipCell.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/TagChipCell.swift index 66129c71..b1e31a1a 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/TagChipCell.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearch/TagChipCell.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import RxSwift import SnapKit diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearchResult/DictionarySearchResultFactoryImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearchResult/DictionarySearchResultFactoryImpl.swift new file mode 100644 index 00000000..22b1aede --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearchResult/DictionarySearchResultFactoryImpl.swift @@ -0,0 +1,20 @@ +import MLSCore +import MLSDictionaryFeatureInterface + +public final class DictionarySearchResultFactoryImpl: DictionarySearchResultFactory { + private let dictionaryListAPIRepository: DictionaryListAPIRepository + private let recentSearchRepository: RecentSearchRepository + private let dictionaryMainListFactory: DictionaryMainListFactory + + public init(dictionaryListAPIRepository: DictionaryListAPIRepository, recentSearchRepository: RecentSearchRepository, dictionaryMainListFactory: DictionaryMainListFactory) { + self.dictionaryListAPIRepository = dictionaryListAPIRepository + self.recentSearchRepository = recentSearchRepository + self.dictionaryMainListFactory = dictionaryMainListFactory + } + + public func make(keyword: String?) -> BaseViewController { + let reactor = DictionarySearchResultReactor(keyword: keyword, dictionaryListAPIRepository: dictionaryListAPIRepository, recentSearchRepository: recentSearchRepository) + let viewController = DictionarySearchResultViewController(dictionaryListFactory: dictionaryMainListFactory, reactor: reactor) + return viewController + } +} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultReactor.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearchResult/DictionarySearchResultReactor.swift similarity index 79% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultReactor.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearchResult/DictionarySearchResultReactor.swift index 70019158..23451aa3 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultReactor.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearchResult/DictionarySearchResultReactor.swift @@ -1,6 +1,6 @@ import ReactorKit -import DomainInterface +import MLSDictionaryFeatureInterface public final class DictionarySearchResultReactor: Reactor { // MARK: - Reactor @@ -39,16 +39,14 @@ public final class DictionarySearchResultReactor: Reactor { var disposeBag = DisposeBag() // MARK: - UseCases - private let dictionarySearchUseCase: FetchDictionarySearchListUseCase - private let dictionarySearchCountUseCase: FetchDictionaryListCountUseCase - private let recentSearchAddUseCase: RecentSearchAddUseCase + private let dictionaryListAPIRepository: DictionaryListAPIRepository + private let recentSearchRepository: RecentSearchRepository // MARK: - init - public init(keyword: String?, dictionarySearchUseCase: FetchDictionarySearchListUseCase, dictionarySearchCountUseCase: FetchDictionaryListCountUseCase, recentSearchAddUseCase: RecentSearchAddUseCase) { + public init(keyword: String?, dictionaryListAPIRepository: DictionaryListAPIRepository, recentSearchRepository: RecentSearchRepository) { self.initialState = State(keyword: keyword) - self.dictionarySearchUseCase = dictionarySearchUseCase - self.dictionarySearchCountUseCase = dictionarySearchCountUseCase - self.recentSearchAddUseCase = recentSearchAddUseCase + self.dictionaryListAPIRepository = dictionaryListAPIRepository + self.recentSearchRepository = recentSearchRepository } // MARK: - Reactor Methods @@ -69,7 +67,7 @@ public final class DictionarySearchResultReactor: Reactor { // 검색 결과 화면에서 재검색 시 case .searchButtonTapped(let keyword): let keyword = keyword ?? "" - return recentSearchAddUseCase.add(keyword: keyword) + return recentSearchRepository.addRecentSearch(keyword: keyword) .andThen(.just(.setKeyword(keyword))) } } @@ -100,7 +98,7 @@ public final class DictionarySearchResultReactor: Reactor { guard let self = self else { return .empty() } let types = ["search", "monsters", "items", "maps", "npcs", "quests"] let countObservables = types.map { type in - self.dictionarySearchCountUseCase.execute(type: type, keyword: keyword) + self.dictionaryListAPIRepository.fetchSearchListCount(type: type, keyword: keyword) .map { $0.count ?? 0 } } return Observable.zip(countObservables) diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearchResult/DictionarySearchResultViewController.swift similarity index 99% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearchResult/DictionarySearchResultViewController.swift index b9bfe4ba..ee6471bf 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/DictionarySearchResult/DictionarySearchResultViewController.swift @@ -1,9 +1,8 @@ import UIKit -import BaseFeature -import DesignSystem -import DictionaryFeatureInterface -import DomainInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface import ReactorKit import RxCocoa diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift similarity index 91% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift index e4a85934..ae2c535a 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift @@ -1,5 +1,5 @@ -import BaseFeature -import DictionaryFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface public struct ItemFilterBottomSheetFactoryImpl: ItemFilterBottomSheetFactory { private let sharedReactor = ItemFilterBottomSheetReactor() diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift similarity index 98% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift index 7882922b..99b2c8ad 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift @@ -32,13 +32,13 @@ public final class ItemFilterBottomSheetReactor: Reactor { public struct State { var sections: [String] = ["직업/레벨", "무기", "발사체", "방어구", "장신구", "주문서", "기타"] - var jobs: [String] = ["없음", "공용", "마법사", "전사", "궁수", "도적"] - var weapons: [String] = ["한손검", "한손도끼", "한손둔기", "창", "단검", "두손검", "두손도끼", "두손둔기", "폴암", "활", "석궁", "완드", "스태프", "아대"] - var projectiles: [String] = ["화살", "표창"] // 불렛은 제거 + var jobs: [String] = ["없음", "공용", "마법사", "전사", "궁수", "도적", "해적"] + var weapons: [String] = ["한손검", "한손도끼", "한손둔기", "창", "단검", "두손검", "두손도끼", "두손둔기", "폴암", "활", "석궁", "완드", "스태프", "아대", "건", "너클"] + var projectiles: [String] = ["화살", "표창", "불릿"] var armors: [String] = ["모자", "상의", "하의", "장갑", "신발", "방패", "전신갑옷"] // 전신 = 전신갑옷? var accessories: [String] = ["귀고리", "망토", "훈장", "눈장식", "얼굴장식", "팬던트", "벨트", "반지", "어깨장식"] // 귀장식 데이터 안줌 -> 귀장식 = 귀고리, 훈장은 서버에서 데이터 안줌. @Pulse var scrollCategories: [String] = ["무기 주문서", "방어구 주문서", "기타 주문서"] - var originWeaponScrolls: [String] = ["한손검", "한손도끼", "한손둔기", "단검", "완드", "스태프", "두손검", "두손도끼", "두손둔기", "창", "폴암", "활", "석궁", "아대"] + var originWeaponScrolls: [String] = ["한손검", "한손도끼", "한손둔기", "단검", "완드", "스태프", "두손검", "두손도끼", "두손둔기", "창", "폴암", "활", "석궁", "아대", "건", "너클"] var originArmorScrolls: [String] = ["투구", "상의", "하의", "전신갑옷", "신발", "장갑", "망토", "방패", "귀장식"] var originEtcScrolls: [String] = ["펫장비", "연성서", "귀환주문서"] @Pulse var weaponScrolls: [String] = [] diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift similarity index 99% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift index 4adbcbe5..dd5d097d 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift @@ -2,7 +2,8 @@ import UIKit -import BaseFeature +import MLSCore +import MLSDesignSystem import ReactorKit import RxCocoa @@ -39,6 +40,7 @@ public final class ItemFilterBottomSheetViewController: BaseViewController, View } } + @MainActor var layout: CompositionalSectionBuilder { switch self { case .level: return LayoutFactory.getLevelRangeSection() @@ -443,7 +445,7 @@ extension ItemFilterBottomSheetViewController { switch section { case .level: - selectedItems.append(("레벨", "레벨 \(state.levelRange.low) ~ \(state.levelRange.high)")) + selectedItems.append(("레벨", "레벨 \(state.levelRange.low ?? 0) ~ \(state.levelRange.high ?? 200)")) case .job: selectedItems.append(("직업", state.jobs[indexPath.row])) case .weapons: diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/FilterLevelSectionCell.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/Views/FilterLevelSectionCell.swift similarity index 98% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/FilterLevelSectionCell.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/Views/FilterLevelSectionCell.swift index ae04c6ee..02f842b0 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/FilterLevelSectionCell.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/Views/FilterLevelSectionCell.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import RxCocoa import RxSwift diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/FilterLevelSectionView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/Views/FilterLevelSectionView.swift similarity index 99% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/FilterLevelSectionView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/Views/FilterLevelSectionView.swift index 800bfe57..0a343338 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/FilterLevelSectionView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/Views/FilterLevelSectionView.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import RxCocoa import RxSwift diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/FilterSlider.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/Views/FilterSlider.swift similarity index 100% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/FilterSlider.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/Views/FilterSlider.swift diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/ItemFilterBottomSheetView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/Views/ItemFilterBottomSheetView.swift similarity index 99% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/ItemFilterBottomSheetView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/Views/ItemFilterBottomSheetView.swift index 1e45758b..7bf2fa70 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/ItemFilterBottomSheetView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/ItemFilterBottomSheet/Views/ItemFilterBottomSheetView.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import SnapKit diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetFactoryImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/MonsterFilterBottomSheet/MonsterFilterBottomSheetFactoryImpl.swift similarity index 89% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetFactoryImpl.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/MonsterFilterBottomSheet/MonsterFilterBottomSheetFactoryImpl.swift index 40e900bd..e4ba006e 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetFactoryImpl.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/MonsterFilterBottomSheet/MonsterFilterBottomSheetFactoryImpl.swift @@ -1,7 +1,8 @@ import Foundation -import BaseFeature -import DictionaryFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface public struct MonsterFilterBottomSheetFactoryImpl: MonsterFilterBottomSheetFactory { public init() {} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetReactor.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/MonsterFilterBottomSheet/MonsterFilterBottomSheetReactor.swift similarity index 100% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetReactor.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/MonsterFilterBottomSheet/MonsterFilterBottomSheetReactor.swift diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/MonsterFilterBottomSheet/MonsterFilterBottomSheetView.swift similarity index 99% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/MonsterFilterBottomSheet/MonsterFilterBottomSheetView.swift index e8e5e901..e4fc57a2 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/MonsterFilterBottomSheet/MonsterFilterBottomSheetView.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import SnapKit diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/MonsterFilterBottomSheet/MonsterFilterBottomSheetViewController.swift similarity index 99% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/MonsterFilterBottomSheet/MonsterFilterBottomSheetViewController.swift index b94640af..7a664852 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/MonsterFilterBottomSheet/MonsterFilterBottomSheetViewController.swift @@ -1,7 +1,7 @@ import UIKit -import BaseFeature -import DesignSystem +import MLSCore +import MLSDesignSystem import ReactorKit import RxKeyboard diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/SortedBottomSheet/SortedBottomSheetFactoryImpl.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/SortedBottomSheet/SortedBottomSheetFactoryImpl.swift similarity index 87% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/SortedBottomSheet/SortedBottomSheetFactoryImpl.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/SortedBottomSheet/SortedBottomSheetFactoryImpl.swift index 54a686ce..e9b1fbae 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/SortedBottomSheet/SortedBottomSheetFactoryImpl.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/SortedBottomSheet/SortedBottomSheetFactoryImpl.swift @@ -1,6 +1,6 @@ -import BaseFeature -import DictionaryFeatureInterface -import DomainInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface public struct SortedBottomSheetFactoryImpl: SortedBottomSheetFactory { public init() {} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/SortedBottomSheet/SortedBottomSheetReactor.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/SortedBottomSheet/SortedBottomSheetReactor.swift similarity index 97% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/SortedBottomSheet/SortedBottomSheetReactor.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/SortedBottomSheet/SortedBottomSheetReactor.swift index 17b528d4..162e4110 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/SortedBottomSheet/SortedBottomSheetReactor.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/SortedBottomSheet/SortedBottomSheetReactor.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSDictionaryFeatureInterface import ReactorKit import RxCocoa diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/SortedBottomSheet/SortedBottomSheetView.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/SortedBottomSheet/SortedBottomSheetView.swift similarity index 98% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/SortedBottomSheet/SortedBottomSheetView.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/SortedBottomSheet/SortedBottomSheetView.swift index 07188061..d7a56627 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/SortedBottomSheet/SortedBottomSheetView.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/SortedBottomSheet/SortedBottomSheetView.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import SnapKit diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/SortedBottomSheet/SortedBottomSheetViewController.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/SortedBottomSheet/SortedBottomSheetViewController.swift similarity index 98% rename from MLS/Presentation/DictionaryFeature/DictionaryFeature/SortedBottomSheet/SortedBottomSheetViewController.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/SortedBottomSheet/SortedBottomSheetViewController.swift index a5e5ae06..c6a8a06d 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/SortedBottomSheet/SortedBottomSheetViewController.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeature/Presentation/SortedBottomSheet/SortedBottomSheetViewController.swift @@ -1,8 +1,8 @@ import UIKit -import BaseFeature -import DesignSystem -import DomainInterface +import MLSCore +import MLSDesignSystem +import MLSDictionaryFeatureInterface import ReactorKit import RxCocoa diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailItemDropMonsterResponse.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailItemDropMonsterResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailItemDropMonsterResponse.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailItemDropMonsterResponse.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailItemResponse.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailItemResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailItemResponse.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailItemResponse.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailMapNpcResponse.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailMapNpcResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailMapNpcResponse.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailMapNpcResponse.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailMapResponse.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailMapResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailMapResponse.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailMapResponse.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailMapSpawnMonsterResponse.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailMapSpawnMonsterResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailMapSpawnMonsterResponse.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailMapSpawnMonsterResponse.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailMonsterDropItemResponse.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailMonsterDropItemResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailMonsterDropItemResponse.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailMonsterDropItemResponse.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailMonsterMapResponse.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailMonsterMapResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailMonsterMapResponse.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailMonsterMapResponse.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailMonsterResponse.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailMonsterResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailMonsterResponse.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailMonsterResponse.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailNpcQuestResponse.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailNpcQuestResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailNpcQuestResponse.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailNpcQuestResponse.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailNpcResponse.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailNpcResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailNpcResponse.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailNpcResponse.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailQuestLinkedQuestsResponse.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailQuestLinkedQuestsResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailQuestLinkedQuestsResponse.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailQuestLinkedQuestsResponse.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailQuestResponse.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailQuestResponse.swift similarity index 94% rename from MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailQuestResponse.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailQuestResponse.swift index 6c065792..e1b1845f 100644 --- a/MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailQuestResponse.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailQuestResponse.swift @@ -63,6 +63,12 @@ public struct Reward: Decodable, Equatable { public let exp: Int? public let meso: Int? public let popularity: Int? + + public init(exp: Int?, meso: Int?, popularity: Int?) { + self.exp = exp + self.meso = meso + self.popularity = popularity + } } public struct RewardItem: Decodable, Equatable { diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailText.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailText.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryDetail/DictionaryDetailText.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryDetailText.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryListQuery.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryListQuery.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryListQuery.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryListQuery.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryMainResponse.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryMainResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryMainResponse.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryMainResponse.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryMainViewType.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryMainViewType.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryMainViewType.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryMainViewType.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryType.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryType.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryType.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionaryType.swift diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryList/DictionnaryItemType.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionnaryItemType.swift similarity index 88% rename from MLS/Domain/DomainInterface/Entity/DictionaryList/DictionnaryItemType.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionnaryItemType.swift index 3ea6cc2e..0833060f 100644 --- a/MLS/Domain/DomainInterface/Entity/DictionaryList/DictionnaryItemType.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/DictionnaryItemType.swift @@ -51,9 +51,24 @@ public enum DictionaryItemType: String { return .quest } } + + public var backgroundColor: UIColor { + switch self { + case .item: + .listItem + case .monster: + .listMonster + case .map: + .listMap + case .npc: + .listNPC + case .quest: + .listQuest + } + } } -public enum DetailType { +public enum DetailType: Equatable { case normal case mapInfo case appearMap diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryList/ItemFilterCriteria.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/ItemFilterCriteria.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/DictionaryList/ItemFilterCriteria.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/ItemFilterCriteria.swift diff --git a/MLS/Domain/DomainInterface/Entity/SearchCount/SearchCountResponse.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/SearchCountResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/SearchCount/SearchCountResponse.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/SearchCountResponse.swift diff --git a/MLS/Domain/DomainInterface/Entity/Filter/SortType.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/SortType.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/Filter/SortType.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Entities/SortType.swift diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DetailOnBoardingFactory.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DetailOnBoardingFactory.swift similarity index 81% rename from MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DetailOnBoardingFactory.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DetailOnBoardingFactory.swift index 4a3cb3e6..0983793b 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DetailOnBoardingFactory.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DetailOnBoardingFactory.swift @@ -1,4 +1,4 @@ -import BaseFeature +import MLSCore public protocol DetailOnBoardingFactory { func make() -> BaseViewController diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionaryDetailFactory.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionaryDetailFactory.swift similarity index 68% rename from MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionaryDetailFactory.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionaryDetailFactory.swift index 562bfe32..795a0144 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionaryDetailFactory.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionaryDetailFactory.swift @@ -1,8 +1,7 @@ -import BaseFeature -import DomainInterface +import MLSCore import RxCocoa -public protocol DictionaryDetailFactory { +public protocol DictionaryDetailFactory: AnyObject { func make(type: DictionaryType, id: Int, bookmarkRelay: PublishRelay<(id: Int, newBookmarkId: Int?)>?, loginRelay: PublishRelay?) -> BaseViewController } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionaryMainListFactory.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionaryMainListFactory.swift similarity index 78% rename from MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionaryMainListFactory.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionaryMainListFactory.swift index e3ccf330..720f0883 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionaryMainListFactory.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionaryMainListFactory.swift @@ -1,5 +1,4 @@ -import BaseFeature -import DomainInterface +import MLSCore public protocol DictionaryMainListFactory { func make(type: DictionaryType, listType: DictionaryMainViewType, keyword: String?) -> BaseViewController diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionaryMainViewFactory.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionaryMainViewFactory.swift similarity index 81% rename from MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionaryMainViewFactory.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionaryMainViewFactory.swift index 1bce179e..b7686839 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionaryMainViewFactory.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionaryMainViewFactory.swift @@ -1,4 +1,4 @@ -import BaseFeature +import MLSCore public protocol DictionaryMainViewFactory { func make() -> BaseViewController diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionaryNotificationFactory.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionaryNotificationFactory.swift similarity index 82% rename from MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionaryNotificationFactory.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionaryNotificationFactory.swift index f7eeefbd..9505ffcc 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionaryNotificationFactory.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionaryNotificationFactory.swift @@ -1,4 +1,4 @@ -import BaseFeature +import MLSCore public protocol DictionaryNotificationFactory { func make() -> BaseViewController diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionarySearchFactory.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionarySearchFactory.swift similarity index 81% rename from MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionarySearchFactory.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionarySearchFactory.swift index 5d5f2cb0..18e2beb2 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionarySearchFactory.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionarySearchFactory.swift @@ -1,4 +1,4 @@ -import BaseFeature +import MLSCore public protocol DictionarySearchFactory { func make() -> BaseViewController diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionarySearchResultFactory.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionarySearchResultFactory.swift similarity index 84% rename from MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionarySearchResultFactory.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionarySearchResultFactory.swift index 3eaf2a8e..ced97fca 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/DictionarySearchResultFactory.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/DictionarySearchResultFactory.swift @@ -1,4 +1,4 @@ -import BaseFeature +import MLSCore public protocol DictionarySearchResultFactory { func make(keyword: String?) -> BaseViewController diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/ItemFilterBottomSheetFactory.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/ItemFilterBottomSheetFactory.swift similarity index 88% rename from MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/ItemFilterBottomSheetFactory.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/ItemFilterBottomSheetFactory.swift index 37257544..91679894 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/ItemFilterBottomSheetFactory.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/ItemFilterBottomSheetFactory.swift @@ -1,4 +1,4 @@ -import BaseFeature +import MLSCore public protocol ItemFilterBottomSheetFactory { func make(onFilterSelected: @escaping ([(String, String)]) -> Void) -> BaseViewController diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/MonsterFilterBottomSheetFactory.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/MonsterFilterBottomSheetFactory.swift similarity index 83% rename from MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/MonsterFilterBottomSheetFactory.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/MonsterFilterBottomSheetFactory.swift index aff5d2db..ed69d9e0 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/MonsterFilterBottomSheetFactory.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/MonsterFilterBottomSheetFactory.swift @@ -1,4 +1,5 @@ -import BaseFeature +import MLSCore +import MLSDesignSystem public protocol MonsterFilterBottomSheetFactory { func make(startLevel: Int, endLevel: Int, onFilterSelected: @escaping ((Int, Int) -> Void)) -> BaseViewController & ModalPresentable diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/SortedBottomSheetFactory.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/SortedBottomSheetFactory.swift similarity index 81% rename from MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/SortedBottomSheetFactory.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/SortedBottomSheetFactory.swift index a4ea31bc..25cd59f1 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/SortedBottomSheetFactory.swift +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Factories/SortedBottomSheetFactory.swift @@ -1,5 +1,5 @@ -import BaseFeature -import DomainInterface +import MLSCore +import MLSDesignSystem public protocol SortedBottomSheetFactory { func make(sortedOptions: [SortType], selectedIndex: Int, onSelectedIndex: @escaping (Int) -> Void) -> BaseViewController & ModalPresentable diff --git a/MLS/Domain/DomainInterface/Repository/DictionaryDetailAPIRepository.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Repositories/DictionaryDetailAPIRepository.swift similarity index 100% rename from MLS/Domain/DomainInterface/Repository/DictionaryDetailAPIRepository.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Repositories/DictionaryDetailAPIRepository.swift diff --git a/MLS/Domain/DomainInterface/Repository/DictionaryListAPIRepository.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Repositories/DictionaryListAPIRepository.swift similarity index 100% rename from MLS/Domain/DomainInterface/Repository/DictionaryListAPIRepository.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Repositories/DictionaryListAPIRepository.swift diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Repositories/DictionaryUserDefaultsRepository.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Repositories/DictionaryUserDefaultsRepository.swift new file mode 100644 index 00000000..21e559dc --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Repositories/DictionaryUserDefaultsRepository.swift @@ -0,0 +1,7 @@ +import RxSwift + +public protocol DictionaryUserDefaultsRepository { + func fetchDictionaryDetail() -> Observable + func saveDictionaryDetail() -> Completable + func checkFirstLaunchRecommendation() -> Observable +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Repositories/RecentSearchRepository.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Repositories/RecentSearchRepository.swift new file mode 100644 index 00000000..4b2022f9 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/Repositories/RecentSearchRepository.swift @@ -0,0 +1,8 @@ +import RxSwift + +public protocol RecentSearchRepository { + func fetchRecentSearch() -> Observable<[String]> + func addRecentSearch(keyword: String) -> Completable + func removeRecentSearch(keyword: String) -> Completable + func removeAllSearch() -> Completable +} diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/CheckLoginUseCase.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/UseCases/CheckLoginUseCase.swift similarity index 100% rename from MLS/Domain/DomainInterface/UseCase/AuthAPI/CheckLoginUseCase.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/UseCases/CheckLoginUseCase.swift diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/CheckNotificationPermissionUseCase.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/UseCases/CheckNotificationPermissionUseCase.swift similarity index 100% rename from MLS/Domain/DomainInterface/UseCase/AuthAPI/CheckNotificationPermissionUseCase.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/UseCases/CheckNotificationPermissionUseCase.swift diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchVisitDictionaryDetailUseCase.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/UseCases/FetchVisitDictionaryDetailUseCase.swift similarity index 100% rename from MLS/Domain/DomainInterface/UseCase/DictionaryDetail/FetchVisitDictionaryDetailUseCase.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/UseCases/FetchVisitDictionaryDetailUseCase.swift diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryList/ParseItemFilterUseCase.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/UseCases/ParseItemFilterUseCase.swift similarity index 100% rename from MLS/Domain/DomainInterface/UseCase/DictionaryList/ParseItemFilterUseCase.swift rename to MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureInterface/UseCases/ParseItemFilterUseCase.swift diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockAppCoordinator.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockAppCoordinator.swift new file mode 100644 index 00000000..4e5605f4 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockAppCoordinator.swift @@ -0,0 +1,17 @@ +import UIKit + +import MLSAppFeatureInterface +import MLSAuthFeatureInterface +import MLSCore + +public final class MockAppCoordinator: AppCoordinatorProtocol { + public var window: UIWindow? + + public init() {} + + public func showMainTab(selectedIndex: Int) { + } + + public func showLogin(exitRoute: LoginExitRoute) { + } +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockBookmarkFailRepository.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockBookmarkFailRepository.swift new file mode 100644 index 00000000..fe3d4a33 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockBookmarkFailRepository.swift @@ -0,0 +1,54 @@ +import Foundation + +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface + +import RxSwift + +public final class MockBookmarkFailRepository: BookmarkRepository { + + public init() {} + + private func failError() -> Observable { + return Observable.error( + NSError( + domain: "MockBookmarkFailRepository", + code: -1, + userInfo: [NSLocalizedDescriptionKey: "Mock failure error"] + ) + ) + } + + public func setBookmark(resourceId: Int, type: DictionaryItemType) -> Observable { + return failError() + } + + public func deleteBookmark(bookmarkId: Int) -> Observable { + return failError() + } + + public func fetchBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + return failError() + } + + public func fetchMonsterBookmark(minLevel: Int?, maxLevel: Int?, sort: String?) -> Observable<[BookmarkResponse]> { + return failError() + } + + public func fetchNPCBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + return failError() + } + + public func fetchQuestBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + return failError() + } + + public func fetchItemBookmark(jobId: Int?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, sort: String?) -> Observable<[BookmarkResponse]> { + return failError() + } + + public func fetchMapBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + return failError() + } +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockBookmarkModalFactory.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockBookmarkModalFactory.swift new file mode 100644 index 00000000..c43b25c5 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockBookmarkModalFactory.swift @@ -0,0 +1,18 @@ +import MLSBookmarkFeatureInterface +import MLSCore + +public final class MockBookmarkModalFactory: BookmarkModalFactory { + public init() {} + + public func make(bookmarkIds: [Int]) -> BaseViewController { + let viewcontroller = BaseViewController() + viewcontroller.view.backgroundColor = .redMLS + return viewcontroller + } + + public func make(bookmarkIds: [Int], onComplete: ((Bool) -> Void)? = nil) -> BaseViewController { + let viewcontroller = BaseViewController() + viewcontroller.view.backgroundColor = .redMLS + return viewcontroller + } +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockBookmarkRepository.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockBookmarkRepository.swift new file mode 100644 index 00000000..d7a5e856 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockBookmarkRepository.swift @@ -0,0 +1,42 @@ +import MLSBookmarkFeatureInterface +import MLSCore +import MLSDictionaryFeatureInterface + +import RxSwift + +public final class MockBookmarkRepository: BookmarkRepository { + + public init() {} + + public func setBookmark(resourceId: Int, type: DictionaryItemType) -> Observable { + return .just(resourceId) + } + + public func deleteBookmark(bookmarkId: Int) -> Observable { + return .just(bookmarkId) + } + + public func fetchBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + return .just([]) + } + + public func fetchMonsterBookmark(minLevel: Int?, maxLevel: Int?, sort: String?) -> Observable<[BookmarkResponse]> { + return .just([]) + } + + public func fetchNPCBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + return .just([]) + } + + public func fetchQuestBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + return .just([]) + } + + public func fetchItemBookmark(jobId: Int?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, sort: String?) -> Observable<[BookmarkResponse]> { + return .just([]) + } + + public func fetchMapBookmark(sort: String?) -> Observable<[BookmarkResponse]> { + return .just([]) + } +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockDictionaryDetailAPIRepository.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockDictionaryDetailAPIRepository.swift new file mode 100644 index 00000000..5c45d81a --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockDictionaryDetailAPIRepository.swift @@ -0,0 +1,263 @@ +import MLSDictionaryFeatureInterface + +import RxSwift + +public final class MockDictionaryDetailAPIRepository: + DictionaryDetailAPIRepository { + + public init() {} + + // MARK: - Monster + + public func fetchMonsterDetail( + id: Int + ) -> Observable { + + .just( + DictionaryDetailMonsterResponse( + monsterId: id, + nameKr: "슬라임", + nameEn: "Slime", + imageUrl: "", + level: 1, + exp: 5, + hp: 50, + mp: 10, + physicalDefense: 3, + magicDefense: 2, + requiredAccuracy: 1, + bonusAccuracyPerLevelLower: 0, + evasionRate: 1, + mesoDropAmount: 12, + mesoDropRate: 100, + typeEffectiveness: Effectiveness( + fire: "WEAK", + lightning: nil, + poison: nil, + holy: nil, + ice: "RESIST", + physical: nil + ), + bookmarkId: nil + ) + ) + } + + public func fetchMonsterDetailDropItem( + id: Int, + sort: String? + ) -> Observable<[DictionaryDetailMonsterDropItemResponse]> { + + .just([ + DictionaryDetailMonsterDropItemResponse( + itemId: 1, + itemName: "빨간 포션", + dropRate: 0.5, + imageUrl: "", + itemLevel: 1 + ) + ]) + } + + public func fetchMonsterDetailMap( + id: Int + ) -> Observable<[DictionaryDetailMonsterMapResponse]> { + + .just([ + DictionaryDetailMonsterMapResponse( + mapId: 1, + mapName: "헤네시스 사냥터", + regionName: "빅토리아 아일랜드", + detailName: "동쪽 풀숲", + topRegionName: "헤네시스", + iconUrl: "", + maxSpawnCount: 10 + ) + ]) + } + + // MARK: - NPC + + public func fetchNpcDetail( + id: Int + ) -> Observable { + + .just( + DictionaryDetailNpcResponse( + npcId: id, + nameKr: "루크", + nameEn: "Luke", + iconUrlDetail: nil, + bookmarkId: nil + ) + ) + } + + public func fetchNpcDetailQuest( + id: Int, + sort: String? + ) -> Observable<[DictionaryDetailNpcQuestResponse]> { + + .just([ + DictionaryDetailNpcQuestResponse( + questId: 1, + questNameKr: "슬라임 퇴치", + questNameEn: "Slime Hunt", + questIconUrl: "", + minLevel: 1, + maxLevel: 10 + ) + ]) + } + + public func fetchNpcDetailMap( + id: Int + ) -> Observable<[DictionaryDetailMonsterMapResponse]> { + + .just([ + DictionaryDetailMonsterMapResponse( + mapId: 1, + mapName: "헤네시스", + regionName: "빅토리아 아일랜드", + detailName: "마을", + topRegionName: "헤네시스", + iconUrl: "", + maxSpawnCount: nil + ) + ]) + } + + // MARK: - Item + + public func fetchItemDetail( + id: Int + ) -> Observable { + + .just( + DictionaryDetailItemResponse( + itemId: id, + nameKr: "빨간 포션", + nameEn: "Red Potion", + descriptionText: "HP를 회복한다.", + imgUrl: "", + npcPrice: 50, + itemType: "consumable", + categoryHierachy: nil, + availableJobs: nil, + requiredStats: nil, + equipmentStats: nil, + scrollDetail: nil, + bookmarkId: nil + ) + ) + } + + public func fetchItemDetailDropMonster( + id: Int, + sort: String? + ) -> Observable<[DictionaryDetailItemDropMonsterResponse]> { + + .just([ + DictionaryDetailItemDropMonsterResponse( + monsterId: 1, + monsterName: "슬라임", + level: 1, + dropRate: 0.5, + imageUrl: "" + ) + ]) + } + + // MARK: - Quest + + public func fetchQuestDetail( + id: Int + ) -> Observable { + + .just( + DictionaryDetailQuestResponse( + questId: id, + titlePrefix: "[초보자]", + nameKr: "슬라임 퇴치", + nameEn: "Slime Hunt", + iconUrl: "", + questType: "NORMAL", + minLevel: 1, + maxLevel: 10, + requiredMesoStart: nil, + startNpcId: 1, + startNpcName: "루크", + endNpcId: 1, + endNpcName: "루크", + reward: MLSDictionaryFeatureInterface.Reward(exp: 100, meso: 50, popularity: nil), + rewardItems: [], + requirements: [], + allowedJobs: [], + bookmarkId: nil + ) + ) + } + + public func fetchQuestDetailLinkedQuestsDetail( + id: Int + ) -> Observable { + + .just( + DictionaryDetailQuestLinkedQuestsResponse( + previousQuests: [], + nextQuests: [] + ) + ) + } + + // MARK: - Map + + public func fetchMapDetail( + id: Int + ) -> Observable { + + .just( + DictionaryDetailMapResponse( + mapId: id, + nameKr: "헤네시스", + nameEn: "Henesys", + regionName: "빅토리아 아일랜드", + detailName: "마을", + topRegionName: "헤네시스", + mapUrl: "", + iconUrl: "", + bookmarkId: nil + ) + ) + } + + public func fetchMapDetailSpawnMonster( + id: Int, + sort: String? + ) -> Observable<[DictionaryDetailMapSpawnMonsterResponse]> { + + .just([ + DictionaryDetailMapSpawnMonsterResponse( + monsterId: 1, + monsterName: "슬라임", + level: 1, + maxSpawnCount: 10, + imageUrl: "" + ) + ]) + } + + public func fetchMapDetailNpc( + id: Int + ) -> Observable<[DictionaryDetailMapNpcResponse]> { + + .just([ + DictionaryDetailMapNpcResponse( + npcId: 1, + npcName: "루크", + npcNameEn: "Luke", + iconUrl: "" + ) + ]) + } +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockDictionaryListAPIRepository.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockDictionaryListAPIRepository.swift new file mode 100644 index 00000000..a5d8ed13 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockDictionaryListAPIRepository.swift @@ -0,0 +1,235 @@ +import MLSDictionaryFeatureInterface + +import RxSwift + +public final class MockDictionaryListAPIRepository: + DictionaryListAPIRepository { + + public init() {} + + // MARK: - Search Count + + public func fetchSearchListCount( + type: String, + keyword: String? + ) -> Observable { + + .just( + SearchCountResponse(count: 99) + ) + } + + // MARK: - Search List + + public func fetchSearchList( + keyword: String? + ) -> Observable { + + .just(makeAllResponse()) + } + + // MARK: - All List + + public func fetchAllList( + keyword: String?, + page: Int? + ) -> Observable { + + .just(makeAllResponse()) + } + + // MARK: - Monster List + + public func fetchMonsterList( + keyword: String?, + minLevel: Int?, + maxLevel: Int?, + page: Int, + size: Int, + sort: String? + ) -> Observable { + + .just( + DictionaryMainResponse( + totalPages: 1, + totalElements: 1, + contents: [ + DictionaryMainItemResponse( + id: 1, + name: "슬라임", + imageUrl: nil, + level: 1, + type: .monster, + bookmarkId: nil + ) + ] + ) + ) + } + + // MARK: - NPC List + + public func fetchNpcList( + keyword: String, + page: Int, + size: Int, + sort: String? + ) -> Observable { + + .just( + DictionaryMainResponse( + totalPages: 1, + totalElements: 1, + contents: [ + DictionaryMainItemResponse( + id: 2, + name: "루크", + imageUrl: nil, + level: nil, + type: .npc, + bookmarkId: nil + ) + ] + ) + ) + } + + // MARK: - Quest List + + public func fetchQuestList( + keyword: String, + page: Int, + size: Int, + sort: String? + ) -> Observable { + + .just( + DictionaryMainResponse( + totalPages: 1, + totalElements: 1, + contents: [ + DictionaryMainItemResponse( + id: 3, + name: "슬라임 퇴치", + imageUrl: nil, + level: 10, + type: .quest, + bookmarkId: nil + ) + ] + ) + ) + } + + // MARK: - Item List + + public func fetchItemList( + keyword: String?, + jobId: [Int]?, + minLevel: Int?, + maxLevel: Int?, + categoryIds: [Int]?, + page: Int?, + size: Int?, + sort: String? + ) -> Observable { + + .just( + DictionaryMainResponse( + totalPages: 1, + totalElements: 1, + contents: [ + DictionaryMainItemResponse( + id: 4, + name: "빨간 포션", + imageUrl: nil, + level: 1, + type: .item, + bookmarkId: nil + ) + ] + ) + ) + } + + // MARK: - Map List + + public func fetchMapList( + keyword: String, + page: Int, + size: Int, + sort: String? + ) -> Observable { + + .just( + DictionaryMainResponse( + totalPages: 1, + totalElements: 1, + contents: [ + DictionaryMainItemResponse( + id: 5, + name: "헤네시스", + imageUrl: nil, + level: nil, + type: .map, + bookmarkId: nil + ) + ] + ) + ) + } +} + +// MARK: - Private + +private extension MockDictionaryListAPIRepository { + + func makeAllResponse() -> DictionaryMainResponse { + DictionaryMainResponse( + totalPages: 1, + totalElements: 5, + contents: [ + DictionaryMainItemResponse( + id: 1, + name: "슬라임", + imageUrl: nil, + level: 1, + type: .monster, + bookmarkId: nil + ), + DictionaryMainItemResponse( + id: 2, + name: "빨간 포션", + imageUrl: nil, + level: 1, + type: .item, + bookmarkId: nil + ), + DictionaryMainItemResponse( + id: 3, + name: "헤네시스", + imageUrl: nil, + level: nil, + type: .map, + bookmarkId: nil + ), + DictionaryMainItemResponse( + id: 4, + name: "루크", + imageUrl: nil, + level: nil, + type: .npc, + bookmarkId: nil + ), + DictionaryMainItemResponse( + id: 5, + name: "슬라임 퇴치", + imageUrl: nil, + level: 10, + type: .quest, + bookmarkId: nil + ) + ] + ) + } +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockFetchProfileUseCase.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockFetchProfileUseCase.swift new file mode 100644 index 00000000..43bc3908 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockFetchProfileUseCase.swift @@ -0,0 +1,13 @@ +import MLSMyPageFeatureInterface + +import RxSwift + +public final class MockFetchProfileUseCase: FetchProfileUseCase { + public var result: Observable = .just(nil) + + public init() {} + + public func execute() -> Observable { + result + } +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockNotificationSettingFactory.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockNotificationSettingFactory.swift new file mode 100644 index 00000000..09564b94 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockNotificationSettingFactory.swift @@ -0,0 +1,14 @@ +import MLSCore +import MLSMyPageFeatureInterface + +public final class MockNotificationSettingFactory: NotificationSettingFactory { + public init() { + + } + + public func make(isAgreeEventNotification: Bool, isAgreeNoticeNotification: Bool, isAgreePatchNoteNotification: Bool) -> BaseViewController { + let viewcontroller = BaseViewController() + viewcontroller.view.backgroundColor = .redMLS + return viewcontroller + } +} diff --git a/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockRecentSearchRepository.swift b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockRecentSearchRepository.swift new file mode 100644 index 00000000..8ff3d2cb --- /dev/null +++ b/MLS/MLSDictionaryFeature/Sources/MLSDictionaryFeatureTesting/Mock/MockRecentSearchRepository.swift @@ -0,0 +1,30 @@ +import MLSDictionaryFeatureInterface + +import RxSwift + +final public class MockRecentSearchRepository: RecentSearchRepository { + private var searches: [String] + + public init(searches: [String] = ["사과", "바나나", "포션"]) { + self.searches = searches + } + + public func fetchRecentSearch() -> Observable<[String]> { + return .just(searches) + } + + public func addRecentSearch(keyword: String) -> Completable { + searches.insert(keyword, at: 0) + return .empty() + } + + public func removeRecentSearch(keyword: String) -> Completable { + searches.removeAll { $0 == keyword } + return .empty() + } + + public func removeAllSearch() -> Completable { + searches.removeAll() + return .empty() + } +} diff --git a/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/DictionaryListReactorTests.swift.swift b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/DictionaryListReactorTests.swift.swift new file mode 100644 index 00000000..2ac03e24 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/DictionaryListReactorTests.swift.swift @@ -0,0 +1,169 @@ +import RxBlocking +import Testing + +@testable import MLSAuthFeatureTesting +@testable import MLSDictionaryFeature +@testable import MLSDictionaryFeatureInterface +@testable import MLSDictionaryFeatureTesting + +@Suite("DictionaryListReactorTests") +struct DictionaryListReactorTests { + // MARK: - Mutate + + @Test("viewWillAppear 시 리스트 mutation 발생") + func viewWillAppear_emitListMutation() throws { + let reactor = makeSUT(type: .monster) + + let mutations = try reactor + .mutate(action: .viewWillAppear) + .toBlocking() + .toArray() + + let hasListMutation = mutations.contains { + if case .setListItem = $0 { + return true + } + return false + } + + #expect(hasListMutation) + } + + @Test("정렬 선택 시 sort mutation 발생") + func sortOptionSelected_emitSortMutation() throws { + let reactor = makeSUT(type: .monster) + + let mutations = try reactor + .mutate(action: .sortOptionSelected(.expASC)) + .toBlocking() + .toArray() + + let hasSortMutation = mutations.contains { + if case .setSort(let value) = $0 { + return value == "exp,asc" + } + return false + } + + #expect(hasSortMutation) + } + + @Test("필터 선택 시 filter mutation 발생") + func filterOptionSelected_emitFilterMutation() throws { + let reactor = makeSUT(type: .monster) + + let mutations = try reactor + .mutate( + action: .filterOptionSelected( + startLevel: 10, + endLevel: 30 + ) + ) + .toBlocking() + .toArray() + + let hasFilterMutation = mutations.contains { + if case .setFilter(let start, let end) = $0 { + return start == 10 && end == 30 + } + return false + } + + #expect(hasFilterMutation) + } + + @Test("로그인 액션 시 login 이벤트 발생") + func showLogin_emitLoginEvent() throws { + let reactor = makeSUT(type: .monster) + + let mutation = try reactor + .mutate(action: .showLogin) + .toBlocking() + .first() + + switch mutation { + case .setEvent(let event): + switch event { + case .login: + #expect(true) + default: + Issue.record("Expected login event") + } + + default: + Issue.record("Expected setEvent mutation") + } + } + + // MARK: - Reduce + + @Test("setCurrentPage mutation 시 페이지 증가") + func reduce_setCurrentPage() { + let reactor = makeSUT(type: .monster) + + let newState = reactor.reduce( + state: reactor.initialState, + mutation: .setCurrentPage + ) + + #expect(newState.currentPage == 1) + } + + @Test("updateBookmarkId mutation 시 bookmarkId 변경") + func reduce_updateBookmarkId() { + let reactor = makeSUT(type: .monster) + + var state = reactor.initialState + + state.listItems = [ + DictionaryMainItemResponse( + id: 1, + name: "슬라임", + imageUrl: "", + level: 1, + type: .monster, + bookmarkId: nil + ) + ] + + let newState = reactor.reduce( + state: state, + mutation: .updateBookmarkId( + id: 1, + newBookmarkId: 999 + ) + ) + + #expect(newState.listItems.first?.bookmarkId == 999) + } +} + +// MARK: - Helpers + +private extension DictionaryListReactorTests { + func makeSUT( + type: DictionaryType, + keyword: String? = nil + ) -> DictionaryListReactor { + DictionaryListReactor( + type: type, + keyword: keyword, + dictionaryListAPIRepository: + MockDictionaryListAPIRepository(), + + checkLoginUseCase: + CheckLoginUseCaseImpl( + authRepository: MockAuthAPIRepository(), + tokenRepository: MockTokenRepository() + ), + + setBookmarkUseCase: + SetBookmarkUseCaseImpl( + repository: MockBookmarkRepository() + ), + + parseItemFilterResultUseCase: + ParseItemFilterResultUseCaseImpl() + ) + } +} diff --git a/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/DictionarySearchReactorTests.swift b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/DictionarySearchReactorTests.swift new file mode 100644 index 00000000..765f3101 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/DictionarySearchReactorTests.swift @@ -0,0 +1,222 @@ +import RxBlocking +import Testing + +@testable import MLSDictionaryFeature +@testable import MLSDictionaryFeatureInterface +@testable import MLSDictionaryFeatureTesting + +@Suite("DictionarySearchReactorTests") +struct DictionarySearchReactorTests { + // MARK: - ViewWillAppear + + @Test("최근 검색어 조회") + func test_viewWillAppear_fetchRecentSearches() throws { + let reactor = makeSUT() + + let mutations = try reactor + .mutate(action: .viewWillAppear) + .toBlocking() + .toArray() + + let hasRecentListMutation = mutations.contains { + if case .setRecentList(let list) = $0 { + return list.isEmpty == false + } + return false + } + + #expect(hasRecentListMutation) + } + + // MARK: - Search + + @Test("검색 실행 시 최근 검색어 추가 및 화면 이동") + func test_searchButtonTapped_addRecentAndNavigate() throws { + let reactor = makeSUT() + + let mutations = try reactor + .mutate(action: .searchButtonTapped("슬라임")) + .toBlocking() + .toArray() + + let hasAddRecentMutation = mutations.contains { + if case .addRecentItem(let keyword) = $0 { + return keyword == "슬라임" + } + return false + } + + let hasNavigateMutation = mutations.contains { + if case .navigateTo(let route) = $0 { + switch route { + case .search(let keyword): + return keyword == "슬라임" + default: + return false + } + } + return false + } + + #expect(hasAddRecentMutation) + #expect(hasNavigateMutation) + } + + @Test("최근 검색어 클릭 시 검색 화면 이동") + func test_recentButtonTapped_navigateSearch() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .recentButtonTapped("주황버섯")) + .toBlocking() + .first() + + guard case .navigateTo(let route)? = mutation else { + Issue.record("Expected navigateTo mutation") + return + } + + switch route { + case .search(let keyword): + #expect(keyword == "주황버섯") + + default: + Issue.record("Expected search route") + } + } + + // MARK: - Delete Recent + + @Test("최근 검색어 삭제") + func test_cancelRecentButtonTapped_deleteRecent() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .cancelRecentButtonTapped("슬라임")) + .toBlocking() + .first() + + guard case .deleteItem(let keyword)? = mutation else { + Issue.record("Expected deleteItem mutation") + return + } + + #expect(keyword == "슬라임") + } + + @Test("전체 최근 검색어 삭제") + func test_deleteAllButtonTapped_deleteAll() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .deleteAllButtonTapped) + .toBlocking() + .first() + + guard case .deleteAllItems? = mutation else { + Issue.record("Expected deleteAllItems mutation") + return + } + + #expect(true) + } + + // MARK: - Navigation + + @Test("뒤로가기 클릭 시 dismiss 이동") + func test_backButtonTapped_dismiss() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .backButtonTapped) + .toBlocking() + .first() + + guard case .navigateTo(let route)? = mutation else { + Issue.record("Expected navigateTo mutation") + return + } + + switch route { + case .dismiss: + #expect(true) + + default: + Issue.record("Expected dismiss route") + } + } + + // MARK: - Reduce + + @Test("reduce - 최근 검색어 추가") + func test_reduce_addRecentItem() { + let reactor = makeSUT() + + let newState = reactor.reduce( + state: reactor.initialState, + mutation: .addRecentItem("슬라임") + ) + + #expect(newState.recentResult.first == "슬라임") + } + + @Test("reduce - 최근 검색어 삭제") + func test_reduce_deleteItem() { + let reactor = makeSUT() + + var state = reactor.initialState + state.recentResult = ["슬라임", "주황버섯"] + + let newState = reactor.reduce( + state: state, + mutation: .deleteItem("슬라임") + ) + + #expect(newState.recentResult.contains("슬라임") == false) + #expect(newState.recentResult.count == 1) + } + + @Test("reduce - 최근 검색어 전체 삭제") + func test_reduce_deleteAllItems() { + let reactor = makeSUT() + + var state = reactor.initialState + state.recentResult = ["슬라임", "주황버섯"] + + let newState = reactor.reduce( + state: state, + mutation: .deleteAllItems + ) + + #expect(newState.recentResult.isEmpty) + } + + @Test("reduce - route 상태 변경") + func test_reduce_navigateTo() { + let reactor = makeSUT() + + let newState = reactor.reduce( + state: reactor.initialState, + mutation: .navigateTo(.search("슬라임")) + ) + + switch newState.route { + case .search(let keyword): + #expect(keyword == "슬라임") + + default: + Issue.record("Expected search route") + } + } +} + +// MARK: - Helpers + +private extension DictionarySearchReactorTests { + func makeSUT() -> DictionarySearchReactor { + DictionarySearchReactor( + recentSearchRepository: + MockRecentSearchRepository() + ) + } +} diff --git a/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/ItemDictionaryDetailReactorTests.swift b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/ItemDictionaryDetailReactorTests.swift new file mode 100644 index 00000000..37d6072e --- /dev/null +++ b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/ItemDictionaryDetailReactorTests.swift @@ -0,0 +1,77 @@ +import RxBlocking +import Testing + +@testable import MLSAuthFeatureTesting +@testable import MLSDictionaryFeature +@testable import MLSDictionaryFeatureInterface +@testable import MLSDictionaryFeatureTesting + +// MARK: - ItemDetail + +@Suite("ItemDictionaryDetailReactorTests") +struct ItemDictionaryDetailReactorTests { + @Test("상세 진입 시 상세 데이터 조회") + func test_fetchItemDetail() throws { + let reactor = makeSUT() + + let state = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + #expect(state.itemDetailInfo.itemId == 1) + } + + @Test("드롭 몬스터 조회 성공") + func test_fetchDropMonsters() throws { + let reactor = makeSUT() + + let state = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + #expect(state.monsters.isEmpty == false) + } +} + +// MARK: - Reduce Helpers + +private func reduce( + _ reactor: ItemDictionaryDetailReactor, + from state: ItemDictionaryDetailReactor.State, + action: ItemDictionaryDetailReactor.Action +) throws -> ItemDictionaryDetailReactor.State { + let mutations = try reactor + .mutate(action: action) + .toBlocking() + .toArray() + + return mutations.reduce(state) { + reactor.reduce(state: $0, mutation: $1) + } +} + +// MARK: - SUT + +private func makeSUT() -> ItemDictionaryDetailReactor { + ItemDictionaryDetailReactor( + dictionaryDetailAPIRepository: + MockDictionaryDetailAPIRepository(), + + checkLoginUseCase: + CheckLoginUseCaseImpl( + authRepository: MockAuthAPIRepository(), + tokenRepository: MockTokenRepository() + ), + + setBookmarkUseCase: + SetBookmarkUseCaseImpl( + repository: MockBookmarkRepository() + ), + + id: 1 + ) +} diff --git a/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/MapDictionaryDetailReactorTests.swift b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/MapDictionaryDetailReactorTests.swift new file mode 100644 index 00000000..8f47c243 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/MapDictionaryDetailReactorTests.swift @@ -0,0 +1,87 @@ +import RxBlocking +import Testing + +@testable import MLSAuthFeatureTesting +@testable import MLSDictionaryFeature +@testable import MLSDictionaryFeatureInterface +@testable import MLSDictionaryFeatureTesting + +@Suite("MapDictionaryDetailReactorTests") +struct MapDictionaryDetailReactorTests { + @Test("상세 진입 시 상세 데이터 조회") + func test_fetchMapDetail() throws { + let reactor = makeSUT() + + let state = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + #expect(state.mapDetailInfo.mapId == 1) + } + + @Test("스폰 몬스터 조회 성공") + func test_fetchSpawnMonsters() throws { + let reactor = makeSUT() + + let state = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + #expect(state.spawnMonsters.isEmpty == false) + } + + @Test("NPC 조회 성공") + func test_fetchNpc() throws { + let reactor = makeSUT() + + let state = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + #expect(state.npcs.isEmpty == false) + } +} + +// MARK: - Reduce Helpers +private func reduce( + _ reactor: MapDictionaryDetailReactor, + from state: MapDictionaryDetailReactor.State, + action: MapDictionaryDetailReactor.Action +) throws -> MapDictionaryDetailReactor.State { + let mutations = try reactor + .mutate(action: action) + .toBlocking() + .toArray() + + return mutations.reduce(state) { + reactor.reduce(state: $0, mutation: $1) + } +} + +// MARK: - SUT + +private func makeSUT() -> MapDictionaryDetailReactor { + MapDictionaryDetailReactor( + dictionaryDetailAPIRepository: + MockDictionaryDetailAPIRepository(), + + checkLoginUseCase: + CheckLoginUseCaseImpl( + authRepository: MockAuthAPIRepository(), + tokenRepository: MockTokenRepository() + ), + + setBookmarkUseCase: + SetBookmarkUseCaseImpl( + repository: MockBookmarkRepository() + ), + + id: 1 + ) +} diff --git a/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/MonsterDictionaryDetailReactorTests.swift b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/MonsterDictionaryDetailReactorTests.swift new file mode 100644 index 00000000..e9d8f4e7 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/MonsterDictionaryDetailReactorTests.swift @@ -0,0 +1,269 @@ +import RxBlocking +import Testing + +@testable import MLSAuthFeatureTesting +@testable import MLSDictionaryFeature +@testable import MLSDictionaryFeatureInterface +@testable import MLSDictionaryFeatureTesting + +// MARK: - MonsterDetail + +@Suite("MonsterDictionaryDetailReactorTests") +struct MonsterDictionaryDetailReactorTests { + // MARK: - Fetch + + @Test("상세 진입 시 상세 데이터 조회") + func test_fetchMonsterDetail() throws { + let reactor = makeSUT() + + let state = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + #expect(state.monsterDetailInfo.monsterId == 1) + } + + @Test("드롭 아이템 조회 성공") + func test_fetchDropItems() throws { + let reactor = makeSUT() + + let state = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + #expect(state.dropItems.isEmpty == false) + } + + @Test("맵 정보 조회 성공") + func test_fetchMaps() throws { + let reactor = makeSUT() + + let state = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + #expect(state.spawnMaps.isEmpty == false) + } + + @Test("로그인 상태 조회") + func test_loginState() throws { + let reactor = makeSUT() + + let state = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + #expect(state.isLogin == false) + } + + // MARK: - Bookmark + + @Test("북마크 추가 성공") + func test_toggleBookmark_addBookmark() throws { + let reactor = makeSUT() + + let loadedState = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + let bookmarkState = try reduce( + reactor, + from: loadedState, + action: .toggleBookmark + ) + + switch bookmarkState.event { + case .add: + #expect(true) + + default: + Issue.record("Expected add event") + } + } + + @Test("북마크 undo 성공") + func test_undoBookmark() throws { + let reactor = makeSUT() + + let loadedState = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + _ = try reduce( + reactor, + from: loadedState, + action: .toggleBookmark + ) + + let undoState = try reduce( + reactor, + from: loadedState, + action: .undoLastDeletedBookmark + ) + + switch undoState.event { + case .add: + #expect(true) + + default: + Issue.record("Expected add event") + } + } + + @Test("북마크 실패 시 에러 route 이동") + func test_toggleBookmark_failure() throws { + let reactor = makeFailureSUT() + + let mutations = try reactor + .mutate(action: .toggleBookmark) + .toBlocking() + .toArray() + + let hasErrorRoute = mutations.contains { + if case .navigateTo(.bookmarkError) = $0 { + return true + } + return false + } + + #expect(hasErrorRoute) + } + + // MARK: - Route + + @Test("드롭 아이템 클릭 시 상세 이동") + func test_reduce_itemDetailRoute() { + let reactor = makeSUT() + + let newState = reactor.reduce( + state: reactor.initialState, + mutation: .navigateTo( + .detail(type: .item, id: 1) + ) + ) + + switch newState.route { + case let .detail(type, id): + #expect(type == .item) + #expect(id == 1) + + default: + Issue.record("Expected item detail route") + } + } + + @Test("맵 클릭 시 상세 이동") + func test_reduce_mapDetailRoute() { + let reactor = makeSUT() + + let newState = reactor.reduce( + state: reactor.initialState, + mutation: .navigateTo( + .detail(type: .map, id: 1) + ) + ) + + switch newState.route { + case let .detail(type, id): + #expect(type == .map) + #expect(id == 1) + + default: + Issue.record("Expected map detail route") + } + } + + @Test("필터 버튼 클릭 시 필터 화면 이동") + func test_filterButtonTapped_navigateFilter() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .filterButtonTapped(.item)) + .toBlocking() + .first() + + guard case let .navigateTo(route)? = mutation else { + Issue.record("Expected navigate route") + return + } + + switch route { + case let .filter(type, _): + #expect(type == .item) + + default: + Issue.record("Expected filter route") + } + } +} + +// MARK: - Reduce Helpers + +private func reduce( + _ reactor: MonsterDictionaryDetailReactor, + from state: MonsterDictionaryDetailReactor.State, + action: MonsterDictionaryDetailReactor.Action +) throws -> MonsterDictionaryDetailReactor.State { + let mutations = try reactor + .mutate(action: action) + .toBlocking() + .toArray() + + return mutations.reduce(state) { + reactor.reduce(state: $0, mutation: $1) + } +} + +// MARK: - SUT + +private func makeSUT() -> MonsterDictionaryDetailReactor { + MonsterDictionaryDetailReactor( + dictionaryDetailAPIRepository: + MockDictionaryDetailAPIRepository(), + + checkLoginUseCase: + CheckLoginUseCaseImpl( + authRepository: MockAuthAPIRepository(), + tokenRepository: MockTokenRepository() + ), + + setBookmarkUseCase: + SetBookmarkUseCaseImpl( + repository: MockBookmarkRepository() + ), + + id: 1 + ) +} + +private func makeFailureSUT() -> MonsterDictionaryDetailReactor { + MonsterDictionaryDetailReactor( + dictionaryDetailAPIRepository: + MockDictionaryDetailAPIRepository(), + + checkLoginUseCase: + CheckLoginUseCaseImpl( + authRepository: MockAuthAPIRepository(), + tokenRepository: MockTokenRepository() + ), + + setBookmarkUseCase: + SetBookmarkUseCaseImpl( + repository: MockBookmarkFailRepository() + ), + + id: 1 + ) +} diff --git a/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/NpcDictionaryDetailReactorTests.swift b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/NpcDictionaryDetailReactorTests.swift new file mode 100644 index 00000000..fdb3fe6b --- /dev/null +++ b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/NpcDictionaryDetailReactorTests.swift @@ -0,0 +1,88 @@ +import RxBlocking +import Testing + +@testable import MLSAuthFeatureTesting +@testable import MLSDictionaryFeature +@testable import MLSDictionaryFeatureInterface +@testable import MLSDictionaryFeatureTesting + +// MARK: - NpcDetail + +@Suite("NpcDictionaryDetailReactorTests") +struct NpcDictionaryDetailReactorTests { + @Test("상세 진입 시 상세 데이터 조회") + func test_fetchNpcDetail() throws { + let reactor = makeSUT() + + let state = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + #expect(state.npcDetailInfo.npcId == 1) + } + + @Test("상세 맵 조회 성공") + func test_fetchMaps() throws { + let reactor = makeSUT() + + let state = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + #expect(state.maps.isEmpty == false) + } + + @Test("퀘스트 조회 성공") + func test_fetchQuests() throws { + let reactor = makeSUT() + + let state = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + #expect(state.quests.isEmpty == false) + } +} + +// MARK: - Reduce Helpers +private func reduce( + _ reactor: NpcDictionaryDetailReactor, + from state: NpcDictionaryDetailReactor.State, + action: NpcDictionaryDetailReactor.Action +) throws -> NpcDictionaryDetailReactor.State { + let mutations = try reactor + .mutate(action: action) + .toBlocking() + .toArray() + + return mutations.reduce(state) { + reactor.reduce(state: $0, mutation: $1) + } +} + +// MARK: - SUT +private func makeSUT() -> NpcDictionaryDetailReactor { + NpcDictionaryDetailReactor( + dictionaryDetailAPIRepository: + MockDictionaryDetailAPIRepository(), + + checkLoginUseCase: + CheckLoginUseCaseImpl( + authRepository: MockAuthAPIRepository(), + tokenRepository: MockTokenRepository() + ), + + setBookmarkUseCase: + SetBookmarkUseCaseImpl( + repository: MockBookmarkRepository() + ), + + id: 1 + ) +} diff --git a/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/ParseItemFilterResultUseCaseTests.swift b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/ParseItemFilterResultUseCaseTests.swift new file mode 100644 index 00000000..8b86c845 --- /dev/null +++ b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/ParseItemFilterResultUseCaseTests.swift @@ -0,0 +1,119 @@ +import Testing + +@testable import MLSDictionaryFeature + +@Suite("ParseItemFilterResultUseCaseTests") +struct ParseItemFilterResultUseCaseTests { + let sut = ParseItemFilterResultUseCaseImpl() + + // MARK: - Job + + @Test("직업 필터 파싱") + func test_parseJobFilter() { + let result = sut.execute( + results: [ + ("직업", "전사"), + ("직업", "궁수") + ] + ) + + #expect(result.jobIds == [100, 300]) + } + + // MARK: - Level + + @Test("레벨 범위 파싱") + func test_parseLevelRange() { + let result = sut.execute( + results: [ + ("레벨", "10레벨 ~ 30레벨") + ] + ) + + #expect(result.startLevel == 10) + #expect(result.endLevel == 30) + } + + // MARK: - Category + + @Test("카테고리 파싱") + func test_parseCategoryFilter() { + let result = sut.execute( + results: [ + ("무기", "한손검"), + ("방어구", "모자") + ] + ) + + #expect(result.categoryIds == [7, 24]) + } + + @Test("주문서 카테고리 파싱") + func test_parseScrollCategoryFilter() { + let result = sut.execute( + results: [ + ("무기주문서", "한손검") + ] + ) + + #expect(result.categoryIds == [50]) + } + + // MARK: - Empty + + @Test("빈 값 처리") + func test_parseEmptyValues() { + let result = sut.execute(results: []) + + #expect(result.jobIds.isEmpty) + #expect(result.categoryIds.isEmpty) + #expect(result.startLevel == nil) + #expect(result.endLevel == nil) + } + + // MARK: - Duplicate + + @Test("중복 값 허용") + func test_duplicateValuesAllowed() { + let result = sut.execute( + results: [ + ("직업", "전사"), + ("직업", "전사") + ] + ) + + #expect(result.jobIds == [100, 100]) + } + + // MARK: - Invalid + + @Test("잘못된 값은 무시") + func test_ignoreInvalidValues() { + let result = sut.execute( + results: [ + ("직업", "해직"), + ("무기", "레이저건") + ] + ) + + #expect(result.jobIds.isEmpty) + #expect(result.categoryIds.isEmpty) + } + + @Test("복합 필터 파싱") + + func test_parseComplexFilters() { + let result = sut.execute( + results: [ + ("직업", "전사"), + ("레벨", "10레벨 ~ 30레벨"), + ("무기", "한손검") + ] + ) + + #expect(result.jobIds == [100]) + #expect(result.startLevel == 10) + #expect(result.endLevel == 30) + #expect(result.categoryIds == [7]) + } +} diff --git a/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/QuestDictionaryDetailReactorTests.swift b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/QuestDictionaryDetailReactorTests.swift new file mode 100644 index 00000000..c386e52c --- /dev/null +++ b/MLS/MLSDictionaryFeature/Tests/MLSDictionaryFeatureTests/QuestDictionaryDetailReactorTests.swift @@ -0,0 +1,80 @@ +import RxBlocking +import Testing + +@testable import MLSAuthFeatureTesting +@testable import MLSDictionaryFeature +@testable import MLSDictionaryFeatureInterface +@testable import MLSDictionaryFeatureTesting + +// MARK: - QuestDetail + +@Suite("QuestDictionaryDetailReactorTests") +struct QuestDictionaryDetailReactorTests { + @Test("상세 진입 시 상세 데이터 조회") + func test_fetchQuestDetail() throws { + let reactor = makeSUT() + + let state = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + #expect(state.detailInfo.questId == 1) + } + + @Test("연계 퀘스트 조회 성공") + func test_fetchLinkedQuests() throws { + let reactor = makeSUT() + + let state = try reduce( + reactor, + from: reactor.initialState, + action: .viewWillAppear + ) + + #expect( + state.linkedQuestInfo.nextQuests != nil || + state.linkedQuestInfo.previousQuests != nil + ) + } +} + +// MARK: - Reduce Helpers + +private func reduce( + _ reactor: QuestDictionaryDetailReactor, + from state: QuestDictionaryDetailReactor.State, + action: QuestDictionaryDetailReactor.Action +) throws -> QuestDictionaryDetailReactor.State { + let mutations = try reactor + .mutate(action: action) + .toBlocking() + .toArray() + + return mutations.reduce(state) { + reactor.reduce(state: $0, mutation: $1) + } +} + +// MARK: - SUT + +private func makeSUT() -> QuestDictionaryDetailReactor { + QuestDictionaryDetailReactor( + dictionaryDetailAPIRepository: + MockDictionaryDetailAPIRepository(), + + checkLoginUseCase: + CheckLoginUseCaseImpl( + authRepository: MockAuthAPIRepository(), + tokenRepository: MockTokenRepository() + ), + + setBookmarkUseCase: + SetBookmarkUseCaseImpl( + repository: MockBookmarkRepository() + ), + + id: 1 + ) +} diff --git a/MLS/MLSDictionaryFeatureExample/AppDelegate.swift b/MLS/MLSDictionaryFeatureExample/AppDelegate.swift new file mode 100644 index 00000000..55e88a8e --- /dev/null +++ b/MLS/MLSDictionaryFeatureExample/AppDelegate.swift @@ -0,0 +1,18 @@ +import UIKit + +import MLSDesignSystem + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + FontManager.registerFonts() + return true + } + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + } +} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Assets.xcassets/AccentColor.colorset/Contents.json b/MLS/MLSDictionaryFeatureExample/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Assets.xcassets/AccentColor.colorset/Contents.json rename to MLS/MLSDictionaryFeatureExample/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/MLS/MLSDictionaryFeatureExample/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 95% rename from MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Assets.xcassets/AppIcon.appiconset/Contents.json rename to MLS/MLSDictionaryFeatureExample/Assets.xcassets/AppIcon.appiconset/Contents.json index c68da6c9..23058801 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/MLS/MLSDictionaryFeatureExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,7 +1,6 @@ { "images" : [ { - "filename" : "icon.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Assets.xcassets/Contents.json b/MLS/MLSDictionaryFeatureExample/Assets.xcassets/Contents.json similarity index 100% rename from MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Assets.xcassets/Contents.json rename to MLS/MLSDictionaryFeatureExample/Assets.xcassets/Contents.json diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/Info.plist b/MLS/MLSDictionaryFeatureExample/Info.plist similarity index 100% rename from MLS/Presentation/DesignSystem/DesignSystemDemo/Info.plist rename to MLS/MLSDictionaryFeatureExample/Info.plist diff --git a/MLS/MLSDictionaryFeatureExample/SceneDelegate.swift b/MLS/MLSDictionaryFeatureExample/SceneDelegate.swift new file mode 100644 index 00000000..67d327e5 --- /dev/null +++ b/MLS/MLSDictionaryFeatureExample/SceneDelegate.swift @@ -0,0 +1,140 @@ +import UIKit + +import MLSAuthFeatureTesting +import MLSDictionaryFeature +import MLSDictionaryFeatureInterface +import MLSDictionaryFeatureTesting +import MLSMyPageFeatureTesting + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + guard let windowScene = (scene as? UIWindowScene) else { return } + + let window = UIWindow(windowScene: windowScene) + self.window = window + + let rootVC = makeDictionaryViewController() + let nav = UINavigationController(rootViewController: rootVC) + nav.navigationBar.isHidden = true + window.rootViewController = nav + window.makeKeyAndVisible() + } + + func makeDictionaryViewController() -> UIViewController { + // MARK: - Repository + let dictionaryListAPIRepository = MockDictionaryListAPIRepository() + let dictionaryDetailAPIRepository = MockDictionaryDetailAPIRepository() + let recentSearchRepository = MockRecentSearchRepository() + let bookmarkRepository = MockBookmarkRepository() + let authRepository = MockAuthAPIRepository() + let tokenRepository = MockTokenRepository() + let alarmRepository = MockAlarmRepository() + + // MARK: - UseCase + let checkLoginUseCase = CheckLoginUseCaseImpl( + authRepository: authRepository, + tokenRepository: tokenRepository + ) + let parseItemFilterResultUseCase = ParseItemFilterResultUseCaseImpl() + let fetchVisitDictionaryDetailUseCase = + FetchVisitDictionaryDetailUseCaseImpl( + repository: UserDefaultsRepositoryImpl() + ) + let fetchProfileUseCase = MockFetchProfileUseCase() + let checkNotificationPermissionUseCase = + CheckNotificationPermissionUseCaseImpl() + + // MARK: - Factory + let loginFactory = MockLoginFactory() + let bookmarkModalFactory = MockBookmarkModalFactory() + let itemFilterFactory = ItemFilterBottomSheetFactoryImpl() + let monsterFilterFactory = MonsterFilterBottomSheetFactoryImpl() + let sortedFactory = SortedBottomSheetFactoryImpl() + let detailOnBoardingFactory = DetailOnBoardingFactoryImpl() + let notificationSettingFactory = + MockNotificationSettingFactory() + + // MARK: - Detail Factory + let detailFactory = DictionaryDetailFactoryImpl( + loginFactory: { + loginFactory + }, + bookmarkModalFactory: bookmarkModalFactory, + detailOnBoardingFactory: detailOnBoardingFactory, + appCoordinator: { + MockAppCoordinator() + }, + dictionaryDetailAPIRepository: dictionaryDetailAPIRepository, + bookmarkRepository: bookmarkRepository, + checkLoginUseCase: checkLoginUseCase, + fetchVisitDictionaryDetailUseCase: + fetchVisitDictionaryDetailUseCase + ) + + // MARK: - Dictionary Main List Factory + let dictionaryMainListFactory = DictionaryListFactoryImpl( + checkLoginUseCase: checkLoginUseCase, + bookmarkRepository: bookmarkRepository, + parseItemFilterResultUseCase: + parseItemFilterResultUseCase, + dictionaryListAPIRepository: + dictionaryListAPIRepository, + itemFilterFactory: itemFilterFactory, + monsterFilterFactory: monsterFilterFactory, + sortedFactory: sortedFactory, + bookmarkModalFactory: bookmarkModalFactory, + detailFactory: detailFactory + ) { + MockLoginFactory() + } + + // MARK: - Search Factory + let dictionarySearchResultFactory = + DictionarySearchResultFactoryImpl( + dictionaryListAPIRepository: + dictionaryListAPIRepository, + recentSearchRepository: + recentSearchRepository, + dictionaryMainListFactory: + dictionaryMainListFactory + ) + let dictionarySearchFactory = + DictionarySearchFactoryImpl( + recentSearchRepository: + recentSearchRepository, + searchResultFactory: + dictionarySearchResultFactory + ) + + // MARK: - Notification Factory + let dictionaryNotificationFactory = + DictionaryNotificationFactoryImpl( + notificationSettingFactory: + notificationSettingFactory, + fetchProfileUseCase: + fetchProfileUseCase, + checkNotificationPermissionUseCase: + checkNotificationPermissionUseCase, + alarmRepository: + alarmRepository + ) + + // MARK: - Main Factory + let dictionaryFactory = DictionaryMainViewFactoryImpl( + dictionaryMainListFactory: + dictionaryMainListFactory, + searchFactory: + dictionarySearchFactory, + notificationFactory: + dictionaryNotificationFactory, + loginFactory: + loginFactory, + fetchProfileUseCase: + fetchProfileUseCase + ) + + return dictionaryFactory.make() + } +} diff --git a/MLS/MLSDictionaryFeatureExample/ViewController.swift b/MLS/MLSDictionaryFeatureExample/ViewController.swift new file mode 100644 index 00000000..bf76c187 --- /dev/null +++ b/MLS/MLSDictionaryFeatureExample/ViewController.swift @@ -0,0 +1,4 @@ +import UIKit + +// SceneDelegate에서 직접 DictionaryMainViewController를 띄우기 때문에 사용하지 않습니다. +class ViewController: UIViewController {} diff --git a/MLS/MLSMyPageFeature/.gitignore b/MLS/MLSMyPageFeature/.gitignore new file mode 100644 index 00000000..0023a534 --- /dev/null +++ b/MLS/MLSMyPageFeature/.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/MLS/Presentation/DesignSystem/DesignSystem.xcodeproj/xcshareddata/xcschemes/DesignSystemDemo.xcscheme b/MLS/MLSMyPageFeature/.swiftpm/xcode/xcshareddata/xcschemes/MLSMyPageFeature.xcscheme similarity index 64% rename from MLS/Presentation/DesignSystem/DesignSystem.xcodeproj/xcshareddata/xcschemes/DesignSystemDemo.xcscheme rename to MLS/MLSMyPageFeature/.swiftpm/xcode/xcshareddata/xcschemes/MLSMyPageFeature.xcscheme index 76a5f02d..3a096e4e 100644 --- a/MLS/Presentation/DesignSystem/DesignSystem.xcodeproj/xcshareddata/xcschemes/DesignSystemDemo.xcscheme +++ b/MLS/MLSMyPageFeature/.swiftpm/xcode/xcshareddata/xcschemes/MLSMyPageFeature.xcscheme @@ -1,6 +1,6 @@ + BlueprintIdentifier = "MLSMyPageFeature" + BuildableName = "MLSMyPageFeature" + BlueprintName = "MLSMyPageFeature" + ReferencedContainer = "container:"> @@ -29,6 +29,18 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES" shouldAutocreateTestPlan = "YES"> + + + + + + - - - - - + + BlueprintIdentifier = "MLSMyPageFeature" + BuildableName = "MLSMyPageFeature" + BlueprintName = "MLSMyPageFeature" + ReferencedContainer = "container:"> - + diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature.xcodeproj/xcshareddata/xcschemes/BookmarkFeatureDemo.xcscheme b/MLS/MLSMyPageFeature/.swiftpm/xcode/xcshareddata/xcschemes/MLSMyPageFeatureTesting.xcscheme similarity index 64% rename from MLS/Presentation/BookmarkFeature/BookmarkFeature.xcodeproj/xcshareddata/xcschemes/BookmarkFeatureDemo.xcscheme rename to MLS/MLSMyPageFeature/.swiftpm/xcode/xcshareddata/xcschemes/MLSMyPageFeatureTesting.xcscheme index 5fb069c5..3aea6ebb 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature.xcodeproj/xcshareddata/xcschemes/BookmarkFeatureDemo.xcscheme +++ b/MLS/MLSMyPageFeature/.swiftpm/xcode/xcshareddata/xcschemes/MLSMyPageFeatureTesting.xcscheme @@ -1,6 +1,6 @@ + BlueprintIdentifier = "MLSMyPageFeatureTesting" + BuildableName = "MLSMyPageFeatureTesting" + BlueprintName = "MLSMyPageFeatureTesting" + ReferencedContainer = "container:"> @@ -40,16 +40,6 @@ debugDocumentVersioning = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES"> - - - - - + + BlueprintIdentifier = "MLSMyPageFeatureTesting" + BuildableName = "MLSMyPageFeatureTesting" + BlueprintName = "MLSMyPageFeatureTesting" + ReferencedContainer = "container:"> - + diff --git a/MLS/MLSMyPageFeature/Package.swift b/MLS/MLSMyPageFeature/Package.swift new file mode 100644 index 00000000..3654f6aa --- /dev/null +++ b/MLS/MLSMyPageFeature/Package.swift @@ -0,0 +1,88 @@ +// swift-tools-version: 6.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "MLSMyPageFeature", + platforms: [.iOS(.v15)], + products: [ + .library( + name: "MLSMyPageFeatureInterface", + targets: ["MLSMyPageFeatureInterface"] + ), + .library( + name: "MLSMyPageFeature", + targets: ["MLSMyPageFeature"] + ), + .library( + name: "MLSMyPageFeatureTesting", + targets: ["MLSMyPageFeatureTesting"] + ) + ], + dependencies: [ + .package(path: "../MLSAppFeature"), + .package(path: "../MLSAuthFeature"), + .package(path: "../MLSCore"), + .package(path: "../MLSDesignSystem"), + .package(url: "https://github.com/ReactorKit/ReactorKit.git", from: "3.2.0"), + .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "6.7.0"), + .package(url: "https://github.com/RxSwiftCommunity/RxKeyboard.git", from: "2.0.0"), + .package(url: "https://github.com/SnapKit/SnapKit.git", from: "5.7.1") + ], + targets: [ + // Interface + .target( + name: "MLSMyPageFeatureInterface", + dependencies: [ + .product(name: "MLSAuthFeatureInterface", package: "MLSAuthFeature"), + .product(name: "MLSCore", package: "MLSCore"), + .product(name: "MLSDesignSystem", package: "MLSDesignSystem"), + .product(name: "RxSwift", package: "RxSwift") + ], + swiftSettings: [.swiftLanguageMode(.v5)] + ), + // Feature + .target( + name: "MLSMyPageFeature", + dependencies: [ + "MLSMyPageFeatureInterface", + .product(name: "MLSAuthFeatureInterface", package: "MLSAuthFeature"), + .product(name: "MLSCore", package: "MLSCore"), + .product(name: "MLSDesignSystem", package: "MLSDesignSystem"), + .product(name: "ReactorKit", package: "ReactorKit"), + .product(name: "RxSwift", package: "RxSwift"), + .product(name: "RxCocoa", package: "RxSwift"), + .product(name: "RxRelay", package: "RxSwift"), + .product(name: "RxKeyboard", package: "RxKeyboard"), + .product(name: "SnapKit", package: "SnapKit") + ], + swiftSettings: [.swiftLanguageMode(.v5)] + ), + // Mock + .target( + name: "MLSMyPageFeatureTesting", + dependencies: [ + "MLSMyPageFeatureInterface", + .product(name: "MLSAppFeatureInterface", package: "MLSAppFeature"), + .product(name: "MLSAppFeatureTesting", package: "MLSAppFeature"), + .product(name: "MLSAuthFeatureInterface", package: "MLSAuthFeature"), + .product(name: "MLSCore", package: "MLSCore"), + .product(name: "RxSwift", package: "RxSwift") + ], + swiftSettings: [.swiftLanguageMode(.v5)] + ), + // Tests + .testTarget( + name: "MLSMyPageFeatureTests", + dependencies: [ + "MLSMyPageFeature", + "MLSMyPageFeatureInterface", + "MLSMyPageFeatureTesting", + .product(name: "MLSAuthFeatureInterface", package: "MLSAuthFeature"), + .product(name: "MLSAuthFeatureTesting", package: "MLSAuthFeature"), + .product(name: "RxBlocking", package: "RxSwift") + ], + ) + ] +) diff --git a/MLS/Data/Data/Network/DTO/AlarmDTO/AlarmResponseDTO.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/DTOs/AlarmResponseDTO.swift similarity index 98% rename from MLS/Data/Data/Network/DTO/AlarmDTO/AlarmResponseDTO.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/DTOs/AlarmResponseDTO.swift index 519a8084..7598e0e8 100644 --- a/MLS/Data/Data/Network/DTO/AlarmDTO/AlarmResponseDTO.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/DTOs/AlarmResponseDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSMyPageFeatureInterface public struct AlarmResponseDTO: Decodable { public let contents: [Content] diff --git a/MLS/Data/Data/Network/DTO/AuthDTO/JobsDTO.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/DTOs/JobsDTO.swift similarity index 94% rename from MLS/Data/Data/Network/DTO/AuthDTO/JobsDTO.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/DTOs/JobsDTO.swift index 3fa00f59..42847b79 100644 --- a/MLS/Data/Data/Network/DTO/AuthDTO/JobsDTO.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/DTOs/JobsDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSAuthFeatureInterface public struct JobsDTO: Decodable { public let jobId: Int diff --git a/MLS/Data/Data/Network/DTO/AuthDTO/MemberDTO.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/DTOs/MemberDTO.swift similarity index 91% rename from MLS/Data/Data/Network/DTO/AuthDTO/MemberDTO.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/DTOs/MemberDTO.swift index 5126bcd5..09320180 100644 --- a/MLS/Data/Data/Network/DTO/AuthDTO/MemberDTO.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/DTOs/MemberDTO.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSMyPageFeatureInterface public struct MemberDTO: Decodable { public let id: String @@ -13,7 +13,7 @@ public struct MemberDTO: Decodable { public let level: Int? public let profileImageUrl: String - func toMyPageDomain() -> MyPageResponse { + func toDomain() -> MyPageResponse { return .init( nickname: nickname, jobId: jobId, @@ -26,5 +26,4 @@ public struct MemberDTO: Decodable { eventAgreement: eventAgreement ) } - } diff --git a/MLS/Data/Data/Network/Endpoints/AlarmEndPoint.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Endpoints/AlarmEndPoint.swift similarity index 97% rename from MLS/Data/Data/Network/Endpoints/AlarmEndPoint.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Endpoints/AlarmEndPoint.swift index 3cf91795..3baa4c8c 100644 --- a/MLS/Data/Data/Network/Endpoints/AlarmEndPoint.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Endpoints/AlarmEndPoint.swift @@ -1,4 +1,5 @@ -import DomainInterface +import MLSCore +import MLSMyPageFeatureInterface public enum AlarmEndPoint { static let base = "https://mapleland.2megabytes.me" diff --git a/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Endpoints/MyPageEndpoint.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Endpoints/MyPageEndpoint.swift new file mode 100644 index 00000000..416a4722 --- /dev/null +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Endpoints/MyPageEndpoint.swift @@ -0,0 +1,40 @@ +import MLSCore +import MLSMyPageFeatureInterface + +public enum MyPageEndpoint { + static let base = "https://mapleland.2megabytes.me" + + public static func fetchProfile() -> ResponsableEndPoint { + .init( + baseURL: base, + path: "/api/v1/auth/me", + method: .GET + ) + } + + public static func fetchJob(jobId: String) -> ResponsableEndPoint { + .init( + baseURL: base, + path: "/api/v1/jobs/\(jobId)", + method: .GET + ) + } + + public static func updateNickName(body: Encodable) -> ResponsableEndPoint { + .init( + baseURL: base, + path: "/api/v1/auth/member/nickname", + method: .PUT, + body: body + ) + } + + public static func updateProfileImage(body: Encodable) -> ResponsableEndPoint { + .init( + baseURL: base, + path: "/api/v1/auth/member/profile-image", + method: .PUT, + body: body + ) + } +} diff --git a/MLS/Data/Data/Repository/AlarmAPIRepositoryImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Repositories/AlarmRepositoryImpl.swift similarity index 94% rename from MLS/Data/Data/Repository/AlarmAPIRepositoryImpl.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Repositories/AlarmRepositoryImpl.swift index ab98afde..45c02ebb 100644 --- a/MLS/Data/Data/Repository/AlarmAPIRepositoryImpl.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Repositories/AlarmRepositoryImpl.swift @@ -1,10 +1,11 @@ import Foundation -import DomainInterface +import MLSCore +import MLSMyPageFeatureInterface import RxSwift -public class AlarmAPIRepositoryImpl: AlarmAPIRepository { +public class AlarmRepositoryImpl: AlarmRepository { private let provider: NetworkProvider private let tokenInterceptor: Interceptor @@ -50,7 +51,7 @@ public class AlarmAPIRepositoryImpl: AlarmAPIRepository { } -private extension AlarmAPIRepositoryImpl { +private extension AlarmRepositoryImpl { struct AlarmQuery: Encodable { let cursor: Int? let pageSize: Int diff --git a/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Repositories/MyPageRepositoryImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Repositories/MyPageRepositoryImpl.swift new file mode 100644 index 00000000..59012969 --- /dev/null +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Repositories/MyPageRepositoryImpl.swift @@ -0,0 +1,45 @@ +import MLSAuthFeatureInterface +import MLSCore +import MLSMyPageFeatureInterface + +import RxSwift + +public class MyPageRepositoryImpl: MyPageRepository { + private let provider: NetworkProvider + private let tokenInterceptor: Interceptor + + public init(provider: NetworkProvider, interceptor: Interceptor) { + self.provider = provider + self.tokenInterceptor = interceptor + } + + public func fetchProfile() -> Observable { + let endpoint = MyPageEndpoint.fetchProfile() + return provider.requestData(endPoint: endpoint, interceptor: tokenInterceptor) + .map { $0.toDomain() } + } + + public func fetchJob(jobId: String) -> Observable { + let endPoint = MyPageEndpoint.fetchJob(jobId: jobId) + return provider.requestData(endPoint: endPoint, interceptor: nil).map { $0.toDomain() } + } + + public func updateNickName(nickName: String) -> Observable { + let endPoint = MyPageEndpoint.updateNickName(body: NickNameBody(nickname: nickName)) + return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) + .map { $0.toDomain() } + } + + public func updateProfileImage(url: String) -> Completable { + let endPoint = MyPageEndpoint.updateProfileImage(body: UpdateProfileImageBody(profileImageUrl: url)) + return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) + } +} + +struct NickNameBody: Encodable { + let nickname: String +} + +struct UpdateProfileImageBody: Encodable { + let profileImageUrl: String +} diff --git a/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Repositories/NotificationPermissionRepositoryImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Repositories/NotificationPermissionRepositoryImpl.swift new file mode 100644 index 00000000..16cab586 --- /dev/null +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Data/Repositories/NotificationPermissionRepositoryImpl.swift @@ -0,0 +1,19 @@ +import UserNotifications + +import MLSMyPageFeatureInterface + +import RxSwift + +public final class NotificationPermissionRepositoryImpl: NotificationPermissionRepository { + public init() {} + + public func fetchAuthorizationStatus() -> Single { + Single.create { single in + UNUserNotificationCenter.current().getNotificationSettings { settings in + single(.success(settings.authorizationStatus == .authorized)) + } + + return Disposables.create() + } + } +} diff --git a/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/CheckEmptyLevelAndRoleUseCaseImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/CheckEmptyLevelAndRoleUseCaseImpl.swift new file mode 100644 index 00000000..fc171039 --- /dev/null +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/CheckEmptyLevelAndRoleUseCaseImpl.swift @@ -0,0 +1,13 @@ +// import MLSAuthFeatureInterface +// +// import RxSwift +// +// public class CheckEmptyLevelAndRoleUseCaseImpl: CheckEmptyLevelAndRoleUseCase { +// public init() {} +// +// public func execute(level: Int?, job: String?) -> Bool { +// let isValidLevel = level.map { (1 ... 200).contains($0) } ?? false +// let isValidRole = job != nil && job != "" +// return isValidLevel && isValidRole +// } +// } diff --git a/MLS/Domain/Domain/UseCaseImpl/Shared/CheckNickNameUseCaseImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/CheckNickNameUseCaseImpl.swift similarity index 93% rename from MLS/Domain/Domain/UseCaseImpl/Shared/CheckNickNameUseCaseImpl.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/CheckNickNameUseCaseImpl.swift index 921d295f..d1284c7b 100644 --- a/MLS/Domain/Domain/UseCaseImpl/Shared/CheckNickNameUseCaseImpl.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/CheckNickNameUseCaseImpl.swift @@ -1,6 +1,6 @@ import Foundation -import DomainInterface +import MLSMyPageFeatureInterface import RxSwift diff --git a/MLS/Domain/Domain/UseCaseImpl/MyPage/FetchProfileUseCaseImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/FetchProfileUseCaseImpl.swift similarity index 66% rename from MLS/Domain/Domain/UseCaseImpl/MyPage/FetchProfileUseCaseImpl.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/FetchProfileUseCaseImpl.swift index 6353a869..282b35f4 100644 --- a/MLS/Domain/Domain/UseCaseImpl/MyPage/FetchProfileUseCaseImpl.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/FetchProfileUseCaseImpl.swift @@ -1,14 +1,12 @@ -import DomainInterface +import MLSMyPageFeatureInterface import RxSwift public class FetchProfileUseCaseImpl: FetchProfileUseCase { - private var repository: AuthAPIRepository - private let fetchJobUseCase: FetchJobUseCase + private var repository: MyPageRepository - public init(repository: AuthAPIRepository, fetchJobUseCase: FetchJobUseCase) { + public init(repository: MyPageRepository) { self.repository = repository - self.fetchJobUseCase = fetchJobUseCase } public func execute() -> Observable { @@ -18,7 +16,7 @@ public class FetchProfileUseCaseImpl: FetchProfileUseCase { return .just(profile) } - return self.fetchJobUseCase.execute(jobId: String(jobId)) + return repository.fetchJob(jobId: String(jobId)) .map { job in var new = profile new?.jobName = job.name diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/LogoutUseCaseImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/LogoutUseCaseImpl.swift similarity index 95% rename from MLS/Domain/Domain/UseCaseImpl/AuthAPI/LogoutUseCaseImpl.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/LogoutUseCaseImpl.swift index 0ece074f..76c73141 100644 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/LogoutUseCaseImpl.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/LogoutUseCaseImpl.swift @@ -1,6 +1,7 @@ import Foundation -import DomainInterface +import MLSAuthFeatureInterface +import MLSMyPageFeatureInterface import RxSwift diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/WithdrawUseCaseImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/WithdrawUseCaseImpl.swift similarity index 94% rename from MLS/Domain/Domain/UseCaseImpl/AuthAPI/WithdrawUseCaseImpl.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/WithdrawUseCaseImpl.swift index fc6b314d..6b01a127 100644 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/WithdrawUseCaseImpl.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Domain/Usecases/WithdrawUseCaseImpl.swift @@ -1,6 +1,7 @@ import Foundation -import DomainInterface +import MLSAuthFeatureInterface +import MLSMyPageFeatureInterface import RxSwift diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Announcement/AnnouncementReactor.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Announcement/AnnouncementReactor.swift similarity index 79% rename from MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Announcement/AnnouncementReactor.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Announcement/AnnouncementReactor.swift index ff7ee125..550d3c17 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Announcement/AnnouncementReactor.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Announcement/AnnouncementReactor.swift @@ -1,6 +1,6 @@ -import ReactorKit +import MLSMyPageFeatureInterface -import DomainInterface +import ReactorKit public final class AnnouncementReactor: Reactor { // MARK: - Reactor @@ -28,13 +28,11 @@ public final class AnnouncementReactor: Reactor { // MARK: - Properties public var initialState: State private let disposeBag = DisposeBag() - private let fetchNoticesUseCase: FetchNoticesUseCase - private let setReadUseCase: SetReadUseCase + private let alarmRepository: AlarmRepository - public init(fetchNoticesUseCase: FetchNoticesUseCase, setReadUseCase: SetReadUseCase) { + public init(alarmRepository: AlarmRepository) { self.initialState = .init() - self.fetchNoticesUseCase = fetchNoticesUseCase - self.setReadUseCase = setReadUseCase + self.alarmRepository = alarmRepository } public func mutate(action: Action) -> Observable { @@ -42,7 +40,7 @@ public final class AnnouncementReactor: Reactor { case .viewWillAppear: return .concat([ .just(.setLoading(true)), - fetchNoticesUseCase.execute(id: nil, pageSize: 20) + alarmRepository.fetchNotices(cursor: nil, pageSize: 20) .map { paged in .setAlarms(paged.items, hasMore: paged.hasMore, reset: true) }, @@ -53,14 +51,14 @@ public final class AnnouncementReactor: Reactor { let lastCursor = currentState.alarms.last?.id return .concat([ .just(.setLoading(true)), - fetchNoticesUseCase.execute(id: lastCursor, pageSize: 20) + alarmRepository.fetchNotices(cursor: lastCursor, pageSize: 20) .map { paged in .setAlarms(paged.items, hasMore: paged.hasMore, reset: false) }, .just(.setLoading(false)) ]) case .itemTapped(let index): - return setReadUseCase.execute(alarmLink: currentState.alarms[index].link) + return alarmRepository.setRead(alarmLink: currentState.alarms[index].link) .andThen(.empty()) } } diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Announcement/AnnouncementViewController.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Announcement/AnnouncementViewController.swift similarity index 98% rename from MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Announcement/AnnouncementViewController.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Announcement/AnnouncementViewController.swift index d7f8a2fd..dff14538 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Announcement/AnnouncementViewController.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Announcement/AnnouncementViewController.swift @@ -1,6 +1,6 @@ import UIKit -import DomainInterface +import MLSMyPageFeatureInterface import ReactorKit diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/CustomerSupportBaseView.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/CustomerSupportBaseView.swift similarity index 99% rename from MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/CustomerSupportBaseView.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/CustomerSupportBaseView.swift index 9ba80e06..910cdef1 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/CustomerSupportBaseView.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/CustomerSupportBaseView.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem /* **고객지원 공통뷰가 될 것 같음** */ @@ -34,7 +34,7 @@ final class CustomerSupportBaseView: UIView { button .setImage( DesignSystemAsset - .image(named: "arrowBack")? + .image(named: "arrowBack") .withRenderingMode(.alwaysTemplate) .resizableImage( withCapInsets: UIEdgeInsets( diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/CustomerSupportBaseViewController.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/CustomerSupportBaseViewController.swift similarity index 98% rename from MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/CustomerSupportBaseViewController.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/CustomerSupportBaseViewController.swift index bf643f53..383b7d78 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/CustomerSupportBaseViewController.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/CustomerSupportBaseViewController.swift @@ -1,9 +1,7 @@ import UIKit -import BaseFeature -import DesignSystem -import DomainInterface -import MyPageFeatureInterface +import MLSCore +import MLSMyPageFeatureInterface import RxCocoa import RxGesture diff --git a/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/CustomerSupportBaseViewFactoryImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/CustomerSupportBaseViewFactoryImpl.swift new file mode 100644 index 00000000..f513616c --- /dev/null +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/CustomerSupportBaseViewFactoryImpl.swift @@ -0,0 +1,43 @@ +import MLSCore +import MLSMyPageFeatureInterface + +public final class CustomerSupportBaseViewFactoryImpl: CustomerSupportFactory { + private let policyFactory: PolicyFactory + + private let alarmRepository: AlarmRepository + + public init( + policyFactory: PolicyFactory, + alarmRepository: AlarmRepository + ) { + self.policyFactory = policyFactory + self.alarmRepository = alarmRepository + } + + public func make(type: CustomerSupportType) -> BaseViewController { + var viewController = BaseViewController() + + switch type { + case .event: + viewController = EventViewController(type: .event) + if let viewController = viewController as? EventViewController { + viewController.reactor = EventReactor(alarmRepository: alarmRepository) + } + case .announcement: + viewController = AnnouncementViewController(type: .announcement) + if let viewController = viewController as? AnnouncementViewController { + viewController.reactor = AnnouncementReactor(alarmRepository: alarmRepository) + } + case .patchNote: + viewController = PatchNoteViewController(type: .patchNote) + if let viewController = viewController as? PatchNoteViewController { + viewController.reactor = PatchNoteReactor(alarmRepository: alarmRepository) + } + case .terms: + viewController = TermsViewController(type: .terms, policyFactory: policyFactory) + } + + viewController.isBottomTabbarHidden = true + return viewController + } +} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Event/EventReactor.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Event/EventReactor.swift similarity index 72% rename from MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Event/EventReactor.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Event/EventReactor.swift index 92b9ebce..18b8ad39 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Event/EventReactor.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Event/EventReactor.swift @@ -1,6 +1,6 @@ import ReactorKit -import DomainInterface +import MLSMyPageFeatureInterface public final class EventReactor: Reactor { // MARK: - Reactor @@ -30,23 +30,19 @@ public final class EventReactor: Reactor { // MARK: - Properties public var initialState: State private let disposeBag = DisposeBag() - private let fetchOngoingEventsUseCase: FetchOngoingEventsUseCase - private let fetchOutdatedEventsUseCase: FetchOutdatedEventsUseCase - private let setReadUseCase: SetReadUseCase + private let alarmRepository: AlarmRepository - public init(fetchOngoingEventsUseCase: FetchOngoingEventsUseCase, fetchOutdatedEventsUseCase: FetchOutdatedEventsUseCase, setReadUseCase: SetReadUseCase) { + public init(alarmRepository: AlarmRepository) { self.initialState = .init() - self.fetchOngoingEventsUseCase = fetchOngoingEventsUseCase - self.fetchOutdatedEventsUseCase = fetchOutdatedEventsUseCase - self.setReadUseCase = setReadUseCase + self.alarmRepository = alarmRepository } public func mutate(action: Action) -> Observable { switch action { case let .selectTab(index): let fetchObservable = (index == 0 - ? fetchOngoingEventsUseCase.execute(id: nil, pageSize: 20) - : fetchOutdatedEventsUseCase.execute(id: nil, pageSize: 20)) + ? alarmRepository.fetchOngoingEvents(cursor: nil, pageSize: 20) + : alarmRepository.fetchOutdatedEvents(cursor: nil, pageSize: 20)) .map { paged -> Mutation in .setAlarms(paged.items, hasMore: paged.hasMore, reset: true) } @@ -68,14 +64,14 @@ public final class EventReactor: Reactor { return .concat([ .just(.setLoading(true)), - (currentState.selectedIndex == 0 ? fetchOngoingEventsUseCase.execute(id: lastCursor, pageSize: 20) : fetchOutdatedEventsUseCase.execute(id: lastCursor, pageSize: 20)) + (currentState.selectedIndex == 0 ? alarmRepository.fetchOngoingEvents(cursor: lastCursor, pageSize: 20) : alarmRepository.fetchOutdatedEvents(cursor: lastCursor, pageSize: 20)) .map { paged in .setAlarms(paged.items, hasMore: paged.hasMore, reset: false) }, .just(.setLoading(false)) ]) case let .itemTapped(index): - return setReadUseCase.execute(alarmLink: currentState.alarms[index].link) + return alarmRepository.setRead(alarmLink: currentState.alarms[index].link) .andThen(.empty()) } } diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Event/EventViewController.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Event/EventViewController.swift similarity index 99% rename from MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Event/EventViewController.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Event/EventViewController.swift index 146c91c1..e4c970d2 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Event/EventViewController.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Event/EventViewController.swift @@ -1,7 +1,9 @@ -import DomainInterface -import ReactorKit import UIKit +import MLSMyPageFeatureInterface + +import ReactorKit + final class EventViewController: CustomerSupportBaseViewController, View { typealias Reactor = EventReactor diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/PatchNote/PatchNoteReactor.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/PatchNote/PatchNoteReactor.swift similarity index 77% rename from MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/PatchNote/PatchNoteReactor.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/PatchNote/PatchNoteReactor.swift index 8aaa10c0..6957307a 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/PatchNote/PatchNoteReactor.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/PatchNote/PatchNoteReactor.swift @@ -1,6 +1,6 @@ import ReactorKit -import DomainInterface +import MLSMyPageFeatureInterface public final class PatchNoteReactor: Reactor { // MARK: - Reactor @@ -28,13 +28,11 @@ public final class PatchNoteReactor: Reactor { // MARK: - Properties public var initialState: State private let disposeBag = DisposeBag() - private let fetchPatchNotesUseCase: FetchPatchNotesUseCase - private let setReadUseCase: SetReadUseCase + private let alarmRepository: AlarmRepository - public init(fetchPatchNotesUseCase: FetchPatchNotesUseCase, setReadUseCase: SetReadUseCase) { + public init(alarmRepository: AlarmRepository) { self.initialState = .init() - self.fetchPatchNotesUseCase = fetchPatchNotesUseCase - self.setReadUseCase = setReadUseCase + self.alarmRepository = alarmRepository } public func mutate(action: Action) -> Observable { @@ -42,7 +40,7 @@ public final class PatchNoteReactor: Reactor { case .viewWillAppear: return .concat([ .just(.setLoading(true)), - fetchPatchNotesUseCase.execute(id: nil, pageSize: 20) + alarmRepository.fetchPatchNotes(cursor: nil, pageSize: 20) .map { paged in .setAlarms(paged.items, hasMore: paged.hasMore, reset: true) }, @@ -53,14 +51,14 @@ public final class PatchNoteReactor: Reactor { let lastCursor = currentState.alarms.last?.id return .concat([ .just(.setLoading(true)), - fetchPatchNotesUseCase.execute(id: lastCursor, pageSize: 20) + alarmRepository.fetchPatchNotes(cursor: lastCursor, pageSize: 20) .map { paged in .setAlarms(paged.items, hasMore: paged.hasMore, reset: false) }, .just(.setLoading(false)) ]) - case .itemTapped(let index): - return setReadUseCase.execute(alarmLink: currentState.alarms[index].link) + case let .itemTapped(index): + return alarmRepository.setRead(alarmLink: currentState.alarms[index].link) .andThen(.empty()) } } diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/PatchNote/PatchNoteViewController.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/PatchNote/PatchNoteViewController.swift similarity index 98% rename from MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/PatchNote/PatchNoteViewController.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/PatchNote/PatchNoteViewController.swift index 3935c851..308e3dd6 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/PatchNote/PatchNoteViewController.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/PatchNote/PatchNoteViewController.swift @@ -1,6 +1,6 @@ import UIKit -import DomainInterface +import MLSMyPageFeatureInterface import ReactorKit diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Policy/PolicyFactoryImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Policy/PolicyFactoryImpl.swift similarity index 85% rename from MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Policy/PolicyFactoryImpl.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Policy/PolicyFactoryImpl.swift index dcadf10a..1ddd45d9 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Policy/PolicyFactoryImpl.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Policy/PolicyFactoryImpl.swift @@ -1,5 +1,5 @@ -import BaseFeature -import MyPageFeatureInterface +import MLSCore +import MLSMyPageFeatureInterface public final class PolicyFactoryImpl: PolicyFactory { public init() {} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Policy/PolicyView.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Policy/PolicyView.swift similarity index 97% rename from MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Policy/PolicyView.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Policy/PolicyView.swift index df7f7fba..3cc03b04 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Policy/PolicyView.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Policy/PolicyView.swift @@ -1,7 +1,7 @@ import UIKit -import DesignSystem -import MyPageFeatureInterface +import MLSDesignSystem +import MLSMyPageFeatureInterface final class PolicyView: UIView { // MARK: - Type diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Policy/PolicyViewController.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Policy/PolicyViewController.swift similarity index 93% rename from MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Policy/PolicyViewController.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Policy/PolicyViewController.swift index bd108f2c..15ab98b1 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Policy/PolicyViewController.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Policy/PolicyViewController.swift @@ -1,9 +1,8 @@ import UIKit -import BaseFeature -import DesignSystem -import DomainInterface -import MyPageFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSMyPageFeatureInterface import RxCocoa import RxGesture diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Policy/TermsViewController.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Policy/TermsViewController.swift similarity index 91% rename from MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Policy/TermsViewController.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Policy/TermsViewController.swift index bf93585c..251a90cf 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/Policy/TermsViewController.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/CustomerSupport/Policy/TermsViewController.swift @@ -1,6 +1,6 @@ import UIKit -import MyPageFeatureInterface +import MLSMyPageFeatureInterface final class TermsViewController: CustomerSupportBaseViewController { diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageListCell.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageListCell.swift similarity index 98% rename from MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageListCell.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageListCell.swift index 7ecb2f75..f0a55cc1 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageListCell.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageListCell.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import SnapKit diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainCell.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainCell.swift similarity index 99% rename from MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainCell.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainCell.swift index a02d2651..2ac5b907 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainCell.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainCell.swift @@ -1,7 +1,7 @@ import UIKit -import BaseFeature -import DesignSystem +import MLSCore +import MLSDesignSystem import RxCocoa import RxSwift diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainFactoryImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainFactoryImpl.swift similarity index 94% rename from MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainFactoryImpl.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainFactoryImpl.swift index 6c45c1f2..1afaf09c 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainFactoryImpl.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainFactoryImpl.swift @@ -1,7 +1,6 @@ -import AuthFeatureInterface -import BaseFeature -import DomainInterface -import MyPageFeatureInterface +import MLSAuthFeatureInterface +import MLSCore +import MLSMyPageFeatureInterface public final class MyPageMainFactoryImpl: MyPageMainFactory { private let loginFactory: LoginFactory diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainReactor.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainReactor.swift similarity index 99% rename from MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainReactor.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainReactor.swift index 514303ad..f157f83a 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainReactor.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainReactor.swift @@ -1,4 +1,4 @@ -import DomainInterface +import MLSMyPageFeatureInterface import ReactorKit diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainView.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainView.swift similarity index 98% rename from MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainView.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainView.swift index 19ac70ed..8b16ccea 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainView.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainView.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import SnapKit diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainViewController.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainViewController.swift similarity index 99% rename from MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainViewController.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainViewController.swift index aa9bc141..55804c0b 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainViewController.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/Main/MyPageMainViewController.swift @@ -1,8 +1,9 @@ import UIKit -import AuthFeatureInterface -import BaseFeature -import MyPageFeatureInterface +import MLSAuthFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSMyPageFeatureInterface import ReactorKit import RxSwift diff --git a/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingFactoryImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingFactoryImpl.swift new file mode 100644 index 00000000..913381b0 --- /dev/null +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingFactoryImpl.swift @@ -0,0 +1,26 @@ +import MLSAuthFeatureInterface +import MLSCore +import MLSMyPageFeatureInterface + +public final class NotificationSettingFactoryImpl: NotificationSettingFactory { + private let notificationRepository: NotificationPermissionRepository + private let authRepository: AuthAPIRepository + + public init(notificationRepository: NotificationPermissionRepository, authRepository: AuthAPIRepository) { + self.notificationRepository = notificationRepository + self.authRepository = authRepository + } + + public func make(isAgreeEventNotification: Bool, isAgreeNoticeNotification: Bool, isAgreePatchNoteNotification: Bool) -> BaseViewController { + let viewController = NotificationSettingViewController( + reactor: NotificationSettingReactor( + notificationRepository: notificationRepository, + authRepository: authRepository, + isAgreeEventNotification: isAgreeEventNotification, + isAgreeNoticeNotification: isAgreeNoticeNotification, + isAgreePatchNoteNotification: isAgreePatchNoteNotification + ) + ) + return viewController + } +} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingItemView.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingItemView.swift similarity index 97% rename from MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingItemView.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingItemView.swift index a4d11545..4df4623b 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingItemView.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingItemView.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import RxCocoa import RxSwift @@ -56,7 +56,7 @@ final class NotificationItemView: UIView { for: .normal ) changeButton.semanticContentAttribute = .forceRightToLeft - changeButton.setImage(DesignSystemAsset.image(named: "arrowForwardSmall")?.withRenderingMode(.alwaysTemplate), for: .normal) + changeButton.setImage(DesignSystemAsset.image(named: "arrowForwardSmall").withRenderingMode(.alwaysTemplate), for: .normal) changeButton.tintColor = .primary700 // addSubviews diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingReactor.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingReactor.swift similarity index 77% rename from MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingReactor.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingReactor.swift index b36a593d..a78afd05 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingReactor.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingReactor.swift @@ -1,4 +1,6 @@ -import DomainInterface +import MLSAuthFeatureInterface +import MLSMyPageFeatureInterface + import ReactorKit public final class NotificationSettingReactor: Reactor { @@ -39,34 +41,37 @@ public final class NotificationSettingReactor: Reactor { public var initialState: State private let disposeBag = DisposeBag() - private let checkNotificationPermissionUseCase: CheckNotificationPermissionUseCase - private let updateNotificationAgreementUseCase: UpdateNotificationAgreementUseCase + private let notificationRepository: NotificationPermissionRepository + private let authRepository: AuthAPIRepository - init(checkNotificationPermissionUseCase: CheckNotificationPermissionUseCase, updateNotificationAgreementUseCase: UpdateNotificationAgreementUseCase, - isAgreeEventNotification: Bool, - isAgreeNoticeNotification: Bool, - isAgreePatchNoteNotification: Bool) { + init( + notificationRepository: NotificationPermissionRepository, + authRepository: AuthAPIRepository, + isAgreeEventNotification: Bool, + isAgreeNoticeNotification: Bool, + isAgreePatchNoteNotification: Bool + ) { self.initialState = .init(isAgreeEventNotification: isAgreeEventNotification, isAgreeNoticeNotification: isAgreeNoticeNotification, isAgreePatchNoteNotification: isAgreePatchNoteNotification) - self.checkNotificationPermissionUseCase = checkNotificationPermissionUseCase - self.updateNotificationAgreementUseCase = updateNotificationAgreementUseCase + self.notificationRepository = notificationRepository + self.authRepository = authRepository } public func mutate(action: Action) -> Observable { switch action { case .viewWillAppear, .appWillEnterForeground: - return checkNotificationPermissionUseCase.execute() + return notificationRepository.fetchAuthorizationStatus() .asObservable() .map { Mutation.setAuthorized($0) } case .backButtonTapped: return .just(.toNavigate(.dismiss)) case .eventViewSwitch(let isAgree): - return updateNotificationAgreementUseCase.execute(noticeAgreement: currentState.isAgreeNoticeNotification, patchNoteAgreement: currentState.isAgreePatchNoteNotification, eventAgreement: isAgree) + return authRepository.updateNotificationAgreement(noticeAgreement: currentState.isAgreeNoticeNotification, patchNoteAgreement: currentState.isAgreePatchNoteNotification, eventAgreement: isAgree) .andThen(.just(.setEventNotification(isAgree))) case .noticeViewSwitch(let isAgree): - return updateNotificationAgreementUseCase.execute(noticeAgreement: isAgree, patchNoteAgreement: currentState.isAgreePatchNoteNotification, eventAgreement: currentState.isAgreeEventNotification) + return authRepository.updateNotificationAgreement(noticeAgreement: isAgree, patchNoteAgreement: currentState.isAgreePatchNoteNotification, eventAgreement: currentState.isAgreeEventNotification) .andThen(.just(.setNoticeNotification(isAgree))) case .patchNoteViewSwitch(let isAgree): - return updateNotificationAgreementUseCase.execute(noticeAgreement: currentState.isAgreeNoticeNotification, patchNoteAgreement: isAgree, eventAgreement: currentState.isAgreeEventNotification) + return authRepository.updateNotificationAgreement(noticeAgreement: currentState.isAgreeNoticeNotification, patchNoteAgreement: isAgree, eventAgreement: currentState.isAgreeEventNotification) .andThen(.just(.setPatchNoteNotification(isAgree))) case .pushGuideViewTapped: return .just(.toNavigate(.setting)) diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingView.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingView.swift similarity index 98% rename from MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingView.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingView.swift index 7c0612bc..106fce30 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingView.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingView.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import RxCocoa import RxSwift @@ -24,7 +24,7 @@ final class NotificationSettingView: UIView { button .setImage( DesignSystemAsset - .image(named: "arrowBack")? + .image(named: "arrowBack") .withRenderingMode(.alwaysTemplate) .resizableImage( withCapInsets: UIEdgeInsets( diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingViewController.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingViewController.swift similarity index 99% rename from MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingViewController.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingViewController.swift index 6a0d9b4a..b088fe46 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingViewController.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/NotificationSetting/NotificationSettingViewController.swift @@ -1,7 +1,7 @@ import UIKit -import BaseFeature -import DomainInterface +import MLSCore +import MLSMyPageFeatureInterface import ReactorKit import RxSwift diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageCell.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageCell.swift similarity index 96% rename from MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageCell.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageCell.swift index 74019a5a..3e0ba38b 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageCell.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageCell.swift @@ -1,10 +1,11 @@ import UIKit -import BaseFeature -import DesignSystem +import MLSCore +import MLSDesignSystem import SnapKit +@MainActor public final class SelectImageCell: UICollectionViewCell { // MARK: - Type enum Constant { @@ -54,7 +55,7 @@ public final class SelectImageCell: UICollectionViewCell { }() private let checkIcon: UIImageView = { - let view = UIImageView(image: DesignSystemAsset.image(named: "checkMark")?.withRenderingMode(.alwaysTemplate)) + let view = UIImageView(image: DesignSystemAsset.image(named: "checkMark").withRenderingMode(.alwaysTemplate)) view.tintColor = .whiteMLS return view }() diff --git a/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageFactoryImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageFactoryImpl.swift new file mode 100644 index 00000000..4e5e06c0 --- /dev/null +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageFactoryImpl.swift @@ -0,0 +1,14 @@ +import MLSCore +import MLSDesignSystem +import MLSMyPageFeatureInterface + +public struct SelectImageFactoryImpl: SelectImageFactory { + public init() {} + + public func make() -> BaseViewController & ModalPresentable { + let viewController = SelectImageViewContoller() + viewController.reactor = SelectImageReactor() + viewController.isBottomTabbarHidden = true + return viewController + } +} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageReactor.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageReactor.swift similarity index 77% rename from MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageReactor.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageReactor.swift index 49be8575..80fe4e3d 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageReactor.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageReactor.swift @@ -1,8 +1,7 @@ -import DesignSystem -import DomainInterface +import MLSDesignSystem +import MLSMyPageFeatureInterface import ReactorKit -import RxCocoa import RxSwift public final class SelectImageReactor: Reactor { @@ -44,12 +43,9 @@ public final class SelectImageReactor: Reactor { public var initialState: State var disposeBag = DisposeBag() - private let updateProfileImageUseCase: UpdateProfileImageUseCase - // MARK: - init - public init(updateProfileImageUseCase: UpdateProfileImageUseCase) { + public init() { self.initialState = State() - self.updateProfileImageUseCase = updateProfileImageUseCase } // MARK: - Reactor Methods @@ -58,9 +54,8 @@ public final class SelectImageReactor: Reactor { case .cancelButtonTapped: return .just(.navigateTo(route: .dismiss)) case .applyButtonTapped: - guard let url = currentState.selectedImage?.url else { return .empty() } - return updateProfileImageUseCase.execute(url: url) - .andThen(.just(.navigateTo(route: .dismissWithSave))) + guard currentState.selectedImage != nil else { return .empty() } + return .just(.navigateTo(route: .dismissWithSave)) case .imageTapped(let index): let image = currentState.images[index] return .just(.selectImage(image)) diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageView.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageView.swift similarity index 98% rename from MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageView.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageView.swift index ff65a882..615dc726 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageView.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageView.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import MLSDesignSystem import SnapKit diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageViewContoller.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageViewContoller.swift similarity index 93% rename from MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageViewContoller.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageViewContoller.swift index 23938438..1e2af1dd 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageViewContoller.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SelectImage/SelectImageViewContoller.swift @@ -1,8 +1,8 @@ import UIKit -import BaseFeature -import DesignSystem -import DomainInterface +import MLSCore +import MLSDesignSystem +import MLSMyPageFeatureInterface import ReactorKit import RxCocoa @@ -17,6 +17,7 @@ public final class SelectImageViewContoller: BaseViewController, ModalPresentabl public typealias Reactor = SelectImageReactor public var disposeBag = DisposeBag() + public var onImageSelected: ((String) -> Void)? // MARK: - Components @@ -91,6 +92,9 @@ extension SelectImageViewContoller { case .dismiss: owner.dismissCurrentModal() case .dismissWithSave: + if let url = reactor.currentState.selectedImage?.url { + owner.onImageSelected?(url) + } owner.dismissCurrentModal() default: break diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SetCharacter/SetCharacterFactoryImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetCharacter/SetCharacterFactoryImpl.swift similarity index 61% rename from MLS/Presentation/MyPageFeature/MyPageFeature/SetCharacter/SetCharacterFactoryImpl.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetCharacter/SetCharacterFactoryImpl.swift index a7a0f4ba..fe7304fb 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SetCharacter/SetCharacterFactoryImpl.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetCharacter/SetCharacterFactoryImpl.swift @@ -1,23 +1,23 @@ -import BaseFeature -import DomainInterface -import MyPageFeatureInterface +import MLSAuthFeatureInterface +import MLSCore +import MLSMyPageFeatureInterface public struct SetCharacterFactoryImpl: SetCharacterFactory { private let checkEmptyUseCase: CheckEmptyLevelAndRoleUseCase private let checkValidLevelUseCase: CheckValidLevelUseCase - private let fetchJobListUseCase: FetchJobListUseCase - private let updateUserInfoUseCase: UpdateUserInfoUseCase + private let authRepository: AuthAPIRepository + private let myPageRepository: MyPageRepository public init( checkEmptyUseCase: CheckEmptyLevelAndRoleUseCase, checkValidLevelUseCase: CheckValidLevelUseCase, - fetchJobListUseCase: FetchJobListUseCase, - updateUserInfoUseCase: UpdateUserInfoUseCase + authRepository: AuthAPIRepository, + myPageRepository: MyPageRepository ) { self.checkEmptyUseCase = checkEmptyUseCase self.checkValidLevelUseCase = checkValidLevelUseCase - self.fetchJobListUseCase = fetchJobListUseCase - self.updateUserInfoUseCase = updateUserInfoUseCase + self.authRepository = authRepository + self.myPageRepository = myPageRepository } public func make() -> BaseViewController { @@ -25,8 +25,8 @@ public struct SetCharacterFactoryImpl: SetCharacterFactory { viewController.reactor = SetCharacterReactor( checkEmptyUseCase: checkEmptyUseCase, checkValidLevelUseCase: checkValidLevelUseCase, - fetchJobListUseCase: fetchJobListUseCase, - updateUserInfoUseCase: updateUserInfoUseCase + authRepository: authRepository, + myPageRepository: myPageRepository ) viewController.isBottomTabbarHidden = true return viewController diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SetCharacter/SetCharacterReactor.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetCharacter/SetCharacterReactor.swift similarity index 56% rename from MLS/Presentation/MyPageFeature/MyPageFeature/SetCharacter/SetCharacterReactor.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetCharacter/SetCharacterReactor.swift index 9d2a37ec..0c969190 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SetCharacter/SetCharacterReactor.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetCharacter/SetCharacterReactor.swift @@ -1,4 +1,5 @@ -import DomainInterface +import MLSAuthFeatureInterface +import MLSMyPageFeatureInterface import ReactorKit import RxSwift @@ -43,21 +44,21 @@ public final class SetCharacterReactor: Reactor { public var initialState: State private let checkEmptyUseCase: CheckEmptyLevelAndRoleUseCase private let checkValidLevelUseCase: CheckValidLevelUseCase - private let fetchJobListUseCase: FetchJobListUseCase - private let updateUserInfoUseCase: UpdateUserInfoUseCase + private let authRepository: AuthAPIRepository + private let myPageRepository: MyPageRepository var disposeBag = DisposeBag() // MARK: - init public init( checkEmptyUseCase: CheckEmptyLevelAndRoleUseCase, checkValidLevelUseCase: CheckValidLevelUseCase, - fetchJobListUseCase: FetchJobListUseCase, - updateUserInfoUseCase: UpdateUserInfoUseCase + authRepository: AuthAPIRepository, + myPageRepository: MyPageRepository ) { self.checkEmptyUseCase = checkEmptyUseCase self.checkValidLevelUseCase = checkValidLevelUseCase - self.fetchJobListUseCase = fetchJobListUseCase - self.updateUserInfoUseCase = updateUserInfoUseCase + self.authRepository = authRepository + self.myPageRepository = myPageRepository self.initialState = State() } @@ -65,32 +66,47 @@ public final class SetCharacterReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .viewWillAppear: - return fetchJobListUseCase.execute() - .map { response in - .setJobList(jobList: response.jobList) + return Observable.zip( + authRepository.fetchJobList(), + myPageRepository.fetchProfile().catchAndReturn(nil) + ) + .flatMap { [weak self] jobResponse, profile -> Observable in + guard let self else { return .empty() } + var mutations: [Observable] = [ + .just(.setJobList(jobList: jobResponse.jobList)) + ] + if let level = profile?.level { + mutations.append(.just(.setLevel(level))) + mutations.append(.just(.setLevelValid(checkValidLevelUseCase.execute(level: level)))) } - .catchAndReturn(.navigateTo(route: .error)) + if let jobId = profile?.jobId, + let job = jobResponse.jobList.first(where: { $0.id == jobId }) { + mutations.append(.just(.setRole(job))) + } + let isEnabled = checkEmptyUseCase.execute(level: profile?.level, job: profile?.jobName) + mutations.append(.just(.setButtonEnabled(isEnabled))) + return Observable.concat(mutations) + } + .catchAndReturn(.navigateTo(route: .error)) case .backButtonTapped: return Observable.just(.navigateTo(route: .dismiss)) case .applyButtonTapped: guard let level = currentState.level, let job = currentState.job else { return Observable.just(.navigateTo(route: .error)) } - return updateUserInfoUseCase.execute(level: level, selectedJobID: job.id) + return authRepository.updateUserInfo(level: level, selectedJobID: job.id) .andThen(Observable.just(.navigateTo(route: .dismissWithSave))) .catchAndReturn(.navigateTo(route: .error)) case .inputLevel(let level): - let changeLevel = Observable.just(Mutation.setLevel(level)) - let validateButton = checkEmptyUseCase.execute(level: level, job: currentState.job?.name) - .map(Mutation.setButtonEnabled) - let validateLevel = checkValidLevelUseCase.execute(level: level) - .map(Mutation.setLevelValid) - return .merge(changeLevel, validateButton, validateLevel) + let isButtonEnabled = checkEmptyUseCase.execute(level: level, job: currentState.job?.name) + let isLevelValid = checkValidLevelUseCase.execute(level: level) + return .of( + .setLevel(level), + .setButtonEnabled(isButtonEnabled), + .setLevelValid(isLevelValid) + ) case .inputRole(let job): - return checkEmptyUseCase.execute(level: currentState.level, job: job?.name) - .map { isValid in - [.setRole(job), .setButtonEnabled(isValid)] - } - .flatMap { Observable.from($0) } + let isButtonEnabled = checkEmptyUseCase.execute(level: currentState.level, job: job?.name) + return .of(.setRole(job), .setButtonEnabled(isButtonEnabled)) } } diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SetCharacter/SetCharacterView.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetCharacter/SetCharacterView.swift similarity index 95% rename from MLS/Presentation/MyPageFeature/MyPageFeature/SetCharacter/SetCharacterView.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetCharacter/SetCharacterView.swift index cdb60720..bde23089 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SetCharacter/SetCharacterView.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetCharacter/SetCharacterView.swift @@ -1,7 +1,7 @@ import UIKit -import BaseFeature -import DesignSystem +import MLSCore +import MLSDesignSystem import RxCocoa import RxSwift @@ -19,7 +19,7 @@ public final class SetCharacterView: CharacterInputView { }() // MARK: - init - override init() { + public init() { super.init() addViews() setupConstraints() diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SetCharacter/SetCharacterViewController.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetCharacter/SetCharacterViewController.swift similarity index 83% rename from MLS/Presentation/MyPageFeature/MyPageFeature/SetCharacter/SetCharacterViewController.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetCharacter/SetCharacterViewController.swift index f9f22874..42aa7582 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SetCharacter/SetCharacterViewController.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetCharacter/SetCharacterViewController.swift @@ -1,7 +1,7 @@ import UIKit -import BaseFeature -import DesignSystem +import MLSCore +import MLSDesignSystem import ReactorKit import RxCocoa @@ -103,6 +103,7 @@ public extension SetCharacterViewController { reactor.state .map { $0.isLevelValid } + .observe(on: MainScheduler.instance) .distinctUntilChanged() .withUnretained(self) .subscribe { owner, isLevelValid in @@ -114,9 +115,32 @@ public extension SetCharacterViewController { reactor.state .map { $0.isButtonEnabled } + .observe(on: MainScheduler.instance) .bind(to: mainView.nextButton.rx.isEnabled) .disposed(by: disposeBag) + reactor.state + .map { $0.level } + .compactMap { $0 } + .take(1) + .observe(on: MainScheduler.instance) + .withUnretained(self) + .subscribe { owner, level in + owner.mainView.inputBox.textField.text = "\(level)" + } + .disposed(by: disposeBag) + + reactor.state + .map { $0.job } + .compactMap { $0 } + .take(1) + .observe(on: MainScheduler.instance) + .withUnretained(self) + .subscribe { owner, job in + owner.mainView.dropDownBox.selectItem(id: job.id) + } + .disposed(by: disposeBag) + rx.viewDidAppear .take(1) .flatMapLatest { _ in reactor.pulse(\.$route) } diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileFactoryImpl.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetProfile/SetProfileFactoryImpl.swift similarity index 69% rename from MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileFactoryImpl.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetProfile/SetProfileFactoryImpl.swift index 59352520..9395626a 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileFactoryImpl.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetProfile/SetProfileFactoryImpl.swift @@ -1,26 +1,25 @@ -import BaseFeature -import DomainInterface -import MyPageFeatureInterface +import MLSCore +import MLSMyPageFeatureInterface public final class SetProfileFactoryImpl: SetProfileFactory { private let selectImageFactory: SelectImageFactory private let checkNickNameUseCase: CheckNickNameUseCase - private let updateNickNameUseCase: UpdateNickNameUseCase private let logoutUseCase: LogoutUseCase private let withdrawUseCase: WithdrawUseCase private let fetchProfileUseCase: FetchProfileUseCase + private let myPageRepository: MyPageRepository public init( selectImageFactory: SelectImageFactory, checkNickNameUseCase: CheckNickNameUseCase, - updateNickNameUseCase: UpdateNickNameUseCase, logoutUseCase: LogoutUseCase, withdrawUseCase: WithdrawUseCase, - fetchProfileUseCase: FetchProfileUseCase + fetchProfileUseCase: FetchProfileUseCase, + myPageRepository: MyPageRepository ) { self.selectImageFactory = selectImageFactory self.checkNickNameUseCase = checkNickNameUseCase - self.updateNickNameUseCase = updateNickNameUseCase + self.myPageRepository = myPageRepository self.logoutUseCase = logoutUseCase self.withdrawUseCase = withdrawUseCase self.fetchProfileUseCase = fetchProfileUseCase @@ -28,7 +27,7 @@ public final class SetProfileFactoryImpl: SetProfileFactory { public func make() -> BaseViewController { let viewController = SetProfileViewController(selectImageFactory: selectImageFactory) - viewController.reactor = SetProfileReactor(checkNickNameUseCase: checkNickNameUseCase, updateNickNameUseCase: updateNickNameUseCase, logoutUseCase: logoutUseCase, withdrawUseCase: withdrawUseCase, fetchProfileUseCase: fetchProfileUseCase) + viewController.reactor = SetProfileReactor(checkNickNameUseCase: checkNickNameUseCase, logoutUseCase: logoutUseCase, withdrawUseCase: withdrawUseCase, fetchProfileUseCase: fetchProfileUseCase, myPageRepository: myPageRepository) viewController.isBottomTabbarHidden = true return viewController } diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileReactor.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetProfile/SetProfileReactor.swift similarity index 72% rename from MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileReactor.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetProfile/SetProfileReactor.swift index da27d604..0c12ba17 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileReactor.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetProfile/SetProfileReactor.swift @@ -1,10 +1,10 @@ -import DomainInterface +import MLSMyPageFeatureInterface import ReactorKit public final class SetProfileReactor: Reactor { // MARK: - Route - public enum Route { + public enum Route: Equatable { case none case dismiss case dismissWithUpdate @@ -23,12 +23,13 @@ public final class SetProfileReactor: Reactor { case showBottomSheet case inputNickName(String) case beginEditingNickName + case imageSelected(String) case logout case withdraw } // MARK: - Mutation - public enum Mutation { + public enum Mutation: Equatable { case toNavigate(Route) case setNickName(String) case setProfile(MyPageResponse?) @@ -37,6 +38,7 @@ public final class SetProfileReactor: Reactor { case beginEditting case cancelEditting case completeEditting + case setPendingImageUrl(String?) } // MARK: - State @@ -47,24 +49,26 @@ public final class SetProfileReactor: Reactor { var isEditingNickName = false var profile: MyPageResponse? var nickName = "" + var pendingImageUrl: String? + var wasUpdated = false } // MARK: - Properties public var initialState = State(setProfileState: .normal) private let checkNickNameUseCase: CheckNickNameUseCase - private let updateNickNameUseCase: UpdateNickNameUseCase private let logoutUseCase: LogoutUseCase private let withdrawUseCase: WithdrawUseCase private let fetchProfileUseCase: FetchProfileUseCase + private let myPageRepository: MyPageRepository // MARK: - Init - public init(checkNickNameUseCase: CheckNickNameUseCase, updateNickNameUseCase: UpdateNickNameUseCase, logoutUseCase: LogoutUseCase, withdrawUseCase: WithdrawUseCase, fetchProfileUseCase: FetchProfileUseCase) { + public init(checkNickNameUseCase: CheckNickNameUseCase, logoutUseCase: LogoutUseCase, withdrawUseCase: WithdrawUseCase, fetchProfileUseCase: FetchProfileUseCase, myPageRepository: MyPageRepository) { self.checkNickNameUseCase = checkNickNameUseCase - self.updateNickNameUseCase = updateNickNameUseCase self.logoutUseCase = logoutUseCase self.withdrawUseCase = withdrawUseCase self.fetchProfileUseCase = fetchProfileUseCase + self.myPageRepository = myPageRepository } // MARK: - Mutate @@ -80,12 +84,15 @@ public final class SetProfileReactor: Reactor { .flatMap { Observable.from($0) } case .beginEditingNickName: return .just(.beginSetText(true)) + case .imageSelected(let url): + return .just(.setPendingImageUrl(url)) case .backButtonTapped: switch currentState.setProfileState { case .edit: return .just(.cancelEditting) case .normal: - return .just(.toNavigate(.dismiss)) + let route: Route = currentState.wasUpdated ? .dismissWithUpdate : .dismiss + return .just(.toNavigate(route)) } case .editButtonTapped: switch currentState.setProfileState { @@ -93,8 +100,15 @@ public final class SetProfileReactor: Reactor { if currentState.isShowError { return .empty() } else { - return updateNickNameUseCase.execute(nickName: currentState.nickName) - .flatMap { profile in + let saveImage: Completable + if let url = currentState.pendingImageUrl { + saveImage = myPageRepository.updateProfileImage(url: url) + } else { + saveImage = .empty() + } + return saveImage + .andThen(myPageRepository.updateNickName(nickName: currentState.nickName)) + .flatMap { profile -> Observable in Observable.concat([ .just(.setProfile(profile)), .just(.completeEditting) @@ -133,15 +147,23 @@ public final class SetProfileReactor: Reactor { newState.isEditingNickName = isEditing case .cancelEditting: newState.setProfileState = .normal + newState.nickName = state.profile?.nickname ?? "" + newState.pendingImageUrl = nil case .beginEditting: newState.setProfileState = .edit + newState.isShowError = false + newState.isEditingNickName = false case .completeEditting: - newState.route = .dismissWithUpdate + newState.setProfileState = .normal + newState.pendingImageUrl = nil + newState.wasUpdated = true case .setProfile(let profile): newState.profile = profile newState.nickName = profile?.nickname ?? "" case .setNickName(let nickname): newState.nickName = nickname + case .setPendingImageUrl(let url): + newState.pendingImageUrl = url } return newState diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileView.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetProfile/SetProfileView.swift similarity index 99% rename from MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileView.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetProfile/SetProfileView.swift index 07655712..f6a32f91 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileView.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetProfile/SetProfileView.swift @@ -1,8 +1,8 @@ import UIKit -import BaseFeature -import DesignSystem -import DomainInterface +import MLSAuthFeatureInterface +import MLSCore +import MLSDesignSystem import RxCocoa import RxSwift diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileViewController.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetProfile/SetProfileViewController.swift similarity index 86% rename from MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileViewController.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetProfile/SetProfileViewController.swift index fd8264d2..e0d85b13 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileViewController.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeature/Presentation/SetProfile/SetProfileViewController.swift @@ -1,7 +1,8 @@ import UIKit -import BaseFeature -import MyPageFeatureInterface +import MLSCore +import MLSDesignSystem +import MLSMyPageFeatureInterface import ReactorKit import RxCocoa @@ -47,6 +48,7 @@ public final class SetProfileViewController: BaseViewController, View { addViews() setupConstraints() } + } // MARK: - Setup @@ -125,8 +127,12 @@ extension SetProfileViewController { .observe(on: MainScheduler.instance) .bind(onNext: { owner, state in owner.view.backgroundColor = state == .edit ? .whiteMLS : .neutral100 + owner.mainView.backButton.isHidden = state == .edit owner.mainView.setCountHidden(state: state) owner.mainView.setState(state: state) + if state == .edit { + owner.mainView.nickNameInputBox.textField.text = reactor.currentState.profile?.nickname ?? "" + } }) .disposed(by: disposeBag) @@ -142,6 +148,17 @@ extension SetProfileViewController { }) .disposed(by: disposeBag) + reactor.state + .map(\.pendingImageUrl) + .distinctUntilChanged() + .compactMap { $0 } + .withUnretained(self) + .observe(on: MainScheduler.instance) + .bind(onNext: { owner, url in + owner.mainView.setImage(imageUrl: url) + }) + .disposed(by: disposeBag) + reactor.state .compactMap(\.nickName) .distinctUntilChanged() @@ -183,16 +200,13 @@ extension SetProfileViewController { case .imageBottomSheet: let viewController = owner.selectImageFactory.make() - if let viewController = viewController as? UIViewController { - viewController.rx - .methodInvoked(#selector(UIViewController.viewDidDisappear)) - .take(1) - .map { _ in Reactor.Action.viewWillAppear } - .bind(to: reactor.action) - .disposed(by: owner.disposeBag) + if let selectImageVC = viewController as? SelectImageViewContoller { + selectImageVC.onImageSelected = { url in + reactor.action.onNext(.imageSelected(url)) + } } - owner.presentModal(viewController) + owner.presentModal(viewController, hideTabBar: true) case .dismiss: owner.didReturn.accept(false) owner.navigationController?.popViewController(animated: true) diff --git a/MLS/Domain/DomainInterface/Entity/Alarm/AlarmResponse.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Entities/AlarmResponse.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/Alarm/AlarmResponse.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Entities/AlarmResponse.swift diff --git a/MLS/Domain/DomainInterface/Entity/MyPage/CustomerSupportType.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Entities/CustomerSupportType.swift similarity index 100% rename from MLS/Domain/DomainInterface/Entity/MyPage/CustomerSupportType.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Entities/CustomerSupportType.swift diff --git a/MLS/Domain/DomainInterface/Entity/MyPage/MyPageResponse.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Entities/MyPageResponse.swift similarity index 67% rename from MLS/Domain/DomainInterface/Entity/MyPage/MyPageResponse.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Entities/MyPageResponse.swift index 05fed33d..46b49d44 100644 --- a/MLS/Domain/DomainInterface/Entity/MyPage/MyPageResponse.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Entities/MyPageResponse.swift @@ -1,3 +1,5 @@ +import MLSAuthFeatureInterface + public struct MyPageResponse: Equatable { public var nickname: String public let jobId: Int? @@ -21,3 +23,19 @@ public struct MyPageResponse: Equatable { self.eventAgreement = eventAgreement ?? false } } + +public extension MyPageResponse { + static func mock() -> MyPageResponse { + return MyPageResponse( + nickname: "테스트", + jobId: 1, + jobName: "전사", + level: 200, + profileUrl: "", + platform: .apple, + noticeAgreement: false, + patchNoteAgreement: false, + eventAgreement: false + ) + } +} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/PolicyType.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Entities/PolicyType.swift similarity index 100% rename from MLS/Presentation/MyPageFeature/MyPageFeatureInterface/PolicyType.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Entities/PolicyType.swift diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/CustomerSupportFactory.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/CustomerSupportFactory.swift similarity index 71% rename from MLS/Presentation/MyPageFeature/MyPageFeatureInterface/CustomerSupportFactory.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/CustomerSupportFactory.swift index fe54602e..52a5f18c 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/CustomerSupportFactory.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/CustomerSupportFactory.swift @@ -1,5 +1,4 @@ -import BaseFeature -import DomainInterface +import MLSCore public protocol CustomerSupportFactory { func make(type: CustomerSupportType) -> BaseViewController diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/MyPageMainFactory.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/MyPageMainFactory.swift similarity index 80% rename from MLS/Presentation/MyPageFeature/MyPageFeatureInterface/MyPageMainFactory.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/MyPageMainFactory.swift index 4bd89971..7a92981c 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/MyPageMainFactory.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/MyPageMainFactory.swift @@ -1,4 +1,4 @@ -import BaseFeature +import MLSCore public protocol MyPageMainFactory { func make() -> BaseViewController diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/NotificationSettingFactory.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/NotificationSettingFactory.swift similarity index 90% rename from MLS/Presentation/MyPageFeature/MyPageFeatureInterface/NotificationSettingFactory.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/NotificationSettingFactory.swift index 45a4e762..748996ac 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/NotificationSettingFactory.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/NotificationSettingFactory.swift @@ -1,4 +1,4 @@ -import BaseFeature +import MLSCore public protocol NotificationSettingFactory { func make(isAgreeEventNotification: Bool, isAgreeNoticeNotification: Bool, isAgreePatchNoteNotification: Bool) -> BaseViewController diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/PolicyFactory.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/PolicyFactory.swift similarity index 82% rename from MLS/Presentation/MyPageFeature/MyPageFeatureInterface/PolicyFactory.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/PolicyFactory.swift index c97ad2ea..ed7bdd83 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/PolicyFactory.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/PolicyFactory.swift @@ -1,4 +1,4 @@ -import BaseFeature +import MLSCore public protocol PolicyFactory { func make(type: PolicyType) -> BaseViewController diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/SelectImageFactory.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/SelectImageFactory.swift similarity index 71% rename from MLS/Presentation/MyPageFeature/MyPageFeatureInterface/SelectImageFactory.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/SelectImageFactory.swift index a71dcd7c..b6265c23 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/SelectImageFactory.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/SelectImageFactory.swift @@ -1,4 +1,5 @@ -import BaseFeature +import MLSCore +import MLSDesignSystem public protocol SelectImageFactory { func make() -> BaseViewController & ModalPresentable diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/SetCharacterFactory.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/SetCharacterFactory.swift similarity index 80% rename from MLS/Presentation/MyPageFeature/MyPageFeatureInterface/SetCharacterFactory.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/SetCharacterFactory.swift index 0ad4a20b..56820a42 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/SetCharacterFactory.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/SetCharacterFactory.swift @@ -1,4 +1,4 @@ -import BaseFeature +import MLSCore public protocol SetCharacterFactory { func make() -> BaseViewController diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/SetProfileFactory.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/SetProfileFactory.swift similarity index 80% rename from MLS/Presentation/MyPageFeature/MyPageFeatureInterface/SetProfileFactory.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/SetProfileFactory.swift index c1f9367d..fa33e846 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureInterface/SetProfileFactory.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Factories/SetProfileFactory.swift @@ -1,4 +1,4 @@ -import BaseFeature +import MLSCore public protocol SetProfileFactory { func make() -> BaseViewController diff --git a/MLS/Domain/DomainInterface/Repository/AlarmAPIRepository.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Repositories/AlarmRepository.swift similarity index 90% rename from MLS/Domain/DomainInterface/Repository/AlarmAPIRepository.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Repositories/AlarmRepository.swift index e5c975b0..fbf1bde8 100644 --- a/MLS/Domain/DomainInterface/Repository/AlarmAPIRepository.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Repositories/AlarmRepository.swift @@ -1,8 +1,6 @@ -import Foundation - import RxSwift -public protocol AlarmAPIRepository { +public protocol AlarmRepository { func fetchPatchNotes(cursor: Int?, pageSize: Int) -> Observable> func fetchNotices(cursor: Int?, pageSize: Int) -> Observable> diff --git a/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Repositories/MyPageRepository.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Repositories/MyPageRepository.swift new file mode 100644 index 00000000..11965d0d --- /dev/null +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Repositories/MyPageRepository.swift @@ -0,0 +1,10 @@ +import MLSAuthFeatureInterface + +import RxSwift + +public protocol MyPageRepository { + func fetchProfile() -> Observable + func fetchJob(jobId: String) -> Observable + func updateNickName(nickName: String) -> Observable + func updateProfileImage(url: String) -> Completable +} diff --git a/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Repositories/NotificationPermissionRepository.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Repositories/NotificationPermissionRepository.swift new file mode 100644 index 00000000..0e5e3ed9 --- /dev/null +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/Repositories/NotificationPermissionRepository.swift @@ -0,0 +1,5 @@ +import RxSwift + +public protocol NotificationPermissionRepository { + func fetchAuthorizationStatus() -> Single +} diff --git a/MLS/Domain/DomainInterface/UseCase/OnBoarding/CheckNickNameUseCase.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/UseCases/CheckNickNameUseCase.swift similarity index 100% rename from MLS/Domain/DomainInterface/UseCase/OnBoarding/CheckNickNameUseCase.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/UseCases/CheckNickNameUseCase.swift diff --git a/MLS/Domain/DomainInterface/UseCase/Alarm/FetchAllAlarmUseCase.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/UseCases/FetchAllAlarmUseCase.swift similarity index 100% rename from MLS/Domain/DomainInterface/UseCase/Alarm/FetchAllAlarmUseCase.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/UseCases/FetchAllAlarmUseCase.swift diff --git a/MLS/Domain/DomainInterface/UseCase/MyPage/FetchProfileUseCase.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/UseCases/FetchProfileUseCase.swift similarity index 100% rename from MLS/Domain/DomainInterface/UseCase/MyPage/FetchProfileUseCase.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/UseCases/FetchProfileUseCase.swift diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/LogoutUseCase.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/UseCases/LogoutUseCase.swift similarity index 100% rename from MLS/Domain/DomainInterface/UseCase/AuthAPI/LogoutUseCase.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/UseCases/LogoutUseCase.swift diff --git a/MLS/Domain/DomainInterface/UseCase/AuthAPI/WithdrawUseCase.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/UseCases/WithdrawUseCase.swift similarity index 100% rename from MLS/Domain/DomainInterface/UseCase/AuthAPI/WithdrawUseCase.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureInterface/UseCases/WithdrawUseCase.swift diff --git a/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockAlarmRepository.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockAlarmRepository.swift new file mode 100644 index 00000000..4612512c --- /dev/null +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockAlarmRepository.swift @@ -0,0 +1,82 @@ +import MLSMyPageFeatureInterface + +import RxSwift + +public final class MockAlarmRepository: AlarmRepository { + + public init() {} + + public func fetchPatchNotes(cursor: Int?, pageSize: Int) -> Observable> { + .just(makePagedAlarmResponse(prefix: "패치노트", cursor: cursor, pageSize: pageSize)) + } + + public func fetchNotices(cursor: Int?, pageSize: Int) -> Observable> { + .just(makePagedAlarmResponse(prefix: "공지사항", cursor: cursor, pageSize: pageSize)) + } + + public func fetchOutdatedEvents(cursor: Int?, pageSize: Int) -> Observable> { + .just(makePagedAlarmResponse(prefix: "종료 이벤트", cursor: cursor, pageSize: pageSize)) + } + + public func fetchOngoingEvents(cursor: Int?, pageSize: Int) -> Observable> { + .just(makePagedAlarmResponse(prefix: "진행중 이벤트", cursor: cursor, pageSize: pageSize)) + } + + public func fetchAll(cursor: Int?, pageSize: Int) -> Observable> { + let startID = cursor ?? 200 + + let items = (0.. pageSize + ) + ) + } + + public func setRead(alarmLink: String) -> Completable { + .empty() + } +} + +// MARK: - Private +private extension MockAlarmRepository { + + func makePagedAlarmResponse( + prefix: String, + cursor: Int?, + pageSize: Int + ) -> PagedEntity { + + let startID = cursor ?? 1000 + + let items = (0.. pageSize + ) + } +} diff --git a/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockInterceptor.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockInterceptor.swift new file mode 100644 index 00000000..7b9c929b --- /dev/null +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockInterceptor.swift @@ -0,0 +1,39 @@ +import Foundation + +import MLSCore + +public final class MockInterceptor: Interceptor { + + public init() {} + + public var adaptCalled = false + public var retryCalled = false + + public var receivedRequest: URLRequest? + public var receivedData: Data? + public var receivedResponse: URLResponse? + public var receivedError: Error? + + public var adaptedRequest: URLRequest? + public var retryResult: Bool = false + + public func adapt(_ request: URLRequest) -> URLRequest { + adaptCalled = true + receivedRequest = request + + return adaptedRequest ?? request + } + + public func retry( + data: Data?, + response: URLResponse?, + error: Error? + ) -> Bool { + retryCalled = true + receivedData = data + receivedResponse = response + receivedError = error + + return retryResult + } +} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Mock/MockLoginFactory.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockLoginFactory.swift similarity index 75% rename from MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Mock/MockLoginFactory.swift rename to MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockLoginFactory.swift index ff98072c..b450546b 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Mock/MockLoginFactory.swift +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockLoginFactory.swift @@ -1,5 +1,6 @@ -import AuthFeatureInterface -import BaseFeature +import MLSAppFeatureInterface +import MLSAuthFeatureInterface +import MLSCore public final class MockLoginFactory: LoginFactory { public func make(exitRoute: LoginExitRoute, onLoginCompleted: (() -> Void)?) -> BaseViewController { @@ -7,4 +8,6 @@ public final class MockLoginFactory: LoginFactory { viewcontroller.view.backgroundColor = .redMLS return viewcontroller } + + public init() {} } diff --git a/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockMyPageRepository.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockMyPageRepository.swift new file mode 100644 index 00000000..62f748f5 --- /dev/null +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockMyPageRepository.swift @@ -0,0 +1,36 @@ +import MLSAuthFeatureInterface +import MLSMyPageFeatureInterface + +import RxSwift + +public final class MockMyPageRepository: MyPageRepository { + + public init() {} + + public var fetchProfileResult: Observable = .just(nil) + public var fetchJobResult: Observable = .just(.init(name: "", id: 0)) + public var updateNickNameResult: Observable = + .just(.init(nickname: "", jobId: nil, jobName: "", level: nil, profileUrl: "", platform: .apple, noticeAgreement: nil, patchNoteAgreement: nil, eventAgreement: nil)) + public var updateProfileImageResult: Completable = .empty() + + public private(set) var fetchJobCalled = false + public private(set) var receivedJobId: String? + + public func fetchProfile() -> Observable { + fetchProfileResult + } + + public func fetchJob(jobId: String) -> Observable { + fetchJobCalled = true + receivedJobId = jobId + return fetchJobResult + } + + public func updateNickName(nickName: String) -> Observable { + updateNickNameResult + } + + public func updateProfileImage(url: String) -> Completable { + updateProfileImageResult + } +} diff --git a/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockNetworkProvider.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockNetworkProvider.swift new file mode 100644 index 00000000..bc6c6c1d --- /dev/null +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockNetworkProvider.swift @@ -0,0 +1,51 @@ +import MLSCore + +import RxSwift + +public final class MockNetworkProvider: NetworkProvider { + + public init() {} + + public var requestWithResponseCalled = false + public var requestCompletableCalled = false + + public var receivedInterceptor: Interceptor? + public var receivedEndPoint: Any? + + public var responseResult: Any? + public var responseError: Error? + + public var completableResult: Completable = .empty() + + public func requestData( + endPoint: T, + interceptor: Interceptor? + ) -> Observable { + + requestWithResponseCalled = true + receivedEndPoint = endPoint + receivedInterceptor = interceptor + + if let error = responseError { + return .error(error) + } + + guard let result = responseResult as? T.Response else { + fatalError("MockNetworkProvider.responseResult 타입이 \(T.Response.self) 와 일치하지 않습니다.") + } + + return .just(result) + } + + public func requestData( + endPoint: Requestable, + interceptor: Interceptor? + ) -> Completable { + + requestCompletableCalled = true + receivedEndPoint = endPoint + receivedInterceptor = interceptor + + return completableResult + } +} diff --git a/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockNotificationPermissionRepository.swift b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockNotificationPermissionRepository.swift new file mode 100644 index 00000000..c9723f57 --- /dev/null +++ b/MLS/MLSMyPageFeature/Sources/MLSMyPageFeatureTesting/Mock/MockNotificationPermissionRepository.swift @@ -0,0 +1,18 @@ +import UserNotifications + +import MLSMyPageFeatureInterface + +import RxSwift + +public final class MockNotificationPermissionRepository: NotificationPermissionRepository { + public var fetchAuthorizationStatusResult = false + + public init() {} + + public func fetchAuthorizationStatus() -> Single { + Single.create { single in + single(.success(self.fetchAuthorizationStatusResult)) + return Disposables.create() + } + } +} diff --git a/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/CustomerSupportReactorTests.swift b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/CustomerSupportReactorTests.swift new file mode 100644 index 00000000..9258794f --- /dev/null +++ b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/CustomerSupportReactorTests.swift @@ -0,0 +1,113 @@ +@testable import MLSMyPageFeature + +import Testing + +import MLSMyPageFeatureInterface +import MLSMyPageFeatureTesting + +import ReactorKit +import RxBlocking +import RxSwift + +@Suite("CustomerSupportReactorTests") +struct CustomerSupportReactorTests { + + // MARK: - 이벤트 페이지 + @Test("첫번째 탭 선택하면 ongoing 이벤트 fetch") + func selectTab_ongoing_emitsMutations() throws { + let repo = MockAlarmRepository() + let reactor = EventReactor(alarmRepository: repo) + + let mutations = try reactor + .mutate(action: .selectTab(0)) + .toBlocking() + .toArray() + + switch mutations[0] { + case .setIndex(let index): + #expect(index == 0) + default: + #expect(Bool(false), "Expected setIndex") + } + } + + @Test("두번째 탭 선택하면 outdated 이벤트 fetch") + func selectTab_outdated_emitsMutations() throws { + let repo = MockAlarmRepository() + let reactor = EventReactor(alarmRepository: repo) + + let mutations = try reactor + .mutate(action: .selectTab(1)) + .toBlocking() + .toArray() + + switch mutations[0] { + case .setIndex(let index): + #expect(index == 1) + default: + #expect(Bool(false), "Expected setIndex") + } + } + + // MARK: - 공통 + @Test("reset = true로 업데이트면 alarm 교체") + func setAlarms_replacesItems() { + let reactor = EventReactor(alarmRepository: MockAlarmRepository()) + + let alarms = [ + AlarmResponse( + id: 1, + type: "notice", + title: "공지", + link: "https://example.com/1", + date: "2026-04-26" + ) + ] + + let state = reactor.reduce( + state: reactor.initialState, + mutation: .setAlarms(alarms, hasMore: true, reset: true) + ) + + #expect(state.alarms.count == 1) + #expect(state.hasMore == true) + } + + @Test("reset = false로 업데이트면 alarm 추가") + func setAlarms_addsItems() { + let reactor = EventReactor(alarmRepository: MockAlarmRepository()) + + let base = EventReactor.State( + alarms: [ + AlarmResponse( + id: 1, + type: "notice", + title: "기존", + link: "https://example.com/1", + date: "2026-04-26" + ) + ], + selectedIndex: 0, + hasMore: true, + isLoading: false + ) + + let newItems = [ + AlarmResponse( + id: 2, + type: "notice", + title: "추가", + link: "https://example.com/2", + date: "2026-04-26" + ) + ] + + let state = reactor.reduce( + state: base, + mutation: .setAlarms(newItems, hasMore: false, reset: false) + ) + + #expect(state.alarms.count == 2) + #expect(state.hasMore == false) + } +} diff --git a/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/MyPageMainReactorTests.swift b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/MyPageMainReactorTests.swift new file mode 100644 index 00000000..df75510e --- /dev/null +++ b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/MyPageMainReactorTests.swift @@ -0,0 +1,166 @@ +@testable import MLSMyPageFeature + +import Testing + +import MLSMyPageFeatureInterface +import MLSMyPageFeatureTesting + +import RxBlocking +import RxSwift + +@Suite("MyPageMainReactorTests") +struct MyPageMainReactorTests { + @Test("fetch profile 성공하면 profile != nil") + func viewWillAppear_user_setsProfile() throws { + let repo = MockMyPageRepository() + + repo.fetchProfileResult = .just( + MyPageResponse.mock() + ) + + let reactor = MyPageMainReactor( + fetchProfileUseCase: FetchProfileUseCaseImpl( + repository: repo + ) + ) + + let mutation = try reactor + .mutate(action: .viewWillAppear) + .toBlocking() + .first()! + + switch mutation { + case .setProfile(let profile): + #expect(profile != nil) + default: + #expect(Bool(false), "Expected setProfile") + } + } + + // MARK: - 비로그인 + @Test("profileButtonTapped + 비로그인이면 login으로 이동") + func profileButtonTapped_guest_routesLogin() throws { + let reactor = makeSUT() + let mutation = try reactor + .mutate(action: .profileButtonTapped) + .toBlocking() + .first()! + + switch mutation { + case .toNavigate(let route): + #expect(route == .login) + default: + #expect(Bool(false), "Expected toNavigate") + } + } + + @Test("setAlarm 메뉴 + 비로그인이면 login으로 이동") + func setAlarm_guest_routesLogin() throws { + let reactor = makeSUT() + let mutation = try reactor + .mutate(action: .menuItemTapped(.setAlarm)) + .toBlocking() + .first()! + + switch mutation { + case .toNavigate(let route): + #expect(route == .login) + default: + #expect(Bool(false), "Expected toNavigate") + } + } + + @Test("setCharacterInfo 메뉴 + 비로그인이면 login으로 이동") + func setCharacter_guest_routesLogin() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .menuItemTapped(.setCharacterInfo(nil))) + .toBlocking() + .first()! + + switch mutation { + case .toNavigate(let route): + #expect(route == .login) + default: + #expect(Bool(false), "Expected toNavigate") + } + } + + // MARK: - 로그인 + @Test("프로필 수정 버튼 + 로그인이면 edit으로 이동") + func profileButtonTapped_user_routesEdit() throws { + let reactor = makeSUT() + + let profile = MyPageResponse.mock() + + let loggedInState = reactor.reduce( + state: reactor.initialState, + mutation: .setProfile(profile) + ) + + let mutation: MyPageMainReactor.Mutation + + if loggedInState.profile != nil { + mutation = .toNavigate(.edit) + } else { + mutation = try reactor + .mutate(action: .profileButtonTapped) + .toBlocking() + .first()! + } + + switch mutation { + case .toNavigate(let route): + #expect(route == .edit) + default: + #expect(Bool(false), "Expected toNavigate") + } + } + + @Test("notice 메뉴 선택하면 notice로 이동") + func showNotice_routesNotice() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .menuItemTapped(.showNotice)) + .toBlocking() + .first()! + + switch mutation { + case .toNavigate(let route): + #expect(route == .notice) + default: + #expect(Bool(false), "Expected toNavigate") + } + } + + @Test("profile을 업데이트하면 캐릭터 정보 반영") + func reduce_setProfile_updatesMenu() { + let reactor = makeSUT() + + let profile = MyPageResponse.mock() + + let state = reactor.reduce( + state: reactor.initialState, + mutation: .setProfile(profile) + ) + + switch state.menus[0][1] { + case .setCharacterInfo(let menuProfile): + #expect(menuProfile?.nickname == "테스트") + default: + #expect(Bool(false), "Expected setCharacterInfo") + } + } +} + +private extension MyPageMainReactorTests { + func makeSUT() -> MyPageMainReactor { + return MyPageMainReactor( + fetchProfileUseCase: FetchProfileUseCaseImpl( + repository: MockMyPageRepository() + ) + ) + } +} diff --git a/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/NotificationSettingReactorTests.swift b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/NotificationSettingReactorTests.swift new file mode 100644 index 00000000..e9effad4 --- /dev/null +++ b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/NotificationSettingReactorTests.swift @@ -0,0 +1,173 @@ +@testable import MLSMyPageFeature + +import Testing + +import MLSAuthFeatureInterface +import MLSAuthFeatureTesting +import MLSMyPageFeatureInterface +import MLSMyPageFeatureTesting + +import ReactorKit +import RxBlocking +import RxSwift + +@Suite("NotificationSettingReactorTests") +struct NotificationSettingReactorTests { + @Test("viewWillAppear에서 authorization 불러오기") + func viewWillAppear_setsAuthorized() throws { + let reactor = makeSUT(authorized: true) + + let mutation = try reactor + .mutate(action: .viewWillAppear) + .toBlocking() + .first()! + + switch mutation { + case .setAuthorized(let value): + #expect(value == true) + default: + #expect(Bool(false), "Expected setAuthorized") + } + } + + @Test("appWillEnterForeground에서 authorization 불러오기") + func appWillEnterForeground_setsAuthorized() throws { + let reactor = makeSUT(authorized: false) + + let mutation = try reactor + .mutate(action: .appWillEnterForeground) + .toBlocking() + .first()! + + switch mutation { + case .setAuthorized(let value): + #expect(value == false) + default: + #expect(Bool(false), "Expected setAuthorized") + } + } + + @Test("backButton 클릭하면 dismiss") + func backButtonTapped_routesDismiss() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .backButtonTapped) + .toBlocking() + .first()! + + switch mutation { + case .toNavigate(let route): + #expect(route == .dismiss) + default: + #expect(Bool(false), "Expected toNavigate") + } + } + + @Test("pushGuideView 클릭하면 setting") + func pushGuideViewTapped_routesSetting() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .pushGuideViewTapped) + .toBlocking() + .first()! + + switch mutation { + case .toNavigate(let route): + #expect(route == .setting) + default: + #expect(Bool(false), "Expected toNavigate") + } + } + + @Test("Authorization을 true로 수정하면 setAuthorized") + func updateAuthorization_setsAuthorized() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .updateAuthorization(true)) + .toBlocking() + .first()! + + switch mutation { + case .setAuthorized(let value): + #expect(value == true) + default: + #expect(Bool(false), "Expected setAuthorized") + } + } + + @Test("eventViewSwitch 변경하면 eventNotification 허용") + func eventViewSwitch_updatesState() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .eventViewSwitch(true)) + .toBlocking() + .first()! + + switch mutation { + case .setEventNotification(let value): + #expect(value == true) + default: + #expect(Bool(false), "Expected setEventNotification") + } + } + + @Test("noticeViewSwitch 변경하면 -> noticeNotification 허용") + func noticeViewSwitch_updatesState() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .noticeViewSwitch(true)) + .toBlocking() + .first()! + + switch mutation { + case .setNoticeNotification(let value): + #expect(value == true) + default: + #expect(Bool(false), "Expected setNoticeNotification") + } + } + + @Test("patchNoteViewSwitch 변경하면 patchNoteNotification 허용") + func patchNoteViewSwitch_updatesState() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .patchNoteViewSwitch(true)) + .toBlocking() + .first()! + + switch mutation { + case .setPatchNoteNotification(let value): + #expect(value == true) + default: + #expect(Bool(false), "Expected setPatchNoteNotification") + } + } +} + +extension NotificationSettingReactorTests { + private func makeSUT( + authorized: Bool = false, + event: Bool = false, + notice: Bool = false, + patch: Bool = false + ) -> NotificationSettingReactor { + let notificationRepo = MockNotificationPermissionRepository() + notificationRepo.fetchAuthorizationStatusResult = authorized + + let authRepo = MockAuthAPIRepository() + + return NotificationSettingReactor( + notificationRepository: notificationRepo, + authRepository: authRepo, + isAgreeEventNotification: event, + isAgreeNoticeNotification: notice, + isAgreePatchNoteNotification: patch + ) + } +} diff --git a/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/SetCharacterReactorTests.swift b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/SetCharacterReactorTests.swift new file mode 100644 index 00000000..11fbc63c --- /dev/null +++ b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/SetCharacterReactorTests.swift @@ -0,0 +1,167 @@ +@testable import MLSMyPageFeature + +import Testing + +import MLSAuthFeatureInterface +import MLSAuthFeatureTesting +import MLSMyPageFeatureInterface +import MLSMyPageFeatureTesting + +import ReactorKit +import RxBlocking +import RxSwift + +@Suite("SetCharacterReactorTests") +struct SetCharacterReactorTests { + private let testJob = Job(name: "전사", id: 1) + + // MARK: - Mutate + + @Test("viewWillAppear에서 jobList 불러오기") + func viewWillAppear_setsJobList() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .viewWillAppear) + .toBlocking() + .first()! + + switch mutation { + case .setJobList(let jobList): + #expect(jobList.count == 5) + #expect(jobList.first?.name == "전사") + default: + #expect(Bool(false), "Expected setJobList") + } + } + + @Test("backButton 클릭하면 dismiss") + func backButtonTapped_routesDismiss() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .backButtonTapped) + .toBlocking() + .first()! + + switch mutation { + case .navigateTo(let route): + #expect(route == .dismiss) + default: + #expect(Bool(false), "Expected navigateTo") + } + } + + @Test("레벨 200 입력하면 level/buttonEnabled 활성화") + func inputLevel_emitsThreeMutations() throws { + let reactor = makeSUT() + + reactor.isStubEnabled = true + reactor.stub.state.value.job = testJob + + let mutations = try reactor + .mutate(action: .inputLevel(200)) + .toBlocking() + .toArray() + + #expect(mutations.count == 3) + + switch mutations[0] { + case .setLevel(let level): + #expect(level == 200) + default: + #expect(Bool(false), "Expected setLevel") + } + + switch mutations[1] { + case .setButtonEnabled(let enabled): + #expect(enabled == true) + default: + #expect(Bool(false), "Expected setButtonEnabled") + } + + switch mutations[2] { + case .setLevelValid(let valid): + #expect(valid == true) + default: + #expect(Bool(false), "Expected setLevelValid") + } + } + + @Test("job 입력하면 role/buttonEnabled 활성화") + func inputRole_emitsTwoMutations() throws { + let reactor = makeSUT() + + reactor.isStubEnabled = true + reactor.stub.state.value.level = 200 + + let mutations = try reactor + .mutate(action: .inputRole(testJob)) + .toBlocking() + .toArray() + + #expect(mutations.count == 2) + + switch mutations[0] { + case .setRole(let job): + #expect(job?.name == "전사") + default: + #expect(Bool(false), "Expected setRole") + } + + switch mutations[1] { + case .setButtonEnabled(let enabled): + #expect(enabled == true) + default: + #expect(Bool(false), "Expected setButtonEnabled") + } + } + + @Test("입력값 없이 applyButton 클릭하면 error 방출") + func applyButtonTapped_empty_routesError() throws { + let reactor = makeSUT() + + let mutation = try reactor + .mutate(action: .applyButtonTapped) + .toBlocking() + .first()! + + switch mutation { + case .navigateTo(let route): + #expect(route == .error) + default: + #expect(Bool(false), "Expected navigateTo") + } + } + + @Test("유효한 값 입력 후 applyButton 클릭하면 dismissWithSave") + func applyButtonTapped_valid_routesDismissWithSave() throws { + let reactor = makeSUT() + + reactor.isStubEnabled = true + reactor.stub.state.value.level = 200 + reactor.stub.state.value.job = testJob + + let mutation = try reactor + .mutate(action: .applyButtonTapped) + .toBlocking() + .first()! + + switch mutation { + case .navigateTo(let route): + #expect(route == .dismissWithSave) + default: + #expect(Bool(false), "Expected navigateTo") + } + } +} + +private extension SetCharacterReactorTests { + func makeSUT() -> SetCharacterReactor { + return SetCharacterReactor( + checkEmptyUseCase: CheckEmptyLevelAndRoleUseCaseImpl(), + checkValidLevelUseCase: CheckValidLevelUseCaseImpl(), + authRepository: MockAuthAPIRepository() + ) + } +} diff --git a/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/SetProfileReactorTests.swift b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/SetProfileReactorTests.swift new file mode 100644 index 00000000..f5aa8909 --- /dev/null +++ b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Reactor/SetProfileReactorTests.swift @@ -0,0 +1,87 @@ +@testable import MLSMyPageFeature + +import Testing + +import MLSAuthFeatureTesting +import MLSMyPageFeatureInterface +import MLSMyPageFeatureTesting + +import ReactorKit +import RxBlocking +import RxSwift + +@Suite("SetProfileReactorTests") +struct SetProfileReactorTests { + @Test("bottomSheet 버튼 클릭하면 imageBottomSheet 이동") + func showBottomSheet_routesBottomSheet() throws { + let reactor = makeSUT() + + let mutation = try reactor.mutate(action: .showBottomSheet) + .toBlocking() + .first()! + + #expect(mutation == .toNavigate(.imageBottomSheet)) + } + + @Test("backButton 클릭하면 dismiss") + func back_normal_routesDismiss() throws { + let reactor = makeSUT() + + let mutation = try reactor.mutate(action: .backButtonTapped) + .toBlocking() + .first()! + + #expect(mutation == .toNavigate(.dismiss)) + } + + @Test("editButton클릭하면 beginEditing") + func edit_normal_beginEditing() throws { + let reactor = makeSUT() + + let mutation = try reactor.mutate(action: .editButtonTapped) + .toBlocking() + .first()! + + #expect(mutation == .beginEditting) + } + + @Test("logoutButton 클릭하면 logoutAlert") + func logoutButtonTapped_routesAlert() throws { + let reactor = makeSUT() + + let mutation = try reactor.mutate(action: .logoutButtonTapped) + .toBlocking() + .first()! + + #expect(mutation == .toNavigate(.logoutAlert)) + } + + @Test("withdrawButton 클릭 하면 withdrawAlert") + func withdrawButtonTapped_routesAlert() throws { + let reactor = makeSUT() + + let mutation = try reactor.mutate(action: .withdrawButtonTapped) + .toBlocking() + .first()! + + #expect(mutation == .toNavigate(.withdrawAlert)) + } +} + +private extension SetProfileReactorTests { + func makeSUT() -> SetProfileReactor { + let authRepo = MockAuthAPIRepository() + let tokenRepo = MockTokenRepository() + return SetProfileReactor( + checkNickNameUseCase: CheckNickNameUseCaseImpl(), + logoutUseCase: LogoutUseCaseImpl( + repository: tokenRepo + ), + withdrawUseCase: WithdrawUseCaseImpl(authRepository: authRepo, tokenRepository: tokenRepo), + fetchProfileUseCase: FetchProfileUseCaseImpl( + repository: MockMyPageRepository() + ), + myPageRepository: MockMyPageRepository() + ) + } +} diff --git a/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Repository/AlarmRepositoryTests.swift b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Repository/AlarmRepositoryTests.swift new file mode 100644 index 00000000..71c7e911 --- /dev/null +++ b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Repository/AlarmRepositoryTests.swift @@ -0,0 +1,206 @@ +@testable import MLSMyPageFeature + +import Testing + +import MLSCore +import MLSMyPageFeatureInterface +import MLSMyPageFeatureTesting + +import RxBlocking +import RxSwift + +@Suite("AlarmAPIRepositoryImplTests") +struct AlarmAPIRepositoryImplTests { + private let normalDTO = AlarmResponseDTO( + contents: [ + .normal( + .init( + id: 1, + type: "notice", + title: "공지사항", + link: "https://example.com/1", + date: "2026-04-01" + ) + ), + .normal( + .init( + id: 2, + type: "patch", + title: "패치노트", + link: "https://example.com/2", + date: "2026-04-02" + ) + ) + ], + hasMore: true + ) + + private let allDTO = AlarmResponseDTO( + contents: [ + .all( + .init( + alrim: .init( + id: 1, + type: "notice", + title: "공지사항", + link: "https://example.com/1", + date: "2026-04-01" + ), + alreadyRead: true + ) + ), + .all( + .init( + alrim: .init( + id: 2, + type: "event", + title: "이벤트", + link: "https://example.com/2", + date: "2026-04-02" + ), + alreadyRead: false + ) + ) + ], + hasMore: false + ) + + @Test("ongoingEvents 가져오기위해 provider 호출 및 domain 반환") + func fetchOngoingEvents_returnsPagedEntity() throws { + let provider = MockNetworkProvider() + provider.responseResult = normalDTO + + let sut = makeSUT(provider: provider) + + let result = + try sut + .fetchOngoingEvents(cursor: nil, pageSize: 20) + .toBlocking() + .first() + + #expect(provider.requestWithResponseCalled) + #expect(result?.items.count == 2) + #expect(result?.hasMore == true) + #expect(result?.items.first?.title == "공지사항") + } + + @Test("outdatedEvents 가져오기위해 provider 호출") + func fetchOutdatedEvents_returnsPagedEntity() throws { + let provider = MockNetworkProvider() + provider.responseResult = normalDTO + + let sut = makeSUT(provider: provider) + + let result = + try sut + .fetchOutdatedEvents(cursor: 10, pageSize: 20) + .toBlocking() + .first() + + #expect(provider.requestWithResponseCalled) + #expect(result?.items.count == 2) + } + + @Test("notices 가져오기위해 provider 호출") + func fetchNotices_returnsPagedEntity() throws { + let provider = MockNetworkProvider() + provider.responseResult = normalDTO + + let sut = makeSUT(provider: provider) + + let result = + try sut + .fetchNotices(cursor: nil, pageSize: 20) + .toBlocking() + .first() + + #expect(provider.requestWithResponseCalled) + #expect(result?.items.first?.type == "notice") + } + + @Test("patchNotes 가져오기위해 provider 호출") + func fetchPatchNotes_returnsPagedEntity() throws { + let provider = MockNetworkProvider() + provider.responseResult = normalDTO + + let sut = makeSUT(provider: provider) + + let result = + try sut + .fetchPatchNotes(cursor: nil, pageSize: 20) + .toBlocking() + .first() + + #expect(provider.requestWithResponseCalled) + #expect(result?.items.last?.type == "patch") + } + + // MARK: - fetchAll + + @Test("모든 Alarm 가져오면 domain 반환") + func fetchAll_returnsPagedAllAlarmEntity() throws { + let provider = MockNetworkProvider() + provider.responseResult = allDTO + + let sut = makeSUT(provider: provider) + + let result = + try sut + .fetchAll(cursor: nil, pageSize: 20) + .toBlocking() + .first() + + #expect(provider.requestWithResponseCalled) + #expect(result?.items.count == 2) + #expect(result?.hasMore == false) + #expect(result?.items.first?.alreadyRead == true) + } + + // MARK: - setRead + + @Test("알람을 읽으면 setRead 호출 후 complete") + func setRead_returnsComplete() throws { + let provider = MockNetworkProvider() + provider.completableResult = .empty() + + let sut = makeSUT(provider: provider) + + _ = + try sut + .setRead(alarmLink: "https://example.com/1") + .toBlocking() + .first() + + #expect(provider.requestCompletableCalled) + } + + @Test("setRead에 실패하면 error 방출") + func setRead_throwsError() { + let provider = MockNetworkProvider() + provider.completableResult = .error(NetworkError.httpError) + + let sut = makeSUT(provider: provider) + + #expect(throws: Error.self) { + _ = + try sut + .setRead(alarmLink: "https://example.com/1") + .toBlocking() + .first() + } + + #expect(provider.requestCompletableCalled) + } +} + +private extension AlarmAPIRepositoryImplTests { + func makeSUT( + provider: MockNetworkProvider = MockNetworkProvider(), + interceptor: MockInterceptor = MockInterceptor() + ) -> AlarmRepositoryImpl { + AlarmRepositoryImpl( + provider: provider, + interceptor: interceptor + ) + } +} diff --git a/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Repository/MyPageRepositoryTests.swift b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Repository/MyPageRepositoryTests.swift new file mode 100644 index 00000000..593e8180 --- /dev/null +++ b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/Repository/MyPageRepositoryTests.swift @@ -0,0 +1,155 @@ +@testable import MLSMyPageFeature + +import Testing + +import MLSAuthFeatureInterface +import MLSCore +import MLSMyPageFeatureInterface +import MLSMyPageFeatureTesting + +import RxBlocking +import RxSwift + +@Suite("MyPageRepositoryImplTests") +struct MyPageRepositoryImplTests { + private let profileDTO = MemberDTO( + id: "1", + provider: "APPLE", + nickname: "테스터", + fcmToken: nil, + marketingAgreement: true, + noticeAgreement: true, + patchNoteAgreement: false, + eventAgreement: true, + jobId: 1, + level: 200, + profileImageUrl: "https://example.com/profile.png" + ) + + private let updatedProfileDTO = MemberDTO( + id: "2", + provider: "KAKAO", + nickname: "수정닉네임", + fcmToken: nil, + marketingAgreement: false, + noticeAgreement: false, + patchNoteAgreement: true, + eventAgreement: false, + jobId: 2, + level: 210, + profileImageUrl: "https://example.com/new.png" + ) + + private let jobDTO = JobsDTO( + jobId: 3, + jobName: "궁수", + jobLevel: 10, + parentJobId: nil, + ) + + @Test("profile 가져오기위해 provider 호출 및 domain 반환") + func fetchProfile_returnsProfile() throws { + let provider = MockNetworkProvider() + provider.responseResult = profileDTO + + let sut = makeSUT(provider: provider) + + let result = try sut + .fetchProfile() + .toBlocking() + .first()! + + #expect(provider.requestWithResponseCalled) + #expect(result?.nickname == "테스터") + #expect(result?.jobId == 1) + #expect(result?.level == 200) + #expect(result?.profileUrl == "https://example.com/profile.png") + #expect(result?.platform == .apple) + } + + // MARK: - fetchJob + + @Test("jobs 가져오기위해 interceptor 없이 호출 및 Job 반환") + func fetchJob_returnsJob() throws { + let provider = MockNetworkProvider() + provider.responseResult = jobDTO + + let sut = makeSUT(provider: provider) + + let result = try sut + .fetchJob(jobId: "3") + .toBlocking() + .first() + + #expect(provider.requestWithResponseCalled) + #expect(result?.id == 3) + #expect(result?.name == "궁수") + } + + // MARK: - updateNickName + + @Test("nickName 업데이트하면 수정된 profile 반환") + func updateNickName_returnsUpdatedProfile() throws { + let provider = MockNetworkProvider() + provider.responseResult = updatedProfileDTO + + let sut = makeSUT(provider: provider) + + let result = try sut + .updateNickName(nickName: "수정닉네임") + .toBlocking() + .first() + + #expect(provider.requestWithResponseCalled) + #expect(result?.nickname == "수정닉네임") + #expect(result?.jobId == 2) + #expect(result?.level == 210) + #expect(result?.platform == .kakao) + } + + // MARK: - updateProfileImage + + @Test("profileImage 업데이트 성공하면 complete") + func updateProfileImage_returnsComplete() throws { + let provider = MockNetworkProvider() + provider.completableResult = .empty() + + let sut = makeSUT(provider: provider) + + _ = try sut + .updateProfileImage(url: "https://example.com/image.png") + .toBlocking() + .first() + + #expect(provider.requestCompletableCalled) + } + + @Test("profileImage 업데이트 실패하면 error 방출") + func updateProfileImage_throwsError() { + let provider = MockNetworkProvider() + provider.completableResult = .error(NetworkError.httpError) + + let sut = makeSUT(provider: provider) + + #expect(throws: Error.self) { + _ = try sut + .updateProfileImage(url: "https://example.com/image.png") + .toBlocking() + .first() + } + + #expect(provider.requestCompletableCalled) + } +} + +private extension MyPageRepositoryImplTests { + func makeSUT( + provider: MockNetworkProvider = MockNetworkProvider(), + interceptor: MockInterceptor = MockInterceptor() + ) -> MyPageRepositoryImpl { + MyPageRepositoryImpl( + provider: provider, + interceptor: interceptor + ) + } +} diff --git a/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/UseCase/AuthUseCaseTests.swift b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/UseCase/AuthUseCaseTests.swift new file mode 100644 index 00000000..3d5e4140 --- /dev/null +++ b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/UseCase/AuthUseCaseTests.swift @@ -0,0 +1,171 @@ +@testable import MLSMyPageFeature + +import Testing + +import MLSAuthFeatureInterface +import MLSAuthFeatureTesting +import MLSMyPageFeatureInterface +import MLSMyPageFeatureTesting + +import RxBlocking +import RxSwift + +@Suite("FetchProfileUseCaseTests") +struct FetchProfileUseCaseTests { + @Test("job + jobName 모두 가져오기 성공") + func profileWithJobId_returnsTrue() throws { + let repo = MockMyPageRepository() + + repo.fetchProfileResult = .just( + MyPageResponse.mock() + ) + + repo.fetchJobResult = .just(Job(name: "iOS 개발자", id: 1)) + + let sut = FetchProfileUseCaseImpl(repository: repo) + + let result = try sut.execute().toBlocking().first() + + #expect(repo.fetchJobCalled == true) + #expect(repo.receivedJobId == "1") + #expect(result??.jobName == "iOS 개발자") + } + + @Test("nil인 profile 가져오면 nil 반환") + func nilProfile_returnsNil() throws { + let repo = MockMyPageRepository() + repo.fetchProfileResult = .just(nil) + + let sut = FetchProfileUseCaseImpl(repository: repo) + + let result = try sut.execute().toBlocking().first() + + #expect(result == .some(nil)) + #expect(repo.fetchJobCalled == false) + } + + @Test("잘못된 jobId이면 jobName이 빈문자열") + func noJobId_returnsFalse() throws { + let repo = MockMyPageRepository() + + repo.fetchProfileResult = .just( + MyPageResponse.mock() + ) + + let sut = FetchProfileUseCaseImpl(repository: repo) + + let result = try sut.execute().toBlocking().first() + + #expect(result??.jobName == "") + #expect(repo.fetchJobCalled == true) + } + + @Test("profile 가져오기 실패하면 error 전파") + func profileError_returnsError() { + let repo = MockMyPageRepository() + + enum DummyError: Error { case test } + + repo.fetchProfileResult = Observable.error(DummyError.test) + + let sut = FetchProfileUseCaseImpl(repository: repo) + + #expect(throws: DummyError.self) { + _ = try sut.execute().toBlocking().first() + } + } +} + +@Suite("LogoutUseCaseTests") +struct LogoutUseCaseTests { + @Test("로그아웃 성공 시 모든 토큰 삭제") + func logout_deletesAllTokens_returnsComplete() throws { + let repo = MockTokenRepository() + + _ = repo.saveToken(type: .accessToken, value: "mock_access") + _ = repo.saveToken(type: .refreshToken, value: "mock_refresh") + _ = repo.saveToken(type: .fcmToken, value: "mock_fcm") + + let sut = LogoutUseCaseImpl(repository: repo) + + _ = try sut.execute().toBlocking().first() + + switch repo.fetchToken(type: .accessToken) { + case .failure: + #expect(true) + case .success: + #expect(Bool(false), "Expected failure") + } + + switch repo.fetchToken(type: .refreshToken) { + case .failure: + #expect(true) + case .success: + #expect(Bool(false), "Expected failure") + } + + switch repo.fetchToken(type: .fcmToken) { + case .failure: + #expect(true) + case .success: + #expect(Bool(false), "Expected failure") + } + } + + @Test("delete 실패 시 에러 전파") + func deleteFailure_returnsError() { + let repo = FailingMockTokenRepository() + + let sut = LogoutUseCaseImpl(repository: repo) + + #expect(throws: Error.self) { + _ = try sut.execute().toBlocking().first() + } + } +} + +@Suite("WithdrawUseCaseTests") +struct WithdrawUseCaseTests { + @Test("회원탈퇴 성공") + func withdraw_success() throws { + let authRepo = MockAuthAPIRepository() + let tokenRepo = MockTokenRepository() + + let sut = WithdrawUseCaseImpl( + authRepository: authRepo, + tokenRepository: tokenRepo + ) + + _ = try sut.execute().toBlocking().first() + } + + @Test("성공 시 토큰 삭제") + func withdraw_deleteTokens() throws { + let authRepo = MockAuthAPIRepository() + let tokenRepo = MockTokenRepository() + + _ = tokenRepo.saveToken(type: .accessToken, value: "access") + _ = tokenRepo.saveToken(type: .refreshToken, value: "refresh") + + let sut = WithdrawUseCaseImpl( + authRepository: authRepo, + tokenRepository: tokenRepo + ) + + _ = try sut.execute().toBlocking().first() + + switch tokenRepo.fetchToken(type: .accessToken) { + case .failure: + #expect(true) + case .success: + #expect(Bool(false), "Expected failure") + } + + switch tokenRepo.fetchToken(type: .refreshToken) { + case .failure: + #expect(true) + case .success: + #expect(Bool(false), "Expected failure") + } + } +} diff --git a/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/UseCase/CheckUseCaseTests.swift b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/UseCase/CheckUseCaseTests.swift new file mode 100644 index 00000000..275933bd --- /dev/null +++ b/MLS/MLSMyPageFeature/Tests/MLSMyPageFeatureTests/UseCase/CheckUseCaseTests.swift @@ -0,0 +1,75 @@ +@testable import MLSMyPageFeature + +import Testing + +import RxBlocking + +@Suite("CheckNickNameUseCase") +struct CheckNickNameUseCaseTests { + private let sut = CheckNickNameUseCaseImpl() + + @Test("nickName이 빈 문자열이면 false") + func emptyName_returnsFalse() throws { + let result = try sut.execute(nickName: "").toBlocking().first() + #expect(result == false) + } + + @Test("nickName이 1글자면 false") + func outOfRangeName_returnsFalse() throws { + let result = try sut.execute(nickName: "메").toBlocking().first() + #expect(result == false) + } + + @Test("nickName이 2~8글자면 true") + func validLengthName_returnsTrue() throws { + let result = try sut.execute(nickName: "메이플").toBlocking().first() + #expect(result == true) + } + + @Test("nickName에 특수문자가 포함이면 false") + func validName_returnsFalse() throws { + let result = try sut.execute(nickName: "*메이플").toBlocking().first() + #expect(result == false) + } + + @Test("nickName에 공백이 포함이면 false") + func blankName_returnsFalse() throws { + let result = try sut.execute(nickName: "메이 플").toBlocking().first() + #expect(result == false) + } +} + +@Suite("CheckValidLevelUseCaseTests") +struct CheckValidLevelUseCaseTests { + private let sut = CheckValidLevelUseCaseImpl() + + @Test("level이 1이면 true") + func minimumLevel_returnsTrue() { + let result = sut.execute(level: 1) + #expect(result == true) + } + + @Test("level이 200이면 true") + func maximumLevel_returnsTrue() { + let result = sut.execute(level: 200) + #expect(result == true) + } + + @Test("level이 0이면 false") + func outOfRangeLowerLevel_returnsFalse() { + let result = sut.execute(level: 0) + #expect(result == false) + } + + @Test("level이 201이면 false") + func outOfRangeUpperLevel_returnsFalse() { + let result = sut.execute(level: 201) + #expect(result == false) + } + + @Test("level이 nil이면 nil") + func nilLevel_returnsFalse() { + let result = sut.execute(level: nil) + #expect(result == nil) + } +} diff --git a/MLS/MLSMyPageFeatureExample/AppDelegate.swift b/MLS/MLSMyPageFeatureExample/AppDelegate.swift new file mode 100644 index 00000000..133694e5 --- /dev/null +++ b/MLS/MLSMyPageFeatureExample/AppDelegate.swift @@ -0,0 +1,44 @@ +// swiftlint:disable line_length + +import UIKit + +import MLSCore +import MLSDesignSystem + +@main +class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + ImageLoader.shared.configure.diskCacheCountLimit = 10 + FontManager.registerFonts() + + let center = UNUserNotificationCenter.current() + center.delegate = self + + center.getNotificationSettings { settings in + switch settings.authorizationStatus { + case .notDetermined: + center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in + if let error = error { + print("알림 권한 요청 실패: \(error)") + } else { + print("알림 권한 허용 여부: \(granted)") + } + } + case .denied: + print("사용자가 알림 권한 거부") + case .authorized, .provisional, .ephemeral: + print("알림 권한 이미 허용됨") + @unknown default: + break + } + } + + return true + } + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {} +} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/Assets.xcassets/AccentColor.colorset/Contents.json b/MLS/MLSMyPageFeatureExample/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from MLS/Presentation/DesignSystem/DesignSystemDemo/Assets.xcassets/AccentColor.colorset/Contents.json rename to MLS/MLSMyPageFeatureExample/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/MLS/Presentation/AuthFeature/AuthFeatureDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/MLS/MLSMyPageFeatureExample/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 95% rename from MLS/Presentation/AuthFeature/AuthFeatureDemo/Assets.xcassets/AppIcon.appiconset/Contents.json rename to MLS/MLSMyPageFeatureExample/Assets.xcassets/AppIcon.appiconset/Contents.json index c68da6c9..23058801 100644 --- a/MLS/Presentation/AuthFeature/AuthFeatureDemo/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/MLS/MLSMyPageFeatureExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,7 +1,6 @@ { "images" : [ { - "filename" : "icon.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/Contents.json b/MLS/MLSMyPageFeatureExample/Assets.xcassets/Contents.json similarity index 100% rename from MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/Contents.json rename to MLS/MLSMyPageFeatureExample/Assets.xcassets/Contents.json diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Base.lproj/LaunchScreen.storyboard b/MLS/MLSMyPageFeatureExample/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Base.lproj/LaunchScreen.storyboard rename to MLS/MLSMyPageFeatureExample/Base.lproj/LaunchScreen.storyboard diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Info.plist b/MLS/MLSMyPageFeatureExample/Info.plist similarity index 92% rename from MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Info.plist rename to MLS/MLSMyPageFeatureExample/Info.plist index 7c5f0be1..0eb786dc 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Info.plist +++ b/MLS/MLSMyPageFeatureExample/Info.plist @@ -2,8 +2,6 @@ - ITSAppUsesNonExemptEncryption - UIApplicationSceneManifest UIApplicationSupportsMultipleScenes diff --git a/MLS/MLSMyPageFeatureExample/SceneDelegate.swift b/MLS/MLSMyPageFeatureExample/SceneDelegate.swift new file mode 100644 index 00000000..dd1db16b --- /dev/null +++ b/MLS/MLSMyPageFeatureExample/SceneDelegate.swift @@ -0,0 +1,71 @@ +import UIKit + +import MLSAuthFeatureTesting +import MLSMyPageFeature +import MLSMyPageFeatureInterface +import MLSMyPageFeatureTesting + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + guard let windowScene = (scene as? UIWindowScene) else { return } + + let window = UIWindow(windowScene: windowScene) + self.window = window + + let rootVC = makeMyPageViewController() + let nav = UINavigationController(rootViewController: rootVC) + nav.navigationBar.isHidden = true + window.rootViewController = nav + window.makeKeyAndVisible() + } + + func makeMyPageViewController() -> UIViewController { + let myPageRepository = MockMyPageRepository() + let authRepository = MockAuthAPIRepository() + let tokenRepository = MockTokenRepository() + let alarmRepository = MockAlarmRepository() + let notificationRepository = NotificationPermissionRepositoryImpl() + + let fetchProfileUseCase = FetchProfileUseCaseImpl(repository: myPageRepository) + + let setProfileFactory = SetProfileFactoryImpl( + selectImageFactory: SelectImageFactoryImpl(myPageRepository: myPageRepository), + checkNickNameUseCase: CheckNickNameUseCaseImpl(), + logoutUseCase: LogoutUseCaseImpl(repository: tokenRepository), + withdrawUseCase: WithdrawUseCaseImpl(authRepository: authRepository, tokenRepository: tokenRepository), + fetchProfileUseCase: fetchProfileUseCase, + myPageRepository: myPageRepository + ) + + let customerSupportFactory = CustomerSupportBaseViewFactoryImpl( + policyFactory: PolicyFactoryImpl(), + alarmRepository: alarmRepository + ) + + let notificationSettingFactory = NotificationSettingFactoryImpl( + notificationRepository: notificationRepository, + authRepository: authRepository + ) + + let setCharacterFactory = SetCharacterFactoryImpl( + checkEmptyUseCase: CheckEmptyLevelAndRoleUseCaseImpl(), + checkValidLevelUseCase: CheckValidLevelUseCaseImpl(), + authRepository: authRepository + ) + + let loginFactory = MockLoginFactory() + + let myPageFactory = MyPageMainFactoryImpl( + loginFactory: loginFactory, + setProfileFactory: setProfileFactory, + customerSupportFactory: customerSupportFactory, + notificationSettingFactory: notificationSettingFactory, + setCharacterFactory: setCharacterFactory, + fetchProfileUseCase: fetchProfileUseCase + ) + + return myPageFactory.make() + } +} diff --git a/MLS/MLSMyPageFeatureExample/ViewController.swift b/MLS/MLSMyPageFeatureExample/ViewController.swift new file mode 100644 index 00000000..edd52383 --- /dev/null +++ b/MLS/MLSMyPageFeatureExample/ViewController.swift @@ -0,0 +1,4 @@ +import UIKit + +// SceneDelegate에서 직접 MyPageMainViewController를 띄우기 때문에 사용하지 않습니다. +class ViewController: UIViewController {} diff --git a/MLS/MLSRecommendationFeature/.gitignore b/MLS/MLSRecommendationFeature/.gitignore new file mode 100644 index 00000000..0023a534 --- /dev/null +++ b/MLS/MLSRecommendationFeature/.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/MLS/MLSRecommendationFeature/Package.swift b/MLS/MLSRecommendationFeature/Package.swift new file mode 100644 index 00000000..2b894f96 --- /dev/null +++ b/MLS/MLSRecommendationFeature/Package.swift @@ -0,0 +1,84 @@ +// swift-tools-version: 6.2 +import PackageDescription + +let package = Package( + name: "MLSRecommendationFeature", + platforms: [.iOS(.v15)], + products: [ + // Interface: Presentation 팩토리 프로토콜 + .library( + name: "MLSRecommendationFeatureInterface", + targets: ["MLSRecommendationFeatureInterface"] + ), + // Feature: Presentation + Domain + Data 구현체 + .library( + name: "MLSRecommendationFeature", + targets: ["MLSRecommendationFeature"] + ), + // Testing: 단위 테스트나 Example 앱에서 사용될 Mock 데이터를 제공하는 모듈 + .library( + name: "MLSRecommendationFeatureTesting", + targets: ["MLSRecommendationFeatureTesting"] + ) + ], + dependencies: [ + .package(path: "../MLSCore"), + .package(path: "../MLSDesignSystem"), + .package(path: "../MLSBookmarkFeature"), + .package(path: "../MLSDictionaryFeature"), + .package(url: "https://github.com/ReactorKit/ReactorKit.git", from: "3.2.0"), + .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "6.7.0"), + .package(url: "https://github.com/RxSwiftCommunity/RxKeyboard.git", from: "2.0.0"), + .package(url: "https://github.com/SnapKit/SnapKit.git", from: "5.7.1") + ], + targets: [ + // Interface 모듈 (Presentation 팩토리 프로토콜) + .target( + name: "MLSRecommendationFeatureInterface", + dependencies: [ + .product(name: "MLSCore", package: "MLSCore"), + .product(name: "MLSDesignSystem", package: "MLSDesignSystem"), + .product(name: "RxSwift", package: "RxSwift") + ], + swiftSettings: [.swiftLanguageMode(.v5)] + ), + // Feature 모듈 (Presentation + Domain + Data 구현체) + .target( + name: "MLSRecommendationFeature", + dependencies: [ + "MLSRecommendationFeatureInterface", + .product(name: "MLSCore", package: "MLSCore"), + .product(name: "MLSDesignSystem", package: "MLSDesignSystem"), + .product(name: "MLSBookmarkFeatureInterface", package: "MLSBookmarkFeature"), + .product(name: "MLSDictionaryFeatureInterface", package: "MLSDictionaryFeature"), + .product(name: "ReactorKit", package: "ReactorKit"), + .product(name: "RxSwift", package: "RxSwift"), + .product(name: "RxCocoa", package: "RxSwift"), + .product(name: "RxRelay", package: "RxSwift"), + .product(name: "RxKeyboard", package: "RxKeyboard"), + .product(name: "SnapKit", package: "SnapKit") + ], + swiftSettings: [.swiftLanguageMode(.v5)] + ), + // Testing 모듈 (Mock 객체) + .target( + name: "MLSRecommendationFeatureTesting", + dependencies: [ + "MLSRecommendationFeatureInterface", + .product(name: "RxSwift", package: "RxSwift") + ], + swiftSettings: [.swiftLanguageMode(.v5)] + ), + // Tests 모듈 + .testTarget( + name: "MLSRecommendationFeatureTests", + dependencies: [ + "MLSRecommendationFeature", + "MLSRecommendationFeatureInterface", + "MLSRecommendationFeatureTesting", + .product(name: "RxBlocking", package: "RxSwift") + ], + swiftSettings: [.swiftLanguageMode(.v5)] + ) + ] +) diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/DTOs/JobDTO.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/DTOs/JobDTO.swift new file mode 100644 index 00000000..5fb8b4cf --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/DTOs/JobDTO.swift @@ -0,0 +1,6 @@ +struct JobDTO: Decodable { + let jobId: Int + let jobName: String + let jobLevel: Int + let parentJobId: Int? +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/DTOs/RecommendationMapDTO.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/DTOs/RecommendationMapDTO.swift new file mode 100644 index 00000000..543e92ed --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/DTOs/RecommendationMapDTO.swift @@ -0,0 +1,27 @@ +import MLSRecommendationFeatureInterface + +struct RecommendationMapDTO: Decodable { + let mapId: Int + let score: Double + let iconUrl: String + let nameKr: String + let bookmarkId: Int? +} + +extension RecommendationMapDTO { + func toDomain() -> RecommendationMap { + RecommendationMap( + mapId: mapId, + score: score, + iconUrl: iconUrl, + nameKr: nameKr, + bookmarkId: bookmarkId + ) + } +} + +extension Array where Element == RecommendationMapDTO { + func toDomain() -> [RecommendationMap] { + map { $0.toDomain() } + } +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/DTOs/UserProfileDTO.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/DTOs/UserProfileDTO.swift new file mode 100644 index 00000000..1a280a3c --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/DTOs/UserProfileDTO.swift @@ -0,0 +1,26 @@ +import MLSRecommendationFeatureInterface + +struct UserProfileDTO: Decodable { + let id: String? + let provider: String? + let nickname: String? + let fcmToken: String? + let marketingAgreement: Bool? + let noticeAgreement: Bool? + let patchNoteAgreement: Bool? + let eventAgreement: Bool? + let jobId: Int? + let level: Int? + let profileImageUrl: String? +} + +extension UserProfileDTO { + func toDomain() -> UserProfile { + UserProfile( + nickname: nickname ?? "", + jobId: jobId, + level: level, + profileImageUrl: profileImageUrl ?? "" + ) + } +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/Endpoints/RecommendationEndPoint.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/Endpoints/RecommendationEndPoint.swift new file mode 100644 index 00000000..7de6070e --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/Endpoints/RecommendationEndPoint.swift @@ -0,0 +1,39 @@ +import MLSCore + +enum RecommendationEndPoint { + static let base = "https://mapleland.2megabytes.me" + + static func fetchRecommendations(level: Int, jobId: Int, limit: Int?) -> ResponsableEndPoint<[RecommendationMapDTO]> { + .init( + baseURL: base, + path: "/api/v1/maps/recommendations", + method: .GET, + query: FetchQuery(level: Int32(level), jobId: Int32(jobId), limit: limit.map { Int32($0) }) + ) + } + + static func fetchProfile() -> ResponsableEndPoint { + .init( + baseURL: base, + path: "/api/v1/auth/me", + method: .GET + ) + } + + static func fetchJob(jobId: Int) -> ResponsableEndPoint { + .init( + baseURL: base, + path: "/api/v1/jobs/\(jobId)", + method: .GET + ) + } + +} + +private extension RecommendationEndPoint { + struct FetchQuery: Encodable { + let level: Int32 + let jobId: Int32 + let limit: Int32? + } +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/Repositories/RecommendationRepositoryImpl.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/Repositories/RecommendationRepositoryImpl.swift new file mode 100644 index 00000000..2568b242 --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/Repositories/RecommendationRepositoryImpl.swift @@ -0,0 +1,32 @@ +import MLSCore +import MLSRecommendationFeatureInterface +import RxSwift + +public final class RecommendationRepositoryImpl: RecommendationRepository { + private let provider: NetworkProvider + private let interceptor: Interceptor? + + public init() { + self.provider = NetworkProviderImpl() + self.interceptor = TokenInterceptor() + } + + public func fetchProfile() -> Observable { + let endpoint = RecommendationEndPoint.fetchProfile() + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + .map { $0.toDomain() } + } + + public func fetchJobName(jobId: Int) -> Observable { + let endpoint = RecommendationEndPoint.fetchJob(jobId: jobId) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + .map { $0.jobName } + } + + public func fetchRecommendations(level: Int, jobId: Int, limit: Int?) -> Observable<[RecommendationMap]> { + let endpoint = RecommendationEndPoint.fetchRecommendations(level: level, jobId: jobId, limit: limit) + return provider.requestData(endPoint: endpoint, interceptor: interceptor) + .map { $0.toDomain() } + } + +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/Repositories/RecommendationUserDefaultsRepositoryImpl.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/Repositories/RecommendationUserDefaultsRepositoryImpl.swift new file mode 100644 index 00000000..95b70313 --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Data/Repositories/RecommendationUserDefaultsRepositoryImpl.swift @@ -0,0 +1,18 @@ +import Foundation + +import MLSRecommendationFeatureInterface + +import RxSwift + +public final class RecommendationUserDefaultsRepositoryImpl: RecommendationUserDefaultsRepository { + private let recommendationkey = "recommendationkey" + + public init() {} + + public func saveFirstLaunch() -> Completable { + if !UserDefaults.standard.bool(forKey: recommendationkey) { + UserDefaults.standard.set(true, forKey: recommendationkey) + } + return .empty() + } +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/RecommendationMainFactoryImpl.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/RecommendationMainFactoryImpl.swift new file mode 100644 index 00000000..0efaf1cf --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/RecommendationMainFactoryImpl.swift @@ -0,0 +1,51 @@ +import UIKit + +import MLSBookmarkFeatureInterface +import MLSCore +import MLSRecommendationFeatureInterface + +public struct RecommendationMainFactoryImpl: RecommendationMainFactory { + private let repository: RecommendationRepository + private let bookmarkRepository: BookmarkRepository + private let recommendationUserDefaultsRepository: RecommendationUserDefaultsRepository + private let makeLoginVC: (() -> UIViewController)? + private let makeCharacterSettingVC: (() -> UIViewController)? + private let makeSearchVC: (() -> UIViewController)? + private let makeNotificationVC: (() -> UIViewController)? + private let makeDetailVC: ((_ mapId: Int) -> UIViewController)? + private let makeBookmarkModalVC: ((_ bookmarkIds: [Int], _ onComplete: ((Bool) -> Void)?) -> UIViewController)? + + public init( + repository: RecommendationRepository, + bookmarkRepository: BookmarkRepository, + recommendationUserDefaultsRepository: RecommendationUserDefaultsRepository, + makeLoginVC: (() -> UIViewController)? = nil, + makeCharacterSettingVC: (() -> UIViewController)? = nil, + makeSearchVC: (() -> UIViewController)? = nil, + makeNotificationVC: (() -> UIViewController)? = nil, + makeDetailVC: ((_ mapId: Int) -> UIViewController)? = nil, + makeBookmarkModalVC: ((_ bookmarkIds: [Int], _ onComplete: ((Bool) -> Void)?) -> UIViewController)? = nil + ) { + self.repository = repository + self.bookmarkRepository = bookmarkRepository + self.recommendationUserDefaultsRepository = recommendationUserDefaultsRepository + self.makeLoginVC = makeLoginVC + self.makeCharacterSettingVC = makeCharacterSettingVC + self.makeSearchVC = makeSearchVC + self.makeNotificationVC = makeNotificationVC + self.makeDetailVC = makeDetailVC + self.makeBookmarkModalVC = makeBookmarkModalVC + } + + public func make() -> BaseViewController { + let vc = RecommendationMainViewController() + vc.reactor = RecommendationMainReactor(repository: repository, bookmarkRepository: bookmarkRepository, userDefaultsRepository: recommendationUserDefaultsRepository) + vc.onLoginTapped = makeLoginVC + vc.onEditTapped = makeCharacterSettingVC + vc.onSearchTapped = makeSearchVC + vc.onNotificationTapped = makeNotificationVC + vc.onDetailTapped = makeDetailVC + vc.onBookmarkModalTapped = makeBookmarkModalVC + return vc + } +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/RecommendationMainReactor.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/RecommendationMainReactor.swift new file mode 100644 index 00000000..624505d1 --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/RecommendationMainReactor.swift @@ -0,0 +1,249 @@ +import MLSBookmarkFeatureInterface +import MLSDictionaryFeatureInterface +import MLSRecommendationFeatureInterface + +import ReactorKit +import RxCocoa +import RxSwift + +final class RecommendationMainReactor: Reactor { + // MARK: - Reactor + enum Action { + case viewWillAppear + case informationButtonTapped + case toggleBookmark(mapId: Int) + case undoLastDeletedBookmark + } + + enum UIEvent { + case none + case added(RecommendationMap) + case deleted(RecommendationMap) + case showAlert + } + + enum Mutation { + case setProfile(UserProfile) + case setJobName(String) + case setRecommendations([RecommendationMap]) + case setLoading(Bool) + case informationButtonToggle + case setLogin(Bool) + case updateBookmarkId(mapId: Int, bookmarkId: Int?) + case setLastDeleted(RecommendationMap?) + case setUIEvent(UIEvent) + case setTogglingBookmark(mapId: Int) + case setHideCommentView(Bool) + } + + struct State { + var profile: UserProfile? + var jobName: String = "" + var recommendations: [RecommendationMap] = [] + var isLoading: Bool = false + var informationButtonIsOn: Bool = false + var isLogin: Bool = false + var lastDeleted: RecommendationMap? + var togglingBookmarkIds: Set = [] + @Pulse var uiEvent: UIEvent = .none + var shouldHideCommentView: Bool = false + } + + // MARK: - Properties + var initialState: State + var disposeBag = DisposeBag() + + private let repository: RecommendationRepository + private let bookmarkRepository: BookmarkRepository + private let userDefaultsRepository: RecommendationUserDefaultsRepository + + // MARK: - Init + init(repository: RecommendationRepository, bookmarkRepository: BookmarkRepository, userDefaultsRepository: RecommendationUserDefaultsRepository) { + self.repository = repository + self.bookmarkRepository = bookmarkRepository + self.userDefaultsRepository = userDefaultsRepository + self.initialState = State() + } + + // MARK: - Reactor Methods + func mutate(action: Action) -> Observable { + switch action { + case .viewWillAppear: + let saveFirstRecommendationLaunch = userDefaultsRepository + .saveFirstLaunch() + .andThen(Observable.empty()) + + let fetchAll = repository.fetchProfile() + .flatMap { [weak self] profile -> Observable in + guard let self else { return .empty() } + + let setLogin = Observable.just(Mutation.setLogin(true)) + let setProfile = Observable.just(Mutation.setProfile(profile)) + + // 프로필 미입력 상태 + guard + let level = profile.level, + let jobId = profile.jobId + else { + return Observable.concat([ + setLogin, + setProfile, + .just(.setUIEvent(.showAlert)) + ]) + } + + // 프로필 입력 완료 상태 + let setJobName = repository.fetchJobName(jobId: jobId) + .map(Mutation.setJobName) + .catch { error in + print("⚠️ [Recommendation] fetchJobName 실패: \(error)") + return .empty() + } + + let setRecommendations = repository.fetchRecommendations( + level: level, + jobId: jobId, + limit: 5 + ) + .map(Mutation.setRecommendations) + .catch { error in + print("⚠️ [Recommendation] fetchRecommendations 실패: \(error)") + return .empty() + } + + return Observable.concat([ + setLogin, + setProfile, + Observable.merge([ + setJobName, + setRecommendations + ]) + ]) + } + .catch { error in + print("⚠️ [Recommendation] fetchProfile 실패: \(error)") + return Observable.just(.setLogin(false)) + } + + return Observable.concat([ + .just(.setHideCommentView(true)), + .just(.setLoading(true)), + Observable.merge([ + fetchAll, + saveFirstRecommendationLaunch + ]), + .just(.setLoading(false)) + ]) + + case .informationButtonTapped: + return .just(.informationButtonToggle) + + case let .toggleBookmark(mapId): + return handleToggleBookmark(mapId: mapId) + + case .undoLastDeletedBookmark: + return handleUndoLastDeleted() + } + } + + func reduce(state: State, mutation: Mutation) -> State { + var newState = state + + switch mutation { + case let .setProfile(profile): + newState.profile = profile + case let .setJobName(jobName): + newState.jobName = jobName + case let .setRecommendations(maps): + newState.recommendations = maps + case let .setLoading(isLoading): + newState.isLoading = isLoading + case .informationButtonToggle: + newState.informationButtonIsOn.toggle() + case let .setLogin(isLogin): + newState.isLogin = isLogin + case let .updateBookmarkId(mapId, bookmarkId): + newState.togglingBookmarkIds.remove(mapId) + if let index = newState.recommendations.firstIndex(where: { $0.mapId == mapId }) { + let old = newState.recommendations[index] + newState.recommendations[index] = RecommendationMap( + mapId: old.mapId, + score: old.score, + iconUrl: old.iconUrl, + nameKr: old.nameKr, + bookmarkId: bookmarkId + ) + } + case let .setLastDeleted(map): + newState.lastDeleted = map + case let .setUIEvent(event): + newState.uiEvent = event + case let .setTogglingBookmark(mapId): + newState.togglingBookmarkIds.insert(mapId) + case let .setHideCommentView(hide): + newState.shouldHideCommentView = hide + } + return newState + } +} + +// MARK: - Methods +private extension RecommendationMainReactor { + func handleToggleBookmark(mapId: Int) -> Observable { + guard !currentState.togglingBookmarkIds.contains(mapId), + let map = currentState.recommendations.first(where: { $0.mapId == mapId }) + else { + return .empty() + } + + if let bookmarkId = map.bookmarkId { + return Observable.concat([ + .just(.setTogglingBookmark(mapId: mapId)), + bookmarkRepository.deleteBookmark(bookmarkId: bookmarkId) + .flatMap { _ -> Observable in + .from([ + .setLastDeleted(map), + .updateBookmarkId(mapId: mapId, bookmarkId: nil), + .setUIEvent(.deleted(map)) + ]) + } + .catch { _ in .just(.updateBookmarkId(mapId: mapId, bookmarkId: bookmarkId)) } + ]) + } else { + return Observable.concat([ + .just(.setTogglingBookmark(mapId: mapId)), + bookmarkRepository.setBookmark(resourceId: mapId, type: .map) + .flatMap { newBookmarkId -> Observable in + let updated = RecommendationMap( + mapId: map.mapId, score: map.score, + iconUrl: map.iconUrl, nameKr: map.nameKr, + bookmarkId: newBookmarkId + ) + return .from([ + .updateBookmarkId(mapId: mapId, bookmarkId: newBookmarkId), + .setUIEvent(.added(updated)) + ]) + } + .catch { _ in .just(.updateBookmarkId(mapId: mapId, bookmarkId: nil)) } + ]) + } + } + + func handleUndoLastDeleted() -> Observable { + guard let last = currentState.lastDeleted else { return .empty() } + return bookmarkRepository.setBookmark(resourceId: last.mapId, type: .map) + .flatMap { newBookmarkId -> Observable in + let restored = RecommendationMap( + mapId: last.mapId, score: last.score, + iconUrl: last.iconUrl, nameKr: last.nameKr, + bookmarkId: newBookmarkId + ) + return .from([ + .setLastDeleted(nil), + .updateBookmarkId(mapId: last.mapId, bookmarkId: newBookmarkId), + .setUIEvent(.added(restored)) + ]) + } + .catch { _ in .empty() } + } +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/RecommendationMainViewController.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/RecommendationMainViewController.swift new file mode 100644 index 00000000..421c6cc5 --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/RecommendationMainViewController.swift @@ -0,0 +1,304 @@ +import UIKit + +import MLSCore +import MLSDesignSystem +import MLSRecommendationFeatureInterface + +import ReactorKit +import RxCocoa +import RxSwift +import SnapKit + +final class RecommendationMainViewController: BaseViewController, View { + typealias Reactor = RecommendationMainReactor + + // MARK: - Properties + var disposeBag = DisposeBag() + var onLoginTapped: (() -> UIViewController?)? + var onEditTapped: (() -> UIViewController?)? + var onSearchTapped: (() -> UIViewController?)? + var onNotificationTapped: (() -> UIViewController?)? + var onDetailTapped: ((_ mapId: Int) -> UIViewController?)? + var onBookmarkModalTapped: ((_ bookmarkIds: [Int], _ onComplete: ((Bool) -> Void)?) -> UIViewController)? + + private var mainView = RecommendationMainView() +} + +// MARK: - Life Cycle +extension RecommendationMainViewController { + override func viewDidLoad() { + super.viewDidLoad() + + addViews() + setupConstraints() + configureUI() + } +} + +// MARK: - SetUp +private extension RecommendationMainViewController { + func addViews() { + view.addSubview(mainView) + } + + func setupConstraints() { + mainView.snp.makeConstraints { make in + make.edges.equalTo(view.safeAreaLayoutGuide) + } + } + + func configureUI() { + mainView.collectionView.delegate = self + mainView.collectionView.dataSource = self + mainView.collectionView.register(CardListCell.self, forCellWithReuseIdentifier: CardListCell.identifier) + } +} + +// MARK: - Bind +extension RecommendationMainViewController { + func bind(reactor: Reactor) { + bindUserActions(reactor: reactor) + bindViewState(reactor: reactor) + bindUIEvents(reactor: reactor) + } + + func bindUserActions(reactor: Reactor) { + mainView.informationButton.rx.tap + .map { Reactor.Action.informationButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.header.firstIconButton.rx.tap + .withUnretained(self) + .subscribe { owner, _ in + guard let vc = owner.onSearchTapped?() else { return } + owner.navigationController?.pushViewController(vc, animated: true) + } + .disposed(by: disposeBag) + + mainView.header.secondIconButton.rx.tap + .withUnretained(self) + .subscribe { owner, _ in + guard let vc = owner.onNotificationTapped?() else { return } + owner.navigationController?.pushViewController(vc, animated: true) + } + .disposed(by: disposeBag) + + rx.viewWillAppear + .map { Reactor.Action.viewWillAppear } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.emptyView.button.rx.tap + .withUnretained(self) + .subscribe { owner, _ in + guard let vc = owner.onLoginTapped?() else { return } + owner.navigationController?.pushViewController(vc, animated: true) + } + .disposed(by: disposeBag) + + mainView.profileView.editButton.rx.tap + .withUnretained(self) + .subscribe { owner, _ in + guard let vc = owner.onEditTapped?() else { return } + owner.navigationController?.pushViewController(vc, animated: true) + } + .disposed(by: disposeBag) + } + + func bindProfile(reactor: Reactor) { + let profileStream = reactor.state + .observe(on: MainScheduler.instance) + .compactMap { $0.profile } + + let jobNameStream = reactor.state + .observe(on: MainScheduler.instance) + .map { $0.jobName } + + Observable.combineLatest(profileStream, jobNameStream) + .withUnretained(self) + .subscribe { owner, pair in + let (profile, jobName) = pair + owner.mainView.profileView.configure( + imageURL: profile.profileImageUrl, + nickName: profile.nickname, + job: jobName, + level: profile.level ?? 0 + ) + } + .disposed(by: disposeBag) + } + + func bindUIEvents(reactor: Reactor) { + rx.viewDidAppear + .take(1) + .flatMapLatest { _ in reactor.pulse(\.$uiEvent) } + .observe(on: MainScheduler.instance) + .withUnretained(self) + .subscribe(onNext: { owner, event in + switch event { + case .added(let map): + owner.presentAddSnackBar(map: map) + case .deleted(let map): + owner.presentDeleteSnackBar(map: map) + case .none: + break + case .showAlert: + GuideAlertFactory.show(mainText: "레벨과 직업을 모두 입력하면\n맞춤 사냥터를 추천받을 수 있어요", ctaText: "입력하기", cancelText: "도감보러 가기", ctaAction: { + guard let vc = owner.onEditTapped?() else { return } + owner.navigationController?.pushViewController(vc, animated: true) + }, cancelAction: { + if let tabBarController = owner.tabBarController as? BottomTabBarController { + tabBarController.selectTab(index: 0) + } + }, ctaRatio: 0.5) + } + }) + .disposed(by: disposeBag) + } + + func bindViewState(reactor: Reactor) { + reactor.state + .observe(on: MainScheduler.instance) + .map { $0.informationButtonIsOn } + .distinctUntilChanged() + .withUnretained(self) + .subscribe { owner, toolTipIsOn in + if toolTipIsOn { + TooltipFactory.show( + text: "같은 레벨·직업 유저들이 자주 언급한 \n사냥터를 기반으로 추천해요.", + anchorView: owner.mainView.informationButton, + tooltipPosition: .topTrailing + ) + } else { + TooltipFactory.dismiss() + } + } + .disposed(by: disposeBag) + + reactor.state + .observe(on: MainScheduler.instance) + .map { $0.recommendations } + .distinctUntilChanged { $0.map { ($0.mapId, $0.bookmarkId) }.elementsEqual($1.map { ($0.mapId, $0.bookmarkId) }) { $0 == $1 } } + .withUnretained(self) + .subscribe { owner, mapData in + owner.mainView.collectionView.reloadData() + owner.mainView.checkEmptyData(isEmpty: mapData.isEmpty) + } + .disposed(by: disposeBag) + + reactor.state + .observe(on: MainScheduler.instance) + .map { $0.isLogin } + .distinctUntilChanged() + .withUnretained(self) + .subscribe { owner, isLogin in + owner.mainView.updateLoginState(isLogin: isLogin) + } + .disposed(by: disposeBag) + + bindProfile(reactor: reactor) + + reactor.state + .observe(on: MainScheduler.instance) + .map { $0.shouldHideCommentView } + .distinctUntilChanged() + .filter { $0 } + .withUnretained(self) + .subscribe { owner, _ in + (owner.tabBarController as? BottomTabBarController)?.checkRecommendationFirstLaunch(isFirst: true) + } + .disposed(by: disposeBag) + } +} + +// MARK: - SnackBar +private extension RecommendationMainViewController { + func presentAddSnackBar(map: RecommendationMap) { + ImageLoader.shared.loadImage(stringURL: map.iconUrl) { [weak self] image in + guard let self, let image else { return } + SnackBarFactory.createSnackBar( + type: .normal, + image: image, + imageBackgroundColor: .clear, + text: "사냥터를 북마크에 추가했어요.", + buttonText: "컬렉션 추가", + buttonAction: { [weak self] in + guard let self, + let bookmarkId = self.reactor?.currentState.recommendations + .first(where: { $0.mapId == map.mapId })?.bookmarkId else { return } + let vc = self.onBookmarkModalTapped?([bookmarkId]) { isAdd in + if isAdd { + ToastFactory.createToast(message: "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요.") + } + } + guard let vc else { return } + vc.modalPresentationStyle = .pageSheet + if let sheet = vc.sheetPresentationController { + sheet.detents = [.medium(), .large()] + sheet.prefersGrabberVisible = true + sheet.preferredCornerRadius = 16 + } + self.present(vc, animated: true) + } + ) + } + } + + func presentDeleteSnackBar(map: RecommendationMap) { + ImageLoader.shared.loadImage(stringURL: map.iconUrl) { [weak self] image in + guard let self, let image else { return } + SnackBarFactory.createSnackBar( + type: .delete, + image: image, + imageBackgroundColor: .clear, + text: "사냥터를 북마크에서 삭제했어요.", + buttonText: "되돌리기", + buttonAction: { [weak self] in + self?.reactor?.action.onNext(.undoLastDeletedBookmark) + } + ) + } + } +} + +// MARK: - UICollectionViewDelegate, UICollectionViewDataSource +extension RecommendationMainViewController: UICollectionViewDelegate, UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + guard let recommendations = reactor?.currentState.recommendations, + indexPath.item < recommendations.count else { return } + let map = recommendations[indexPath.item] + guard let vc = onDetailTapped?(map.mapId) else { return } + navigationController?.pushViewController(vc, animated: true) + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return reactor?.currentState.recommendations.count ?? 0 + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CardListCell.identifier, for: indexPath) as? CardListCell, + let map = reactor?.currentState.recommendations[indexPath.item] + else { + return UICollectionViewCell() + } + + cell.cardView.setMainText(text: map.nameKr) + cell.cardView.setType(type: .recommended(rank: indexPath.row + 1)) + cell.cardView.isIconSelected = map.isBookmarked + cell.cardView.onIconTapped = { [weak self] in + self?.reactor?.action.onNext(.toggleBookmark(mapId: map.mapId)) + } + + ImageLoader.shared.loadImage(stringURL: map.iconUrl) { [weak self] image in + guard let self, let image else { return } + DispatchQueue.main.async { + guard let currentCell = self.mainView.collectionView.cellForItem(at: indexPath) as? CardListCell else { return } + currentCell.cardView.setImage(image: image, backgroundColor: .clear) + } + } + + return cell + } +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/Views/CardListCell.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/Views/CardListCell.swift new file mode 100644 index 00000000..029eb6c1 --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/Views/CardListCell.swift @@ -0,0 +1,41 @@ +import UIKit + +import MLSDesignSystem + +import SnapKit + +final class CardListCell: UICollectionViewCell { + + // MARK: - Properties + let cardView = CardList() + + // MARK: - init + override init(frame: CGRect) { + super.init(frame: frame) + + addViews() + setupConstraints() + } + + required init?(coder: NSCoder) { + fatalError("\(#file), \(#function) Error") + } + + override func prepareForReuse() { + super.prepareForReuse() + cardView.onIconTapped = nil + } +} + +// MARK: - SetUp +private extension CardListCell { + func addViews() { + contentView.addSubview(cardView) + } + + func setupConstraints() { + cardView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/Views/RecommendationMainView.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/Views/RecommendationMainView.swift new file mode 100644 index 00000000..34ea39c9 --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/Views/RecommendationMainView.swift @@ -0,0 +1,166 @@ +import UIKit + +import MLSCore +import MLSDesignSystem + +import SnapKit + +internal final class RecommendationMainView: UIView { + // MARK: - Type + private enum Constant { + static let profileTopOffset: CGFloat = 21 + static let profileHorizontalInset: CGFloat = 40 + static let grayViewTopOffset: CGFloat = 30 + static let informationButtonTopInset: CGFloat = 14 + static let informationButtonTrailingInset: CGFloat = 16 + static let informationIconSize: CGFloat = 24 + static let informationLabelIconSpacing: CGFloat = 3 + static let collectionViewTopOffset: CGFloat = 14 + static let collectionViewHorizontalInset: CGFloat = 16 + static let cellHeight: CGFloat = 104 + static let cellSpacing: CGFloat = 8 + static let bottomTabHeight: CGFloat = 64 + static let emptyLabelTopOffset: CGFloat = 16 + } + + // MARK: - Properties + internal let header = Header(style: .main, title: "추천") + internal let profileView = RecommendationProfileView() + + internal let grayBackgroundView: UIView = { + let view = UIView() + view.backgroundColor = .neutral100 + return view + }() + + internal let collectionView: UICollectionView = { + let layout = CompositionalLayoutBuilder() + .section { + $0.item(width: .fractionalWidth(1), height: .fractionalHeight(1)) + .group(.vertical, width: .fractionalWidth(1), height: .absolute(Constant.cellHeight)) + .buildSection() + .interGroupSpacing(Constant.cellSpacing) + } + .build() + let view = UICollectionView(frame: .zero, collectionViewLayout: layout) + view.backgroundColor = .clear + view.showsVerticalScrollIndicator = false + return view + }() + + internal let informationButton = UIButton() + private let informationLabel: UILabel = { + let label = UILabel() + label.font = .korFont(style: .regular, size: 16) + label.text = "추천" + return label + }() + + private let informationIconView: UIImageView = { + let view = UIImageView() + view.image = DesignSystemAsset.image(named: "errorBlack").withTintColor(.primary700) + return view + }() + + private let emptyDataLabel: UILabel = { + let label = UILabel() + label.attributedText = .makeStyledString(font: .b_s_r, text: "추천 정보가 존재하지 않습니다.", color: .neutral600, alignment: .center) + label.isHidden = true + return label + }() + + internal let emptyView = ToLoginView(type: .recommend) + + // MARK: - init + init() { + super.init(frame: .zero) + + addViews() + setupConstraints() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("\(#file), \(#function) Error") + } +} + +// MARK: - SetUp +private extension RecommendationMainView { + func addViews() { + addSubview(header) + addSubview(profileView) + addSubview(grayBackgroundView) + grayBackgroundView.addSubview(informationButton) + informationButton.addSubview(informationLabel) + informationButton.addSubview(informationIconView) + grayBackgroundView.addSubview(collectionView) + addSubview(emptyView) + addSubview(emptyDataLabel) + } + + func setupConstraints() { + header.snp.makeConstraints { make in + make.top.horizontalEdges.equalTo(safeAreaLayoutGuide) + } + + profileView.snp.makeConstraints { make in + make.top.equalTo(header.snp.bottom).offset(Constant.profileTopOffset) + make.horizontalEdges.equalToSuperview().inset(Constant.profileHorizontalInset) + } + + grayBackgroundView.snp.makeConstraints { make in + make.top.equalTo(profileView.snp.bottom).offset(Constant.grayViewTopOffset) + make.horizontalEdges.equalToSuperview() + make.bottom.equalToSuperview().inset(Constant.bottomTabHeight) + } + + informationButton.snp.makeConstraints { make in + make.top.equalToSuperview().inset(Constant.informationButtonTopInset) + make.trailing.equalToSuperview().inset(Constant.informationButtonTrailingInset) + } + + informationIconView.snp.makeConstraints { make in + make.size.equalTo(Constant.informationIconSize) + make.verticalEdges.trailing.equalToSuperview() + } + + informationLabel.snp.makeConstraints { make in + make.centerY.leading.equalToSuperview() + make.trailing.equalTo(informationIconView.snp.leading).offset(-Constant.informationLabelIconSpacing) + } + + collectionView.snp.makeConstraints { make in + make.top.equalTo(informationButton.snp.bottom).offset(Constant.collectionViewTopOffset) + make.horizontalEdges.equalToSuperview().inset(Constant.collectionViewHorizontalInset) + make.bottom.equalToSuperview() + } + + emptyView.snp.makeConstraints { make in + make.top.equalTo(header.snp.bottom) + make.horizontalEdges.equalToSuperview() + make.bottom.equalToSuperview().inset(Constant.bottomTabHeight) + } + emptyView.isHidden = true + + emptyDataLabel.snp.makeConstraints { make in + make.top.equalTo(informationButton.snp.bottom).offset(Constant.emptyLabelTopOffset) + make.centerX.equalToSuperview() + } + } +} + +// MARK: - Login State +extension RecommendationMainView { + func updateLoginState(isLogin: Bool) { + profileView.isHidden = !isLogin + grayBackgroundView.isHidden = !isLogin + emptyView.isHidden = isLogin + emptyDataLabel.isHidden = !isLogin + } + + func checkEmptyData(isEmpty: Bool) { + collectionView.isHidden = isEmpty + emptyDataLabel.isHidden = !isEmpty + } +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/Views/RecommendationProfileView.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/Views/RecommendationProfileView.swift new file mode 100644 index 00000000..e86c9b2c --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeature/Presentation/RecommendationMain/Views/RecommendationProfileView.swift @@ -0,0 +1,90 @@ +import UIKit + +import MLSCore +import MLSDesignSystem + +import SnapKit + +internal final class RecommendationProfileView: UIStackView { + // MARK: - Type + private enum Constant { + static let profileImageViewSize: CGFloat = 100 + static let outerStackViewSpacing: CGFloat = 38 + static let verticalStackViewSpacing: CGFloat = 18 + static let horizontalStackViewSpacing: CGFloat = 12 + } + + // MARK: - Properties + internal let profileImageView: UIImageView = .init() + internal let nicknameLabel: UILabel = .init() + internal let jobLabel: UILabel = .init() + internal let levelBadge = Badge(style: .element("")) + internal let editButton: UIButton = { + let button = UIButton() + button.setAttributedTitle(.makeStyledUnderlinedString(font: .korFont(style: .regular, size: 14), text: "수정하기"), for: .normal) + return button + }() + + // MARK: - StackView + private let verticalStackView: UIStackView = .init() + private let horizontalStackView: UIStackView = .init() + + // MARK: - init + internal init() { + super.init(frame: .zero) + + addViews() + setupConstraints() + configureUI() + } + + @available(*, unavailable) + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: - SetUp +private extension RecommendationProfileView { + func addViews() { + horizontalStackView.addArrangedSubview(jobLabel) + horizontalStackView.addArrangedSubview(levelBadge) + horizontalStackView.addArrangedSubview(editButton) + + verticalStackView.addArrangedSubview(nicknameLabel) + verticalStackView.addArrangedSubview(horizontalStackView) + + addArrangedSubview(profileImageView) + addArrangedSubview(verticalStackView) + } + + func setupConstraints() { + profileImageView.snp.makeConstraints { make in + make.size.equalTo(Constant.profileImageViewSize) + } + } + + func configureUI() { + alignment = .center + spacing = Constant.outerStackViewSpacing + + verticalStackView.axis = .vertical + verticalStackView.alignment = .leading + verticalStackView.spacing = Constant.verticalStackViewSpacing + + horizontalStackView.alignment = .center + horizontalStackView.spacing = Constant.horizontalStackViewSpacing + } +} + +// MARK: - Configure +internal extension RecommendationProfileView { + func configure(imageURL: String?, nickName: String, job: String, level: Int) { + ImageLoader.shared.loadImage(stringURL: imageURL) { [weak self] image in + self?.profileImageView.image = image + } + nicknameLabel.attributedText = .makeStyledString(font: .korFont(style: .bold, size: 18), text: nickName) + jobLabel.attributedText = .makeStyledString(font: .korFont(style: .regular, size: 16), text: job) + levelBadge.update(style: .element("Lv.\(level)")) + } +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Entities/RecommendationMap.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Entities/RecommendationMap.swift new file mode 100644 index 00000000..c066a3a2 --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Entities/RecommendationMap.swift @@ -0,0 +1,17 @@ +public struct RecommendationMap { + public let mapId: Int + public let score: Double + public let iconUrl: String + public let nameKr: String + public let bookmarkId: Int? + + public var isBookmarked: Bool { bookmarkId != nil } + + public init(mapId: Int, score: Double, iconUrl: String, nameKr: String, bookmarkId: Int?) { + self.mapId = mapId + self.score = score + self.iconUrl = iconUrl + self.nameKr = nameKr + self.bookmarkId = bookmarkId + } +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Entities/UserProfile.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Entities/UserProfile.swift new file mode 100644 index 00000000..8e8e0c1b --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Entities/UserProfile.swift @@ -0,0 +1,13 @@ +public struct UserProfile { + public let nickname: String + public let jobId: Int? + public let level: Int? + public let profileImageUrl: String + + public init(nickname: String, jobId: Int?, level: Int?, profileImageUrl: String) { + self.nickname = nickname + self.jobId = jobId + self.level = level + self.profileImageUrl = profileImageUrl + } +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Factories/RecommendationMainFactory.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Factories/RecommendationMainFactory.swift new file mode 100644 index 00000000..7272689b --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Factories/RecommendationMainFactory.swift @@ -0,0 +1,5 @@ +import MLSCore + +public protocol RecommendationMainFactory { + func make() -> BaseViewController +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Repositories/RecommendationRepository 2.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Repositories/RecommendationRepository 2.swift new file mode 100644 index 00000000..cdf87b34 --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Repositories/RecommendationRepository 2.swift @@ -0,0 +1,5 @@ +import RxSwift + +public protocol RecommendationUserDefaultsRepository { + func saveFirstLaunch() -> Completable +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Repositories/RecommendationRepository.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Repositories/RecommendationRepository.swift new file mode 100644 index 00000000..58c67792 --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureInterface/Repositories/RecommendationRepository.swift @@ -0,0 +1,7 @@ +import RxSwift + +public protocol RecommendationRepository { + func fetchProfile() -> Observable + func fetchJobName(jobId: Int) -> Observable + func fetchRecommendations(level: Int, jobId: Int, limit: Int?) -> Observable<[RecommendationMap]> +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureTesting/FailingMockRecommendationRepository.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureTesting/FailingMockRecommendationRepository.swift new file mode 100644 index 00000000..5046718a --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureTesting/FailingMockRecommendationRepository.swift @@ -0,0 +1,26 @@ +import MLSRecommendationFeatureInterface + +import RxSwift + +/// 모든 요청이 항상 실패하는 Mock +public final class FailingMockRecommendationRepository: RecommendationRepository { + + public init() {} + + public func fetchProfile() -> Observable { + return .error(RecommendationRepositoryError.fetchFailed) + } + + public func fetchJobName(jobId: Int) -> Observable { + return .error(RecommendationRepositoryError.fetchFailed) + } + + public func fetchRecommendations(level: Int, jobId: Int, limit: Int?) -> Observable<[RecommendationMap]> { + return .error(RecommendationRepositoryError.fetchFailed) + } + +} + +public enum RecommendationRepositoryError: Error { + case fetchFailed +} diff --git a/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureTesting/MockRecommendationRepository.swift b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureTesting/MockRecommendationRepository.swift new file mode 100644 index 00000000..dca1d630 --- /dev/null +++ b/MLS/MLSRecommendationFeature/Sources/MLSRecommendationFeatureTesting/MockRecommendationRepository.swift @@ -0,0 +1,31 @@ +import MLSRecommendationFeatureInterface + +import RxSwift + +public final class MockRecommendationRepository: RecommendationRepository { + + public init() {} + + public func fetchProfile() -> Observable { + return .just(UserProfile( + nickname: "익명의 판타지", + jobId: 4, + level: 275, + profileImageUrl: "" + )) + } + + public func fetchJobName(jobId: Int) -> Observable { + return .just("도적") + } + + public func fetchRecommendations(level: Int, jobId: Int, limit: Int?) -> Observable<[RecommendationMap]> { + return .just([ + RecommendationMap(mapId: 100000000, score: 10, iconUrl: "", nameKr: "헤네시스", bookmarkId: nil), + RecommendationMap(mapId: 100000001, score: 9, iconUrl: "", nameKr: "엘리니아", bookmarkId: 1), + RecommendationMap(mapId: 100000002, score: 8, iconUrl: "", nameKr: "페리온", bookmarkId: nil), + RecommendationMap(mapId: 100000003, score: 7, iconUrl: "", nameKr: "슬리피우드", bookmarkId: 2), + RecommendationMap(mapId: 100000004, score: 6, iconUrl: "", nameKr: "노틸러스 항구", bookmarkId: nil) + ]) + } +} diff --git a/MLS/MLSRecommendationFeature/Tests/MLSRecommendationFeatureTests/MLSRecommendationFeatureTests.swift b/MLS/MLSRecommendationFeature/Tests/MLSRecommendationFeatureTests/MLSRecommendationFeatureTests.swift new file mode 100644 index 00000000..ca06a023 --- /dev/null +++ b/MLS/MLSRecommendationFeature/Tests/MLSRecommendationFeatureTests/MLSRecommendationFeatureTests.swift @@ -0,0 +1,6 @@ +@testable import MLSRecommendationFeature +import Testing + +@Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. +} diff --git a/MLS/MLSRecommendationFeatureExample/AppDelegate.swift b/MLS/MLSRecommendationFeatureExample/AppDelegate.swift new file mode 100644 index 00000000..d179aeef --- /dev/null +++ b/MLS/MLSRecommendationFeatureExample/AppDelegate.swift @@ -0,0 +1,25 @@ +import UIKit + +import MLSDesignSystem + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + FontManager.registerFonts() + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } +} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/AccentColor.colorset/Contents.json b/MLS/MLSRecommendationFeatureExample/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/AccentColor.colorset/Contents.json rename to MLS/MLSRecommendationFeatureExample/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/MLS/MLSRecommendationFeatureExample/Assets.xcassets/AppIcon.appiconset/AppIcon_1024 (1).png b/MLS/MLSRecommendationFeatureExample/Assets.xcassets/AppIcon.appiconset/AppIcon_1024 (1).png new file mode 100644 index 00000000..68f3c890 Binary files /dev/null and b/MLS/MLSRecommendationFeatureExample/Assets.xcassets/AppIcon.appiconset/AppIcon_1024 (1).png differ diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/MLS/MLSRecommendationFeatureExample/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 93% rename from MLS/Presentation/DesignSystem/DesignSystemDemo/Assets.xcassets/AppIcon.appiconset/Contents.json rename to MLS/MLSRecommendationFeatureExample/Assets.xcassets/AppIcon.appiconset/Contents.json index 8562116f..f0d60e38 100644 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/MLS/MLSRecommendationFeatureExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "DesignSystemLogo.png", + "filename" : "AppIcon_1024 (1).png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/Contents.json b/MLS/MLSRecommendationFeatureExample/Assets.xcassets/Contents.json similarity index 100% rename from MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/Contents.json rename to MLS/MLSRecommendationFeatureExample/Assets.xcassets/Contents.json diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/Base.lproj/LaunchScreen.storyboard b/MLS/MLSRecommendationFeatureExample/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from MLS/Presentation/DesignSystem/DesignSystemDemo/Base.lproj/LaunchScreen.storyboard rename to MLS/MLSRecommendationFeatureExample/Base.lproj/LaunchScreen.storyboard diff --git a/MLS/MLSAuthFeatureExample/Base.lproj/Main.storyboard b/MLS/MLSRecommendationFeatureExample/Base.lproj/Main.storyboard similarity index 100% rename from MLS/MLSAuthFeatureExample/Base.lproj/Main.storyboard rename to MLS/MLSRecommendationFeatureExample/Base.lproj/Main.storyboard diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Info.plist b/MLS/MLSRecommendationFeatureExample/Info.plist similarity index 90% rename from MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Info.plist rename to MLS/MLSRecommendationFeatureExample/Info.plist index 7c5f0be1..dd3c9afd 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Info.plist +++ b/MLS/MLSRecommendationFeatureExample/Info.plist @@ -2,8 +2,6 @@ - ITSAppUsesNonExemptEncryption - UIApplicationSceneManifest UIApplicationSupportsMultipleScenes @@ -17,6 +15,8 @@ Default Configuration UISceneDelegateClassName $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main diff --git a/MLS/MLSRecommendationFeatureExample/SceneDelegate.swift b/MLS/MLSRecommendationFeatureExample/SceneDelegate.swift new file mode 100644 index 00000000..017d699a --- /dev/null +++ b/MLS/MLSRecommendationFeatureExample/SceneDelegate.swift @@ -0,0 +1,70 @@ +import UIKit + +import MLSBookmarkFeatureTesting +import MLSCore +import MLSRecommendationFeature +import MLSRecommendationFeatureInterface +import MLSRecommendationFeatureTesting + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + guard let windowScene = (scene as? UIWindowScene) else { return } + + registerDependencies() + + let window = UIWindow(windowScene: windowScene) + self.window = window + + let factory = DIContainer.resolve(type: RecommendationMainFactory.self) + let recommendationVC = factory.make() + let nav = UINavigationController(rootViewController: recommendationVC) + nav.navigationBar.isHidden = true + window.rootViewController = nav + window.makeKeyAndVisible() + } + + // MARK: - Private + + private func registerDependencies() { + DIContainer.register(type: RecommendationMainFactory.self) { + RecommendationMainFactoryImpl( + repository: MockRecommendationRepository(), + bookmarkRepository: MockBookmarkRepository(), + recommendationUserDefaultsRepository: RecommendationUserDefaultsRepositoryImpl(), + makeLoginVC: { + let vc = UIViewController() + vc.view.backgroundColor = .white + return vc + }, + makeCharacterSettingVC: { + let vc = UIViewController() + vc.view.backgroundColor = .white + return vc + }, + makeSearchVC: { + let vc = UIViewController() + vc.view.backgroundColor = .white + return vc + }, + makeNotificationVC: { + let vc = UIViewController() + vc.view.backgroundColor = .white + return vc + }, + makeDetailVC: { _ in + let vc = UIViewController() + vc.view.backgroundColor = .white + return vc + }, + makeBookmarkModalVC: { _, _ in + let vc = UIViewController() + vc.view.backgroundColor = .white + return vc + } + ) + } + } +} diff --git a/MLS/MLSRecommendationFeatureExample/ViewController.swift b/MLS/MLSRecommendationFeatureExample/ViewController.swift new file mode 100644 index 00000000..0bcd93ff --- /dev/null +++ b/MLS/MLSRecommendationFeatureExample/ViewController.swift @@ -0,0 +1,12 @@ +// +// ViewController.swift +// MLSRecommendationFeatureExample +// +// Created by SeoJunYoung on 4/26/26. +// + +import UIKit + +class ViewController: UIViewController { + +} diff --git a/MLS/MLSTests/ProviderTests.swift b/MLS/MLSTests/ProviderTests.swift deleted file mode 100644 index be81daf3..00000000 --- a/MLS/MLSTests/ProviderTests.swift +++ /dev/null @@ -1,215 +0,0 @@ -import XCTest - -import Core -import Data -import DomainInterface - -import RxSwift - -// Post Model -struct Post: Decodable { - let id: Int - let title: String - let body: String - let userId: Int -} - -// Post Request Body -struct PostBody: Encodable { - let title: String - let body: String - let userId: Int -} - -class ProviderTests: XCTestCase { - var provider: NetworkProvider? - var disposeBag: DisposeBag? - - override func setUp() { - super.setUp() - provider = NetworkProviderImpl() - disposeBag = DisposeBag() - } - - override func tearDown() { - provider = nil - disposeBag = nil - super.tearDown() - } - - func testRequestableEndPoint() { - guard let provider = provider, - let disposeBag = disposeBag else { - XCTFail("provider or disposeBag is nil") - return - } - - let expectation = XCTestExpectation(description: "Request data success") - let request = ResponsableEndPoint<[Post]>( - baseURL: "https://jsonplaceholder.typicode.com", - path: "/posts", - method: .GET - ) - - provider.requestData(endPoint: request, interceptor: nil) - .subscribe(onNext: { (posts: [Post]) in - XCTAssertFalse(posts.isEmpty, "Posts should not be empty") - XCTAssertEqual(posts.first?.title, "sunt aut facere repellat provident occaecati excepturi optio reprehenderit") - expectation.fulfill() - }, onError: { error in - XCTFail("Expected success, but got error: \(error)") - }) - .disposed(by: disposeBag) - - wait(for: [expectation], timeout: 5.0) - } - - func testEndPoint() { - guard let provider = provider, - let disposeBag = disposeBag else { - XCTFail("provider or disposeBag is nil") - return - } - - let expectation = XCTestExpectation(description: "Request completable success") - let request = EndPoint( - baseURL: "https://jsonplaceholder.typicode.com", - path: "/posts", - method: .POST, - body: PostBody(title: "Test Title", body: "Test Body", userId: 1) - ) - - provider.requestData(endPoint: request, interceptor: nil) - .subscribe(onCompleted: { - expectation.fulfill() - }, onError: { error in - XCTFail("Expected completion, but got error: \(error)") - }) - .disposed(by: disposeBag) - - wait(for: [expectation], timeout: 5.0) - } - - func test404Error() { - guard let provider = provider, - let disposeBag = disposeBag else { - XCTFail("provider or disposeBag is nil") - return - } - - let expectation = XCTestExpectation(description: "Request 404 error") - let request = ResponsableEndPoint( - baseURL: "https://jsonplaceholder.typicode.com", - path: "/posts/999", - method: .GET - ) - - provider.requestData(endPoint: request, interceptor: nil) - .subscribe(onNext: { _ in - XCTFail("Expected error, but got success") - }, onError: { error in - if case NetworkError.statusError(let code, let message) = error { - XCTAssertEqual(code, 404, "Expected 404 status code") - XCTAssertEqual(message, "{}", "Expected empty JSON object") - expectation.fulfill() - } else { - XCTFail("Expected statusError, but got \(error)") - } - }) - .disposed(by: disposeBag) - - wait(for: [expectation], timeout: 5.0) - } - - func testInvalidURL() { - guard let provider = provider, - let disposeBag = disposeBag else { - XCTFail("provider or disposeBag is nil") - return - } - - let expectation = XCTestExpectation(description: "Invalid URL error") - let request = ResponsableEndPoint( - baseURL: "url", - path: "/posts", - method: .GET - ) - - provider.requestData(endPoint: request, interceptor: nil) - .subscribe(onNext: { _ in - XCTFail("Expected error, but got success") - }, onError: { error in - if case NetworkError.urlRequest(_) = error { - expectation.fulfill() - } else { - XCTFail("Expected urlRequest error, but got \(error)") - } - }) - .disposed(by: disposeBag) - - wait(for: [expectation], timeout: 5.0) - } - - func testDecodeError() { - guard let provider = provider, - let disposeBag = disposeBag else { - XCTFail("provider or disposeBag is nil") - return - } - - struct DecodeFailResponse: Decodable { - let invalidKey: String // API 응답에 없는 키 - } - - let expectation = XCTestExpectation(description: "Decode error") - let request = ResponsableEndPoint( - baseURL: "https://jsonplaceholder.typicode.com", - path: "/posts/1", - method: .GET - ) - - provider.requestData(endPoint: request, interceptor: nil) - .subscribe(onNext: { (_: DecodeFailResponse) in - XCTFail("Expected error, but got success") - }, onError: { error in - if case NetworkError.decodeError(_) = error { - expectation.fulfill() - } else { - XCTFail("Expected decodeError, but got \(error)") - } - }) - .disposed(by: disposeBag) - - wait(for: [expectation], timeout: 5.0) - } - - func testProviderDeallocated() { - guard let disposeBag = disposeBag else { - XCTFail("disposeBag is nil") - return - } - - let expectation = XCTestExpectation(description: "Provider deallocated error") - let request = ResponsableEndPoint<[Post]>( - baseURL: "https://jsonplaceholder.typicode.com", - path: "/posts", - method: .GET - ) - - var localProvider: NetworkProviderImpl? = NetworkProviderImpl() - localProvider?.requestData(endPoint: request, interceptor: nil) - .subscribe(onNext: { (_: [Post]) in - XCTFail("Expected error, but got success") - }, onError: { error in - if case NetworkError.providerDeallocated = error { - expectation.fulfill() - } else { - XCTFail("Expected providerDeallocated, but got \(error)") - } - }) - .disposed(by: disposeBag) - - localProvider = nil - wait(for: [expectation], timeout: 5.0) - } -} diff --git a/MLS/Presentation/.DS_Store b/MLS/Presentation/.DS_Store deleted file mode 100644 index a6dedd51..00000000 Binary files a/MLS/Presentation/.DS_Store and /dev/null differ diff --git a/MLS/Presentation/AuthFeature/.DS_Store b/MLS/Presentation/AuthFeature/.DS_Store deleted file mode 100644 index 1d52553e..00000000 Binary files a/MLS/Presentation/AuthFeature/.DS_Store and /dev/null differ diff --git a/MLS/Presentation/AuthFeature/AuthFeature.xcodeproj/project.pbxproj b/MLS/Presentation/AuthFeature/AuthFeature.xcodeproj/project.pbxproj deleted file mode 100644 index 414870e2..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature.xcodeproj/project.pbxproj +++ /dev/null @@ -1,969 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXBuildFile section */ - 08501BEA2DDCACD20055CBCE /* AuthFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08501BC62DDCA9DF0055CBCE /* AuthFeatureInterface.framework */; }; - 0850205F2DDCD9240055CBCE /* BaseFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08EECF932DD908D00047AFE2 /* BaseFeature.framework */; }; - 085020612DDCD9250055CBCE /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0858ABEF2DCFDA450060EBCA /* DesignSystem.framework */; }; - 085020642DDCD9640055CBCE /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 085020632DDCD9640055CBCE /* RxCocoa */; }; - 085020662DDCD9640055CBCE /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 085020652DDCD9640055CBCE /* RxRelay */; }; - 085020682DDCD9640055CBCE /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 085020672DDCD9640055CBCE /* RxSwift */; }; - 0850206A2DDCD9720055CBCE /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 085020692DDCD9720055CBCE /* SnapKit */; }; - 0850206C2DDCD97E0055CBCE /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 0850206B2DDCD97E0055CBCE /* ReactorKit */; }; - 085BDA622DF18617009CFB90 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 085BDA612DF18617009CFB90 /* DomainInterface.framework */; }; - 085BDA6D2DF1A4DA009CFB90 /* BaseFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 085BDA6C2DF1A4DA009CFB90 /* BaseFeature.framework */; }; - 085BDA6E2DF1A4DA009CFB90 /* BaseFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 085BDA6C2DF1A4DA009CFB90 /* BaseFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 085BDF572DF459FE009CFB90 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 085BDF562DF459FE009CFB90 /* DomainInterface.framework */; }; - 08E424F72DF0BB3C00D6ACD3 /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08E424F62DF0BB3C00D6ACD3 /* Domain.framework */; }; - 08E424F82DF0BB3C00D6ACD3 /* Domain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 08E424F62DF0BB3C00D6ACD3 /* Domain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 08E424F92DF0BC5C00D6ACD3 /* AuthFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0858AB3F2DCFD62C0060EBCA /* AuthFeature.framework */; }; - 08E424FA2DF0BC5C00D6ACD3 /* AuthFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0858AB3F2DCFD62C0060EBCA /* AuthFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 08E424FB2DF0BC5E00D6ACD3 /* AuthFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08501BC62DDCA9DF0055CBCE /* AuthFeatureInterface.framework */; }; - 08E424FC2DF0BC5E00D6ACD3 /* AuthFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 08501BC62DDCA9DF0055CBCE /* AuthFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 08E424FD2DF0BC7800D6ACD3 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0858ABEF2DCFDA450060EBCA /* DesignSystem.framework */; }; - 08E424FE2DF0BC7800D6ACD3 /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0858ABEF2DCFDA450060EBCA /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 08E425002DF0BC9F00D6ACD3 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08E424FF2DF0BC9F00D6ACD3 /* RxSwift */; }; - 08E425022DF0BCA400D6ACD3 /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 08E425012DF0BCA400D6ACD3 /* RxRelay */; }; - 08E425042DF0BCCC00D6ACD3 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 08E425032DF0BCCC00D6ACD3 /* RxCocoa */; }; - 08E4255B2DF17D5300D6ACD3 /* DataMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08E4255A2DF17D5300D6ACD3 /* DataMock.framework */; }; - 08E4255C2DF17D5300D6ACD3 /* DataMock.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 08E4255A2DF17D5300D6ACD3 /* DataMock.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7746ABE22E8BD9DC0046F603 /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 7746ABE12E8BD9DC0046F603 /* RxKeyboard */; }; - 7787BFB92DED80ED0011C907 /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 7787BFB82DED80ED0011C907 /* RxKeyboard */; }; - 77EB17C62DED816D004FB380 /* BaseFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77EB17C52DED816D004FB380 /* BaseFeature.framework */; }; - 77EB17CA2DED81E0004FB380 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 77EB17C92DED81E0004FB380 /* SnapKit */; }; - 77EB17CC2DED81F2004FB380 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 77EB17CB2DED81F2004FB380 /* ReactorKit */; }; - 77EB18E62DEDA126004FB380 /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77EB18E12DEDA126004FB380 /* Core.framework */; }; - 77EB18E72DEDA126004FB380 /* Core.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77EB18E12DEDA126004FB380 /* Core.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77EB18E82DEDA126004FB380 /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77EB18E22DEDA126004FB380 /* Data.framework */; }; - 77EB18E92DEDA126004FB380 /* Data.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77EB18E22DEDA126004FB380 /* Data.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77EB18EA2DEDA126004FB380 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77EB18E32DEDA126004FB380 /* DomainInterface.framework */; }; - 77EB18EB2DEDA126004FB380 /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77EB18E32DEDA126004FB380 /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 08501BD72DDCA9DF0055CBCE /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0858AB362DCFD62C0060EBCA /* Project object */; - proxyType = 1; - remoteGlobalIDString = 08501BC52DDCA9DF0055CBCE; - remoteInfo = AuthFeatureInterface; - }; - 08501BEC2DDCACD20055CBCE /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0858AB362DCFD62C0060EBCA /* Project object */; - proxyType = 1; - remoteGlobalIDString = 08501BC52DDCA9DF0055CBCE; - remoteInfo = AuthFeatureInterface; - }; - 08686A152DD1C0E300868C70 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0858AB362DCFD62C0060EBCA /* Project object */; - proxyType = 1; - remoteGlobalIDString = 0858AB3E2DCFD62C0060EBCA; - remoteInfo = AuthFeature; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 77EB18EC2DEDA126004FB380 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 08E424FC2DF0BC5E00D6ACD3 /* AuthFeatureInterface.framework in Embed Frameworks */, - 08E424FE2DF0BC7800D6ACD3 /* DesignSystem.framework in Embed Frameworks */, - 08E424F82DF0BB3C00D6ACD3 /* Domain.framework in Embed Frameworks */, - 77EB18E92DEDA126004FB380 /* Data.framework in Embed Frameworks */, - 08E4255C2DF17D5300D6ACD3 /* DataMock.framework in Embed Frameworks */, - 77EB18EB2DEDA126004FB380 /* DomainInterface.framework in Embed Frameworks */, - 77EB18E72DEDA126004FB380 /* Core.framework in Embed Frameworks */, - 085BDA6E2DF1A4DA009CFB90 /* BaseFeature.framework in Embed Frameworks */, - 08E424FA2DF0BC5C00D6ACD3 /* AuthFeature.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 08501BC62DDCA9DF0055CBCE /* AuthFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AuthFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 0858AB3F2DCFD62C0060EBCA /* AuthFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AuthFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 0858ABEF2DCFDA450060EBCA /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 0858ABF32DCFDA510060EBCA /* Presentation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Presentation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 085BDA612DF18617009CFB90 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 085BDA6C2DF1A4DA009CFB90 /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 085BDF562DF459FE009CFB90 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 086869FE2DD1C08800868C70 /* AuthFeatureDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AuthFeatureDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 08E424F62DF0BB3C00D6ACD3 /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 08E4255A2DF17D5300D6ACD3 /* DataMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DataMock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 08EECF932DD908D00047AFE2 /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77EB17C52DED816D004FB380 /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77EB18E02DEDA126004FB380 /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77EB18E12DEDA126004FB380 /* Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77EB18E22DEDA126004FB380 /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77EB18E32DEDA126004FB380 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 08686A0F2DD1C08A00868C70 /* Exceptions for "AuthFeatureDemo" folder in "AuthFeatureDemo" target */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Info.plist, - ); - target = 086869FD2DD1C08800868C70 /* AuthFeatureDemo */; - }; -/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 08501BC72DDCA9DF0055CBCE /* AuthFeatureInterface */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = AuthFeatureInterface; - sourceTree = ""; - }; - 0858AB412DCFD62C0060EBCA /* AuthFeature */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = AuthFeature; - sourceTree = ""; - }; - 086869FF2DD1C08800868C70 /* AuthFeatureDemo */ = { - isa = PBXFileSystemSynchronizedRootGroup; - exceptions = ( - 08686A0F2DD1C08A00868C70 /* Exceptions for "AuthFeatureDemo" folder in "AuthFeatureDemo" target */, - ); - path = AuthFeatureDemo; - sourceTree = ""; - }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - 08501BC32DDCA9DF0055CBCE /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 085BDF572DF459FE009CFB90 /* DomainInterface.framework in Frameworks */, - 77EB17C62DED816D004FB380 /* BaseFeature.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0858AB3C2DCFD62C0060EBCA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 085BDA622DF18617009CFB90 /* DomainInterface.framework in Frameworks */, - 085020682DDCD9640055CBCE /* RxSwift in Frameworks */, - 7787BFB92DED80ED0011C907 /* RxKeyboard in Frameworks */, - 085020662DDCD9640055CBCE /* RxRelay in Frameworks */, - 0850206C2DDCD97E0055CBCE /* ReactorKit in Frameworks */, - 085020612DDCD9250055CBCE /* DesignSystem.framework in Frameworks */, - 0850206A2DDCD9720055CBCE /* SnapKit in Frameworks */, - 08501BEA2DDCACD20055CBCE /* AuthFeatureInterface.framework in Frameworks */, - 085020642DDCD9640055CBCE /* RxCocoa in Frameworks */, - 0850205F2DDCD9240055CBCE /* BaseFeature.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 086869FB2DD1C08800868C70 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 77EB17CA2DED81E0004FB380 /* SnapKit in Frameworks */, - 77EB18E62DEDA126004FB380 /* Core.framework in Frameworks */, - 08E424F92DF0BC5C00D6ACD3 /* AuthFeature.framework in Frameworks */, - 08E425042DF0BCCC00D6ACD3 /* RxCocoa in Frameworks */, - 7746ABE22E8BD9DC0046F603 /* RxKeyboard in Frameworks */, - 08E424F72DF0BB3C00D6ACD3 /* Domain.framework in Frameworks */, - 77EB18E82DEDA126004FB380 /* Data.framework in Frameworks */, - 085BDA6D2DF1A4DA009CFB90 /* BaseFeature.framework in Frameworks */, - 08E425022DF0BCA400D6ACD3 /* RxRelay in Frameworks */, - 08E424FB2DF0BC5E00D6ACD3 /* AuthFeatureInterface.framework in Frameworks */, - 08E424FD2DF0BC7800D6ACD3 /* DesignSystem.framework in Frameworks */, - 77EB17CC2DED81F2004FB380 /* ReactorKit in Frameworks */, - 77EB18EA2DEDA126004FB380 /* DomainInterface.framework in Frameworks */, - 08E4255B2DF17D5300D6ACD3 /* DataMock.framework in Frameworks */, - 08E425002DF0BC9F00D6ACD3 /* RxSwift in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 0858AB352DCFD62C0060EBCA = { - isa = PBXGroup; - children = ( - 0858AB412DCFD62C0060EBCA /* AuthFeature */, - 086869FF2DD1C08800868C70 /* AuthFeatureDemo */, - 08501BC72DDCA9DF0055CBCE /* AuthFeatureInterface */, - 0858ABEE2DCFDA450060EBCA /* Frameworks */, - 0858AB402DCFD62C0060EBCA /* Products */, - ); - sourceTree = ""; - }; - 0858AB402DCFD62C0060EBCA /* Products */ = { - isa = PBXGroup; - children = ( - 0858AB3F2DCFD62C0060EBCA /* AuthFeature.framework */, - 086869FE2DD1C08800868C70 /* AuthFeatureDemo.app */, - 08501BC62DDCA9DF0055CBCE /* AuthFeatureInterface.framework */, - ); - name = Products; - sourceTree = ""; - }; - 0858ABEE2DCFDA450060EBCA /* Frameworks */ = { - isa = PBXGroup; - children = ( - 085BDF562DF459FE009CFB90 /* DomainInterface.framework */, - 085BDA6C2DF1A4DA009CFB90 /* BaseFeature.framework */, - 085BDA612DF18617009CFB90 /* DomainInterface.framework */, - 08E4255A2DF17D5300D6ACD3 /* DataMock.framework */, - 08E424F62DF0BB3C00D6ACD3 /* Domain.framework */, - 77EB18E02DEDA126004FB380 /* BaseFeature.framework */, - 77EB18E12DEDA126004FB380 /* Core.framework */, - 77EB18E22DEDA126004FB380 /* Data.framework */, - 77EB18E32DEDA126004FB380 /* DomainInterface.framework */, - 77EB17C52DED816D004FB380 /* BaseFeature.framework */, - 08EECF932DD908D00047AFE2 /* BaseFeature.framework */, - 0858ABF32DCFDA510060EBCA /* Presentation.framework */, - 0858ABEF2DCFDA450060EBCA /* DesignSystem.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 08501BC12DDCA9DF0055CBCE /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0858AB3A2DCFD62C0060EBCA /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 08501BC52DDCA9DF0055CBCE /* AuthFeatureInterface */ = { - isa = PBXNativeTarget; - buildConfigurationList = 08501BDF2DDCA9DF0055CBCE /* Build configuration list for PBXNativeTarget "AuthFeatureInterface" */; - buildPhases = ( - 08501BC12DDCA9DF0055CBCE /* Headers */, - 08501BC22DDCA9DF0055CBCE /* Sources */, - 08501BC32DDCA9DF0055CBCE /* Frameworks */, - 08501BC42DDCA9DF0055CBCE /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - 08501BC72DDCA9DF0055CBCE /* AuthFeatureInterface */, - ); - name = AuthFeatureInterface; - packageProductDependencies = ( - ); - productName = AuthFeatureInterface; - productReference = 08501BC62DDCA9DF0055CBCE /* AuthFeatureInterface.framework */; - productType = "com.apple.product-type.framework"; - }; - 0858AB3E2DCFD62C0060EBCA /* AuthFeature */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0858AB452DCFD62C0060EBCA /* Build configuration list for PBXNativeTarget "AuthFeature" */; - buildPhases = ( - 0858AB3A2DCFD62C0060EBCA /* Headers */, - 0858AB3B2DCFD62C0060EBCA /* Sources */, - 0858AB3C2DCFD62C0060EBCA /* Frameworks */, - 0858AB3D2DCFD62C0060EBCA /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 08501BED2DDCACD20055CBCE /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 0858AB412DCFD62C0060EBCA /* AuthFeature */, - ); - name = AuthFeature; - packageProductDependencies = ( - 085020632DDCD9640055CBCE /* RxCocoa */, - 085020652DDCD9640055CBCE /* RxRelay */, - 085020672DDCD9640055CBCE /* RxSwift */, - 085020692DDCD9720055CBCE /* SnapKit */, - 0850206B2DDCD97E0055CBCE /* ReactorKit */, - 7787BFB82DED80ED0011C907 /* RxKeyboard */, - ); - productName = AuthFeature; - productReference = 0858AB3F2DCFD62C0060EBCA /* AuthFeature.framework */; - productType = "com.apple.product-type.framework"; - }; - 086869FD2DD1C08800868C70 /* AuthFeatureDemo */ = { - isa = PBXNativeTarget; - buildConfigurationList = 08686A102DD1C08A00868C70 /* Build configuration list for PBXNativeTarget "AuthFeatureDemo" */; - buildPhases = ( - 086869FA2DD1C08800868C70 /* Sources */, - 086869FB2DD1C08800868C70 /* Frameworks */, - 086869FC2DD1C08800868C70 /* Resources */, - 77EB18EC2DEDA126004FB380 /* Embed Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 08686A162DD1C0E300868C70 /* PBXTargetDependency */, - 08501BD82DDCA9DF0055CBCE /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 086869FF2DD1C08800868C70 /* AuthFeatureDemo */, - ); - name = AuthFeatureDemo; - packageProductDependencies = ( - 77EB17C92DED81E0004FB380 /* SnapKit */, - 77EB17CB2DED81F2004FB380 /* ReactorKit */, - 08E424FF2DF0BC9F00D6ACD3 /* RxSwift */, - 08E425012DF0BCA400D6ACD3 /* RxRelay */, - 08E425032DF0BCCC00D6ACD3 /* RxCocoa */, - 7746ABE12E8BD9DC0046F603 /* RxKeyboard */, - ); - productName = AuthFeatureDemo; - productReference = 086869FE2DD1C08800868C70 /* AuthFeatureDemo.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 0858AB362DCFD62C0060EBCA /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1620; - LastUpgradeCheck = 1620; - TargetAttributes = { - 08501BC52DDCA9DF0055CBCE = { - CreatedOnToolsVersion = 16.2; - LastSwiftMigration = 1620; - }; - 0858AB3E2DCFD62C0060EBCA = { - CreatedOnToolsVersion = 16.2; - LastSwiftMigration = 1620; - }; - 086869FD2DD1C08800868C70 = { - CreatedOnToolsVersion = 16.2; - }; - }; - }; - buildConfigurationList = 0858AB392DCFD62C0060EBCA /* Build configuration list for PBXProject "AuthFeature" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 0858AB352DCFD62C0060EBCA; - minimizedProjectReferenceProxies = 1; - packageReferences = ( - 086869EE2DD1BF8F00868C70 /* XCRemoteSwiftPackageReference "ReactorKit" */, - 086869F12DD1BF9F00868C70 /* XCRemoteSwiftPackageReference "RxSwift" */, - 08686A392DD1C78500868C70 /* XCRemoteSwiftPackageReference "SnapKit" */, - 7787BFB72DED80ED0011C907 /* XCRemoteSwiftPackageReference "RxKeyboard" */, - ); - preferredProjectObjectVersion = 77; - productRefGroup = 0858AB402DCFD62C0060EBCA /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 0858AB3E2DCFD62C0060EBCA /* AuthFeature */, - 08501BC52DDCA9DF0055CBCE /* AuthFeatureInterface */, - 086869FD2DD1C08800868C70 /* AuthFeatureDemo */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 08501BC42DDCA9DF0055CBCE /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0858AB3D2DCFD62C0060EBCA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 086869FC2DD1C08800868C70 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 08501BC22DDCA9DF0055CBCE /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0858AB3B2DCFD62C0060EBCA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 086869FA2DD1C08800868C70 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 08501BD82DDCA9DF0055CBCE /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 08501BC52DDCA9DF0055CBCE /* AuthFeatureInterface */; - targetProxy = 08501BD72DDCA9DF0055CBCE /* PBXContainerItemProxy */; - }; - 08501BED2DDCACD20055CBCE /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 08501BC52DDCA9DF0055CBCE /* AuthFeatureInterface */; - targetProxy = 08501BEC2DDCACD20055CBCE /* PBXContainerItemProxy */; - }; - 08686A162DD1C0E300868C70 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 0858AB3E2DCFD62C0060EBCA /* AuthFeature */; - targetProxy = 08686A152DD1C0E300868C70 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 08501BDB2DDCA9DF0055CBCE /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 15.2; - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.AuthFeatureInterface; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = auto; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - XROS_DEPLOYMENT_TARGET = 2.2; - }; - name = Debug; - }; - 08501BDC2DDCA9DF0055CBCE /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 15.2; - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.AuthFeatureInterface; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = auto; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - XROS_DEPLOYMENT_TARGET = 2.2; - }; - name = Release; - }; - 0858AB462DCFD62C0060EBCA /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.AuthFeature; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 0858AB472DCFD62C0060EBCA /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.AuthFeature; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; - 0858AB482DCFD62C0060EBCA /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 0858AB492DCFD62C0060EBCA /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 08686A112DD1C08A00868C70 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = AuthFeatureDemo/Info.plist; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLS.AuthFeatureDemo; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = MLSAuthFeatureProvisioningProfileDev; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 08686A122DD1C08A00868C70 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = AuthFeatureDemo/Info.plist; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLS.AuthFeatureDemo; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = MLSAuthFeatureProvisioningProfile; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 08501BDF2DDCA9DF0055CBCE /* Build configuration list for PBXNativeTarget "AuthFeatureInterface" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 08501BDB2DDCA9DF0055CBCE /* Debug */, - 08501BDC2DDCA9DF0055CBCE /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 0858AB392DCFD62C0060EBCA /* Build configuration list for PBXProject "AuthFeature" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0858AB482DCFD62C0060EBCA /* Debug */, - 0858AB492DCFD62C0060EBCA /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 0858AB452DCFD62C0060EBCA /* Build configuration list for PBXNativeTarget "AuthFeature" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0858AB462DCFD62C0060EBCA /* Debug */, - 0858AB472DCFD62C0060EBCA /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 08686A102DD1C08A00868C70 /* Build configuration list for PBXNativeTarget "AuthFeatureDemo" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 08686A112DD1C08A00868C70 /* Debug */, - 08686A122DD1C08A00868C70 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - 086869EE2DD1BF8F00868C70 /* XCRemoteSwiftPackageReference "ReactorKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactorKit/ReactorKit"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 3.2.0; - }; - }; - 086869F12DD1BF9F00868C70 /* XCRemoteSwiftPackageReference "RxSwift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactiveX/RxSwift"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 6.9.0; - }; - }; - 08686A392DD1C78500868C70 /* XCRemoteSwiftPackageReference "SnapKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/SnapKit/SnapKit.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 5.7.1; - }; - }; - 7787BFB72DED80ED0011C907 /* XCRemoteSwiftPackageReference "RxKeyboard" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/RxSwiftCommunity/RxKeyboard.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.0.1; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - 085020632DDCD9640055CBCE /* RxCocoa */ = { - isa = XCSwiftPackageProductDependency; - package = 086869F12DD1BF9F00868C70 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxCocoa; - }; - 085020652DDCD9640055CBCE /* RxRelay */ = { - isa = XCSwiftPackageProductDependency; - package = 086869F12DD1BF9F00868C70 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxRelay; - }; - 085020672DDCD9640055CBCE /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 086869F12DD1BF9F00868C70 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; - 085020692DDCD9720055CBCE /* SnapKit */ = { - isa = XCSwiftPackageProductDependency; - package = 08686A392DD1C78500868C70 /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = SnapKit; - }; - 0850206B2DDCD97E0055CBCE /* ReactorKit */ = { - isa = XCSwiftPackageProductDependency; - package = 086869EE2DD1BF8F00868C70 /* XCRemoteSwiftPackageReference "ReactorKit" */; - productName = ReactorKit; - }; - 08E424FF2DF0BC9F00D6ACD3 /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 086869F12DD1BF9F00868C70 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; - 08E425012DF0BCA400D6ACD3 /* RxRelay */ = { - isa = XCSwiftPackageProductDependency; - package = 086869F12DD1BF9F00868C70 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxRelay; - }; - 08E425032DF0BCCC00D6ACD3 /* RxCocoa */ = { - isa = XCSwiftPackageProductDependency; - package = 086869F12DD1BF9F00868C70 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxCocoa; - }; - 7746ABE12E8BD9DC0046F603 /* RxKeyboard */ = { - isa = XCSwiftPackageProductDependency; - package = 7787BFB72DED80ED0011C907 /* XCRemoteSwiftPackageReference "RxKeyboard" */; - productName = RxKeyboard; - }; - 7787BFB82DED80ED0011C907 /* RxKeyboard */ = { - isa = XCSwiftPackageProductDependency; - package = 7787BFB72DED80ED0011C907 /* XCRemoteSwiftPackageReference "RxKeyboard" */; - productName = RxKeyboard; - }; - 77EB17C92DED81E0004FB380 /* SnapKit */ = { - isa = XCSwiftPackageProductDependency; - package = 08686A392DD1C78500868C70 /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = SnapKit; - }; - 77EB17CB2DED81F2004FB380 /* ReactorKit */ = { - isa = XCSwiftPackageProductDependency; - package = 086869EE2DD1BF8F00868C70 /* XCRemoteSwiftPackageReference "ReactorKit" */; - productName = ReactorKit; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = 0858AB362DCFD62C0060EBCA /* Project object */; -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MLS/Presentation/AuthFeature/AuthFeature.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/MLS/Presentation/AuthFeature/AuthFeature/Login/LoginFactoryImpl.swift b/MLS/Presentation/AuthFeature/AuthFeature/Login/LoginFactoryImpl.swift deleted file mode 100644 index a75289b0..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/Login/LoginFactoryImpl.swift +++ /dev/null @@ -1,65 +0,0 @@ -import AuthFeatureInterface -import BaseFeature -import DomainInterface - -import RxSwift - -public struct LoginFactoryImpl: LoginFactory { - private let termsAgreementsFactory: TermsAgreementFactory - private let appleLoginUseCase: FetchSocialCredentialUseCase - private let kakaoLoginUseCase: FetchSocialCredentialUseCase - private let loginWithAppleUseCase: LoginWithAppleUseCase - private let loginWithKakaoUseCase: LoginWithKakaoUseCase - private let fetchTokenUseCase: FetchTokenFromLocalUseCase - private let putFCMTokenUseCase: PutFCMTokenUseCase - private let fetchPlatformUseCase: FetchPlatformUseCase - - public init( - termsAgreementsFactory: TermsAgreementFactory, - appleLoginUseCase: FetchSocialCredentialUseCase, - kakaoLoginUseCase: FetchSocialCredentialUseCase, - loginWithAppleUseCase: LoginWithAppleUseCase, - loginWithKakaoUseCase: LoginWithKakaoUseCase, - fetchTokenUseCase: FetchTokenFromLocalUseCase, - putFCMTokenUseCase: PutFCMTokenUseCase, - fetchPlatformUseCase: FetchPlatformUseCase - ) { - self.termsAgreementsFactory = termsAgreementsFactory - self.appleLoginUseCase = appleLoginUseCase - self.kakaoLoginUseCase = kakaoLoginUseCase - self.loginWithAppleUseCase = loginWithAppleUseCase - self.loginWithKakaoUseCase = loginWithKakaoUseCase - self.fetchTokenUseCase = fetchTokenUseCase - self.putFCMTokenUseCase = putFCMTokenUseCase - self.fetchPlatformUseCase = fetchPlatformUseCase - } - - public func make(exitRoute: LoginExitRoute, onLoginCompleted: (() -> Void)?) -> BaseViewController { - let viewController = LoginViewController(termsAgreementsFactory: termsAgreementsFactory) - viewController.isBottomTabbarHidden = true - - viewController.reactor = LoginReactor( - fetchAppleCredentialUseCase: appleLoginUseCase, - fetchKakaoCredentialUseCase: kakaoLoginUseCase, - loginWithAppleUseCase: loginWithAppleUseCase, - loginWithKakaoUseCase: loginWithKakaoUseCase, - fetchTokenUseCase: fetchTokenUseCase, - putFCMTokenUseCase: putFCMTokenUseCase, - fetchPlatformUseCase: fetchPlatformUseCase - ) - - viewController.routeToHome - .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak viewController] in - switch exitRoute { - case .home: - onLoginCompleted?() - case .pop: - viewController?.navigationController?.popViewController(animated: true) - } - }) - .disposed(by: viewController.disposeBag) - - return viewController - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/Login/LoginReactor.swift b/MLS/Presentation/AuthFeature/AuthFeature/Login/LoginReactor.swift deleted file mode 100644 index 7bf0b45d..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/Login/LoginReactor.swift +++ /dev/null @@ -1,196 +0,0 @@ -import DomainInterface -import NotificationCenter - -import ReactorKit -import RxSwift - -public final class LoginReactor: Reactor { - public enum Route { - case none - case termsAgreements(credential: Credential, platform: LoginPlatform) - case error - case home - case dismiss - } - - // MARK: - Reactor - public enum Action { - case viewWillAppear - case kakaoLoginButtonTapped - case appleLoginButtonTapped - case guestLoginButtonTapped - case backButtonTapped - } - - public enum Mutation { - case navigateTo(route: Route) - case setRelogin(LoginPlatform?) - } - - public struct State { - @Pulse var route: Route = .none - var platform: LoginPlatform? - } - - // MARK: - properties - public var initialState: State - var disposeBag = DisposeBag() - private let fetchAppleCredentialUseCase: FetchSocialCredentialUseCase - private let fetchKakaoCredentialUseCase: FetchSocialCredentialUseCase - private let loginWithAppleUseCase: LoginWithAppleUseCase - private let loginWithKakaoUseCase: LoginWithKakaoUseCase - private let fetchTokenUseCase: FetchTokenFromLocalUseCase - private let putFCMTokenUseCase: PutFCMTokenUseCase - private let fetchPlatformUseCase: FetchPlatformUseCase - - // MARK: - init - public init( - fetchAppleCredentialUseCase: FetchSocialCredentialUseCase, - fetchKakaoCredentialUseCase: FetchSocialCredentialUseCase, - loginWithAppleUseCase: LoginWithAppleUseCase, - loginWithKakaoUseCase: LoginWithKakaoUseCase, - fetchTokenUseCase: FetchTokenFromLocalUseCase, - putFCMTokenUseCase: PutFCMTokenUseCase, - fetchPlatformUseCase: FetchPlatformUseCase - ) { - self.fetchAppleCredentialUseCase = fetchAppleCredentialUseCase - self.fetchKakaoCredentialUseCase = fetchKakaoCredentialUseCase - self.loginWithAppleUseCase = loginWithAppleUseCase - self.loginWithKakaoUseCase = loginWithKakaoUseCase - self.fetchTokenUseCase = fetchTokenUseCase - self.putFCMTokenUseCase = putFCMTokenUseCase - self.fetchPlatformUseCase = fetchPlatformUseCase - self.initialState = State() - } - - // MARK: - Reactor Methods - public func mutate(action: Action) -> Observable { - switch action { - case .viewWillAppear: - return fetchPlatformUseCase.execute() - .map { Mutation.setRelogin($0) } - case .kakaoLoginButtonTapped: - return handleKakaoLogin() - case .appleLoginButtonTapped: - return handleAppleLogin() - case .guestLoginButtonTapped: - return .just(.navigateTo(route: .home)) - case .backButtonTapped: - return .just(.navigateTo(route: .dismiss)) - } - } - - public func reduce(state: State, mutation: Mutation) -> State { - var newState = state - switch mutation { - case .navigateTo(let route): - newState.route = route - case .setRelogin(let platform): - newState.platform = platform - } - return newState - } -} - -// MARK: - Methods -private extension LoginReactor { - func handleKakaoLogin() -> Observable { - return checkNotificationPermissionAndFetchFCMToken() - .withUnretained(self) - .flatMap { owner, fcmToken in - // 1. 카카오 로그인 자격 증명 가져오기 - owner.fetchKakaoCredentialUseCase.execute() - .flatMap { credential in - // 2. 자격 증명을 바탕으로 로그인 요청 - owner.loginWithKakaoUseCase.execute(credential: credential) - .flatMap { response -> Observable in - if response.isRegister { - // 3. 회원가입된 유저면 FCM 토큰 등록 후 홈으로 이동 - return owner.putFCMTokenUseCase.execute(fcmToken: fcmToken) - .andThen(.just(.navigateTo(route: .home))) - } else { - // 4. 미가입 유저면 약관 동의 화면으로 이동 - return .just(.navigateTo(route: .termsAgreements( - credential: credential, - platform: .kakao - ))) - } - } - .catch { error in - // 5. 로그인 실패 예외 처리 - if case AuthError.userNotFound = error { - return .just(.navigateTo(route: .termsAgreements( - credential: credential, - platform: .kakao - ))) - } else { - return .just(.navigateTo(route: .error)) - } - } - } - } - } - - func handleAppleLogin() -> Observable { - return checkNotificationPermissionAndFetchFCMToken() - .withUnretained(self) - .flatMap { owner, fcmToken in - // 1. 애플 로그인 자격 증명 가져오기 - owner.fetchAppleCredentialUseCase.execute() - .flatMap { credential in - // 2. 자격 증명을 바탕으로 로그인 요청 - owner.loginWithAppleUseCase.execute(credential: credential) - .flatMap { response -> Observable in - if response.isRegister { - // 3. 회원가입된 유저면 FCM 토큰 등록 후 홈으로 이동 - return owner.putFCMTokenUseCase.execute(fcmToken: fcmToken) - .andThen(.just(.navigateTo(route: .home))) - } else { - // 4. 미가입 유저면 약관 동의 화면으로 이동 - return .just(.navigateTo(route: .termsAgreements( - credential: credential, - platform: .apple - ))) - } - } - .catch { error in - // 5. 로그인 실패 예외 처리 - if case AuthError.userNotFound = error { - return .just(.navigateTo(route: .termsAgreements( - credential: credential, - platform: .apple - ))) - } else { - return .just(.navigateTo(route: .error)) - } - } - } - } - } - - func checkNotificationPermissionAndFetchFCMToken() -> Observable { - return Observable.create { [weak self] observer in - guard let self else { - observer.onNext(nil) - observer.onCompleted() - return Disposables.create() - } - - UNUserNotificationCenter.current().getNotificationSettings { settings in - switch settings.authorizationStatus { - case .authorized, .provisional, .ephemeral: - let result = self.fetchTokenUseCase.execute(type: .fcmToken) - switch result { - case .success(let token): observer.onNext(token) - case .failure: observer.onNext(nil) - } - default: - observer.onNext(nil) - } - observer.onCompleted() - } - - return Disposables.create() - } - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/Login/LoginView.swift b/MLS/Presentation/AuthFeature/AuthFeature/Login/LoginView.swift deleted file mode 100644 index f6f1fa32..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/Login/LoginView.swift +++ /dev/null @@ -1,238 +0,0 @@ -import UIKit - -import DesignSystem -import DomainInterface - -import SnapKit - -final class LoginView: UIView { - // MARK: - Type - private enum Constant { - static let buttonLogoImageSize: CGFloat = 18 - static let buttonLogoImageLeadingInset: CGFloat = 14 - static let buttonHeight: CGFloat = 44 - static let buttonCornerRadius: CGFloat = 8 - static let buttonSpacing: CGFloat = 8 - static let buttonStackViewBottomInset: CGFloat = 16 - static let horizontalInset: CGFloat = 16 - static let buttonCenterXInset: CGFloat = buttonLogoImageLeadingInset + buttonLogoImageSize - static let labelHeight: CGFloat = 28 - static let subTitleBottomSpacing: CGFloat = -25 - static let recentLogoWidth: CGFloat = 82 - static let recentLogoHeight: CGFloat = 30 - static let recentLogoInset: CGFloat = 8 - } - - // MARK: - Properties - public let header = NavigationBar(type: .arrowLeft) - - private let loginImageView: UIImageView = { - let image = DesignSystemAsset.image(named: "Login_KV_img") - let view = UIImageView(image: image) - return view - }() - - private let buttonStackView: UIStackView = { - let view = UIStackView() - view.axis = .vertical - view.spacing = Constant.buttonSpacing - return view - }() - - let kakaoLoginButton: UIButton = { - let button = UIButton() - button.backgroundColor = .init(hexCode: "#FEE500", alpha: 1) - button.layer.cornerRadius = Constant.buttonCornerRadius - return button - }() - - private let kakaoLogoImageView: UIImageView = { - let image = DesignSystemAsset.image(named: "kakaoLogo") - let view = UIImageView(image: image) - view.contentMode = .scaleAspectFit - return view - }() - - private let kakaoLoginLabel: UILabel = { - let label = UILabel() - label.attributedText = .makeStyledString(font: .korFont(style: .semiBold, size: 15), text: "카카오로 계속하기", color: .init(hexCode: "#000000", alpha: 0.85)) - return label - }() - - let appleLoginButton: UIButton = { - let button = UIButton() - button.backgroundColor = .init(hexCode: "#000000", alpha: 1) - button.layer.cornerRadius = Constant.buttonCornerRadius - return button - }() - - private let appleLogoImageView: UIImageView = { - let image = DesignSystemAsset.image(named: "appleLogo") - let view = UIImageView(image: image) - view.contentMode = .scaleAspectFit - return view - }() - - let appleLoginLabel: UILabel = { - let label = UILabel() - label.attributedText = .makeStyledString(font: .korFont(style: .semiBold, size: 15), text: "Apple로 계속하기", color: .init(hexCode: "#FFFFFF")) - return label - }() - - let guestLoginButton: CommonButton = { - let button = CommonButton(style: .text, title: "가입 없이 둘러보기", disabledTitle: "가입 없이 둘러보기") - return button - }() - - private let mainTitleLabel: UILabel = { - let label = UILabel() - label.attributedText = .makeStyledString(font: .h_xl_b, text: "모험가님,") - return label - }() - - private let subTitleLabel: UILabel = { - let label = UILabel() - label.attributedText = .makeStyledString(font: .h_xl_r, text: "다시 오신 걸 환영해요!") - return label - }() - - private let recentLoginImageView: UIImageView = { - let view = UIImageView(image: DesignSystemAsset.image(named: "recentLoginLogo")) - view.isHidden = true - return view - }() - - // MARK: - init - init() { - super.init(frame: .zero) - - addViews() - setupConstraints() - configureUI() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension LoginView { - func addViews() { - addSubview(loginImageView) - addSubview(buttonStackView) - addSubview(recentLoginImageView) - - buttonStackView.addArrangedSubview(kakaoLoginButton) - buttonStackView.addArrangedSubview(appleLoginButton) - - kakaoLoginButton.addSubview(kakaoLogoImageView) - kakaoLoginButton.addSubview(kakaoLoginLabel) - appleLoginButton.addSubview(appleLogoImageView) - appleLoginButton.addSubview(appleLoginLabel) - - addSubview(header) - } - - func setupConstraints() { - header.snp.makeConstraints { make in - make.top.horizontalEdges.equalTo(safeAreaLayoutGuide) - } - - loginImageView.snp.makeConstraints { make in - make.width.equalToSuperview() - make.height.equalTo(UIScreen.main.bounds.width * 1.49) - make.top.horizontalEdges.equalToSuperview() - } - - buttonStackView.snp.makeConstraints { make in - make.horizontalEdges.equalToSuperview().inset(Constant.horizontalInset) - make.bottom.equalToSuperview().inset(Constant.buttonStackViewBottomInset) - } - - kakaoLoginButton.snp.makeConstraints { make in - make.height.equalTo(Constant.buttonHeight) - } - - kakaoLogoImageView.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(Constant.buttonLogoImageLeadingInset) - make.size.equalTo(Constant.buttonLogoImageSize) - } - - kakaoLoginLabel.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.centerX.equalToSuperview().inset(Constant.buttonCenterXInset) - } - - appleLoginButton.snp.makeConstraints { make in - make.height.equalTo(Constant.buttonHeight) - } - - appleLogoImageView.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(Constant.buttonLogoImageLeadingInset) - make.size.equalTo(Constant.buttonLogoImageSize) - } - - appleLoginLabel.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.centerX.equalToSuperview().inset(Constant.buttonCenterXInset) - } - } - - func configureUI() {} -} - -extension LoginView { - func update(loginPlatform: LoginPlatform?) { - mainTitleLabel.removeFromSuperview() - subTitleLabel.removeFromSuperview() - guestLoginButton.removeFromSuperview() - - switch loginPlatform { - case .kakao, .apple: - // 최근로그인 라벨 추가 - addSubview(mainTitleLabel) - addSubview(subTitleLabel) - - subTitleLabel.snp.remakeConstraints { make in - make.bottom.equalTo(buttonStackView.snp.top).offset(Constant.subTitleBottomSpacing) - make.centerX.equalToSuperview() - make.height.equalTo(Constant.labelHeight) - } - mainTitleLabel.snp.remakeConstraints { make in - make.bottom.equalTo(subTitleLabel.snp.top) - make.centerX.equalToSuperview() - make.height.equalTo(Constant.labelHeight) - } - - recentLoginImageView.isHidden = false - - recentLoginImageView.snp.remakeConstraints { make in - switch loginPlatform { - case .apple: - make.leading.equalTo(appleLoginButton).offset(Constant.recentLogoInset) - make.bottom.equalTo(appleLoginButton.snp.top).offset(Constant.recentLogoInset) - case .kakao: - make.leading.equalTo(kakaoLoginButton).offset(Constant.recentLogoInset) - make.bottom.equalTo(kakaoLoginButton.snp.top).offset(Constant.recentLogoInset) - default: - break - } - make.width.equalTo(Constant.recentLogoWidth) - make.height.equalTo(Constant.recentLogoHeight) - } - case nil: - buttonStackView.addArrangedSubview(guestLoginButton) - guestLoginButton.snp.remakeConstraints { make in - make.height.equalTo(Constant.buttonHeight) - } - recentLoginImageView.isHidden = true - } - - setNeedsLayout() - layoutIfNeeded() - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/Login/LoginViewController.swift b/MLS/Presentation/AuthFeature/AuthFeature/Login/LoginViewController.swift deleted file mode 100644 index 0fc4c2ba..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/Login/LoginViewController.swift +++ /dev/null @@ -1,168 +0,0 @@ -import UIKit - -import AuthFeatureInterface -import BaseFeature - -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit - -public final class LoginViewController: BaseViewController, View { - public typealias Reactor = LoginReactor - - // MARK: - Properties - public var disposeBag = DisposeBag() - - public let routeToHome = PublishRelay() - - private let mainView: LoginView - - private let termsAgreementsFactory: TermsAgreementFactory - - public init(termsAgreementsFactory: TermsAgreementFactory) { - self.mainView = LoginView() - self.termsAgreementsFactory = termsAgreementsFactory - super.init() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -public extension LoginViewController { - override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - } -} - -// MARK: - SetUp -private extension LoginViewController { - func addViews() { - view.addSubview(mainView) - } - - func setupConstraints() { - mainView.snp.makeConstraints { make in - make.top.horizontalEdges.equalToSuperview() - make.bottom.equalTo(view.safeAreaLayoutGuide) - } - } - - func configureUI() { - view.backgroundColor = .systemBackground - - if let navigationController = navigationController, - navigationController.viewControllers.count > 1 { - mainView.header.leftButton.isHidden = false - } else { - mainView.header.leftButton.isHidden = true - } - } -} - -public extension LoginViewController { - func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindViewState(reactor: reactor) - } - - func bindUserActions(reactor: Reactor) { - rx.viewWillAppear - .map { _ in Reactor.Action.viewWillAppear } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.kakaoLoginButton.rx.tap - .map { Reactor.Action.kakaoLoginButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.kakaoLoginButton.rx.controlEvent(.touchDown) - .withUnretained(self) - .subscribe { owner, _ in - owner.mainView.kakaoLoginButton.backgroundColor = .init(hexCode: "#E5CE00") - } - .disposed(by: disposeBag) - - mainView.kakaoLoginButton.rx.controlEvent([.touchUpInside, .touchUpOutside, .touchCancel]) - .withUnretained(self) - .subscribe { owner, _ in - owner.mainView.kakaoLoginButton.backgroundColor = .init(hexCode: "#FEE500") - } - .disposed(by: disposeBag) - - mainView.appleLoginButton.rx.tap - .map { Reactor.Action.appleLoginButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.appleLoginButton.rx.controlEvent(.touchDown) - .withUnretained(self) - .subscribe { owner, _ in - owner.mainView.appleLoginLabel.textColor = .init(hexCode: "#E5E5E5") - } - .disposed(by: disposeBag) - - mainView.appleLoginButton.rx.controlEvent([.touchUpInside, .touchUpOutside, .touchCancel]) - .withUnretained(self) - .subscribe { owner, _ in - owner.mainView.appleLoginLabel.textColor = .whiteMLS - } - .disposed(by: disposeBag) - - mainView.guestLoginButton.rx.tap - .map { Reactor.Action.guestLoginButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.header.leftButton.rx.tap - .map { Reactor.Action.backButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - } - - func bindViewState(reactor: Reactor) { - reactor.state - .observe(on: MainScheduler.instance) - .map { $0.platform } - .distinctUntilChanged() - .withUnretained(self) - .subscribe { owner, platform in - owner.mainView.update(loginPlatform: platform) - } - .disposed(by: disposeBag) - - rx.viewDidAppear - .take(1) - .flatMapLatest { _ in reactor.pulse(\.$route) } - .observe(on: MainScheduler.instance) - .withUnretained(self) - .subscribe { owner, route in - switch route { - case .termsAgreements(let credential, let platform): - let controller = owner.termsAgreementsFactory.make(credential: credential, platform: platform) - owner.navigationController?.pushViewController(controller, animated: true) - case .home: - owner.routeToHome.accept(()) - case .error: - DispatchQueue.main.async { - let controller = BaseErrorViewController() - owner.present(controller, animated: true) - } - case .dismiss: - owner.navigationController?.popViewController(animated: true) - default: - break - } - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationFactoryImpl.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationFactoryImpl.swift deleted file mode 100644 index dbccc528..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationFactoryImpl.swift +++ /dev/null @@ -1,19 +0,0 @@ -import AuthFeatureInterface -import BaseFeature - -public struct OnBoardingNotificationFactoryImpl: OnBoardingNotificationFactory { - private let onBoardingNotificationSheetFactory: OnBoardingNotificationSheetFactory - private let appCoordinator: () -> AppCoordinatorProtocol - - public init(onBoardingNotificationSheetFactory: OnBoardingNotificationSheetFactory, appCoordinator: @escaping () -> AppCoordinatorProtocol) { - self.onBoardingNotificationSheetFactory = onBoardingNotificationSheetFactory - self.appCoordinator = appCoordinator - } - - public func make(selectedLevel: Int, selectedJobID: Int) -> BaseViewController { - let viewController = OnBoardingNotificationViewController(onBoardingNotificationSheetFactory: onBoardingNotificationSheetFactory, appCoordinator: appCoordinator()) - viewController.isBottomTabbarHidden = true - viewController.reactor = OnBoardingNotificationReactor(selectedLevel: selectedLevel, selectedJobID: selectedJobID) - return viewController - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationReactor.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationReactor.swift deleted file mode 100644 index 042dc04e..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationReactor.swift +++ /dev/null @@ -1,60 +0,0 @@ -import ReactorKit -import RxSwift - -public final class OnBoardingNotificationReactor: Reactor { - // MARK: - Reactor - public enum Route { - case none - case notificationAlert - case pop - case home - } - - public enum Action { - case nextButtonTapped - case skipButtonTapped - case backButtonTapped - } - - public enum Mutation { - case navigateTo(route: Route) - } - - public struct State { - @Pulse var route: Route = .none - let selectedLevel: Int - let selectedJobID: Int - } - - // MARK: - properties - public var initialState: State - var disposeBag = DisposeBag() - - // MARK: - init - public init(selectedLevel: Int, selectedJobID: Int) { - self.initialState = State(selectedLevel: selectedLevel, selectedJobID: selectedJobID) - } - - // MARK: - Reactor Methods - public func mutate(action: Action) -> Observable { - switch action { - case .nextButtonTapped: - return .just(.navigateTo(route: .notificationAlert)) - case .skipButtonTapped: - return .just(.navigateTo(route: .home)) - case .backButtonTapped: - return .just(.navigateTo(route: .pop)) - } - } - - public func reduce(state: State, mutation: Mutation) -> State { - var newState = state - - switch mutation { - case .navigateTo(let route): - newState.route = route - } - - return newState - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationView.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationView.swift deleted file mode 100644 index 2be88be7..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationView.swift +++ /dev/null @@ -1,82 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -public final class OnBoardingNotificationView: OnBoardingBaseView { - // MARK: - Type - private enum Constant { - static let horizontalInset = 16 - static let verticalInset = 16 - static let imgSize = 220 - static let resizeCenterY = 70 - } - - // MARK: - Components - private let imageView: UIImageView = { - let view = UIImageView() - view.image = DesignSystemAsset.image(named: "getNotify") - return view - }() - - private let boldTextLabel: UILabel = { - let label = UILabel() - label.attributedText = .makeStyledString(font: .h_xxl_b, text: "메이플랜드에서 이벤트가 생기면\n알림을 보내드리고 있어요") - label.numberOfLines = 2 - return label - }() - - private lazy var contentView: UIView = { - let view = UIView() - view.addSubview(imageView) - view.addSubview(boldTextLabel) - - imageView.snp.makeConstraints { make in - make.top.equalToSuperview() - make.centerX.equalToSuperview() - make.size.equalTo(Constant.imgSize) - } - - boldTextLabel.snp.makeConstraints { make in - make.top.equalTo(imageView.snp.bottom).offset(4) - make.horizontalEdges.bottom.equalToSuperview() - } - - return view - }() - - public let nextButton = CommonButton(style: .normal, title: "다음", disabledTitle: "") - - // MARK: - init - init() { - super.init() - addViews() - setupConstraints() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension OnBoardingNotificationView { - func addViews() { - addSubview(contentView) - addSubview(nextButton) - } - - func setupConstraints() { - contentView.snp.makeConstraints { make in - make.centerY.equalToSuperview().offset(-Constant.resizeCenterY) - make.horizontalEdges.equalToSuperview() - } - - nextButton.snp.makeConstraints { make in - make.horizontalEdges.equalToSuperview().inset(Constant.horizontalInset) - make.bottom.equalTo(safeAreaLayoutGuide).inset(Constant.verticalInset) - } - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationViewController.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationViewController.swift deleted file mode 100644 index 08566c23..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationViewController.swift +++ /dev/null @@ -1,112 +0,0 @@ -import UIKit -import UserNotifications - -import AuthFeatureInterface -import BaseFeature -import DictionaryFeatureInterface - -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit - -public class OnBoardingNotificationViewController: BaseViewController, View { - // MARK: - Properties - public typealias Reactor = OnBoardingNotificationReactor - - public var disposeBag = DisposeBag() - - private let onBoardingNotificationSheetFactory: OnBoardingNotificationSheetFactory - private let appCoordinator: AppCoordinatorProtocol - - // MARK: - Components - - private var mainView = OnBoardingNotificationView() - - public init(onBoardingNotificationSheetFactory: OnBoardingNotificationSheetFactory, appCoordinator: AppCoordinatorProtocol) { - self.onBoardingNotificationSheetFactory = onBoardingNotificationSheetFactory - self.appCoordinator = appCoordinator - super.init() - } - - @available(*, unavailable) - @MainActor required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -public extension OnBoardingNotificationViewController { - override func viewDidLoad() { - super.viewDidLoad() - configureUI() - } -} - -// MARK: - SetUp -private extension OnBoardingNotificationViewController { - func addViews() { - view.addSubview(mainView) - } - - func setupConstraints() { - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } - - func configureUI() { - addViews() - setupConstraints() - } -} - -// MARK: - Private Methods -private extension OnBoardingNotificationViewController {} - -// MARK: - Bind -public extension OnBoardingNotificationViewController { - func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindViewState(reactor: reactor) - } - - func bindUserActions(reactor: Reactor) { - mainView.nextButton.rx.tap - .map { Reactor.Action.nextButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.headerView.leftButton.rx.tap - .map { Reactor.Action.backButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.headerView.underlineTextButton.rx.tap - .map { Reactor.Action.skipButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - } - - func bindViewState(reactor: Reactor) { - rx.viewDidAppear - .take(1) - .flatMapLatest { _ in reactor.pulse(\.$route) } - .withUnretained(self) - .observe(on: MainScheduler.instance) - .subscribe { owner, route in - switch route { - case .notificationAlert: - let viewController = owner.onBoardingNotificationSheetFactory.make(selectedLevel: reactor.currentState.selectedLevel, selectedJobID: reactor.currentState.selectedJobID) - owner.presentModal(viewController, hideTabBar: true) - case .home: - owner.appCoordinator.showMainTab() - case .pop: - owner.navigationController?.popViewController(animated: true) - default: - break - } - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoarding/OnBoardingBaseView.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoarding/OnBoardingBaseView.swift deleted file mode 100644 index 3f25b5f4..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoarding/OnBoardingBaseView.swift +++ /dev/null @@ -1,44 +0,0 @@ -import UIKit - -import DesignSystem - -/// 온보딩 화면에서 사용하기 위한 헤더(타이틀 버튼 포함)를 가진 뷰 -public class OnBoardingBaseView: UIView { - // MARK: - Components - public let headerView: NavigationBar = { - let view = NavigationBar(type: .withUnderLine("다음에 하기")) - return view - }() - - // MARK: - init - init(leftButtonIsHidden: Bool = false, underlineTextButtonIsHidden: Bool = false) { - super.init(frame: .zero) - addViews() - setupConstraints() - configureUI() - if leftButtonIsHidden { headerView.leftButton.isHidden = true } - if underlineTextButtonIsHidden { headerView.underlineTextButton.isHidden = true } - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension OnBoardingBaseView { - func addViews() { - addSubview(headerView) - } - - func setupConstraints() { - headerView.snp.makeConstraints { make in - make.top.horizontalEdges.equalToSuperview() - } - } - - func configureUI() { - backgroundColor = .clearMLS - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputFactoryImpl.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputFactoryImpl.swift deleted file mode 100644 index 19243aeb..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputFactoryImpl.swift +++ /dev/null @@ -1,40 +0,0 @@ -import AuthFeatureInterface -import BaseFeature -import DictionaryFeatureInterface -import DomainInterface - -public struct OnBoardingInputFactoryImpl: OnBoardingInputFactory { - private let checkEmptyUseCase: CheckEmptyLevelAndRoleUseCase - private let checkValidLevelUseCase: CheckValidLevelUseCase - private let fetchJobListUseCase: FetchJobListUseCase - private let updateUserInfoUseCase: UpdateUserInfoUseCase - private let onBoardingNotificationFactory: OnBoardingNotificationFactory - private let appCoordinator: () -> AppCoordinatorProtocol - - public init( - checkEmptyUseCase: CheckEmptyLevelAndRoleUseCase, - checkValidLevelUseCase: CheckValidLevelUseCase, - fetchJobListUseCase: FetchJobListUseCase, - updateUserInfoUseCase: UpdateUserInfoUseCase, - onBoardingNotificationFactory: OnBoardingNotificationFactory, - appCoordinator: @escaping () -> AppCoordinatorProtocol - ) { - self.checkEmptyUseCase = checkEmptyUseCase - self.checkValidLevelUseCase = checkValidLevelUseCase - self.fetchJobListUseCase = fetchJobListUseCase - self.updateUserInfoUseCase = updateUserInfoUseCase - self.onBoardingNotificationFactory = onBoardingNotificationFactory - self.appCoordinator = appCoordinator - } - - public func make() -> BaseViewController { - let viewController = OnBoardingInputViewController(onBoardingNotificationFactory: onBoardingNotificationFactory, appCoordinator: appCoordinator()) - viewController.isBottomTabbarHidden = true - viewController.reactor = OnBoardingInputReactor( - checkEmptyUseCase: checkEmptyUseCase, - checkValidLevelUseCase: checkValidLevelUseCase, - fetchJobListUseCase: fetchJobListUseCase - ) - return viewController - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputReactor.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputReactor.swift deleted file mode 100644 index 84b244c8..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputReactor.swift +++ /dev/null @@ -1,115 +0,0 @@ -import DomainInterface - -import ReactorKit -import RxSwift - -public final class OnBoardingInputReactor: Reactor { - // MARK: - Reactor - public enum Route { - case none - case dismiss - case home - case notification - case error - } - - public enum Action { - case viewWillAppear - case backButtonTapped - case skipButtonTapped - case nextButtonTapped - case inputLevel(Int?) - case inputRole(Job?) - } - - public enum Mutation { - case setJobList(jobList: [Job]) - case setButtonEnabled(Bool) - case setLevelValid(Bool?) - case setLevel(Int?) - case setRole(Job?) - case navigateTo(route: Route) - } - - public struct State { - @Pulse var route: Route = .none - - var level: Int? - var job: Job? - var isButtonEnabled: Bool = false - var isLevelValid: Bool? - var jobList: [Job] = [] - } - - // MARK: - properties - public var initialState: State - var disposeBag = DisposeBag() - - private let checkEmptyUseCase: CheckEmptyLevelAndRoleUseCase - private let checkValidLevelUseCase: CheckValidLevelUseCase - private let fetchJobListUseCase: FetchJobListUseCase - - // MARK: - init - public init( - checkEmptyUseCase: CheckEmptyLevelAndRoleUseCase, - checkValidLevelUseCase: CheckValidLevelUseCase, - fetchJobListUseCase: FetchJobListUseCase - ) { - self.checkEmptyUseCase = checkEmptyUseCase - self.checkValidLevelUseCase = checkValidLevelUseCase - self.fetchJobListUseCase = fetchJobListUseCase - self.initialState = State() - } - - // MARK: - Reactor Methods - public func mutate(action: Action) -> Observable { - switch action { - case .viewWillAppear: - return fetchJobListUseCase.execute() - .map { response in - .setJobList(jobList: response.jobList) - } - .catchAndReturn(.navigateTo(route: .error)) - case .backButtonTapped: - return Observable.just(.navigateTo(route: .dismiss)) - case .skipButtonTapped: - return Observable.just(.navigateTo(route: .home)) - case .nextButtonTapped: - return Observable.just(.navigateTo(route: .notification)) - case .inputLevel(let level): - let changeLevel = Observable.just(Mutation.setLevel(level)) - let validateJob = checkEmptyUseCase.execute(level: level, job: currentState.job?.name) - .map(Mutation.setButtonEnabled) - let validateLevel = checkValidLevelUseCase.execute(level: level) - .map(Mutation.setLevelValid) - return .merge(changeLevel, validateJob, validateLevel) - case .inputRole(let job): - return checkEmptyUseCase.execute(level: currentState.level, job: job?.name) - .map { isValid in - [.setRole(job), .setButtonEnabled(isValid)] - } - .flatMap { Observable.from($0) } - } - } - - public func reduce(state: State, mutation: Mutation) -> State { - var newState = state - - switch mutation { - case .setJobList(let jobList): - newState.jobList = jobList - case .setButtonEnabled(let isEnabled): - newState.isButtonEnabled = isEnabled - case .setLevelValid(let isValid): - newState.isLevelValid = isValid - case .setLevel(let level): - newState.level = level - case .setRole(let role): - newState.job = role - case .navigateTo(let route): - newState.route = route - } - - return newState - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputView.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputView.swift deleted file mode 100644 index e21e0f05..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputView.swift +++ /dev/null @@ -1,57 +0,0 @@ -import UIKit - -import BaseFeature -import DesignSystem - -import RxCocoa -import RxSwift -import SnapKit - -public final class OnBoardingInputView: CharacterInputView { - // MARK: - Type - - // MARK: - Properties - - // MARK: - Components - public let headerView: NavigationBar = { - let view = NavigationBar(type: .withUnderLine("다음에 하기")) - return view - }() - - // MARK: - init - init(leftButtonIsHidden: Bool = false, underlineTextButtonIsHidden: Bool = false) { - super.init() - addViews() - setupConstraints() - configureUI() - if leftButtonIsHidden { headerView.leftButton.isHidden = true } - if underlineTextButtonIsHidden { headerView.underlineTextButton.isHidden = true } - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension OnBoardingInputView { - func addViews() { - addSubview(headerView) - } - - func setupConstraints() { - headerView.snp.makeConstraints { make in - make.top.horizontalEdges.equalToSuperview() - } - - descriptionLabel.snp.makeConstraints { make in - make.top.equalTo(headerView.snp.bottom).offset(Constant.verticalInset) - make.horizontalEdges.equalToSuperview().inset(Constant.horizontalInset) - } - } - - func configureUI() { - backgroundColor = .clearMLS - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputViewController.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputViewController.swift deleted file mode 100644 index 1da435e4..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputViewController.swift +++ /dev/null @@ -1,161 +0,0 @@ -import UIKit - -import AuthFeatureInterface -import BaseFeature -import DesignSystem -import DictionaryFeatureInterface - -import ReactorKit -import RxCocoa -import RxKeyboard -import RxSwift -import SnapKit - -public class OnBoardingInputViewController: BaseViewController, View { - // MARK: - Properties - public typealias Reactor = OnBoardingInputReactor - - public var disposeBag = DisposeBag() - - private let onBoardingNotificationFactory: OnBoardingNotificationFactory - private let appCoordinator: AppCoordinatorProtocol - - // MARK: - Components - - private var mainView = OnBoardingInputView() - - init(onBoardingNotificationFactory: OnBoardingNotificationFactory, appCoordinator: AppCoordinatorProtocol) { - self.onBoardingNotificationFactory = onBoardingNotificationFactory - self.appCoordinator = appCoordinator - super.init() - } -} - -// MARK: - Life Cycle -public extension OnBoardingInputViewController { - override func viewDidLoad() { - super.viewDidLoad() - addViews() - setupConstraints() - configureUI() - } -} - -// MARK: - SetUp -private extension OnBoardingInputViewController { - func addViews() { - view.addSubview(mainView) - } - - func setupConstraints() { - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } - - func configureUI() { - setupKeyboard() - } - - func setupKeyboard() { - setupKeyboard(inset: OnBoardingInputView.Constant.bottomInset) { [weak self] height in - self?.mainView.nextButtonBottomConstraint?.update(inset: height) - } - } -} - -// MARK: - Bind -public extension OnBoardingInputViewController { - func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindViewState(reactor: reactor) - } - - func bindUserActions(reactor: Reactor) { - rx.viewWillAppear - .map { Reactor.Action.viewWillAppear } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.nextButton.rx.tap - .map { Reactor.Action.nextButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.inputBox.textField.rx.text.orEmpty - .map { text -> Int? in - Int(text) - } - .map { Reactor.Action.inputLevel($0) } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.dropDownBox.onItemSelected = { [weak self] job in - guard let self = self else { return } - self.reactor?.action.onNext(.inputRole(.init(name: job.name, id: job.id))) - } - - mainView.headerView.leftButton.rx.tap - .map { Reactor.Action.backButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.headerView.underlineTextButton.rx.tap - .map { Reactor.Action.skipButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - } - - func bindViewState(reactor: Reactor) { - reactor.state - .map { $0.jobList } - .observe(on: MainScheduler.instance) - .distinctUntilChanged() - .withUnretained(self) - .subscribe { owner, list in - owner.mainView.dropDownBox.items = list.map { .init(name: $0.name, id: $0.id) } - } - .disposed(by: disposeBag) - - reactor.state - .map { $0.isLevelValid } - .distinctUntilChanged() - .withUnretained(self) - .subscribe { owner, isLevelValid in - guard let isLevelValid = isLevelValid else { return } - owner.mainView.inputBox.setType(type: isLevelValid ? InputBoxType.edit : InputBoxType.error) - owner.mainView.errorMessage.isHidden = isLevelValid - } - .disposed(by: disposeBag) - - reactor.state - .map { $0.isButtonEnabled } - .bind(to: mainView.nextButton.rx.isEnabled) - .disposed(by: disposeBag) - - rx.viewDidAppear - .take(1) - .flatMapLatest { _ in reactor.pulse(\.$route) } - .observe(on: MainScheduler.instance) - .withUnretained(self) - .subscribe(onNext: { owner, route in - switch route { - case .dismiss: - owner.navigationController?.popViewController(animated: true) - case .home: - owner.appCoordinator.showMainTab() - case .error: - let errorViewController = BaseErrorViewController() - owner.present(errorViewController, animated: true) - case .notification: - guard let selecteLevel = reactor.currentState.level, - let selectedJobID = reactor.currentState.job?.id else { return } - let viewController = owner.onBoardingNotificationFactory.make(selectedLevel: selecteLevel, selectedJobID: selectedJobID) - owner.navigationController?.pushViewController(viewController, animated: true) - default: - break - } - }) - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetFactoryImpl.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetFactoryImpl.swift deleted file mode 100644 index 97613d4e..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetFactoryImpl.swift +++ /dev/null @@ -1,40 +0,0 @@ -import AuthFeatureInterface -import BaseFeature -import DictionaryFeatureInterface -import DomainInterface - -public struct OnBoardingNotificationSheetFactoryImpl: OnBoardingNotificationSheetFactory { - private let checkNotificationPermissionUseCase: CheckNotificationPermissionUseCase - private let openNotificationSettingUseCase: OpenNotificationSettingUseCase - private let updateNotificationAgreementUseCase: UpdateNotificationAgreementUseCase - private let updateUserInfoUseCase: UpdateUserInfoUseCase - private let appCoordinator: () -> AppCoordinatorProtocol - - public init( - checkNotificationPermissionUseCase: CheckNotificationPermissionUseCase, - openNotificationSettingUseCase: OpenNotificationSettingUseCase, - updateNotificationAgreementUseCase: UpdateNotificationAgreementUseCase, - updateUserInfoUseCase: UpdateUserInfoUseCase, - appCoordinator: @escaping () -> AppCoordinatorProtocol - ) { - self.checkNotificationPermissionUseCase = checkNotificationPermissionUseCase - self.openNotificationSettingUseCase = openNotificationSettingUseCase - self.updateNotificationAgreementUseCase = updateNotificationAgreementUseCase - self.updateUserInfoUseCase = updateUserInfoUseCase - self.appCoordinator = appCoordinator - } - - public func make(selectedLevel: Int, selectedJobID: Int) -> BaseViewController & ModalPresentable { - let viewController = OnBoardingNotificationSheetViewController(appCoordinator: appCoordinator()) - viewController.isBottomTabbarHidden = true - viewController.reactor = OnBoardingNotificationSheetReactor( - selectedLevel: selectedLevel, - selectedJobID: selectedJobID, - checkNotificationPermissionUseCase: checkNotificationPermissionUseCase, - openNotificationSettingUseCase: openNotificationSettingUseCase, - updateNotificationAgreementUseCase: updateNotificationAgreementUseCase, - updateUserInfoUseCase: updateUserInfoUseCase - ) - return viewController - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetReactor.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetReactor.swift deleted file mode 100644 index b9e1f8ce..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetReactor.swift +++ /dev/null @@ -1,113 +0,0 @@ -import DomainInterface - -import ReactorKit -import RxCocoa -import RxSwift - -public final class OnBoardingNotificationSheetReactor: Reactor { - public enum Route { - case none - case dismiss - case home - case setting - } - - // MARK: - Reactor - public enum Action { - case viewWillAppear - case toggleSwitchButton(Bool) - case setButtonTapped - case cancelButtonTapped - case applyButtonTapped - case skipButtonTapped - case updateAuthorization(Bool) - case appWillEnterForeground - } - - public enum Mutation { - case navigateTo(route: Route) - case setLocalNotification(Bool) - case setRemoteNotification(Bool) - case setAuthorized(Bool) - } - - public struct State { - @Pulse var route: Route = .none - var selectedLevel: Int - var selectedJobID: Int - var isAgreeLocalNotification = false - var isAgreeRemoteNotification = true - } - - // MARK: - properties - public var initialState: State - var disposeBag = DisposeBag() - - private let checkNotificationPermissionUseCase: CheckNotificationPermissionUseCase - private let openNotificationSettingUseCase: OpenNotificationSettingUseCase - private let updateNotificationAgreementUseCase: UpdateNotificationAgreementUseCase - private let updateUserInfoUseCase: UpdateUserInfoUseCase - - // MARK: - init - public init( - selectedLevel: Int, - selectedJobID: Int, - checkNotificationPermissionUseCase: CheckNotificationPermissionUseCase, - openNotificationSettingUseCase: OpenNotificationSettingUseCase, - updateNotificationAgreementUseCase: UpdateNotificationAgreementUseCase, - updateUserInfoUseCase: UpdateUserInfoUseCase - ) { - self.initialState = State(selectedLevel: selectedLevel, selectedJobID: selectedJobID) - self.checkNotificationPermissionUseCase = checkNotificationPermissionUseCase - self.openNotificationSettingUseCase = openNotificationSettingUseCase - self.updateNotificationAgreementUseCase = updateNotificationAgreementUseCase - self.updateUserInfoUseCase = updateUserInfoUseCase - } - - // MARK: - Reactor Methods - public func mutate(action: Action) -> Observable { - switch action { - case .viewWillAppear, .appWillEnterForeground: - return checkNotificationPermissionUseCase.execute() - .asObservable() - .map { .setLocalNotification($0) } - case .toggleSwitchButton(let isAgree): - return .just(.setRemoteNotification(isAgree)) - case .setButtonTapped: - openNotificationSettingUseCase.execute() - return .just(.navigateTo(route: .setting)) - case .applyButtonTapped: - return updateUserInfoUseCase.execute(level: currentState.selectedLevel, selectedJobID: currentState.selectedJobID) - .andThen(updateNotificationAgreementUseCase.execute( - noticeAgreement: true, - patchNoteAgreement: true, - eventAgreement: true - )) - .andThen(Observable.just(.navigateTo(route: .home))) - .catchAndReturn(.navigateTo(route: .dismiss)) - case .cancelButtonTapped: - return .just(.navigateTo(route: .dismiss)) - case .skipButtonTapped: - return .just(.navigateTo(route: .home)) - case .updateAuthorization(let authorized): - return .just(.setAuthorized(authorized)) - } - } - - public func reduce(state: State, mutation: Mutation) -> State { - var newState = state - - switch mutation { - case .navigateTo(let route): - newState.route = route - case .setLocalNotification(let isAgree): - newState.isAgreeLocalNotification = isAgree - case .setRemoteNotification(let isAgree): - newState.isAgreeRemoteNotification = isAgree - case let .setAuthorized(isAuthorized): - newState.isAgreeLocalNotification = isAuthorized - } - - return newState - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetView.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetView.swift deleted file mode 100644 index aacb3853..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetView.swift +++ /dev/null @@ -1,100 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -final class OnBoardingNotificationSheetView: UIView { - private enum Constant { - static let inset: CGFloat = 16 - static let spacing: CGFloat = 14 - static let buttonTopMargin: CGFloat = 20 - } - - // MARK: - Properties - let header: Header = { - let header = Header(style: .filter, title: "신규 이벤트 알림 설정") - return header - }() - - private let descriptionLabel: UILabel = { - let label = UILabel() - label.numberOfLines = 2 - return label - }() - - private let buttonStackView: UIStackView = { - let view = UIStackView() - view.axis = .vertical - view.spacing = Constant.spacing - return view - }() - - let notificationToggleBox = ToggleBox(text: "알림 설정") - let applyButton = CommonButton(style: .normal, title: "적용", disabledTitle: nil) - let settingButton = CommonButton(style: .normal, title: "변경하기", disabledTitle: nil) - let skipButton = CommonButton(style: .border, title: "나중에 하기", disabledTitle: nil) - - // MARK: - init - init() { - super.init(frame: .zero) - - addViews() - setupConstraints() - configureUI() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension OnBoardingNotificationSheetView { - func addViews() { - addSubview(header) - addSubview(descriptionLabel) - addSubview(buttonStackView) - } - - func setupConstraints() { - header.snp.makeConstraints { make in - make.top.equalToSuperview().inset(Constant.inset) - make.horizontalEdges.equalToSuperview() - } - - descriptionLabel.snp.makeConstraints { make in - make.top.equalTo(header.snp.bottom).offset(Constant.spacing) - make.horizontalEdges.equalToSuperview().inset(Constant.inset) - } - - buttonStackView.snp.makeConstraints { make in - make.top.equalTo(descriptionLabel.snp.bottom).offset(Constant.buttonTopMargin) - make.horizontalEdges.bottom.equalToSuperview().inset(Constant.inset) - } - } - - func configureUI() { - notificationToggleBox.toggle.isOn = true - } -} - -// MARK: - Methods -extension OnBoardingNotificationSheetView { - func setUI(isAgree: Bool) { - buttonStackView.arrangedSubviews.forEach { view in - buttonStackView.removeArrangedSubview(view) - view.removeFromSuperview() - } - - descriptionLabel.attributedText = .makeStyledString(font: .cp_s_r, text: isAgree ? "메이플랜드 이벤트 소식을\n푸시 알림으로 빠르게 받아보세요." : "기기 알림 설정을 변경해야 이벤트 소식을 받을 수 있어요.", color: .neutral700, alignment: .left) - if isAgree { - buttonStackView.addArrangedSubview(notificationToggleBox) - buttonStackView.addArrangedSubview(applyButton) - } else { - buttonStackView.addArrangedSubview(settingButton) - buttonStackView.addArrangedSubview(skipButton) - } - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetViewController.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetViewController.swift deleted file mode 100644 index ba30890a..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetViewController.swift +++ /dev/null @@ -1,157 +0,0 @@ -import UIKit - -import BaseFeature -import DesignSystem -import DictionaryFeatureInterface - -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit - -public final class OnBoardingNotificationSheetViewController: BaseViewController, ModalPresentable, View { - public var modalHeight: CGFloat? - - public typealias Reactor = OnBoardingNotificationSheetReactor - - public var disposeBag = DisposeBag() - - // MARK: - Properties - - private let appCoordinator: AppCoordinatorProtocol - - // MARK: - Components - private var mainView = OnBoardingNotificationSheetView() - - init(appCoordinator: AppCoordinatorProtocol) { - self.appCoordinator = appCoordinator - super.init() - } -} - -// MARK: - Life Cycle -public extension OnBoardingNotificationSheetViewController { - override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - } -} - -// MARK: - SetUp -private extension OnBoardingNotificationSheetViewController { - func addViews() { - view.addSubview(mainView) - } - - func setupConstraints() { - mainView.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - } -} - -extension OnBoardingNotificationSheetViewController { - public func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindViewState(reactor: reactor) - } - - func bindUserActions(reactor: Reactor) { - rx.viewWillAppear - .take(1) - .do(onNext: { [weak self] _ in - self?.checkNotificationAuthorization() - }) - .map { _ in Reactor.Action.viewWillAppear } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - NotificationCenter.default.rx.notification(UIApplication.willEnterForegroundNotification) - .map { _ in Reactor.Action.appWillEnterForeground } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.header.firstIconButton.rx.tap - .map { Reactor.Action.cancelButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.skipButton.rx.tap - .map { Reactor.Action.skipButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.settingButton.rx.tap - .map { Reactor.Action.setButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.applyButton.rx.tap - .map { Reactor.Action.applyButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.notificationToggleBox.toggle.rx.isOn - .map { Reactor.Action.toggleSwitchButton($0) } - .bind(to: reactor.action) - .disposed(by: disposeBag) - } - - func bindViewState(reactor: Reactor) { - reactor.state - .observe(on: MainScheduler.instance) - .map { $0.isAgreeLocalNotification } - .withUnretained(self) - .subscribe { owner, isAgree in - owner.mainView.setUI(isAgree: isAgree) - } - .disposed(by: disposeBag) - - rx.viewDidAppear - .take(1) - .observe(on: MainScheduler.instance) - .flatMapLatest { _ in reactor.pulse(\.$route) } - .withUnretained(self) - .subscribe { owner, route in - DispatchQueue.main.async { - switch route { - case .dismiss: - owner.dismissCurrentModal() - case .home: - owner.appCoordinator.showMainTab() - case .setting: - guard let url = URL(string: UIApplication.openSettingsURLString), - UIApplication.shared.canOpenURL(url) else { return } - UIApplication.shared.open(url, options: [:], completionHandler: nil) - default: - break - } - } - } - .disposed(by: disposeBag) - } -} - -// MARK: - Notification Authorization -private extension OnBoardingNotificationSheetViewController { - func checkNotificationAuthorization() { - guard let reactor = reactor else { return } - - NotificationPermissionManager.shared.getStatus { status in - switch status { - case .authorized, .provisional: - reactor.action.onNext(.updateAuthorization(true)) - case .denied: - reactor.action.onNext(.updateAuthorization(false)) - case .notDetermined: - NotificationPermissionManager.shared.requestIfNeeded { granted in - reactor.action.onNext(.updateAuthorization(granted)) - } - default: - reactor.action.onNext(.updateAuthorization(false)) - } - } - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionFactoryImpl.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionFactoryImpl.swift deleted file mode 100644 index 051cb8ba..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionFactoryImpl.swift +++ /dev/null @@ -1,18 +0,0 @@ -import AuthFeatureInterface -import BaseFeature - -public struct OnBoardingQuestionFactoryImpl: OnBoardingQuestionFactory { - - private let onBoardingInputFactory: OnBoardingInputFactory - - public init(onBoardingInputFactory: OnBoardingInputFactory) { - self.onBoardingInputFactory = onBoardingInputFactory - } - - public func make() -> BaseViewController { - let viewController = OnBoardingQuestionViewController(factory: onBoardingInputFactory) - viewController.isBottomTabbarHidden = true - viewController.reactor = OnBoardingQuestionReactor() - return viewController - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionReactor.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionReactor.swift deleted file mode 100644 index 746674fc..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionReactor.swift +++ /dev/null @@ -1,65 +0,0 @@ -import ReactorKit -import RxSwift - -public final class OnBoardingQuestionReactor: Reactor { - // MARK: - Reactor - public enum Route { - case none - case dismiss - case home - case input - } - - public enum Action { - case viewDidLoad - case nextButtonTapped - case backButtonTapped - case skipButtonTapped - } - - public enum Mutation { - case showToast - case navigateTo(route: Route) - } - - public struct State { - @Pulse var route: Route = .none - @Pulse var isShowToast: Bool = false - } - - // MARK: - properties - public var initialState: State - var disposeBag = DisposeBag() - - // MARK: - init - public init() { - self.initialState = State() - } - - // MARK: - Reactor Methods - public func mutate(action: Action) -> Observable { - switch action { - case .viewDidLoad: - return Observable.just(.showToast) - case .nextButtonTapped: - return Observable.just(.navigateTo(route: .input)) - case .backButtonTapped: - return Observable.just(.navigateTo(route: .dismiss)) - case .skipButtonTapped: - return Observable.just(.navigateTo(route: .home)) - } - } - - public func reduce(state: State, mutation: Mutation) -> State { - var newState = state - - switch mutation { - case .showToast: - newState.isShowToast.toggle() - case .navigateTo(let route): - newState.route = route - } - - return newState - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionView.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionView.swift deleted file mode 100644 index 160c0e83..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionView.swift +++ /dev/null @@ -1,94 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -public final class OnBoardingQuestionView: OnBoardingBaseView { - // MARK: - Type - private enum Constant { - static let horizontalInset = 16 - static let verticalInset = 16 - static let imgSize = 220 - static let resizeCenterY = 70 - } - - // MARK: - Components - private let imageView: UIImageView = { - let view = UIImageView() - view.image = DesignSystemAsset.image(named: "questionNotify") - return view - }() - - private let boldTextLabel: UILabel = { - let label = UILabel() - label.attributedText = .makeStyledString(font: .h_xxxl_b, text: "효율적인 메이플랜드 플레이를\n위해 몇가지만 물어볼게요") - label.numberOfLines = 2 - return label - }() - - private let regularTeextLabel: UILabel = { - let label = UILabel() - label.attributedText = .makeStyledString(font: .b_m_r, text: "나도 예티를 잡을 수 있을까?", color: .neutral700) - return label - }() - - private lazy var contentView: UIView = { - let view = UIView() - view.addSubview(imageView) - view.addSubview(boldTextLabel) - view.addSubview(regularTeextLabel) - - imageView.snp.makeConstraints { make in - make.top.equalToSuperview() - make.centerX.equalToSuperview() - make.size.equalTo(Constant.imgSize) - } - - boldTextLabel.snp.makeConstraints { make in - make.top.equalTo(imageView.snp.bottom).offset(4) - make.horizontalEdges.equalToSuperview() - } - - regularTeextLabel.snp.makeConstraints { make in - make.top.equalTo(boldTextLabel.snp.bottom).offset(Constant.verticalInset) - make.horizontalEdges.bottom.equalToSuperview() - } - - return view - }() - - public let nextButton = CommonButton(style: .normal, title: "다음", disabledTitle: "") - - // MARK: - init - init() { - super.init(leftButtonIsHidden: true, underlineTextButtonIsHidden: true) - addViews() - setupConstraints() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension OnBoardingQuestionView { - func addViews() { - addSubview(contentView) - addSubview(nextButton) - } - - func setupConstraints() { - contentView.snp.makeConstraints { make in - make.centerY.equalToSuperview().offset(-Constant.resizeCenterY) - make.horizontalEdges.equalToSuperview() - } - - nextButton.snp.makeConstraints { make in - make.horizontalEdges.equalToSuperview().inset(Constant.horizontalInset) - make.bottom.equalTo(safeAreaLayoutGuide).inset(Constant.verticalInset) - } - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionViewController.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionViewController.swift deleted file mode 100644 index c1dbcf47..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionViewController.swift +++ /dev/null @@ -1,119 +0,0 @@ -import UIKit - -import AuthFeatureInterface -import BaseFeature - -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit - -public class OnBoardingQuestionViewController: BaseViewController, View { - // MARK: - Properties - public typealias Reactor = OnBoardingQuestionReactor - - public var disposeBag = DisposeBag() - - private let onBoardingInputFactory: OnBoardingInputFactory - - // MARK: - Components - - private var mainView = OnBoardingQuestionView() - - public init(factory: OnBoardingInputFactory) { - self.onBoardingInputFactory = factory - super.init() - } - - @available(*, unavailable) - @MainActor required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -public extension OnBoardingQuestionViewController { - override func viewDidLoad() { - super.viewDidLoad() - addViews() - setupConstraints() - } -} - -// MARK: - SetUp -private extension OnBoardingQuestionViewController { - func addViews() { - view.addSubview(mainView) - } - - func setupConstraints() { - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -// MARK: - Bind -public extension OnBoardingQuestionViewController { - func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindViewState(reactor: reactor) - } - - func bindUserActions(reactor: Reactor) { - rx.viewDidLoad - .map { Reactor.Action.viewDidLoad } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.nextButton.rx.tap - .map { Reactor.Action.nextButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.headerView.leftButton.rx.tap - .map { Reactor.Action.backButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.headerView.underlineTextButton.rx.tap - .map { Reactor.Action.skipButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - } - - func bindViewState(reactor: Reactor) { - reactor.pulse(\.$isShowToast) - .subscribe { isShowToast in - if isShowToast { - let currentDate = Date() - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy.MM.dd" - let formattedDate = dateFormatter.string(from: currentDate) - ToastFactory.createToast(message: "\(formattedDate) 약관에 동의했어요.") - } - } - .disposed(by: disposeBag) - - rx.viewDidAppear - .take(1) - .flatMapLatest { _ in reactor.pulse(\.$route) } - .withUnretained(self) - .subscribe { owner, route in - switch route { - case .dismiss: - owner.navigationController?.popViewController(animated: true) - case .home: - let homeViewController = UIViewController() - homeViewController.view.backgroundColor = .green - owner.navigationController?.pushViewController(homeViewController, animated: true) - case .input: - let inputViewController = owner.onBoardingInputFactory.make() - owner.navigationController?.pushViewController(inputViewController, animated: true) - default: - break - } - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementFactoryImpl.swift b/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementFactoryImpl.swift deleted file mode 100644 index e069897f..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementFactoryImpl.swift +++ /dev/null @@ -1,39 +0,0 @@ -import AuthFeatureInterface -import BaseFeature -import DomainInterface - -public struct TermsAgreementFactoryImpl: TermsAgreementFactory { - private let onBoardingQuestionFactory: OnBoardingQuestionFactory - - private let signUpWithKakaoUseCase: SignUpWithKakaoUseCase - private let signUpWithAppleUseCase: SignUpWithAppleUseCase - private let fetchTokenUseCase: FetchTokenFromLocalUseCase - private let updateMarketingAgreementUseCase: UpdateMarketingAgreementUseCase - - public init( - onBoardingQuestionFactory: OnBoardingQuestionFactory, - signUpWithKakaoUseCase: SignUpWithKakaoUseCase, - signUpWithAppleUseCase: SignUpWithAppleUseCase, - fetchTokenUseCase: FetchTokenFromLocalUseCase, - updateMarketingAgreementUseCase: UpdateMarketingAgreementUseCase - ) { - self.onBoardingQuestionFactory = onBoardingQuestionFactory - self.signUpWithKakaoUseCase = signUpWithKakaoUseCase - self.signUpWithAppleUseCase = signUpWithAppleUseCase - self.fetchTokenUseCase = fetchTokenUseCase - self.updateMarketingAgreementUseCase = updateMarketingAgreementUseCase - } - - public func make(credential: Credential, platform: LoginPlatform) -> BaseViewController { - let viewController = TermsAgreementViewController(onBoardingQuestionFactory: onBoardingQuestionFactory) - viewController.isBottomTabbarHidden = true - viewController.reactor = TermsAgreementReactor( - credential: credential, - socialPlatform: platform, - signUpWithKakaoUseCase: signUpWithKakaoUseCase, - signUpWithAppleUseCase: signUpWithAppleUseCase, - fetchTokenUseCase: fetchTokenUseCase, updateMarketingAgreementUseCase: updateMarketingAgreementUseCase - ) - return viewController - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementReactor.swift b/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementReactor.swift deleted file mode 100644 index 3aeb36ce..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementReactor.swift +++ /dev/null @@ -1,155 +0,0 @@ -import DomainInterface -import NotificationCenter - -import ReactorKit -import RxSwift - -public final class TermsAgreementReactor: Reactor { - // MARK: - Type - public enum AgreeType { - case total, age, serviceTerms, personalInfo, marketing - } - - public enum Route { - case none - case dismiss - case onBoarding - case error - case ageAgreement - case serviceAgreement - case personalAgreement - case marketingAgreement - } - - // MARK: - Reactor - public enum Action { - case backButtonTapped - case toggleAgree(type: AgreeType) - case bottomButtonTapped - case navigateTo(route: Route) - } - - public enum Mutation { - case setAgreeState(type: AgreeType, isOn: Bool) - case navigateTo(route: Route) - } - - public struct State { - @Pulse var route: Route = .none - - var isTotalAgree: Bool = false - var isAgeAgree: Bool = false - var isServiceTermsAgree: Bool = false - var isPersonalInformationAgree: Bool = false - var isMarketingAgree: Bool = false - var bottomButtonIsEnabled: Bool = false - } - - // MARK: - properties - public var initialState: State - var disposeBag = DisposeBag() - private let credential: Credential - private let socialPlatform: LoginPlatform - private let signUpWithKakaoUseCase: SignUpWithKakaoUseCase - private let signUpWithAppleUseCase: SignUpWithAppleUseCase - private let fetchTokenUseCase: FetchTokenFromLocalUseCase - private let updateMarketingAgreementUseCase: UpdateMarketingAgreementUseCase - - // MARK: - init - public init( - credential: Credential, - socialPlatform: LoginPlatform, - signUpWithKakaoUseCase: SignUpWithKakaoUseCase, - signUpWithAppleUseCase: SignUpWithAppleUseCase, - fetchTokenUseCase: FetchTokenFromLocalUseCase, - updateMarketingAgreementUseCase: UpdateMarketingAgreementUseCase - ) { - self.credential = credential - self.socialPlatform = socialPlatform - self.signUpWithKakaoUseCase = signUpWithKakaoUseCase - self.signUpWithAppleUseCase = signUpWithAppleUseCase - self.fetchTokenUseCase = fetchTokenUseCase - self.updateMarketingAgreementUseCase = updateMarketingAgreementUseCase - self.initialState = State() - } - - // MARK: - Reactor Methods - public func mutate(action: Action) -> Observable { - switch action { - case .backButtonTapped: - return .just(.navigateTo(route: .dismiss)) - case .toggleAgree(let type): - let isOn: Bool - switch type { - case .total: - isOn = !currentState.isTotalAgree - case .age: - isOn = !currentState.isAgeAgree - case .serviceTerms: - isOn = !currentState.isServiceTermsAgree - case .personalInfo: - isOn = !currentState.isPersonalInformationAgree - case .marketing: - isOn = !currentState.isMarketingAgree - } - return .just(.setAgreeState(type: type, isOn: isOn)) - case .bottomButtonTapped: - let fcmToken: String? = { - if case .success(let token) = fetchTokenUseCase.execute(type: .fcmToken) { - return token - } else { - return nil - } - }() - - switch socialPlatform { - case .kakao: - return signUpWithKakaoUseCase - .execute(credential: credential, isMarketingAgreement: currentState.isMarketingAgree, fcmToken: fcmToken) - .map { _ in .navigateTo(route: .onBoarding) } - .catchAndReturn(.navigateTo(route: .error)) - - case .apple: - return signUpWithAppleUseCase - .execute(credential: credential, isMarketingAgreement: currentState.isMarketingAgree, fcmToken: fcmToken) - .map { _ in .navigateTo(route: .onBoarding) } - .catchAndReturn(.navigateTo(route: .error)) - } - - case .navigateTo(let route): - return .just(.navigateTo(route: route)) - } - } - - public func reduce(state: State, mutation: Mutation) -> State { - var newState = state - switch mutation { - case .setAgreeState(let type, let isOn): - switch type { - case .total: - newState.isTotalAgree = isOn - newState.isAgeAgree = isOn - newState.isServiceTermsAgree = isOn - newState.isPersonalInformationAgree = isOn - newState.isMarketingAgree = isOn - case .age: - newState.isAgeAgree = isOn - case .serviceTerms: - newState.isServiceTermsAgree = isOn - case .personalInfo: - newState.isPersonalInformationAgree = isOn - case .marketing: - newState.isMarketingAgree = isOn - } - case .navigateTo(let route): - newState.route = route - } - - // bottomButton 활성화 체크 - let allRequiredAgreed = newState.isAgeAgree && newState.isServiceTermsAgree && newState.isPersonalInformationAgree - newState.bottomButtonIsEnabled = allRequiredAgreed - newState.isTotalAgree = allRequiredAgreed && newState.isMarketingAgree - - return newState - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementView.swift b/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementView.swift deleted file mode 100644 index bd55eff5..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementView.swift +++ /dev/null @@ -1,148 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -public final class TermsAgreementView: UIView { - // MARK: - Type - private enum Constant { - static let imageTopSpacing: CGFloat = 20 - static let imageSize: CGFloat = 60 - static let horizontalInset: CGFloat = 16 - static let totalButtonBottomSpacing: CGFloat = -14 - static let titleLabelTopSpacing: CGFloat = 16 - static let titleLabelHeight: CGFloat = 30 - static let subTitleLabelTopSpacing: CGFloat = 4 - static let subTitleLabelHeight: CGFloat = 21 - static let stackViewBottomSpacing: CGFloat = -26 - static let bottomButtonBottomSpacing: CGFloat = 16 - static let termsSpacing: CGFloat = 4 - } - - // MARK: - Properties - let headerView: NavigationBar = { - let view = NavigationBar(type: .arrowLeft) - view.rightButton.isHidden = true - return view - }() - - private let logoImageView: UIImageView = { - let image = DesignSystemAsset.image(named: "logo") - let view = UIImageView(image: image) - view.contentMode = .scaleAspectFill - return view - }() - - private let titleLabel: UILabel = { - let label = UILabel() - label.attributedText = .makeStyledString(font: .h_xxxl_b, text: "필수약관에 동의해주세요") - return label - }() - - private let subTitleLabel: UILabel = { - let label = UILabel() - label.attributedText = .makeStyledString(font: .sub_m_m, text: "메랜사를 더 편하게 즐기기 위해 필요한 항목이에요", color: .neutral700) - return label - }() - - public let totalAgreeButton: CheckBoxButton = { - let button = CheckBoxButton(style: .normal, mainTitle: "전체동의", subTitle: "(선택 약관 포함)") - return button - }() - - private let termsStackView: UIStackView = { - let view = UIStackView() - view.axis = .vertical - view.isUserInteractionEnabled = true - view.spacing = Constant.termsSpacing - return view - }() - - public let ageAgreeButton: CheckBoxButton = { - let button = CheckBoxButton(style: .listMedium, mainTitle: "(필수) 만 14세 이상", subTitle: nil) - return button - }() - - public let serviceTermsAgreeButton: CheckBoxButton = { - let button = CheckBoxButton(style: .listMedium, mainTitle: "(필수) 메랜사 서비스 이용약관 동의", subTitle: nil) - return button - }() - - public let personalInformationAgreeButton: CheckBoxButton = { - let button = CheckBoxButton(style: .listMedium, mainTitle: "(필수) 개인정보 수집 및 이용 동의", subTitle: nil) - return button - }() - - public let marketingAgreeButton: CheckBoxButton = { - let button = CheckBoxButton(style: .listMedium, mainTitle: "(선택) 마케팅 정보 수신 동의", subTitle: nil) - return button - }() - - public let bottomButton: CommonButton = { - let button = CommonButton(style: .normal, title: "다음", disabledTitle: "다음") - return button - }() - - // MARK: - init - init() { - super.init(frame: .zero) - addViews() - setupConstraints() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension TermsAgreementView { - func addViews() { - addSubview(headerView) - addSubview(logoImageView) - addSubview(titleLabel) - addSubview(subTitleLabel) - addSubview(totalAgreeButton) - addSubview(bottomButton) - addSubview(termsStackView) - termsStackView.addArrangedSubview(ageAgreeButton) - termsStackView.addArrangedSubview(serviceTermsAgreeButton) - termsStackView.addArrangedSubview(personalInformationAgreeButton) - termsStackView.addArrangedSubview(marketingAgreeButton) - } - - func setupConstraints() { - headerView.snp.makeConstraints { make in - make.top.horizontalEdges.equalToSuperview() - } - logoImageView.snp.makeConstraints { make in - make.top.equalTo(headerView.snp.bottom).offset(Constant.imageTopSpacing) - make.size.equalTo(Constant.imageSize) - make.leading.equalToSuperview().inset(Constant.horizontalInset) - } - titleLabel.snp.makeConstraints { make in - make.top.equalTo(logoImageView.snp.bottom).offset(Constant.titleLabelTopSpacing) - make.height.equalTo(Constant.titleLabelHeight) - make.leading.equalTo(logoImageView) - } - subTitleLabel.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(Constant.subTitleLabelTopSpacing) - make.height.equalTo(Constant.subTitleLabelHeight) - make.leading.equalTo(logoImageView) - } - bottomButton.snp.makeConstraints { make in - make.horizontalEdges.equalToSuperview().inset(Constant.horizontalInset) - make.bottom.equalToSuperview().inset(Constant.bottomButtonBottomSpacing) - } - termsStackView.snp.makeConstraints { make in - make.horizontalEdges.equalToSuperview().inset(Constant.horizontalInset) - make.bottom.equalTo(bottomButton.snp.top).offset(Constant.stackViewBottomSpacing) - } - totalAgreeButton.snp.makeConstraints { make in - make.bottom.equalTo(termsStackView.snp.top).offset(Constant.totalButtonBottomSpacing) - make.horizontalEdges.equalToSuperview().inset(Constant.horizontalInset) - } - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementViewController.swift b/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementViewController.swift deleted file mode 100644 index bb7d2325..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementViewController.swift +++ /dev/null @@ -1,181 +0,0 @@ -import UIKit - -import AuthFeatureInterface -import BaseFeature - -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit - -public class TermsAgreementViewController: BaseViewController, View { - public typealias Reactor = TermsAgreementReactor - - // MARK: - Properties - public var disposeBag = DisposeBag() - - private let onBoardingQuestionFactory: OnBoardingQuestionFactory - - private var mainView = TermsAgreementView() - - public init(onBoardingQuestionFactory: OnBoardingQuestionFactory) { - self.onBoardingQuestionFactory = onBoardingQuestionFactory - super.init() - } - - @available(*, unavailable) - @MainActor required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -public extension TermsAgreementViewController { - override func viewDidLoad() { - super.viewDidLoad() - addViews() - setupConstraints() - configureUI() - } -} - -// MARK: - SetUp -private extension TermsAgreementViewController { - func addViews() { - view.addSubview(mainView) - } - - func setupConstraints() { - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } - - func configureUI() { - view.backgroundColor = .systemBackground - navigationController?.navigationBar.isHidden = true - } -} - -public extension TermsAgreementViewController { - func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindViewState(reactor: reactor) - } - - func bindUserActions(reactor: Reactor) { - mainView.headerView.leftButton.rx.tap - .map { Reactor.Action.backButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - let agreeButtons: [(button: UIButton, type: TermsAgreementReactor.AgreeType, isRightButton: Bool)] = [ - (mainView.totalAgreeButton, .total, false), - (mainView.ageAgreeButton, .age, false), - (mainView.ageAgreeButton.rightButton, .age, true), - (mainView.serviceTermsAgreeButton, .serviceTerms, false), - (mainView.serviceTermsAgreeButton.rightButton, .serviceTerms, true), - (mainView.personalInformationAgreeButton, .personalInfo, false), - (mainView.personalInformationAgreeButton.rightButton, .personalInfo, true), - (mainView.marketingAgreeButton, .marketing, false), - (mainView.marketingAgreeButton.rightButton, .marketing, true) - ] - - agreeButtons.forEach { button, type, _ in - button.rx.tap - .map { Reactor.Action.toggleAgree(type: type) } - .bind(to: reactor.action) - .disposed(by: disposeBag) - } - - mainView.bottomButton.rx.tap - .map { Reactor.Action.bottomButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - } - - func bindViewState(reactor: Reactor) { - reactor.state - .map { $0.isTotalAgree } - .distinctUntilChanged() - .withUnretained(self) - .subscribe { owner, isAgree in - owner.mainView.totalAgreeButton.isSelected = isAgree - } - .disposed(by: disposeBag) - - reactor.state - .map { $0.isAgeAgree } - .distinctUntilChanged() - .withUnretained(self) - .subscribe { owner, isAgree in - owner.mainView.ageAgreeButton.isSelected = isAgree - } - .disposed(by: disposeBag) - - reactor.state - .map { $0.isServiceTermsAgree } - .distinctUntilChanged() - .withUnretained(self) - .subscribe { owner, isAgree in - owner.mainView.serviceTermsAgreeButton.isSelected = isAgree - } - .disposed(by: disposeBag) - - reactor.state - .map { $0.isPersonalInformationAgree } - .distinctUntilChanged() - .withUnretained(self) - .subscribe { owner, isAgree in - owner.mainView.personalInformationAgreeButton.isSelected = isAgree - } - .disposed(by: disposeBag) - - reactor.state - .map { $0.isMarketingAgree } - .distinctUntilChanged() - .withUnretained(self) - .subscribe { owner, isAgree in - owner.mainView.marketingAgreeButton.isSelected = isAgree - } - .disposed(by: disposeBag) - - reactor.state - .map { $0.bottomButtonIsEnabled } - .distinctUntilChanged() - .withUnretained(self) - .subscribe { owner, isEnabled in - owner.mainView.bottomButton.isEnabled = isEnabled - } - .disposed(by: disposeBag) - - rx.viewDidAppear - .take(1) - .flatMapLatest { _ in reactor.pulse(\.$route) } - .withUnretained(self) - .observe(on: MainScheduler.instance) - .subscribe { owner, route in - switch route { - case .dismiss: - owner.navigationController?.popViewController(animated: true) - case .onBoarding: - let questionViewController = owner.onBoardingQuestionFactory.make() - owner.navigationController?.setViewControllers([questionViewController], animated: true) - case .error: - let errorViewController = BaseErrorViewController() - owner.present(errorViewController, animated: true) - case .ageAgreement: - break - case .serviceAgreement: - break - case .personalAgreement: - break - case .marketingAgreement: - break - default: - break - } - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeatureDemo/.DS_Store b/MLS/Presentation/AuthFeature/AuthFeatureDemo/.DS_Store deleted file mode 100644 index c9b08e1e..00000000 Binary files a/MLS/Presentation/AuthFeature/AuthFeatureDemo/.DS_Store and /dev/null differ diff --git a/MLS/Presentation/AuthFeature/AuthFeatureDemo/AppDelegate.swift b/MLS/Presentation/AuthFeature/AuthFeatureDemo/AppDelegate.swift deleted file mode 100644 index 3238b4bf..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeatureDemo/AppDelegate.swift +++ /dev/null @@ -1,233 +0,0 @@ -import UIKit - -import AuthFeature -import AuthFeatureInterface -import BaseFeature -import Core -import Data -import DataMock -import DesignSystem -import DictionaryFeatureInterface -import Domain -import DomainInterface - -import KakaoSDKCommon - -@main -class AppDelegate: UIResponder, UIApplicationDelegate { - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - ImageLoader.shared.configure.diskCacheCountLimit = 10 - FontManager.registerFonts() - registerDependencies() - return true - } - - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) - } - - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {} -} - -private extension AppDelegate { - func registerDependencies() { - registerProvider() - registerRepository() - registerUseCase() - registerFactory() - } - - func registerProvider() { - DIContainer.register(type: NetworkProvider.self) { - NetworkProviderImpl() - } - DIContainer.register(type: SocialAuthenticatableProvider.self, name: "kakao") { - KakaoLoginProviderImpl() - } - DIContainer.register(type: SocialAuthenticatableProvider.self, name: "apple") { - AppleLoginProviderImpl() - } - } - - func registerRepository() { - DIContainer.register(type: AuthAPIRepository.self) { - AuthAPIRepositoryImpl( - provider: DIContainer.resolve(type: NetworkProvider.self), - interceptor: TokenInterceptor(fetchTokenUseCase: DIContainer.resolve(type: FetchTokenFromLocalUseCase.self)) - ) - } - DIContainer.register(type: TokenRepository.self) { - KeyChainRepositoryImpl() - } - DIContainer.register(type: UserDefaultsRepository.self) { - UserDefaultsRepositoryImpl() - } - } - - func registerUseCase() { - DIContainer.register(type: FetchSocialCredentialUseCase.self, name: "kakao") { - let provider = DIContainer.resolve(type: SocialAuthenticatableProvider.self, name: "kakao") - return SocialLoginUseCaseImpl(provider: provider) - } - DIContainer.register(type: FetchSocialCredentialUseCase.self, name: "apple") { - let provider = DIContainer.resolve(type: SocialAuthenticatableProvider.self, name: "apple") - return SocialLoginUseCaseImpl(provider: provider) - } - DIContainer.register(type: CheckEmptyLevelAndRoleUseCase.self) { - CheckEmptyLevelAndRoleUseCaseImpl() - } - DIContainer.register(type: CheckValidLevelUseCase.self) { - CheckValidLevelUseCaseImpl() - } - DIContainer.register(type: FetchJobListUseCase.self) { - FetchJobListUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: LoginWithAppleUseCase.self) { - LoginWithAppleUseCaseImpl(authRepository: DIContainer.resolve(type: AuthAPIRepository.self), tokenRepository: DIContainer.resolve(type: TokenRepository.self), userDefaultsRepository: DIContainer.resolve(type: UserDefaultsRepository.self)) - } - DIContainer.register(type: LoginWithKakaoUseCase.self) { - LoginWithKakaoUseCaseImpl(authRepository: DIContainer.resolve(type: AuthAPIRepository.self), tokenRepository: DIContainer.resolve(type: TokenRepository.self), userDefaultsRepository: DIContainer.resolve(type: UserDefaultsRepository.self)) - } - DIContainer.register(type: SignUpWithAppleUseCase.self) { - SignUpWithAppleUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: SignUpWithKakaoUseCase.self) { - SignUpWithKakaoUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: UpdateUserInfoUseCase.self) { - UpdateUserInfoUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: FetchTokenFromLocalUseCase.self) { - FetchTokenFromLocalUseCaseImpl(repository: DIContainer.resolve(type: TokenRepository.self)) - } - DIContainer.register(type: SaveTokenToLocalUseCase.self) { - SaveTokenToLocalUseCaseImpl(repository: DIContainer.resolve(type: TokenRepository.self)) - } - DIContainer.register(type: DeleteTokenFromLocalUseCase.self) { - DeleteTokenFromLocalUseCaseImpl(repository: DIContainer.resolve(type: TokenRepository.self)) - } - DIContainer.register(type: UpdateMarketingAgreementUseCase.self) { - UpdateMarketingAgreementUseCaseImpl(authRepository: DIContainer.resolve(type: AuthAPIRepository.self), tokenRepository: DIContainer.resolve(type: TokenRepository.self)) - } - DIContainer.register(type: PutFCMTokenUseCase.self) { - PutFCMTokenUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: CheckNotificationPermissionUseCase.self) { - CheckNotificationPermissionUseCaseImpl() - } - DIContainer.register(type: OpenNotificationSettingUseCase.self) { - OpenNotificationSettingUseCaseImpl() - } - DIContainer.register(type: UpdateNotificationAgreementUseCase.self) { - UpdateNotificationAgreementUseCaseImpl(authRepository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryMapListUseCase.self) { - FetchDictionaryMapListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryItemListUseCase.self) { - FetchDictionaryItemListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryQuestListUseCase.self) { - FetchDictionaryQuestListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryNpcListUseCase.self) { - FetchDictionaryNpcListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryMonsterListUseCase.self) { - FetchDictionaryMonsterListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMonsterUseCase.self) { - FetchDictionaryDetailMonsterUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMonsterItemsUseCase.self) { - FetchDictionaryDetailMonsterDropItemUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMonsterMapUseCase.self) { - FetchDictionaryDetailMonsterMapUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailNpcUseCase.self) { - FetchDictionaryDetailNpcUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailNpcQuestUseCase.self) { - FetchDictionaryDetailNpcQuestUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailNpcMapUseCase.self) { - FetchDictionaryDetailNpcMapUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailItemUseCase.self) { - FetchDictionaryDetailItemUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailItemDropMonsterUseCase.self) { - FetchDictionaryDetailItemDropMonsterUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailQuestUseCase.self) { - FetchDictionaryDetailQuestUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailQuestLinkedQuestsUseCase.self) { - FetchDictionaryDetailQuestLinkedQuestsUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMapUseCase.self) { - FetchDictionaryDetailMapUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMapSpawnMonsterUseCase.self) { - FetchDictionaryDetailMapSpawnMonsterUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMapNpcUseCase.self) { - FetchDictionaryDetailMapNpcUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchPlatformUseCase.self) { - FetchPlatformUseCaseImpl(repository: DIContainer.resolve(type: UserDefaultsRepository.self)) - } - } - - func registerFactory() { - DIContainer.register(type: OnBoardingNotificationSheetFactory.self) { - OnBoardingNotificationSheetFactoryImpl( - checkNotificationPermissionUseCase: DIContainer - .resolve(type: CheckNotificationPermissionUseCase.self), - openNotificationSettingUseCase: DIContainer - .resolve(type: OpenNotificationSettingUseCase.self), - updateNotificationAgreementUseCase: DIContainer - .resolve(type: UpdateNotificationAgreementUseCase.self), updateUserInfoUseCase: DIContainer.resolve(type: UpdateUserInfoUseCase.self), dictionaryMainViewFactory: DictionaryFactoryMock() - ) - } - DIContainer.register(type: OnBoardingNotificationFactory.self) { - OnBoardingNotificationFactoryImpl(onBoardingNotificationSheetFactory: DIContainer.resolve(type: OnBoardingNotificationSheetFactory.self)) - } - DIContainer.register(type: OnBoardingInputFactory.self) { - OnBoardingInputFactoryImpl( - checkEmptyUseCase: DIContainer.resolve(type: CheckEmptyLevelAndRoleUseCase.self), - checkValidLevelUseCase: DIContainer.resolve(type: CheckValidLevelUseCase.self), - fetchJobListUseCase: DIContainer.resolve(type: FetchJobListUseCase.self), - updateUserInfoUseCase: DIContainer.resolve(type: UpdateUserInfoUseCase.self), - onBoardingNotificationFactory: DIContainer.resolve(type: OnBoardingNotificationFactory.self) - ) - } - DIContainer.register(type: OnBoardingQuestionFactory.self) { - OnBoardingQuestionFactoryImpl( - onBoardingInputFactory: DIContainer.resolve(type: OnBoardingInputFactory.self) - ) - } - DIContainer.register(type: TermsAgreementFactory.self) { - TermsAgreementFactoryImpl( - onBoardingQuestionFactory: DIContainer.resolve(type: OnBoardingQuestionFactory.self), - signUpWithKakaoUseCase: DIContainer.resolve(type: SignUpWithKakaoUseCase.self), - signUpWithAppleUseCase: DIContainer.resolve(type: SignUpWithAppleUseCase.self), - saveTokenUseCase: DIContainer.resolve(type: SaveTokenToLocalUseCase.self), - fetchTokenUseCase: DIContainer.resolve(type: FetchTokenFromLocalUseCase.self), updateMarketingAgreementUseCase: DIContainer.resolve(type: UpdateMarketingAgreementUseCase.self) - ) - } - DIContainer.register(type: LoginFactory.self) { - LoginFactoryImpl( - termsAgreementsFactory: DIContainer.resolve(type: TermsAgreementFactory.self), - appleLoginUseCase: DIContainer.resolve(type: FetchSocialCredentialUseCase.self, name: "apple"), - kakaoLoginUseCase: DIContainer.resolve(type: FetchSocialCredentialUseCase.self, name: "kakao"), - loginWithAppleUseCase: DIContainer.resolve(type: LoginWithAppleUseCase.self), - loginWithKakaoUseCase: DIContainer.resolve(type: LoginWithKakaoUseCase.self), - fetchTokenUseCase: DIContainer.resolve(type: FetchTokenFromLocalUseCase.self), - putFCMTokenUseCase: DIContainer.resolve(type: PutFCMTokenUseCase.self), - fetchPlatformUseCase: DIContainer.resolve(type: FetchPlatformUseCase.self) - ) - } - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeatureDemo/Assets.xcassets/AppIcon.appiconset/icon.png b/MLS/Presentation/AuthFeature/AuthFeatureDemo/Assets.xcassets/AppIcon.appiconset/icon.png deleted file mode 100644 index 78870a63..00000000 Binary files a/MLS/Presentation/AuthFeature/AuthFeatureDemo/Assets.xcassets/AppIcon.appiconset/icon.png and /dev/null differ diff --git a/MLS/Presentation/AuthFeature/AuthFeatureDemo/Info.plist b/MLS/Presentation/AuthFeature/AuthFeatureDemo/Info.plist deleted file mode 100644 index 7d5d1c29..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeatureDemo/Info.plist +++ /dev/null @@ -1,27 +0,0 @@ - - - - - ITSAppUsesNonExemptEncryption - - NSUserNotificationAlertUsageDescription - 이벤트 소식을 알려드리기 위해 푸시 알림 권한이 필요합니다. - UIApplicationSceneManifest - - UIApplicationSupportsMultipleScenes - - UISceneConfigurations - - UIWindowSceneSessionRoleApplication - - - UISceneConfigurationName - Default Configuration - UISceneDelegateClassName - $(PRODUCT_MODULE_NAME).SceneDelegate - - - - - - diff --git a/MLS/Presentation/AuthFeature/AuthFeatureDemo/SceneDelegate.swift b/MLS/Presentation/AuthFeature/AuthFeatureDemo/SceneDelegate.swift deleted file mode 100644 index e4fcd4d6..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeatureDemo/SceneDelegate.swift +++ /dev/null @@ -1,26 +0,0 @@ -import UIKit - -import AuthFeature -import AuthFeatureInterface -import BaseFeature - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - var window: UIWindow? - - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - guard let windowScene = (scene as? UIWindowScene) else { return } - window = UIWindow(windowScene: windowScene) - window?.rootViewController = UINavigationController(rootViewController: ViewController()) - window?.makeKeyAndVisible() - } - - func sceneDidDisconnect(_ scene: UIScene) {} - - func sceneDidBecomeActive(_ scene: UIScene) {} - - func sceneWillResignActive(_ scene: UIScene) {} - - func sceneWillEnterForeground(_ scene: UIScene) {} - - func sceneDidEnterBackground(_ scene: UIScene) {} -} diff --git a/MLS/Presentation/AuthFeature/AuthFeatureDemo/ViewController.swift b/MLS/Presentation/AuthFeature/AuthFeatureDemo/ViewController.swift deleted file mode 100644 index 75594d1d..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeatureDemo/ViewController.swift +++ /dev/null @@ -1,76 +0,0 @@ -import UIKit - -import AuthFeature -import AuthFeatureInterface -import Core -import Data -import Domain -import DomainInterface - -import RxCocoa -import RxSwift -import SnapKit - -class ViewController: UIViewController { - let tableView: UITableView = { - let view = UITableView(frame: .zero, style: .plain) - return view - }() - - lazy var views: [UIViewController] = { - let loginVC = DIContainer.resolve(type: LoginFactory.self).make(exitRoute: .pop) - loginVC.title = "로그인" - - let termVC = DIContainer.resolve(type: TermsAgreementFactory.self).make(credential: KakaoCredential(token: "", providerID: ""), platform: .apple) - termVC.title = "약관 동의" - - let questionVC = DIContainer.resolve(type: OnBoardingQuestionFactory.self).make() - questionVC.title = "온보딩 진입" - - let inputVC = DIContainer.resolve(type: OnBoardingInputFactory.self).make() - inputVC.title = "온보딩 입력" - - let notiVC = DIContainer.resolve(type: OnBoardingNotificationFactory.self).make(selectedLevel: 1, selectedJobID: 0) - notiVC.title = "알림" - - return [ - notiVC, - loginVC, - termVC, - questionVC, - inputVC - ] - }() - - override func viewDidLoad() { - super.viewDidLoad() - view.backgroundColor = .systemBackground - tableView.dataSource = self - tableView.delegate = self - navigationItem.title = "MLS Feature System" - - view.addSubview(tableView) - - tableView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -extension ViewController: UITableViewDataSource, UITableViewDelegate { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return views.count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = UITableViewCell() - cell.textLabel?.text = views[indexPath.row].title - cell.selectionStyle = .none - return cell - } - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let nextController = views[indexPath.row] - navigationController?.pushViewController(nextController, animated: true) - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeatureInterface/LoginFactory.swift b/MLS/Presentation/AuthFeature/AuthFeatureInterface/LoginFactory.swift deleted file mode 100644 index 4231837f..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeatureInterface/LoginFactory.swift +++ /dev/null @@ -1,11 +0,0 @@ -import BaseFeature - -public protocol LoginFactory { - func make(exitRoute: LoginExitRoute, onLoginCompleted: (() -> Void)?) -> BaseViewController -} - -public extension LoginFactory { - func make(exitRoute: LoginExitRoute) -> BaseViewController { - make(exitRoute: exitRoute, onLoginCompleted: nil) - } -} diff --git a/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoadingNotificationFactory.swift b/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoadingNotificationFactory.swift deleted file mode 100644 index bfe601a8..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoadingNotificationFactory.swift +++ /dev/null @@ -1,5 +0,0 @@ -import BaseFeature - -public protocol OnBoardingNotificationFactory { - func make(selectedLevel: Int, selectedJobID: Int) -> BaseViewController -} diff --git a/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoardingInputFactory.swift b/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoardingInputFactory.swift deleted file mode 100644 index e7a90517..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoardingInputFactory.swift +++ /dev/null @@ -1,5 +0,0 @@ -import BaseFeature - -public protocol OnBoardingInputFactory { - func make() -> BaseViewController -} diff --git a/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoardingModalFactory.swift b/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoardingModalFactory.swift deleted file mode 100644 index 2e94bae9..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoardingModalFactory.swift +++ /dev/null @@ -1,5 +0,0 @@ -import BaseFeature - -public protocol OnBoardingModalFactory { - func make() -> BaseViewController & ModalPresentable -} diff --git a/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoardingNotificationSheetFactory.swift b/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoardingNotificationSheetFactory.swift deleted file mode 100644 index 6add1e37..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoardingNotificationSheetFactory.swift +++ /dev/null @@ -1,5 +0,0 @@ -import BaseFeature - -public protocol OnBoardingNotificationSheetFactory { - func make(selectedLevel: Int, selectedJobID: Int) -> BaseViewController & ModalPresentable -} diff --git a/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoardingQuestionFactory.swift b/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoardingQuestionFactory.swift deleted file mode 100644 index b196ecb9..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeatureInterface/OnBoardingQuestionFactory.swift +++ /dev/null @@ -1,5 +0,0 @@ -import BaseFeature - -public protocol OnBoardingQuestionFactory { - func make() -> BaseViewController -} diff --git a/MLS/Presentation/AuthFeature/AuthFeatureInterface/TermsAgreementFactory.swift b/MLS/Presentation/AuthFeature/AuthFeatureInterface/TermsAgreementFactory.swift deleted file mode 100644 index a5b81ffa..00000000 --- a/MLS/Presentation/AuthFeature/AuthFeatureInterface/TermsAgreementFactory.swift +++ /dev/null @@ -1,7 +0,0 @@ -import BaseFeature - -import DomainInterface - -public protocol TermsAgreementFactory { - func make(credential: Credential, platform: LoginPlatform) -> BaseViewController -} diff --git a/MLS/Presentation/BaseFeature/.DS_Store b/MLS/Presentation/BaseFeature/.DS_Store deleted file mode 100644 index 82813aac..00000000 Binary files a/MLS/Presentation/BaseFeature/.DS_Store and /dev/null differ diff --git a/MLS/Presentation/BaseFeature/BaseFeature.xcodeproj/project.pbxproj b/MLS/Presentation/BaseFeature/BaseFeature.xcodeproj/project.pbxproj deleted file mode 100644 index 3a5733d0..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature.xcodeproj/project.pbxproj +++ /dev/null @@ -1,476 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXBuildFile section */ - 0850204B2DDCD6AE0055CBCE /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0850204A2DDCD6AE0055CBCE /* RxSwift */; }; - 0850204E2DDCD6C20055CBCE /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 0850204D2DDCD6C20055CBCE /* SnapKit */; }; - 085020542DDCD8E20055CBCE /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 085020532DDCD8E20055CBCE /* RxCocoa */; }; - 085020562DDCD8E20055CBCE /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 085020552DDCD8E20055CBCE /* RxRelay */; }; - 085020592DDCD8EE0055CBCE /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 085020582DDCD8EE0055CBCE /* ReactorKit */; }; - 772022362DF6BE7800BF8B5D /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 085020452DDCD6040055CBCE /* DesignSystem.framework */; }; - 773A42D42E717BB800F75B30 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 773A42D32E717BB800F75B30 /* DomainInterface.framework */; }; - 777C28112E7D8787000765F2 /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 777C28102E7D8787000765F2 /* RxKeyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 085020452DDCD6040055CBCE /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 08EECED12DD908400047AFE2 /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 773A42D32E717BB800F75B30 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 08EECED32DD908400047AFE2 /* BaseFeature */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = BaseFeature; - sourceTree = ""; - }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - 08EECECE2DD908400047AFE2 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 085020562DDCD8E20055CBCE /* RxRelay in Frameworks */, - 0850204E2DDCD6C20055CBCE /* SnapKit in Frameworks */, - 0850204B2DDCD6AE0055CBCE /* RxSwift in Frameworks */, - 773A42D42E717BB800F75B30 /* DomainInterface.framework in Frameworks */, - 772022362DF6BE7800BF8B5D /* DesignSystem.framework in Frameworks */, - 777C28112E7D8787000765F2 /* RxKeyboard in Frameworks */, - 085020542DDCD8E20055CBCE /* RxCocoa in Frameworks */, - 085020592DDCD8EE0055CBCE /* ReactorKit in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 085020442DDCD6040055CBCE /* Frameworks */ = { - isa = PBXGroup; - children = ( - 773A42D32E717BB800F75B30 /* DomainInterface.framework */, - 085020452DDCD6040055CBCE /* DesignSystem.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 08EECEC72DD908400047AFE2 = { - isa = PBXGroup; - children = ( - 08EECED32DD908400047AFE2 /* BaseFeature */, - 085020442DDCD6040055CBCE /* Frameworks */, - 08EECED22DD908400047AFE2 /* Products */, - ); - sourceTree = ""; - }; - 08EECED22DD908400047AFE2 /* Products */ = { - isa = PBXGroup; - children = ( - 08EECED12DD908400047AFE2 /* BaseFeature.framework */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 08EECECC2DD908400047AFE2 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 08EECED02DD908400047AFE2 /* BaseFeature */ = { - isa = PBXNativeTarget; - buildConfigurationList = 08EECED72DD908400047AFE2 /* Build configuration list for PBXNativeTarget "BaseFeature" */; - buildPhases = ( - 08EECECC2DD908400047AFE2 /* Headers */, - 08EECECD2DD908400047AFE2 /* Sources */, - 08EECECE2DD908400047AFE2 /* Frameworks */, - 08EECECF2DD908400047AFE2 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - 08EECED32DD908400047AFE2 /* BaseFeature */, - ); - name = BaseFeature; - packageProductDependencies = ( - 0850204A2DDCD6AE0055CBCE /* RxSwift */, - 0850204D2DDCD6C20055CBCE /* SnapKit */, - 085020532DDCD8E20055CBCE /* RxCocoa */, - 085020552DDCD8E20055CBCE /* RxRelay */, - 085020582DDCD8EE0055CBCE /* ReactorKit */, - 777C28102E7D8787000765F2 /* RxKeyboard */, - ); - productName = BaseFeature; - productReference = 08EECED12DD908400047AFE2 /* BaseFeature.framework */; - productType = "com.apple.product-type.framework"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 08EECEC82DD908400047AFE2 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastUpgradeCheck = 1620; - TargetAttributes = { - 08EECED02DD908400047AFE2 = { - CreatedOnToolsVersion = 16.2; - }; - }; - }; - buildConfigurationList = 08EECECB2DD908400047AFE2 /* Build configuration list for PBXProject "BaseFeature" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 08EECEC72DD908400047AFE2; - minimizedProjectReferenceProxies = 1; - packageReferences = ( - 085020492DDCD6AE0055CBCE /* XCRemoteSwiftPackageReference "RxSwift" */, - 0850204C2DDCD6C20055CBCE /* XCRemoteSwiftPackageReference "SnapKit" */, - 085020572DDCD8EE0055CBCE /* XCRemoteSwiftPackageReference "ReactorKit" */, - 777C280F2E7D8787000765F2 /* XCRemoteSwiftPackageReference "RxKeyboard" */, - ); - preferredProjectObjectVersion = 77; - productRefGroup = 08EECED22DD908400047AFE2 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 08EECED02DD908400047AFE2 /* BaseFeature */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 08EECECF2DD908400047AFE2 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 08EECECD2DD908400047AFE2 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 08EECED82DD908400047AFE2 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.BaseFeature; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 08EECED92DD908400047AFE2 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.BaseFeature; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; - 08EECEDA2DD908400047AFE2 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 08EECEDB2DD908400047AFE2 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 08EECECB2DD908400047AFE2 /* Build configuration list for PBXProject "BaseFeature" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 08EECEDA2DD908400047AFE2 /* Debug */, - 08EECEDB2DD908400047AFE2 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 08EECED72DD908400047AFE2 /* Build configuration list for PBXNativeTarget "BaseFeature" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 08EECED82DD908400047AFE2 /* Debug */, - 08EECED92DD908400047AFE2 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - 085020492DDCD6AE0055CBCE /* XCRemoteSwiftPackageReference "RxSwift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactiveX/RxSwift"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 6.9.0; - }; - }; - 0850204C2DDCD6C20055CBCE /* XCRemoteSwiftPackageReference "SnapKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/SnapKit/SnapKit.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 5.7.1; - }; - }; - 085020572DDCD8EE0055CBCE /* XCRemoteSwiftPackageReference "ReactorKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactorKit/ReactorKit"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 3.2.0; - }; - }; - 777C280F2E7D8787000765F2 /* XCRemoteSwiftPackageReference "RxKeyboard" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/RxSwiftCommunity/RxKeyboard"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.0.1; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - 0850204A2DDCD6AE0055CBCE /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 085020492DDCD6AE0055CBCE /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; - 0850204D2DDCD6C20055CBCE /* SnapKit */ = { - isa = XCSwiftPackageProductDependency; - package = 0850204C2DDCD6C20055CBCE /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = SnapKit; - }; - 085020532DDCD8E20055CBCE /* RxCocoa */ = { - isa = XCSwiftPackageProductDependency; - package = 085020492DDCD6AE0055CBCE /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxCocoa; - }; - 085020552DDCD8E20055CBCE /* RxRelay */ = { - isa = XCSwiftPackageProductDependency; - package = 085020492DDCD6AE0055CBCE /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxRelay; - }; - 085020582DDCD8EE0055CBCE /* ReactorKit */ = { - isa = XCSwiftPackageProductDependency; - package = 085020572DDCD8EE0055CBCE /* XCRemoteSwiftPackageReference "ReactorKit" */; - productName = ReactorKit; - }; - 777C28102E7D8787000765F2 /* RxKeyboard */ = { - isa = XCSwiftPackageProductDependency; - package = 777C280F2E7D8787000765F2 /* XCRemoteSwiftPackageReference "RxKeyboard" */; - productName = RxKeyboard; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = 08EECEC82DD908400047AFE2 /* Project object */; -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MLS/Presentation/BaseFeature/BaseFeature.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Base/BaseErrorViewController.swift b/MLS/Presentation/BaseFeature/BaseFeature/Base/BaseErrorViewController.swift deleted file mode 100644 index 6646537e..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Base/BaseErrorViewController.swift +++ /dev/null @@ -1,102 +0,0 @@ -import UIKit - -import DesignSystem - -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit - -public final class BaseErrorViewController: BaseViewController { - // MARK: - Type - private enum Constant { - static let imageHeight: CGFloat = 171 - static let imageWidth: CGFloat = 165 - static let componentsSpacing: CGFloat = 24 - static let bottomButtonBottomSpacing: CGFloat = 16 - static let centerYMultiplied: CGFloat = 0.7 - } - - // MARK: - Properties - private var disposeBag = DisposeBag() - - private let containerView: UIView = UIView() - - private let imageView: UIImageView = { - let image = DesignSystemAsset.image(named: "errorImage") - let view = UIImageView(image: image) - return view - }() - - private let descriptionLabel: UILabel = { - let label = UILabel() - label.numberOfLines = 0 - label.attributedText = .makeStyledString(font: .b_m_r, text: "알 수 없는 오류가 발생했어요.\n이전 화면으로 돌아가 다시 시도해 주세요.") - return label - }() - - private let backButton = CommonButton(style: .normal, title: "뒤로가기", disabledTitle: nil) - - public override init() { - super.init() - modalPresentationStyle = .fullScreen - } - - @MainActor public required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension BaseErrorViewController { - public override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - bind() - } -} - -// MARK: - SetUp -private extension BaseErrorViewController { - func addViews() { - view.addSubview(backButton) - containerView.addSubview(imageView) - containerView.addSubview(descriptionLabel) - view.addSubview(containerView) - } - - func setupConstraints() { - backButton.snp.makeConstraints { make in - make.bottom.horizontalEdges.equalTo(view.safeAreaLayoutGuide).inset(Constant.bottomButtonBottomSpacing) - } - imageView.snp.makeConstraints { make in - make.height.equalTo(Constant.imageHeight) - make.width.equalTo(Constant.imageWidth) - make.top.equalToSuperview().inset(Constant.componentsSpacing) - make.centerX.equalToSuperview() - } - descriptionLabel.snp.makeConstraints { make in - make.top.equalTo(imageView.snp.bottom).offset(Constant.componentsSpacing) - make.horizontalEdges.equalToSuperview() - make.bottom.equalToSuperview() - } - containerView.snp.makeConstraints { make in - make.centerX.equalToSuperview() - make.centerY.equalToSuperview().multipliedBy(Constant.centerYMultiplied) - } - } - - func configureUI() { } - - func bind() { - backButton.rx.tap - .withUnretained(self) - .subscribe { (owner, _) in - owner.dismiss(animated: true) - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Base/BaseViewController.swift b/MLS/Presentation/BaseFeature/BaseFeature/Base/BaseViewController.swift deleted file mode 100644 index ed78ec89..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Base/BaseViewController.swift +++ /dev/null @@ -1,73 +0,0 @@ -import os -import UIKit - -import DesignSystem - -import RxKeyboard -import RxSwift - -open class BaseViewController: UIViewController { - private let disposeBag = DisposeBag() - - open var isBottomTabbarHidden: Bool = false { - didSet { - if let tabBarController = tabBarController as? BottomTabBarController { - tabBarController.setHidden(hidden: isBottomTabbarHidden, animated: false) - } - } - } - - public init() { - super.init(nibName: nil, bundle: nil) - os_log("➕init: \(String(describing: self))") - } - - @available(*, unavailable) - public required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - deinit { - os_log("➖deinit: \(String(describing: self))") - } -} - -// MARK: - Life Cycle -extension BaseViewController { - override open func viewDidLoad() { - super.viewDidLoad() - configureUI() - } - - override open func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - if let tabBarController = tabBarController as? BottomTabBarController { - tabBarController.setHidden(hidden: isBottomTabbarHidden, animated: animated) - } - } -} - -// MARK: - SetUp -private extension BaseViewController { - func configureUI() { - navigationController?.navigationBar.isHidden = true - view.backgroundColor = .systemBackground - } -} - -// MARK: - Methods -public extension BaseViewController { - func setupKeyboard(inset: CGFloat = 0, completion: @escaping (CGFloat) -> Void) { - RxKeyboard.instance.visibleHeight - .drive(onNext: { [weak self] height in - guard let self = self else { return } - let safeBottom = self.view.safeAreaInsets.bottom - let inset = height > 0 - ? height - safeBottom + inset - : inset - completion(inset) - }) - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Interface/AppCoordinatorProtocol.swift b/MLS/Presentation/BaseFeature/BaseFeature/Interface/AppCoordinatorProtocol.swift deleted file mode 100644 index 24f9b2ca..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Interface/AppCoordinatorProtocol.swift +++ /dev/null @@ -1,7 +0,0 @@ -import UIKit - -public protocol AppCoordinatorProtocol: AnyObject { - var window: UIWindow? { get set } - func showMainTab() - func showLogin(exitRoute: LoginExitRoute) -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Interface/LoginExitRoute.swift b/MLS/Presentation/BaseFeature/BaseFeature/Interface/LoginExitRoute.swift deleted file mode 100644 index 6807d771..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Interface/LoginExitRoute.swift +++ /dev/null @@ -1,6 +0,0 @@ -import UIKit - -public enum LoginExitRoute { - case pop - case home -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Shared/CharacterInputView.swift b/MLS/Presentation/BaseFeature/BaseFeature/Shared/CharacterInputView.swift deleted file mode 100644 index 82aef9a5..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Shared/CharacterInputView.swift +++ /dev/null @@ -1,133 +0,0 @@ -import UIKit - -import DesignSystem - -import RxCocoa -import RxSwift -import SnapKit - -open class CharacterInputView: UIView { - // MARK: - Type - public enum Constant { - public static let horizontalInset: CGFloat = 16 - public static let verticalInset: CGFloat = 40 - static let verticalSpacing: CGFloat = 28 - static let horizontalSpacing: CGFloat = 8 - public static let bottomInset: CGFloat = 16 - static let messageSpacing: CGFloat = 8 - static let boxInset: CGFloat = (horizontalInset + (horizontalSpacing / 2)) / 2 - } - - // MARK: - Properties - private let disposeBag = DisposeBag() - public var nextButtonBottomConstraint: Constraint? - - // MARK: - Components - public let descriptionLabel: UILabel = { - let label = UILabel() - label.attributedText = .makeStyledString(font: .h_xxl_b, text: "현재 레벨과 직업을\n입력해주세요.", alignment: .left) - label.numberOfLines = 2 - return label - }() - - public let inputBox: InputBox = { - let box = InputBox(label: "레벨", placeHodler: "1~200") - box.textField.keyboardType = .numberPad - return box - }() - - public let dropDownBox = DropDownBox(label: "직업", placeHodler: "선택", items: []) - - public let errorMessage = ErrorMessage(message: "1에서 200까지 숫자만 입력해주세요") - - public let nextButton = CommonButton(style: .normal, title: "다음", disabledTitle: "다음") - - // MARK: - init - public init() { - super.init(frame: .zero) - addViews() - setupConstraints() - configureUI() - setGesture() - } - - @available(*, unavailable) - required public init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension CharacterInputView { - func addViews() { - addSubview(descriptionLabel) - addSubview(inputBox) - addSubview(dropDownBox) - addSubview(errorMessage) - addSubview(nextButton) - } - - func setupConstraints() { - inputBox.snp.makeConstraints { make in - make.top.equalTo(descriptionLabel.snp.bottom).offset(Constant.verticalSpacing) - make.leading.equalToSuperview().inset(Constant.horizontalInset) - make.width.equalToSuperview().multipliedBy(0.5).inset(Constant.boxInset) - } - - dropDownBox.snp.makeConstraints { make in - make.top.equalTo(inputBox) - make.leading.equalTo(inputBox.snp.trailing).offset(Constant.horizontalSpacing) - make.trailing.equalToSuperview().inset(Constant.horizontalInset) - make.width.equalToSuperview().multipliedBy(0.5).inset(Constant.boxInset) - } - - errorMessage.snp.makeConstraints { make in - make.centerX.equalToSuperview() - } - - nextButton.snp.makeConstraints { make in - make.top.equalTo(errorMessage.snp.bottom).offset(Constant.messageSpacing) - make.horizontalEdges.equalToSuperview().inset(Constant.horizontalInset) - nextButtonBottomConstraint = make.bottom.equalToSuperview().inset(Constant.bottomInset).constraint - } - } - - func configureUI() { - inputBox.textField.delegate = self - errorMessage.isHidden = true - } - - /// inputBox를 제외한 영역 선택시 키보드 제거 - func setGesture() { - let tapGesture = UITapGestureRecognizer() - tapGesture.cancelsTouchesInView = false - addGestureRecognizer(tapGesture) - - Observable.merge( - tapGesture.rx.event.map { $0.location(in: self) }.asObservable() - ) - .withUnretained(self) - .filter { owner, location in - !owner.inputBox.frame.contains(location) - } - .subscribe { owner, _ in - owner.inputBox.textField.resignFirstResponder() - } - .disposed(by: disposeBag) - } -} - -// MARK: - UITextFieldDelegate -extension CharacterInputView: UITextFieldDelegate { - /// textField의 붙여넣기 기능 차단 - /// - Parameters: - /// - action: 선택자를 나타내는 selector - /// - sender: 동작을 트리거한 객체 - /// - Returns: 붙여넣기가 허용되면 true / 허용되지 않으면 false - override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { - if action == #selector(paste(_:)) { - return false - } - return super.canPerformAction(action, withSender: sender) - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Shared/DataEmptyView.swift b/MLS/Presentation/BaseFeature/BaseFeature/Shared/DataEmptyView.swift deleted file mode 100644 index f92b8c2b..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Shared/DataEmptyView.swift +++ /dev/null @@ -1,104 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -public enum EmptyViewType { - case dictionary - case bookmark -} - -public final class DataEmptyView: UIView { - // MARK: - Type - enum Constant { - static let imageSize: CGFloat = 220 - static let textSpacing: CGFloat = 10 - static let buttonSpacing: CGFloat = 24 - static let buttonWidth: CGFloat = 186 - } - - // MARK: - Components - public let imageView = UIImageView() - private let mainLabel = UILabel() - private let subLabel = UILabel() - - public let button = CommonButton() - - // MARK: - Init - public init(type: EmptyViewType) { - super.init(frame: .zero) - addViews() - setupConstraints() - configureUI(type: type) - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension DataEmptyView { - func addViews() { - addSubview(imageView) - addSubview(mainLabel) - addSubview(subLabel) - addSubview(button) - } - - func setupConstraints() { - imageView.snp.makeConstraints { make in - make.centerX.equalToSuperview() - make.size.equalTo(Constant.imageSize) - } - - mainLabel.snp.makeConstraints { make in - make.top.equalTo(imageView.snp.bottom) - make.centerY.equalToSuperview() - make.horizontalEdges.equalToSuperview() - } - - subLabel.snp.makeConstraints { make in - make.top.equalTo(mainLabel.snp.bottom).offset(Constant.textSpacing) - make.centerX.equalToSuperview() - } - - button.snp.makeConstraints { make in - make.top.equalTo(subLabel.snp.bottom).offset(Constant.buttonSpacing) - make.centerX.equalToSuperview() - make.width.equalTo(Constant.buttonWidth) - } - } - - func configureUI(type: EmptyViewType) { - backgroundColor = .neutral100 - - switch type { - case .dictionary: - imageView.image = DesignSystemAsset.image(named: "noResult") - mainLabel.attributedText = .makeStyledString( - font: .b_m_r, - text: "검색 결과가 없습니다." - ) - - subLabel.isHidden = true - button.isHidden = true - case .bookmark: - imageView.image = DesignSystemAsset.image(named: "noShowList") - mainLabel.attributedText = .makeStyledString( - font: .h_xl_b, - text: "아직 아무것도 없어요!" - ) - - subLabel.attributedText = .makeStyledString( - font: .cp_s_r, - text: "북마크해서 추가해보세요.", - color: .neutral600 - ) - - button.updateTitle(title: "북마크하러 가기") - } - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Shared/ToLoginView.swift b/MLS/Presentation/BaseFeature/BaseFeature/Shared/ToLoginView.swift deleted file mode 100644 index 5be73445..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Shared/ToLoginView.swift +++ /dev/null @@ -1,86 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -public final class ToLoginView: UIView { - // MARK: - Type - enum Constant { - static let imageSize: CGFloat = 220 - static let textSpacing: CGFloat = 10 - static let buttonSpacing: CGFloat = 24 - static let buttonWidth: CGFloat = 186 - } - - // MARK: - Components - public let imageView = UIImageView() - private let mainLabel = UILabel() - private let subLabel = UILabel() - - public let button = CommonButton() - - // MARK: - Init - public init() { - super.init(frame: .zero) - addViews() - setupConstraints() - configureUI() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension ToLoginView { - func addViews() { - addSubview(imageView) - addSubview(mainLabel) - addSubview(subLabel) - addSubview(button) - } - - func setupConstraints() { - imageView.snp.makeConstraints { make in - make.centerX.equalToSuperview() - make.size.equalTo(Constant.imageSize) - } - - mainLabel.snp.makeConstraints { make in - make.top.equalTo(imageView.snp.bottom) - make.centerY.equalToSuperview() - make.horizontalEdges.equalToSuperview() - } - - subLabel.snp.makeConstraints { make in - make.top.equalTo(mainLabel.snp.bottom).offset(Constant.textSpacing) - make.centerX.equalToSuperview() - } - - button.snp.makeConstraints { make in - make.top.equalTo(subLabel.snp.bottom).offset(Constant.buttonSpacing) - make.centerX.equalToSuperview() - make.width.equalTo(Constant.buttonWidth) - } - } - - func configureUI() { - backgroundColor = .neutral100 - imageView.image = DesignSystemAsset.image(named: "noShowList") - mainLabel.attributedText = .makeStyledString( - font: .h_xl_b, - text: "북마크는 로그인 후 이용 가능해요!" - ) - - subLabel.attributedText = .makeStyledString( - font: .cp_s_r, - text: "자주 보는 정보, 검색 없이 바로 확인 할 수 있어요.", - color: .neutral600 - ) - - button.updateTitle(title: "로그인하러 가기") - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/BadgeCell.swift b/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/BadgeCell.swift deleted file mode 100644 index 81884c57..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/BadgeCell.swift +++ /dev/null @@ -1,50 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift -import SnapKit - -public class BadgeCell: UICollectionViewCell { - - public var badge = Badge(style: .currentQuest) - - override init(frame: CGRect) { - super.init(frame: frame) - addViews() - setupConstraints() - configureUI() - } - - public var disposeBag = DisposeBag() - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - public override func prepareForReuse() { - super.prepareForReuse() - self.disposeBag = DisposeBag() - } -} - -// MARK: - SetUp -private extension BadgeCell { - func addViews() { - contentView.addSubview(badge) - } - - func setupConstraints() { - badge.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - } - - func configureUI() { } -} - -public extension BadgeCell { - func inject(name: String) { - badge.update(style: .element(name)) - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/TagChipCell.swift b/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/TagChipCell.swift deleted file mode 100644 index f273bca0..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/UICollectionReusableViews/CollectionViewCells/TagChipCell.swift +++ /dev/null @@ -1,54 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift -import SnapKit - -public class TagChipCell: UICollectionViewCell { - - public let button: TagChip = { - let button = TagChip(style: .normal, text: "") - return button - }() - - override init(frame: CGRect) { - super.init(frame: frame) - addViews() - setupConstraints() - configureUI() - } - - public var disposeBag = DisposeBag() - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - public override func prepareForReuse() { - super.prepareForReuse() - self.disposeBag = DisposeBag() - } -} - -// MARK: - SetUp -private extension TagChipCell { - func addViews() { - contentView.addSubview(button) - } - - func setupConstraints() { - button.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - } - - func configureUI() { } -} - -public extension TagChipCell { - func inject(title: String?) { - button.text = title ?? "" - button.titleLabel?.numberOfLines = 1 - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/AppRouter.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/AppRouter.swift deleted file mode 100644 index 1a74585b..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/AppRouter.swift +++ /dev/null @@ -1,20 +0,0 @@ -import UIKit - -public enum AppRouter { - public static func setRoot(_ viewController: UIViewController, animated: Bool = true) { - guard let window = UIApplication.shared.connectedScenes - .compactMap({ $0 as? UIWindowScene }) - .flatMap({ $0.windows }) - .first(where: { $0.isKeyWindow }) else { return } - - if animated { - UIView.transition(with: window, duration: 0.3, options: .transitionCrossDissolve, animations: { - window.rootViewController = viewController - }) - } else { - window.rootViewController = viewController - } - - window.makeKeyAndVisible() - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/CompositionalLayoutBuilder/CompositionalLayoutBuilder.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/CompositionalLayoutBuilder/CompositionalLayoutBuilder.swift deleted file mode 100644 index 358cdad1..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/CompositionalLayoutBuilder/CompositionalLayoutBuilder.swift +++ /dev/null @@ -1,28 +0,0 @@ -import UIKit - -public final class CompositionalLayoutBuilder { - private var sections: [NSCollectionLayoutSection] = [] - - public init() {} - - @discardableResult - public func section(_ build: (CompositionalSectionBuilder) -> CompositionalSectionBuilder) -> Self { - let builder = CompositionalSectionBuilder() - if let section = build(builder).build() { - sections.append(section) - } - return self - } - - public func build() -> UICollectionViewCompositionalLayout { - return UICollectionViewCompositionalLayout { index, _ in - guard index < self.sections.count else { return nil } - return self.sections[index] - } - } - - public func setSections(_ sections: [NSCollectionLayoutSection]) -> Self { - self.sections = sections - return self - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/CompositionalLayoutBuilder/CompositionalSectionBuilder.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/CompositionalLayoutBuilder/CompositionalSectionBuilder.swift deleted file mode 100644 index 98a39810..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/CompositionalLayoutBuilder/CompositionalSectionBuilder.swift +++ /dev/null @@ -1,201 +0,0 @@ -import UIKit - -public final class CompositionalSectionBuilder { - private var item: NSCollectionLayoutItem? - private var group: NSCollectionLayoutGroup? - private var section: NSCollectionLayoutSection? - - public enum Direction { - case horizontal, vertical - } - - public init(item: NSCollectionLayoutItem? = nil, group: NSCollectionLayoutGroup? = nil, section: NSCollectionLayoutSection? = nil) { - self.item = item - self.group = group - self.section = section - } - - @discardableResult - public func item(width: NSCollectionLayoutDimension, height: NSCollectionLayoutDimension) -> Self { - let size = NSCollectionLayoutSize(widthDimension: width, heightDimension: height) - item = NSCollectionLayoutItem(layoutSize: size) - return self - } - - @discardableResult - public func group(_ direction: Direction, width: NSCollectionLayoutDimension, height: NSCollectionLayoutDimension, count: Int? = nil) -> Self { - guard let item = item else { return self } - let size = NSCollectionLayoutSize(widthDimension: width, heightDimension: height) - switch direction { - case .horizontal: - if let count = count { - group = NSCollectionLayoutGroup.horizontal(layoutSize: size, subitem: item, count: count) - } else { - group = NSCollectionLayoutGroup.horizontal(layoutSize: size, subitems: [item]) - } - case .vertical: - if let count = count { - group = NSCollectionLayoutGroup.vertical(layoutSize: size, subitem: item, count: count) - } else { - group = NSCollectionLayoutGroup.vertical(layoutSize: size, subitems: [item]) - } - } - return self - } - - @discardableResult - public func customGroup(group: (NSCollectionLayoutItem) -> NSCollectionLayoutGroup) -> Self { - guard let item = item else { return self } - self.group = group(item) - return self - } - - @discardableResult - public func buildSection() -> Self { - guard let group = group else { return self } - section = NSCollectionLayoutSection(group: group) - return self - } - - @discardableResult - public func visibleItemsInvalidationHandler( - handler: @escaping ([any NSCollectionLayoutVisibleItem], CGPoint, any NSCollectionLayoutEnvironment) -> Void - ) -> Self { - section?.visibleItemsInvalidationHandler = { visibleItems, scrollOffset, environment in - handler(visibleItems, scrollOffset, environment) - } - return self - } - - @discardableResult - public func orthogonalScrolling(_ behavior: UICollectionLayoutSectionOrthogonalScrollingBehavior) -> Self { - section?.orthogonalScrollingBehavior = behavior - return self - } - - @discardableResult - public func contentInsets(_ insets: NSDirectionalEdgeInsets) -> Self { - section?.contentInsets = insets - return self - } - - @discardableResult - public func header(height: CGFloat, isSticky: Bool = false) -> Self { - guard let section = section else { return self } - - let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(height)) - let header = NSCollectionLayoutBoundarySupplementaryItem( - layoutSize: size, - elementKind: UICollectionView.elementKindSectionHeader, - alignment: .top - ) - - header.pinToVisibleBounds = isSticky - section.boundarySupplementaryItems = [header] - return self - } - - public func build() -> NSCollectionLayoutSection? { - return section - } - - @discardableResult - public func interGroupSpacing(_ spacing: CGFloat) -> Self { - section?.interGroupSpacing = spacing - return self - } - - @discardableResult - public func interItemSpacing(_ spacing: NSCollectionLayoutSpacing) -> Self { - group?.interItemSpacing = spacing - return self - } - - @discardableResult - public func footer(height: CGFloat) -> Self { - let footer = NSCollectionLayoutBoundarySupplementaryItem( - layoutSize: .init( - widthDimension: .fractionalWidth(1), - heightDimension: .absolute(height) - ), - elementKind: UICollectionView.elementKindSectionFooter, - alignment: .bottom - ) - section?.boundarySupplementaryItems.append(footer) - return self - } - - @discardableResult - public func supplementaryItem( - kind: String, - height: CGFloat, - alignment: NSRectAlignment - ) -> Self { - let header = NSCollectionLayoutBoundarySupplementaryItem( - layoutSize: .init( - widthDimension: .fractionalWidth(1), - heightDimension: .absolute(height) - ), - elementKind: kind, - alignment: alignment - ) - section?.boundarySupplementaryItems.append(header) - return self - } - - @discardableResult - public func decorationItem( - kind: String, - insets: NSDirectionalEdgeInsets = .zero - ) -> Self { - let decoration = NSCollectionLayoutDecorationItem.background(elementKind: kind) - decoration.contentInsets = insets - section?.decorationItems.append(decoration) - return self - } - - @discardableResult - public func nestedGroup( - outerDirection: Direction, - outerWidth: NSCollectionLayoutDimension, - outerHeight: NSCollectionLayoutDimension, - innerDirection: Direction, - innerWidth: NSCollectionLayoutDimension, - innerHeight: NSCollectionLayoutDimension, - innerCount: Int? = nil, - innerSpacing: CGFloat - ) -> Self { - guard let item = item else { return self } - - // 내부 그룹 - let innerSize = NSCollectionLayoutSize(widthDimension: innerWidth, heightDimension: innerHeight) - let innerGroup: NSCollectionLayoutGroup - switch innerDirection { - case .horizontal: - if let count = innerCount { - innerGroup = NSCollectionLayoutGroup.horizontal(layoutSize: innerSize, subitem: item, count: count) - } else { - innerGroup = NSCollectionLayoutGroup.horizontal(layoutSize: innerSize, subitems: [item]) - } - innerGroup.interItemSpacing = .fixed(innerSpacing) - case .vertical: - if let count = innerCount { - innerGroup = NSCollectionLayoutGroup.vertical(layoutSize: innerSize, subitem: item, count: count) - } else { - innerGroup = NSCollectionLayoutGroup.vertical(layoutSize: innerSize, subitems: [item]) - } - innerGroup.interItemSpacing = .fixed(innerSpacing) - } - - // 외부 그룹 - let outerSize = NSCollectionLayoutSize(widthDimension: outerWidth, heightDimension: outerHeight) - switch outerDirection { - case .horizontal: - group = NSCollectionLayoutGroup.horizontal(layoutSize: outerSize, subitems: [innerGroup]) - case .vertical: - group = NSCollectionLayoutGroup.vertical(layoutSize: outerSize, subitems: [innerGroup]) - } - - return self - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/Extension/Int+.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/Extension/Int+.swift deleted file mode 100644 index 26a74e1b..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/Extension/Int+.swift +++ /dev/null @@ -1,16 +0,0 @@ -import Foundation - -extension Array where Element == Int { - public func changeKoreanDate() -> String { - return "\(self[0])년 \(self[1])월 \(self[2])일 \(self[3]):\(String(format: "%02d", self[4]))" - } -} - -extension Int { - var formatted: String { - let formatter = NumberFormatter() - formatter.numberStyle = .decimal - formatter.locale = Locale(identifier: "ko_KR") - return formatter.string(from: NSNumber(value: self)) ?? "\(self)" - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/Extension/Reactive+.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/Extension/Reactive+.swift deleted file mode 100644 index 68a22b6c..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/Extension/Reactive+.swift +++ /dev/null @@ -1,32 +0,0 @@ -import UIKit - -import RxCocoa -import RxSwift - -public extension Reactive where Base: UIViewController { - - var viewDidLoad: ControlEvent { - let source = self.methodInvoked(#selector(Base.viewDidLoad)).map( { _ in }) - return ControlEvent(events: source) - } - - var viewWillAppear: ControlEvent { - let source = self.methodInvoked(#selector(Base.viewWillAppear)).map( { _ in }) - return ControlEvent(events: source) - } - - var viewDidAppear: ControlEvent { - let source = self.methodInvoked(#selector(Base.viewDidAppear)).map( { _ in }) - return ControlEvent(events: source) - } - - var viewWillDisappear: ControlEvent { - let source = self.methodInvoked(#selector(Base.viewWillDisappear)).map( { _ in }) - return ControlEvent(events: source) - } - - var viewDidDisappear: ControlEvent { - let source = self.methodInvoked(#selector(Base.viewDidDisappear)).map( { _ in }) - return ControlEvent(events: source) - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/Extension/String+.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/Extension/String+.swift deleted file mode 100644 index 59091f42..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/Extension/String+.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Foundation - -extension String { - public func isOnlyKorean() -> Bool { - return !self.contains { char in - guard let scalar = char.unicodeScalars.first else { return false } - return (0x3131...0x3163).contains(scalar.value) - } - } - - public func toDisplayDateString() -> String { - let inputFormatter = DateFormatter() - inputFormatter.locale = Locale(identifier: "ko_KR") - inputFormatter.timeZone = TimeZone(identifier: "Asia/Seoul") - inputFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" - - guard let date = inputFormatter.date(from: self) else { return self } - - let outputFormatter = DateFormatter() - outputFormatter.locale = Locale(identifier: "ko_KR") - outputFormatter.dateFormat = "yyyy.MM.dd HH:mm" - - return outputFormatter.string(from: date) - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/Extension/UICollectionReusableView+.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/Extension/UICollectionReusableView+.swift deleted file mode 100644 index 19232f48..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/Extension/UICollectionReusableView+.swift +++ /dev/null @@ -1,7 +0,0 @@ -import UIKit - -extension UICollectionReusableView { - public static var identifier: String { - return String(describing: self) - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/GuideAlertFactory.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/GuideAlertFactory.swift deleted file mode 100644 index d5d39683..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/GuideAlertFactory.swift +++ /dev/null @@ -1,109 +0,0 @@ -import UIKit - -import DesignSystem - -import RxCocoa -import RxSwift -import SnapKit - -public enum GuideAlertFactory { - private static var currentAlertView: GuideAlert? - private static var dimmedView: UIView? - private static var containerView: UIView? - private static var disposeBag = DisposeBag() - - public static func show( - mainText: String, - ctaText: String, - cancelText: String? = nil, - ctaAction: @escaping () -> Void, - cancelAction: (() -> Void)? = nil - ) { - let alert = GuideAlert(mainText: mainText, ctaText: ctaText, cancelText: cancelText) - presentAlert(alert: alert, ctaAction: ctaAction, cancelAction: cancelAction) - } - - public static func showAuthAlert( - type: AuthGuideAlert.AuthGuideAlertType, - ctaAction: @escaping () -> Void, - cancelAction: (() -> Void)? = nil - ) { - let alert = AuthGuideAlert(type: type) - presentAlert(alert: alert, ctaAction: ctaAction, cancelAction: cancelAction) - } - - private static func presentAlert( - alert: GuideAlert, - ctaAction: @escaping () -> Void, - cancelAction: (() -> Void)? = nil - ) { - guard currentAlertView == nil, dimmedView == nil else { return } - guard let windowScene = UIApplication.shared.connectedScenes - .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene, - let window = windowScene.windows.first(where: { $0.isKeyWindow }) else { return } - - let container = UIView(frame: window.bounds) - window.addSubview(container) - - let dimmed = UIView() - dimmed.backgroundColor = UIColor.black.withAlphaComponent(0.4) - dimmed.alpha = 0 - container.addSubview(dimmed) - dimmed.snp.makeConstraints { $0.edges.equalToSuperview() } - - alert.alpha = 0 - container.addSubview(alert) - alert.snp.makeConstraints { - $0.center.equalToSuperview() - $0.leading.greaterThanOrEqualToSuperview().offset(16) - $0.trailing.lessThanOrEqualToSuperview().offset(-16) - } - - disposeBag = DisposeBag() - - alert.ctaButton.rx.tap - .bind { - dismiss() - ctaAction() - } - .disposed(by: disposeBag) - - if let cancelButton = alert.cancelButton { - cancelButton.rx.tap - .bind { - dismiss() - cancelAction?() - } - .disposed(by: disposeBag) - } - - currentAlertView = alert - dimmedView = dimmed - containerView = container - - UIView.animate(withDuration: 0.25) { - dimmed.alpha = 1 - alert.alpha = 1 - } - } - - public static func dismiss() { - guard let alert = currentAlertView, - let dimmed = dimmedView, - let container = containerView - else { return } - - UIView.animate(withDuration: 0.25, animations: { - alert.alpha = 0 - dimmed.alpha = 0 - }, completion: { _ in - alert.removeFromSuperview() - dimmed.removeFromSuperview() - container.removeFromSuperview() - currentAlertView = nil - dimmedView = nil - containerView = nil - disposeBag = DisposeBag() - }) - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/ImageLoader/ImageLoader.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/ImageLoader/ImageLoader.swift deleted file mode 100644 index 16bcd70d..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/ImageLoader/ImageLoader.swift +++ /dev/null @@ -1,173 +0,0 @@ -import UIKit - -import DesignSystem - -public enum ImageLoaderError: Error { - case invalidURL - case networkError(description: String?) - case convertError(description: String?) - case cacheNotFoundError -} - -/// 이미지 로더 설정 클래스 -public final class ImageLoaderConfigure { - /// 메모리 캐시 만료 시간 (기본값 300초) - public var memoryCacheExpiration: TimeInterval = 300 - /// 메모리 캐시 갯수 제한 (기본값 100개) - public var memoryCacheCountLimit = 100 // 최대 100개까지 저장 - /// 메모리 코스트 제한 (최대 50MB까지 저장 기본 값 50MB) - public var memoryCacheTotalCostLimit = 50 * 1024 * 1024 - /// 디스크 캐시 만료 시간 (기본값 일주일) - public var diskCacheExpiration: TimeInterval = 7 * 24 * 60 * 60 // 7일 - /// 디스크 캐시 갯수 제한 - public var diskCacheCountLimit = 1000 // 최대 1000개 파일 - /// 디스크 캐시 용량 제한 (최대 500MB) - public var diskCacheSizeLimit = 500 * 1024 * 1024 // 500MB -} - -/// URL을 통해 이미지를 비동기적으로 로드하는 클래스 -public final class ImageLoader { - public static let shared = ImageLoader() - - /// 이미지 로더 설정 객체 - public let configure = ImageLoaderConfigure() - - private init() {} - - public func loadImage(stringURL: String?, defaultImage: UIImage? = DesignSystemAsset.image(named: "connectionError"), completion: @escaping (UIImage?) -> Void) { - guard let stringURL, - let url = URL(string: stringURL), - ["http", "https"].contains(url.scheme?.lowercased() ?? "") - else { - DispatchQueue.main.async { - completion(defaultImage) - } - return - } - loadImage(url: url, defaultImage: defaultImage, completion: completion) - } - - /// URL을 통해 이미지를 로드하고, 실패 시 기본 이미지를 반환하는 메서드 - /// - Parameters: - /// - stringURL: 이미지 URL 문자열 - /// - defaultImage: 로드 실패 시 반환할 기본 이미지 - /// - completion: 로드 완료 후 호출되는 클로저 - public func loadImage(url: URL?, defaultImage: UIImage? = DesignSystemAsset.image(named: "connectionError"), completion: @escaping (UIImage?) -> Void) { - loadImage(url: url) { result in - DispatchQueue.main.async { - switch result { - case .success(let image): - completion(image) - case .failure: - completion(defaultImage) - } - } - } - } -} - -private extension ImageLoader { - /// 네트워크 및 스토리지를 통해 이미지를 로드하는 내부 메서드 - /// - Parameters: - /// - stringURL: 이미지 URL 문자열 - /// - completion: 로드 완료 후 호출되는 클로저 - func loadImage(url: URL?, completion: @escaping (Result) -> Void) { - guard let url else { - completion(.failure(ImageLoaderError.invalidURL)) - return - } - - // 1. 메모리 캐시 확인 - if let cachedImage = MemoryStorage.shared.fetchImage(stringURL: url.absoluteString) { - completion(.success(cachedImage)) - return - } - - // 2. 디스크 캐시 확인 - DiskStorage.shared.fetchImage(url: url) { image in - if let image { - MemoryStorage.shared.saveImage(image: image, stringURL: url.absoluteString) - completion(.success(image)) - } else { - // 3. 네트워크 요청 - self.fetchDataFrom(url: url) { result in - switch result { - case .success(let data): - guard let data else { - completion(.failure(ImageLoaderError.convertError(description: "No data received"))) - return - } - - self.decodeImageData(data, url: url, completion: completion) - - case .failure(let error): - completion(.failure(error)) - } - } - } - } - } - - private func decodeImageData(_ data: Data, url: URL, completion: @escaping (Result) -> Void) { - if let gifImage = decodeGIF(data: data, url: url) { - completion(.success(gifImage)) - return - } - - // 정적 이미지 처리 - if let image = UIImage(data: data) { - MemoryStorage.shared.saveImage(image: image, stringURL: url.absoluteString) - DiskStorage.shared.saveImage(image: image, url: url) - completion(.success(image)) - } else { - completion(.failure(ImageLoaderError.convertError(description: "Failed to convert data to UIImage"))) - } - } - - private func decodeGIF(data: Data, url: URL) -> UIImage? { - guard let source = CGImageSourceCreateWithData(data as CFData, nil) else { return nil } - - let frameCount = CGImageSourceGetCount(source) - guard frameCount > 1 else { return nil } // GIF가 아님 - - var images: [UIImage] = [] - var duration: TimeInterval = 0 - - for index in 0 ..< frameCount { - guard let cgImage = CGImageSourceCreateImageAtIndex(source, index, nil) else { continue } - let uiImage = UIImage(cgImage: cgImage) - images.append(uiImage) - - if let properties = CGImageSourceCopyPropertiesAtIndex(source, index, nil) as? [String: Any], - let gifProps = properties[kCGImagePropertyGIFDictionary as String] as? [String: Any], - let delay = gifProps[kCGImagePropertyGIFUnclampedDelayTime as String] as? Double { - duration += delay - } - } - - let finalDuration = duration > 0 ? duration : Double(frameCount) * 0.1 - let animatedImage = UIImage.animatedImage(with: images, duration: finalDuration) - - if let animatedImage { - MemoryStorage.shared.saveImage(image: animatedImage, stringURL: url.absoluteString) - DiskStorage.shared.saveImage(image: animatedImage, url: url) - } - - return animatedImage - } - - /// URL을 통해 데이터를 요청하는 메서드 - /// - Parameters: - /// - url: 요청할 URL 객체 - /// - completion: 요청 완료 후 호출되는 클로저 - func fetchDataFrom(url: URL, completion: @escaping (Result) -> Void) { - let task = URLSession.shared.dataTask(with: url) { data, _, error in - if let error = error { - completion(.failure(ImageLoaderError.networkError(description: "Network Error: \(error.localizedDescription)"))) - return - } - completion(.success(data)) - } - task.resume() - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/ImageLoader/Storage/DiskStorage.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/ImageLoader/Storage/DiskStorage.swift deleted file mode 100644 index e4c1cd0c..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/ImageLoader/Storage/DiskStorage.swift +++ /dev/null @@ -1,209 +0,0 @@ -import os -import UIKit - -/// 캐시할 이미지와 추가 데이터를 저장하는 구조체 -internal struct CacheData { - let fileName: String - let creationDate: Date - let expirationDate: Date - let fileSize: Int - - /// 초기화 메소드 - /// - Parameters: - /// - fileName: 재구성한 파일명 - /// - creationDate: 만료일 계산을 위한 생성날짜 - /// - expiration: 계산된 만료기간 - /// - fileSize: 파일 크기 - init(fileName: String, creationDate: Date, expiration: TimeInterval, fileSize: Int) { - self.fileName = fileName - self.creationDate = creationDate - self.expirationDate = creationDate.addingTimeInterval(expiration) - self.fileSize = fileSize - } - - var isExpired: Bool { - return Date() > expirationDate - } -} - -internal final class DiskStorage { - // 싱글톤 인스턴스 - static let shared = DiskStorage() - - private let fileManager = FileManager.default - private let cacheDirectory: URL - // 우선순위 보장과 저장시 안정성을 위한 큐 - private let queue = DispatchQueue(label: "com.diskStorage", attributes: .concurrent) - // fileName을 키로 캐시데이터 저장 - private var cacheDatas: [String: CacheData] = [:] - private var totalCacheSize: Int = 0 - - private init() { - guard let cacheDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first else { - fatalError("시스템 손상") - } - self.cacheDirectory = cacheDirectory.appendingPathComponent("ImageCache") - try? fileManager.createDirectory(at: cacheDirectory, withIntermediateDirectories: true) - // 캐시저장소 로드 - loadCacheData() - // 주기적으로 정리 - startCacheCleanup() - } - - /// 캐싱된 이미지를 불러오는 메소드 - /// - Parameters: - /// - url: 이미지 주소 - /// - completion: url에 해당하는 이미지를 return (없으면 nil) - func fetchImage(url: URL, completion: @escaping (UIImage?) -> Void) { - queue.async { [weak self] in - guard let self else { - DispatchQueue.main.async { completion(nil) } - return - } - - let fileName = self.createFilePath(url: url) - let fileURL = self.cacheDirectory.appendingPathComponent(fileName) - - // 기존 저장소 확인 - guard let cacheData = self.cacheDatas[fileName], !cacheData.isExpired else { - self.cleanCache(key: fileName) - DispatchQueue.main.async { completion(nil) } - return - } - - if self.fileManager.fileExists(atPath: fileURL.path), - let imageData = try? Data(contentsOf: fileURL), - let image = UIImage(data: imageData) { - DispatchQueue.main.async { completion(image) } - } else { - // 파일이 손상된 경우에만 cleanCache 호출 - self.cleanCache(key: fileName) - DispatchQueue.main.async { completion(nil) } - } - } - } - - /// 이미지를 캐시에 저장하는 메소드 - /// - Parameters: - /// - image: 이미지 - /// - url: 이미지 주소 - func saveImage(image: UIImage, url: URL) { - queue.async(flags: .barrier) { [weak self] in - guard let self, let data = image.jpegData(compressionQuality: 0.8) else { return } - - let fileName = self.createFilePath(url: url) - let fileURL = self.cacheDirectory.appendingPathComponent(fileName) - - if !self.fileManager.fileExists(atPath: fileURL.path) { - do { - try data.write(to: fileURL) - let cacheData = CacheData( - fileName: fileName, - creationDate: Date(), - expiration: ImageLoader.shared.configure.diskCacheExpiration, - fileSize: data.count - ) - self.cacheDatas[fileName] = cacheData - self.totalCacheSize += data.count - self.checkCache() - } catch { - os_log("디스크 캐시 저장 실패") - } - } - } - } - - /// 충돌 방지를 위한 파일 이름 재구성 메소드 - /// - Parameter url: 이미지 주소 - /// - Returns: 변경된 파일명을 return - private func createFilePath(url: URL) -> String { - // ToBe: - 파일명 길이 확인 - url.absoluteString - .replacingOccurrences(of: "/", with: "_") - .replacingOccurrences(of: ":", with: "_") - .addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? url.lastPathComponent - } - - /// 캐시저장소를 로드하는 메소드 - private func loadCacheData() { - queue.async(flags: .barrier) { [weak self] in - guard let self else { return } - - do { - let files = try self.fileManager.contentsOfDirectory(at: self.cacheDirectory, includingPropertiesForKeys: [.creationDateKey, .fileSizeKey]) - for fileURL in files { - let fileName = fileURL.lastPathComponent - let attributes = try fileURL.resourceValues(forKeys: [.creationDateKey, .fileSizeKey]) - let creationDate = attributes.creationDate ?? Date() - let fileSize = attributes.fileSize ?? 0 - - // 기본 만료 시간 적용 - let cacheData = CacheData( - fileName: fileName, - creationDate: creationDate, - expiration: ImageLoader.shared.configure.diskCacheExpiration, - fileSize: fileSize - ) - self.cacheDatas[fileName] = cacheData - self.totalCacheSize += fileSize - } - } catch { - os_log("캐시 로드 실패") - } - } - } - - /// 캐시 삭제를 위한 제약을 검사하는 메소드 - private func checkCache() { - queue.async(flags: .barrier) { [weak self] in - guard let self else { return } - - let config = ImageLoader.shared.configure - - /// 만료된 캐시 제거 - self.cacheDatas.forEach { key, data in - if data.isExpired { - self.cleanCache(key: key) - } - } - - /// 개수 제한 - while self.cacheDatas.count > config.diskCacheCountLimit { - if let oldest = self.cacheDatas.min(by: { $0.value.creationDate < $1.value.creationDate }) { - self.cleanCache(key: oldest.key) - } - } - - /// 용량 제한 - while self.totalCacheSize > config.diskCacheSizeLimit { - if let oldest = self.cacheDatas.min(by: { $0.value.creationDate < $1.value.creationDate }) { - self.cleanCache(key: oldest.key) - } - } - } - } - - /// 저장된 캐시를 제거하는 메소드 - private func cleanCache(key: String) { - queue.async(flags: .barrier) { [weak self] in - guard let self, let cacheData = self.cacheDatas[key] else { return } - let fileURL = self.cacheDirectory.appendingPathComponent(cacheData.fileName) - try? self.fileManager.removeItem(at: fileURL) - self.totalCacheSize -= cacheData.fileSize - self.cacheDatas.removeValue(forKey: key) - } - } - - /// 주기적으로 캐시를 정리하는 메소드 - private func startCacheCleanup() { - DispatchQueue.global(qos: .background).async { [weak self] in - let timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in - self?.checkCache() - } - // 백그라운드에서 실행되는 타이머를 메인 루프에 추가 - RunLoop.current.add(timer, forMode: .common) - // 백그라운드 스레드에서 타이머를 계속 실행하기 위해 RunLoop를 유지 - RunLoop.current.run() - } - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/ImageLoader/Storage/MemoryStorage.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/ImageLoader/Storage/MemoryStorage.swift deleted file mode 100644 index 65f936cf..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/ImageLoader/Storage/MemoryStorage.swift +++ /dev/null @@ -1,101 +0,0 @@ -import UIKit - -/// 캐시할 이미지와 만료 시간을 저장하는 클래스 -internal class StorageData: NSObject { - let image: UIImage? /// 캐시된 이미지 - let expirationDate: Date /// 캐시 만료 시간 - - /// 초기화 메서드 - /// - Parameters: - /// - image: 저장할 이미지 - /// - expiration: 만료 시간 (초 단위) - init(image: UIImage?, expiration: TimeInterval) { - self.image = image - self.expirationDate = Date().addingTimeInterval(expiration) - } - - /// 캐시가 만료되었는지 확인하는 메서드 - /// - Returns: 만료 여부 (true: 만료됨, false: 유효함) - func isExpired() -> Bool { - return Date() > expirationDate - } -} - -/// 메모리 캐시를 관리하는 클래스 -internal final class MemoryStorage { - - /// 싱글톤 인스턴스 - static let shared = MemoryStorage() - - /// 이미지 캐시 저장소 - private let cache: NSCache = { - let cache = NSCache() - cache.countLimit = ImageLoader.shared.configure.memoryCacheCountLimit - cache.totalCostLimit = ImageLoader.shared.configure.memoryCacheTotalCostLimit - return cache - }() - - /// 현재 캐시에 저장된 키 목록 - private var cachedKeys: Set = [] - - /// 초기화 (자동 캐시 정리 시작) - private init() { - startCacheCleanup() - } - - /// 이미지를 캐시에 저장하는 메서드 - /// - Parameters: - /// - image: 저장할 이미지 - /// - stringURL: 이미지 URL 문자열 - func saveImage(image: UIImage?, stringURL: String) { - let cachedData = StorageData(image: image, expiration: ImageLoader.shared.configure.memoryCacheExpiration) - let cost = image?.jpegData(compressionQuality: 1.0)?.count ?? 1 - cache.setObject(cachedData, forKey: stringURL as NSString, cost: cost) - cachedKeys.insert(stringURL) - } - - /// 캐시에서 이미지를 가져오는 메서드 - /// - Parameter stringURL: 이미지 URL 문자열 - /// - Returns: 캐시된 UIImage (없으면 nil) - func fetchImage(stringURL: String) -> UIImage? { - if let cachedData = cache.object(forKey: stringURL as NSString), !cachedData.isExpired() { - return cachedData.image - } else { - removeData(stringURL: stringURL) - return nil - } - } - - /// 특정 URL의 캐시 데이터를 제거하는 메서드 - /// - Parameter stringURL: 제거할 이미지의 URL 문자열 - func removeData(stringURL: String) { - cache.removeObject(forKey: stringURL as NSString) - cachedKeys.remove(stringURL) - } - - /// 모든 캐시 데이터를 삭제하는 메서드 - func clearCache() { - cache.removeAllObjects() - cachedKeys.removeAll() - } - - /// 주기적으로 만료된 캐시를 정리하는 메서드 - private func startCacheCleanup() { - DispatchQueue.global(qos: .background).async { [weak self] in - guard let self = self else { return } - let cleanTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in - for key in self.cachedKeys { - let nsKey = key as NSString - if let cachedData = self.cache.object(forKey: nsKey), cachedData.isExpired() { - self.cache.removeObject(forKey: nsKey) - self.cachedKeys.remove(key) - } - } - } - // 백그라운드에서 실행되는 타이머를 메인 루프에 추가 - RunLoop.current.add(cleanTimer, forMode: .common) - // 백그라운드 스레드에서 타이머를 계속 실행하기 위해 RunLoop를 유지 - RunLoop.current.run() - } - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/ModalPresentable/ModalPresentable.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/ModalPresentable/ModalPresentable.swift deleted file mode 100644 index 8d2b0a76..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/ModalPresentable/ModalPresentable.swift +++ /dev/null @@ -1,25 +0,0 @@ -import UIKit - -public protocol ModalPresentable { - var modalHeight: CGFloat? { get } - var allowsTapToDismiss: Bool { get } -} - -public extension ModalPresentable { - var allowsTapToDismiss: Bool { return false } -} - -// 모달 구성 관련 상수 정의 -internal enum ModalConfig { - static let containerTransformY: CGFloat = 400 - static let containerBottomInset: CGFloat = 8 - static let containerHorizontalInset: CGFloat = 8 - static let containerCornerRadius: CGFloat = 20 - static let bottomSheetStyleBottomInset: CGFloat = 34 - static let bottomSheetStyleHorizontalInset: CGFloat = 20 - static let alertSheetStyleInset: CGFloat = 20 - - static let gestureBarTopInset: CGFloat = 12 - static let gestureBarWidth: CGFloat = 60 - static let gestureBarHeight: CGFloat = 4 -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/ModalPresentable/ModalWrapperView.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/ModalPresentable/ModalWrapperView.swift deleted file mode 100644 index 62b04e92..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/ModalPresentable/ModalWrapperView.swift +++ /dev/null @@ -1,74 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -final class ModalWrapperView: UIView { - - weak var parentViewController: UIViewController? - - let dimView = UIView() - let containerView = UIView() - - private var initialY: CGFloat = 0 - private var containerBottomConstraint: Constraint? - - init(contentViewController: UIViewController & ModalPresentable, parent: UIViewController) { - super.init(frame: .zero) - self.parentViewController = parent - - // 뒷배경 뷰 (반투명) - dimView.backgroundColor = .overlays - dimView.alpha = 0 - addSubview(dimView) - dimView.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - - if contentViewController.allowsTapToDismiss { - // 탭 시 모달 닫기 - let tap = UITapGestureRecognizer(target: parent, action: #selector(parent.dismissCurrentModal)) - dimView.addGestureRecognizer(tap) - } - - // 모달 컨테이너 - containerView.backgroundColor = .systemBackground - containerView.layer.cornerRadius = ModalConfig.containerCornerRadius - containerView.clipsToBounds = true - containerView.transform = CGAffineTransform(translationX: 0, y: ModalConfig.containerTransformY) - addSubview(containerView) - - containerView.snp.makeConstraints { make in - self.containerBottomConstraint = make.bottom.equalToSuperview().constraint - make.horizontalEdges.equalToSuperview() - } - - // 자식 뷰컨트롤러 embed - parent.addChild(contentViewController) - containerView.addSubview(contentViewController.view) - contentViewController.view.snp.makeConstraints { make in - make.bottom.equalToSuperview().inset(ModalConfig.bottomSheetStyleBottomInset) - make.top.horizontalEdges.equalToSuperview() - if let height = contentViewController.modalHeight { make.height.equalTo(height) } - } - contentViewController.didMove(toParent: parent) - } - - func animateDismiss(completion: @escaping () -> Void) { - if let bottomConstraint = containerBottomConstraint { - // 아래로 내리기 위해 음수 inset - bottomConstraint.update(inset: -containerView.bounds.height) - UIView.animate(withDuration: 0.35, delay: 0, options: [.curveEaseIn], animations: { - self.layoutIfNeeded() - self.dimView.alpha = 0 - }, completion: { _ in - completion() - }) - } - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/ModalPresentable/UIViewController+.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/ModalPresentable/UIViewController+.swift deleted file mode 100644 index eec0382f..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/ModalPresentable/UIViewController+.swift +++ /dev/null @@ -1,133 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -private var modalWrapperKey: UInt8 = 0 -private var modalHideTabBarKey: UInt8 = 0 - -public extension UIViewController { - - private var modalWrapperView: ModalWrapperView? { - get { objc_getAssociatedObject(self, &modalWrapperKey) as? ModalWrapperView } - set { objc_setAssociatedObject(self, &modalWrapperKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } - } - - private var modalHideTabBar: Bool { - get { (objc_getAssociatedObject(self, &modalHideTabBarKey) as? Bool) ?? false } - set { objc_setAssociatedObject(self, &modalHideTabBarKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } - } - - /// 커스텀 모달 프레젠트 - /// - Parameters: - /// - viewController: 표시할 모달 뷰컨 - /// - hideTabBar: 탭바를 숨길지 여부 (기본값: true) - func presentModal( - _ viewController: UIViewController & ModalPresentable, - hideTabBar: Bool = false - ) { - let wrapper = ModalWrapperView(contentViewController: viewController, parent: self) - - // 이전 상태 초기화 - modalHideTabBar = false - modalWrapperView = wrapper - - // 새 설정 적용 - modalHideTabBar = hideTabBar - - view.addSubview(wrapper) - wrapper.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - - // 필요 시 탭바 숨김 - if hideTabBar, let tabBarController = findTabBarController() { - tabBarController.setHidden(hidden: true, animated: false) - } - - // present 애니메이션 - UIView.animate( - withDuration: 0.5, - delay: 0, - usingSpringWithDamping: 0.85, - initialSpringVelocity: 0.8, - options: [.curveEaseOut] - ) { - wrapper.dimView.alpha = 1 - wrapper.containerView.transform = .identity - DispatchQueue.main.async { - viewController.beginAppearanceTransition(true, animated: true) - viewController.endAppearanceTransition() - } - } - } - - /// 현재 모달 닫기 - @objc internal func dismissCurrentModal() { - guard let wrapper = modalWrapperView else { return } - - let shouldKeepHidden = modalHideTabBar - let tabBarController = findTabBarController() - - if shouldKeepHidden, let tabBarController { - tabBarController.setHidden(hidden: true, animated: false) - } - - UIView.animate(withDuration: 0.35, delay: 0, options: [.curveEaseInOut]) { - wrapper.dimView.alpha = 0 - wrapper.containerView.transform = CGAffineTransform(translationX: 0, y: 300) - } completion: { _ in - wrapper.removeFromSuperview() - self.modalWrapperView = nil - - // false인 경우 복원 - if !shouldKeepHidden, let tabBarController { - tabBarController.setHidden(hidden: false, animated: false) - } - - self.modalHideTabBar = false - } - } - - private func findTabBarController() -> BottomTabBarController? { - var parentVC: UIViewController? = self - while let current = parentVC { - if let tabBarController = current as? BottomTabBarController { - return tabBarController - } - parentVC = current.parent - } - return nil - } -} - -// 모달 내부에서 닫기 기능 제공 -extension ModalPresentable where Self: UIViewController { - public func dismissCurrentModal() { - parent?.dismissCurrentModal() - } -} - -private var fabKey: UInt8 = 0 - -public extension UIViewController { - func addFloatingButton(_ action: @escaping () -> Void) { - let fab = FloatingActionButton(action: action) - objc_setAssociatedObject(self, &fabKey, fab, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - - view.addSubview(fab) - fab.snp.makeConstraints { make in - make.trailing.equalTo(view.safeAreaLayoutGuide).inset(16) - make.bottom.equalTo(view.safeAreaLayoutGuide).inset(16) - make.size.equalTo(CGSize(width: 48, height: 48)) - } - } - - func removeFloatingButton() { - if let fab = objc_getAssociatedObject(self, &fabKey) as? UIView { - fab.removeFromSuperview() - objc_setAssociatedObject(self, &fabKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/NotificationPermissionManager.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/NotificationPermissionManager.swift deleted file mode 100644 index 8f41e7cd..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/NotificationPermissionManager.swift +++ /dev/null @@ -1,56 +0,0 @@ -import UIKit -import UserNotifications - -public final class NotificationPermissionManager { - - public static let shared = NotificationPermissionManager() - private init() {} - - public func getStatus(completion: @escaping (UNAuthorizationStatus) -> Void) { - UNUserNotificationCenter.current().getNotificationSettings { settings in - completion(settings.authorizationStatus) - } - } - - public func requestIfNeeded( - application: UIApplication = .shared, - completion: ((Bool) -> Void)? = nil - ) { - let center = UNUserNotificationCenter.current() - center.getNotificationSettings { settings in - switch settings.authorizationStatus { - case .notDetermined: - center.requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in - if let error = error { - print("error: \(error.localizedDescription)") - completion?(false) - return - } - if granted { - DispatchQueue.main.async { - application.registerForRemoteNotifications() - } - print("알림 권한 허용") - completion?(true) - } else { - print("알림 권한 거부") - completion?(false) - } - } - - case .authorized, .provisional: - DispatchQueue.main.async { - application.registerForRemoteNotifications() - } - completion?(true) - - case .denied: - print("🚫 알림 권한 거부 상태입니다. 설정에서 변경해야 함") - completion?(false) - - default: - completion?(false) - } - } - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/SnackBarFactory.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/SnackBarFactory.swift deleted file mode 100644 index 2c215ff4..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/SnackBarFactory.swift +++ /dev/null @@ -1,111 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift -import SnapKit - -public enum SnackBarFactory { - // MARK: - Properties - - /// 현재 디바이스 최상단 Window를 지정 - static var window: UIWindow? { - return UIApplication - .shared - .connectedScenes - .flatMap { ($0 as? UIWindowScene)?.windows ?? [] } - .first { $0.isKeyWindow } - } - - /// 최상단의 ViewController를 가져오는 메서드 - private static func topViewController( - _ rootViewController: UIViewController? = window?.rootViewController - ) -> UIViewController? { - if let navigationController = rootViewController as? UINavigationController { - return topViewController(navigationController.visibleViewController) - } - if let tabBarController = rootViewController as? UITabBarController { - return topViewController(tabBarController.selectedViewController) - } - if let presentedViewController = rootViewController?.presentedViewController { - return topViewController(presentedViewController) - } - return rootViewController - } - - private static var currentSnackBar: SnackBar? - private static var disposeBag = DisposeBag() -} - -public extension SnackBarFactory { - // MARK: - Method - - /// SnackBar를 생성하는 메소드 - /// - Parameters: - /// - type: normal / delete - /// - image: 스낵바 이미지 - /// - imageBackgroundColor: 이미지 배경색상 - /// - text: 스낵바에 들어갈 내용 - /// - buttonText: 버튼 제목 - /// - buttonAction: 버튼이 눌렸을때의 액션 - static func createSnackBar( - type: SnackBar.SnackBarType, - image: UIImage? = nil, - imageUrl: String? = nil, - imageBackgroundColor: UIColor, - text: String, - buttonText: String, - buttonAction: (() -> Void)?, - bottomMargin: CGFloat = 76 - ) { - DispatchQueue.main.async { - currentSnackBar?.removeFromSuperview() - currentSnackBar = nil - - let snackBar = SnackBar( - type: type, - image: image, - imageBackgroundColor: imageBackgroundColor, - text: text, - buttonText: buttonText, - buttonAction: buttonAction - ) - snackBar.alpha = 0 - - // ✅ window 대신 topViewController의 view 사용 - guard let topVC = topViewController() else { return } - topVC.view.addSubview(snackBar) - currentSnackBar = snackBar - - snackBar.snp.makeConstraints { make in - make.bottom.equalTo(topVC.view.safeAreaLayoutGuide.snp.bottom).inset(bottomMargin) - make.centerX.equalToSuperview() - } - - if let img = image { - snackBar.imageView.setImage(image: img, backgroundColor: imageBackgroundColor) - } else if let urlString = imageUrl, let url = URL(string: urlString) { - ImageLoader.shared.loadImage(stringURL: urlString) { image in - DispatchQueue.main.async { - snackBar.imageView.setImage(image: image, backgroundColor: imageBackgroundColor) - } - } - } - - UIView.animate(withDuration: 0.25) { - snackBar.alpha = 1 - } - - DispatchQueue.main.asyncAfter(deadline: .now() + 2.3) { - UIView.animate(withDuration: 0.6, animations: { - snackBar.alpha = 0 - }, completion: { _ in - snackBar.removeFromSuperview() - if currentSnackBar == snackBar { - currentSnackBar = nil - } - }) - } - } - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/TabBarUnderlineController.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/TabBarUnderlineController.swift deleted file mode 100644 index e6ff6130..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/TabBarUnderlineController.swift +++ /dev/null @@ -1,144 +0,0 @@ -import UIKit - -import DesignSystem - -import RxCocoa -import RxSwift -import SnapKit - -public final class TabBarUnderlineController { - // MARK: - UI Components - - /// 선택된 탭 아래를 표시하는 인디케이터 뷰 - private let selectionIndicatorView: UIView = { - let view = UIView() - view.backgroundColor = .textColor - return view - }() - - /// 탭 바 하단의 구분선 - private let bottomUnderlineView: UIView = { - let view = UIView() - view.backgroundColor = .neutral300 - return view - }() - - // MARK: - Properties - - private var collectionView: UICollectionView? - private let disposeBag = DisposeBag() - - /// 현재 컬렉션 뷰의 스크롤 오프셋 (인디케이터 위치 계산용) - private var currentScrollOffset: CGPoint? - - // MARK: - Initialization - - public init() {} - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Setup - -private extension TabBarUnderlineController { - func addIndicatorViews(to collectionView: UICollectionView) { - guard let superview = collectionView.superview else { return } - - superview.addSubview(bottomUnderlineView) - bottomUnderlineView.snp.makeConstraints { make in - make.horizontalEdges.bottom.equalTo(collectionView) - make.height.equalTo(1) - } - - superview.addSubview(selectionIndicatorView) - selectionIndicatorView.frame = CGRect( - x: 0, - y: collectionView.frame.maxY - 2, - width: 0, - height: 2 - ) - } -} - -// MARK: - Public Interface - -public extension TabBarUnderlineController { - /// 컬렉션 뷰에 인디케이터 컨트롤러 연결 - func configure(with collectionView: UICollectionView) { - self.collectionView = collectionView - addIndicatorViews(to: collectionView) - } - - /// 컬렉션 뷰 스크롤 오프셋이 변경될 때 호출 (scrollViewDidScroll 등에서) - func updateScrollOffset(_ offset: CGPoint) { - currentScrollOffset = offset - updateIndicatorFrameWithoutAnimation() - } - - /// 선택된 셀 위치에 인디케이터를 즉시 이동 - func updateIndicatorFrameWithoutAnimation() { - guard let collectionView, - let indexPath = collectionView.indexPathsForSelectedItems?.first, - let selectedFrame = collectionView.cellForItem(at: indexPath)?.frame - else { return } - - let xOffset = selectedFrame.minX - (currentScrollOffset?.x ?? 0) - selectionIndicatorView.frame.origin.x = xOffset - } - - /// 선택된 셀 위치로 인디케이터를 애니메이션으로 이동 - func animateIndicatorToSelectedItem() { - guard let collectionView, - let indexPath = collectionView.indexPathsForSelectedItems?.first, - let selectedFrame = collectionView.cellForItem(at: indexPath)?.frame - else { return } - - let xOffset = selectedFrame.minX - (currentScrollOffset?.x ?? 0) - let targetFrame = CGRect( - x: xOffset, - y: collectionView.frame.maxY - 2, - width: selectedFrame.width, - height: 2 - ) - - UIView.animate(withDuration: 0.2, delay: 0, options: [.curveEaseInOut], animations: { [weak self] in - self?.selectionIndicatorView.frame = targetFrame - }) - } - - /// 선택된 셀 위치로 인디케이터를 애니메이션으로 이동 - func setInitialIndicator() { - guard let collectionView, - let indexPath = collectionView.indexPathsForSelectedItems?.first, - let selectedFrame = collectionView.cellForItem(at: indexPath)?.frame - else { return } - - let xOffset = selectedFrame.minX - (currentScrollOffset?.x ?? 0) - let targetFrame = CGRect( - x: xOffset, - y: collectionView.frame.maxY - 2, - width: selectedFrame.width, - height: 2 - ) - selectionIndicatorView.frame = targetFrame - } - - func setHidden(hidden: Bool, animated: Bool = false) { - let alpha: CGFloat = hidden ? 0 : 1 - if animated { - UIView.animate(withDuration: 0.25) { - self.selectionIndicatorView.alpha = alpha - self.bottomUnderlineView.alpha = alpha - } - } else { - selectionIndicatorView.alpha = alpha - bottomUnderlineView.alpha = alpha - } - - selectionIndicatorView.isHidden = hidden - bottomUnderlineView.isHidden = hidden - } -} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Utills/ToastFactory.swift b/MLS/Presentation/BaseFeature/BaseFeature/Utills/ToastFactory.swift deleted file mode 100644 index 7e571b85..00000000 --- a/MLS/Presentation/BaseFeature/BaseFeature/Utills/ToastFactory.swift +++ /dev/null @@ -1,77 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift -import SnapKit - -public final class ToastFactory { - - // MARK: - Properties - - /// 현재 디바이스 최상단 Window를 지정 - static var window: UIWindow? { - return UIApplication - .shared - .connectedScenes - .flatMap { ($0 as? UIWindowScene)?.windows ?? [] } - .first { $0.isKeyWindow } - } - - /// 최상단의 ViewController를 가져오는 메서드 - private static func topViewController( - _ rootViewController: UIViewController? = window?.rootViewController - ) -> UIViewController? { - if let navigationController = rootViewController as? UINavigationController { - return topViewController(navigationController.visibleViewController) - } - if let tabBarController = rootViewController as? UITabBarController { - return topViewController(tabBarController.selectedViewController) - } - if let presentedViewController = rootViewController?.presentedViewController { - return topViewController(presentedViewController) - } - return rootViewController - } - - private static var currentToast: Toast? - private static var disposeBag = DisposeBag() -} - -extension ToastFactory { - - // MARK: - Method - - /// 토스트 메시지를 생성하는 메서드 - /// - Parameter message: 토스트 메세지에 담길 String 타입 - public static func createToast(message: String) { - - currentToast?.removeFromSuperview() - currentToast = nil - let toastMSG = Toast(message: message) - guard let window = window else { return } - window.addSubview(toastMSG) - currentToast = toastMSG - - toastMSG.snp.makeConstraints { make in - make.bottom.equalTo(window.snp.bottom).inset(120) - make.centerX.equalTo(window.snp.centerX) - } - - toastMSG.alpha = 0 - UIView.animate(withDuration: 0.25) { - toastMSG.alpha = 1 - } - - UIView.animate( - withDuration: 0.6, - delay: 2.3, - options: .curveEaseOut - ) { - toastMSG.alpha = 0 - } completion: { _ in - toastMSG.removeFromSuperview() - if currentToast == toastMSG { currentToast = nil } - } - } -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature.xcodeproj/project.pbxproj b/MLS/Presentation/BookmarkFeature/BookmarkFeature.xcodeproj/project.pbxproj deleted file mode 100644 index 9a3405e5..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature.xcodeproj/project.pbxproj +++ /dev/null @@ -1,981 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXBuildFile section */ - 778C5E442E7D78DA00FD8439 /* MyPageFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 778C5E422E7D78DA00FD8439 /* MyPageFeature.framework */; }; - 778C5E452E7D78DA00FD8439 /* MyPageFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 778C5E422E7D78DA00FD8439 /* MyPageFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 778C5E462E7D78DA00FD8439 /* MyPageFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 778C5E432E7D78DA00FD8439 /* MyPageFeatureInterface.framework */; }; - 778C5E472E7D78DA00FD8439 /* MyPageFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 778C5E432E7D78DA00FD8439 /* MyPageFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 779360FC2E38C2D20099B107 /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 779360FB2E38C2D20099B107 /* RxRelay */; }; - 779360FE2E38C4BF0099B107 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 779360FD2E38C4BF0099B107 /* ReactorKit */; }; - 779361002E38C7240099B107 /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 779360FF2E38C7240099B107 /* RxKeyboard */; }; - 779A50BA2E2E1DC100ABDE4F /* BookmarkFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77DBC78C2E182A9200529428 /* BookmarkFeatureInterface.framework */; }; - 77C97A752E37AF83007198DA /* BookmarkFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77DBC7742E1829AB00529428 /* BookmarkFeature.framework */; }; - 77C97A762E37AF83007198DA /* BookmarkFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77DBC7742E1829AB00529428 /* BookmarkFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C97A792E37AF83007198DA /* BookmarkFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77DBC78C2E182A9200529428 /* BookmarkFeatureInterface.framework */; }; - 77C97A7A2E37AF83007198DA /* BookmarkFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77DBC78C2E182A9200529428 /* BookmarkFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C97A802E37AF8D007198DA /* AuthFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A7E2E37AF8D007198DA /* AuthFeature.framework */; }; - 77C97A812E37AF8D007198DA /* AuthFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A7E2E37AF8D007198DA /* AuthFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C97A822E37AF8D007198DA /* AuthFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A7F2E37AF8D007198DA /* AuthFeatureInterface.framework */; }; - 77C97A832E37AF8D007198DA /* AuthFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A7F2E37AF8D007198DA /* AuthFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C97A8F2E37AF97007198DA /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A852E37AF97007198DA /* Core.framework */; }; - 77C97A902E37AF97007198DA /* Core.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A852E37AF97007198DA /* Core.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C97A912E37AF97007198DA /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A862E37AF97007198DA /* Data.framework */; }; - 77C97A922E37AF97007198DA /* Data.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A862E37AF97007198DA /* Data.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C97A932E37AF97007198DA /* DataMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A872E37AF97007198DA /* DataMock.framework */; }; - 77C97A942E37AF97007198DA /* DataMock.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A872E37AF97007198DA /* DataMock.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C97A952E37AF97007198DA /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A882E37AF97007198DA /* DesignSystem.framework */; }; - 77C97A962E37AF97007198DA /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A882E37AF97007198DA /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C97A972E37AF97007198DA /* DictionaryFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A892E37AF97007198DA /* DictionaryFeature.framework */; }; - 77C97A982E37AF97007198DA /* DictionaryFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A892E37AF97007198DA /* DictionaryFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C97A992E37AF97007198DA /* DictionaryFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A8A2E37AF97007198DA /* DictionaryFeatureInterface.framework */; }; - 77C97A9A2E37AF97007198DA /* DictionaryFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A8A2E37AF97007198DA /* DictionaryFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C97A9B2E37AF97007198DA /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A8B2E37AF97007198DA /* Domain.framework */; }; - 77C97A9C2E37AF97007198DA /* Domain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A8B2E37AF97007198DA /* Domain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C97A9D2E37AF97007198DA /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A8C2E37AF97007198DA /* DomainInterface.framework */; }; - 77C97A9E2E37AF97007198DA /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A8C2E37AF97007198DA /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C97AA02E37AFAA007198DA /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 77C97A9F2E37AFAA007198DA /* SnapKit */; }; - 77C97AA22E37AFC9007198DA /* BaseFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97AA12E37AFC9007198DA /* BaseFeature.framework */; }; - 77C97AA72E37B1B7007198DA /* BaseFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97AA52E37B1B7007198DA /* BaseFeature.framework */; }; - 77C97AA92E37B1B7007198DA /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97AA62E37B1B7007198DA /* DesignSystem.framework */; }; - 77C97AAD2E37B1D5007198DA /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97AAC2E37B1D5007198DA /* DomainInterface.framework */; }; - 77C97AB02E37B27A007198DA /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97AAF2E37B27A007198DA /* DomainInterface.framework */; }; - 77C97DA22E37B493007198DA /* BaseFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A842E37AF97007198DA /* BaseFeature.framework */; }; - 77C97DA32E37B493007198DA /* BaseFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97A842E37AF97007198DA /* BaseFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C97DA52E37CF26007198DA /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 77C97DA42E37CF26007198DA /* RxRelay */; }; - 77C97DA72E37D46E007198DA /* AuthFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97DA62E37D46E007198DA /* AuthFeatureInterface.framework */; }; - 77C97DAF2E37D4B6007198DA /* AuthFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97DAE2E37D4B6007198DA /* AuthFeatureInterface.framework */; }; - 77DBC7B62E182BD900529428 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 77DBC7B52E182BD900529428 /* ReactorKit */; }; - 77DBC7B92E182BE200529428 /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 77DBC7B82E182BE200529428 /* RxKeyboard */; }; - 77DBC7BC2E182BF500529428 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 77DBC7BB2E182BF500529428 /* RxCocoa */; }; - 77DBC7C02E182BF500529428 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 77DBC7BF2E182BF500529428 /* RxSwift */; }; - 77DBC7C32E182BFE00529428 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 77DBC7C22E182BFE00529428 /* SnapKit */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 779A50BC2E2E1DC100ABDE4F /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 77DBC76B2E1829AB00529428 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 77DBC78B2E182A9200529428; - remoteInfo = BookmarkFeatureInterface; - }; - 77C97A772E37AF83007198DA /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 77DBC76B2E1829AB00529428 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 77DBC7732E1829AB00529428; - remoteInfo = BookmarkFeature; - }; - 77C97A7B2E37AF83007198DA /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 77DBC76B2E1829AB00529428 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 77DBC78B2E182A9200529428; - remoteInfo = BookmarkFeatureInterface; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 77C97A7D2E37AF83007198DA /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 77C97DA32E37B493007198DA /* BaseFeature.framework in Embed Frameworks */, - 77C97A9C2E37AF97007198DA /* Domain.framework in Embed Frameworks */, - 77C97A762E37AF83007198DA /* BookmarkFeature.framework in Embed Frameworks */, - 77C97A902E37AF97007198DA /* Core.framework in Embed Frameworks */, - 778C5E472E7D78DA00FD8439 /* MyPageFeatureInterface.framework in Embed Frameworks */, - 77C97A942E37AF97007198DA /* DataMock.framework in Embed Frameworks */, - 77C97A982E37AF97007198DA /* DictionaryFeature.framework in Embed Frameworks */, - 778C5E452E7D78DA00FD8439 /* MyPageFeature.framework in Embed Frameworks */, - 77C97A9A2E37AF97007198DA /* DictionaryFeatureInterface.framework in Embed Frameworks */, - 77C97A962E37AF97007198DA /* DesignSystem.framework in Embed Frameworks */, - 77C97A9E2E37AF97007198DA /* DomainInterface.framework in Embed Frameworks */, - 77C97A832E37AF8D007198DA /* AuthFeatureInterface.framework in Embed Frameworks */, - 77C97A922E37AF97007198DA /* Data.framework in Embed Frameworks */, - 77C97A7A2E37AF83007198DA /* BookmarkFeatureInterface.framework in Embed Frameworks */, - 77C97A812E37AF8D007198DA /* AuthFeature.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 777313172E7DB00700AA92F6 /* MyPageFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MyPageFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 778C5E422E7D78DA00FD8439 /* MyPageFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MyPageFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 778C5E432E7D78DA00FD8439 /* MyPageFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MyPageFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97A7E2E37AF8D007198DA /* AuthFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97A7F2E37AF8D007198DA /* AuthFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97A842E37AF97007198DA /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97A852E37AF97007198DA /* Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97A862E37AF97007198DA /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97A872E37AF97007198DA /* DataMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DataMock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97A882E37AF97007198DA /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97A892E37AF97007198DA /* DictionaryFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DictionaryFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97A8A2E37AF97007198DA /* DictionaryFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DictionaryFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97A8B2E37AF97007198DA /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97A8C2E37AF97007198DA /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97AA12E37AFC9007198DA /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97AA52E37B1B7007198DA /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97AA62E37B1B7007198DA /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97AAC2E37B1D5007198DA /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97AAF2E37B27A007198DA /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97DA62E37D46E007198DA /* AuthFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97DAE2E37D4B6007198DA /* AuthFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77DBC7742E1829AB00529428 /* BookmarkFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BookmarkFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77DBC78C2E182A9200529428 /* BookmarkFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BookmarkFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77DBC7D52E1833FC00529428 /* BookmarkFeatureDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BookmarkFeatureDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 77DBC7E62E1833FD00529428 /* Exceptions for "BookmarkFeatureDemo" folder in "BookmarkFeatureDemo" target */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Info.plist, - ); - target = 77DBC7D42E1833FC00529428 /* BookmarkFeatureDemo */; - }; -/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 77DBC7762E1829AB00529428 /* BookmarkFeature */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = BookmarkFeature; - sourceTree = ""; - }; - 77DBC78D2E182A9200529428 /* BookmarkFeatureInterface */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = BookmarkFeatureInterface; - sourceTree = ""; - }; - 77DBC7D62E1833FC00529428 /* BookmarkFeatureDemo */ = { - isa = PBXFileSystemSynchronizedRootGroup; - exceptions = ( - 77DBC7E62E1833FD00529428 /* Exceptions for "BookmarkFeatureDemo" folder in "BookmarkFeatureDemo" target */, - ); - path = BookmarkFeatureDemo; - sourceTree = ""; - }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - 77DBC7712E1829AB00529428 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 77C97DA52E37CF26007198DA /* RxRelay in Frameworks */, - 77C97AAD2E37B1D5007198DA /* DomainInterface.framework in Frameworks */, - 77C97AA72E37B1B7007198DA /* BaseFeature.framework in Frameworks */, - 77DBC7C02E182BF500529428 /* RxSwift in Frameworks */, - 779A50BA2E2E1DC100ABDE4F /* BookmarkFeatureInterface.framework in Frameworks */, - 77C97AA92E37B1B7007198DA /* DesignSystem.framework in Frameworks */, - 77DBC7B62E182BD900529428 /* ReactorKit in Frameworks */, - 77DBC7C32E182BFE00529428 /* SnapKit in Frameworks */, - 77C97DA72E37D46E007198DA /* AuthFeatureInterface.framework in Frameworks */, - 77C97DAF2E37D4B6007198DA /* AuthFeatureInterface.framework in Frameworks */, - 77DBC7B92E182BE200529428 /* RxKeyboard in Frameworks */, - 77DBC7BC2E182BF500529428 /* RxCocoa in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 77DBC7892E182A9200529428 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 77C97AB02E37B27A007198DA /* DomainInterface.framework in Frameworks */, - 77C97AA22E37AFC9007198DA /* BaseFeature.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 77DBC7D22E1833FC00529428 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 779360FC2E38C2D20099B107 /* RxRelay in Frameworks */, - 77C97A9B2E37AF97007198DA /* Domain.framework in Frameworks */, - 779360FE2E38C4BF0099B107 /* ReactorKit in Frameworks */, - 779361002E38C7240099B107 /* RxKeyboard in Frameworks */, - 77C97A752E37AF83007198DA /* BookmarkFeature.framework in Frameworks */, - 77C97A8F2E37AF97007198DA /* Core.framework in Frameworks */, - 77C97AA02E37AFAA007198DA /* SnapKit in Frameworks */, - 77C97A932E37AF97007198DA /* DataMock.framework in Frameworks */, - 77C97A972E37AF97007198DA /* DictionaryFeature.framework in Frameworks */, - 77C97DA22E37B493007198DA /* BaseFeature.framework in Frameworks */, - 77C97A992E37AF97007198DA /* DictionaryFeatureInterface.framework in Frameworks */, - 77C97A952E37AF97007198DA /* DesignSystem.framework in Frameworks */, - 778C5E442E7D78DA00FD8439 /* MyPageFeature.framework in Frameworks */, - 77C97A9D2E37AF97007198DA /* DomainInterface.framework in Frameworks */, - 778C5E462E7D78DA00FD8439 /* MyPageFeatureInterface.framework in Frameworks */, - 77C97A822E37AF8D007198DA /* AuthFeatureInterface.framework in Frameworks */, - 77C97A912E37AF97007198DA /* Data.framework in Frameworks */, - 77C97A792E37AF83007198DA /* BookmarkFeatureInterface.framework in Frameworks */, - 77C97A802E37AF8D007198DA /* AuthFeature.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 77DBC76A2E1829AB00529428 = { - isa = PBXGroup; - children = ( - 77DBC7762E1829AB00529428 /* BookmarkFeature */, - 77DBC78D2E182A9200529428 /* BookmarkFeatureInterface */, - 77DBC7D62E1833FC00529428 /* BookmarkFeatureDemo */, - 77DBC7A52E182AFF00529428 /* Frameworks */, - 77DBC7752E1829AB00529428 /* Products */, - ); - sourceTree = ""; - }; - 77DBC7752E1829AB00529428 /* Products */ = { - isa = PBXGroup; - children = ( - 77DBC7742E1829AB00529428 /* BookmarkFeature.framework */, - 77DBC78C2E182A9200529428 /* BookmarkFeatureInterface.framework */, - 77DBC7D52E1833FC00529428 /* BookmarkFeatureDemo.app */, - ); - name = Products; - sourceTree = ""; - }; - 77DBC7A52E182AFF00529428 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 777313172E7DB00700AA92F6 /* MyPageFeatureInterface.framework */, - 778C5E422E7D78DA00FD8439 /* MyPageFeature.framework */, - 778C5E432E7D78DA00FD8439 /* MyPageFeatureInterface.framework */, - 77C97DAE2E37D4B6007198DA /* AuthFeatureInterface.framework */, - 77C97DA62E37D46E007198DA /* AuthFeatureInterface.framework */, - 77C97AAF2E37B27A007198DA /* DomainInterface.framework */, - 77C97AAC2E37B1D5007198DA /* DomainInterface.framework */, - 77C97AA52E37B1B7007198DA /* BaseFeature.framework */, - 77C97AA62E37B1B7007198DA /* DesignSystem.framework */, - 77C97AA12E37AFC9007198DA /* BaseFeature.framework */, - 77C97A842E37AF97007198DA /* BaseFeature.framework */, - 77C97A852E37AF97007198DA /* Core.framework */, - 77C97A862E37AF97007198DA /* Data.framework */, - 77C97A872E37AF97007198DA /* DataMock.framework */, - 77C97A882E37AF97007198DA /* DesignSystem.framework */, - 77C97A892E37AF97007198DA /* DictionaryFeature.framework */, - 77C97A8A2E37AF97007198DA /* DictionaryFeatureInterface.framework */, - 77C97A8B2E37AF97007198DA /* Domain.framework */, - 77C97A8C2E37AF97007198DA /* DomainInterface.framework */, - 77C97A7E2E37AF8D007198DA /* AuthFeature.framework */, - 77C97A7F2E37AF8D007198DA /* AuthFeatureInterface.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 77DBC76F2E1829AB00529428 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 77DBC7872E182A9200529428 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 77DBC7732E1829AB00529428 /* BookmarkFeature */ = { - isa = PBXNativeTarget; - buildConfigurationList = 77DBC77C2E1829AB00529428 /* Build configuration list for PBXNativeTarget "BookmarkFeature" */; - buildPhases = ( - 77DBC76F2E1829AB00529428 /* Headers */, - 77DBC7702E1829AB00529428 /* Sources */, - 77DBC7712E1829AB00529428 /* Frameworks */, - 77DBC7722E1829AB00529428 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 779A50BD2E2E1DC100ABDE4F /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 77DBC7762E1829AB00529428 /* BookmarkFeature */, - ); - name = BookmarkFeature; - packageProductDependencies = ( - 77DBC7B52E182BD900529428 /* ReactorKit */, - 77DBC7B82E182BE200529428 /* RxKeyboard */, - 77DBC7BB2E182BF500529428 /* RxCocoa */, - 77DBC7BF2E182BF500529428 /* RxSwift */, - 77DBC7C22E182BFE00529428 /* SnapKit */, - 77C97DA42E37CF26007198DA /* RxRelay */, - ); - productName = BookmarkFeature; - productReference = 77DBC7742E1829AB00529428 /* BookmarkFeature.framework */; - productType = "com.apple.product-type.framework"; - }; - 77DBC78B2E182A9200529428 /* BookmarkFeatureInterface */ = { - isa = PBXNativeTarget; - buildConfigurationList = 77DBC7932E182A9200529428 /* Build configuration list for PBXNativeTarget "BookmarkFeatureInterface" */; - buildPhases = ( - 77DBC7872E182A9200529428 /* Headers */, - 77DBC7882E182A9200529428 /* Sources */, - 77DBC7892E182A9200529428 /* Frameworks */, - 77DBC78A2E182A9200529428 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - 77DBC78D2E182A9200529428 /* BookmarkFeatureInterface */, - ); - name = BookmarkFeatureInterface; - packageProductDependencies = ( - ); - productName = BookmarkFeatureInterface; - productReference = 77DBC78C2E182A9200529428 /* BookmarkFeatureInterface.framework */; - productType = "com.apple.product-type.framework"; - }; - 77DBC7D42E1833FC00529428 /* BookmarkFeatureDemo */ = { - isa = PBXNativeTarget; - buildConfigurationList = 77DBC7E72E1833FD00529428 /* Build configuration list for PBXNativeTarget "BookmarkFeatureDemo" */; - buildPhases = ( - 77DBC7D12E1833FC00529428 /* Sources */, - 77DBC7D22E1833FC00529428 /* Frameworks */, - 77DBC7D32E1833FC00529428 /* Resources */, - 77C97A7D2E37AF83007198DA /* Embed Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 77C97A782E37AF83007198DA /* PBXTargetDependency */, - 77C97A7C2E37AF83007198DA /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 77DBC7D62E1833FC00529428 /* BookmarkFeatureDemo */, - ); - name = BookmarkFeatureDemo; - packageProductDependencies = ( - 77C97A9F2E37AFAA007198DA /* SnapKit */, - 779360FB2E38C2D20099B107 /* RxRelay */, - 779360FD2E38C4BF0099B107 /* ReactorKit */, - 779360FF2E38C7240099B107 /* RxKeyboard */, - ); - productName = BookmarkFeatureDemo; - productReference = 77DBC7D52E1833FC00529428 /* BookmarkFeatureDemo.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 77DBC76B2E1829AB00529428 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1620; - LastUpgradeCheck = 1620; - TargetAttributes = { - 77DBC7732E1829AB00529428 = { - CreatedOnToolsVersion = 16.2; - LastSwiftMigration = 1620; - }; - 77DBC78B2E182A9200529428 = { - CreatedOnToolsVersion = 16.2; - LastSwiftMigration = 1620; - }; - 77DBC7D42E1833FC00529428 = { - CreatedOnToolsVersion = 16.2; - }; - }; - }; - buildConfigurationList = 77DBC76E2E1829AB00529428 /* Build configuration list for PBXProject "BookmarkFeature" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 77DBC76A2E1829AB00529428; - minimizedProjectReferenceProxies = 1; - packageReferences = ( - 77DBC7B42E182BD900529428 /* XCRemoteSwiftPackageReference "ReactorKit" */, - 77DBC7B72E182BE200529428 /* XCRemoteSwiftPackageReference "RxKeyboard" */, - 77DBC7BA2E182BF500529428 /* XCRemoteSwiftPackageReference "RxSwift" */, - 77DBC7C12E182BFE00529428 /* XCRemoteSwiftPackageReference "SnapKit" */, - ); - preferredProjectObjectVersion = 77; - productRefGroup = 77DBC7752E1829AB00529428 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 77DBC7732E1829AB00529428 /* BookmarkFeature */, - 77DBC78B2E182A9200529428 /* BookmarkFeatureInterface */, - 77DBC7D42E1833FC00529428 /* BookmarkFeatureDemo */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 77DBC7722E1829AB00529428 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 77DBC78A2E182A9200529428 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 77DBC7D32E1833FC00529428 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 77DBC7702E1829AB00529428 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 77DBC7882E182A9200529428 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 77DBC7D12E1833FC00529428 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 779A50BD2E2E1DC100ABDE4F /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 77DBC78B2E182A9200529428 /* BookmarkFeatureInterface */; - targetProxy = 779A50BC2E2E1DC100ABDE4F /* PBXContainerItemProxy */; - }; - 77C97A782E37AF83007198DA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 77DBC7732E1829AB00529428 /* BookmarkFeature */; - targetProxy = 77C97A772E37AF83007198DA /* PBXContainerItemProxy */; - }; - 77C97A7C2E37AF83007198DA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 77DBC78B2E182A9200529428 /* BookmarkFeatureInterface */; - targetProxy = 77C97A7B2E37AF83007198DA /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 77DBC77D2E1829AB00529428 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.BookmarkFeature; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 77DBC77E2E1829AB00529428 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.BookmarkFeature; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; - 77DBC77F2E1829AB00529428 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 77DBC7802E1829AB00529428 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 77DBC7942E182A9200529428 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.BookmarkFeatureInterface; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 77DBC7952E182A9200529428 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.BookmarkFeatureInterface; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; - 77DBC7E82E1833FD00529428 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = BookmarkFeatureDemo/Info.plist; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.BookmarkFeatureDemo; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = MLSBookMarkFeatureProvisioningProfileDev; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 77DBC7E92E1833FD00529428 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = BookmarkFeatureDemo/Info.plist; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.BookmarkFeatureDemo; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = MLSBookMarkFeatureProvisioningProfile; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 77DBC76E2E1829AB00529428 /* Build configuration list for PBXProject "BookmarkFeature" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 77DBC77F2E1829AB00529428 /* Debug */, - 77DBC7802E1829AB00529428 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 77DBC77C2E1829AB00529428 /* Build configuration list for PBXNativeTarget "BookmarkFeature" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 77DBC77D2E1829AB00529428 /* Debug */, - 77DBC77E2E1829AB00529428 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 77DBC7932E182A9200529428 /* Build configuration list for PBXNativeTarget "BookmarkFeatureInterface" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 77DBC7942E182A9200529428 /* Debug */, - 77DBC7952E182A9200529428 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 77DBC7E72E1833FD00529428 /* Build configuration list for PBXNativeTarget "BookmarkFeatureDemo" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 77DBC7E82E1833FD00529428 /* Debug */, - 77DBC7E92E1833FD00529428 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - 77DBC7B42E182BD900529428 /* XCRemoteSwiftPackageReference "ReactorKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactorKit/ReactorKit.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 3.2.0; - }; - }; - 77DBC7B72E182BE200529428 /* XCRemoteSwiftPackageReference "RxKeyboard" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/RxSwiftCommunity/RxKeyboard"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.0.1; - }; - }; - 77DBC7BA2E182BF500529428 /* XCRemoteSwiftPackageReference "RxSwift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactiveX/RxSwift"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 6.9.0; - }; - }; - 77DBC7C12E182BFE00529428 /* XCRemoteSwiftPackageReference "SnapKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/SnapKit/SnapKit"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 5.7.1; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - 779360FB2E38C2D20099B107 /* RxRelay */ = { - isa = XCSwiftPackageProductDependency; - package = 77DBC7BA2E182BF500529428 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxRelay; - }; - 779360FD2E38C4BF0099B107 /* ReactorKit */ = { - isa = XCSwiftPackageProductDependency; - package = 77DBC7B42E182BD900529428 /* XCRemoteSwiftPackageReference "ReactorKit" */; - productName = ReactorKit; - }; - 779360FF2E38C7240099B107 /* RxKeyboard */ = { - isa = XCSwiftPackageProductDependency; - package = 77DBC7B72E182BE200529428 /* XCRemoteSwiftPackageReference "RxKeyboard" */; - productName = RxKeyboard; - }; - 77C97A9F2E37AFAA007198DA /* SnapKit */ = { - isa = XCSwiftPackageProductDependency; - package = 77DBC7C12E182BFE00529428 /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = SnapKit; - }; - 77C97DA42E37CF26007198DA /* RxRelay */ = { - isa = XCSwiftPackageProductDependency; - package = 77DBC7BA2E182BF500529428 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxRelay; - }; - 77DBC7B52E182BD900529428 /* ReactorKit */ = { - isa = XCSwiftPackageProductDependency; - package = 77DBC7B42E182BD900529428 /* XCRemoteSwiftPackageReference "ReactorKit" */; - productName = ReactorKit; - }; - 77DBC7B82E182BE200529428 /* RxKeyboard */ = { - isa = XCSwiftPackageProductDependency; - package = 77DBC7B72E182BE200529428 /* XCRemoteSwiftPackageReference "RxKeyboard" */; - productName = RxKeyboard; - }; - 77DBC7BB2E182BF500529428 /* RxCocoa */ = { - isa = XCSwiftPackageProductDependency; - package = 77DBC7BA2E182BF500529428 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxCocoa; - }; - 77DBC7BF2E182BF500529428 /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 77DBC7BA2E182BF500529428 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; - 77DBC7C22E182BFE00529428 /* SnapKit */ = { - isa = XCSwiftPackageProductDependency; - package = 77DBC7C12E182BFE00529428 /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = SnapKit; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = 77DBC76B2E1829AB00529428 /* Project object */; -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MLS/Presentation/BookmarkFeature/BookmarkFeature.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 94b2795e..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionFactoryImpl.swift deleted file mode 100644 index dd6e1a8f..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionFactoryImpl.swift +++ /dev/null @@ -1,19 +0,0 @@ -import BaseFeature -import BookmarkFeatureInterface -import DomainInterface - -public final class AddCollectionFactoryImpl: AddCollectionFactory { - private let createCollectionListUseCase: CreateCollectionListUseCase - private let setCollectionUseCase: UpdateCollectionUseCase - - public init(createCollectionListUseCase: CreateCollectionListUseCase, setCollectionUseCase: UpdateCollectionUseCase) { - self.createCollectionListUseCase = createCollectionListUseCase - self.setCollectionUseCase = setCollectionUseCase - } - - public func make(collection: CollectionResponse?) -> BaseViewController { - let viewController = AddCollectionViewController() - viewController.reactor = AddCollectionModalReactor(collection: collection, createCollectionListUseCase: createCollectionListUseCase, setCollectionUseCase: setCollectionUseCase) - return viewController - } -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionReactor.swift deleted file mode 100644 index 2a3c4952..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionReactor.swift +++ /dev/null @@ -1,110 +0,0 @@ -import BookmarkFeatureInterface -import DomainInterface - -import ReactorKit - -public final class AddCollectionModalReactor: Reactor { - // MARK: - Route - public enum Route { - case dismiss - case dismissWithData - case createError - case updateError - } - - // MARK: - Action - public enum Action { - case inputTextChanged(String?) - case backButtonTapped - case completeButtonTapped - } - - // MARK: - Mutation - public enum Mutation { - case saveInput(String) - case setError(Bool) - case setButtonEnabled(Bool) - case toNavigate(Route) - } - - // MARK: - State - public struct State { - @Pulse var route: Route? - var collection: CollectionResponse? - var inputText: String? - var isError: Bool = false - var isButtonEnabled: Bool = false - } - - // MARK: - Properties - public var initialState = State(collection: nil) - - private let createCollectionListUseCase: CreateCollectionListUseCase - private let setCollectionUseCase: UpdateCollectionUseCase - - // MARK: - Init - public init(collection: CollectionResponse?, createCollectionListUseCase: CreateCollectionListUseCase, setCollectionUseCase: UpdateCollectionUseCase) { - self.initialState = State(collection: collection, inputText: collection?.name) - self.createCollectionListUseCase = createCollectionListUseCase - self.setCollectionUseCase = setCollectionUseCase - } - - // MARK: - Mutate - public func mutate(action: Action) -> Observable { - switch action { - case .inputTextChanged(let text): - let trimmed = text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" - return Observable.from([ - .setButtonEnabled(!trimmed.isEmpty), - .saveInput(trimmed) - ]) - - case .backButtonTapped: - return .just(.toNavigate(.dismiss)) - - case .completeButtonTapped: - guard let text = currentState.inputText else { return .empty() } - let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines) - - if trimmed.count > 18 { - return .just(.setError(true)) - } - - if currentState.collection == nil { - return createCollectionListUseCase.execute(name: trimmed) - .andThen(.just(.toNavigate(.dismissWithData))) - .catch { _ in - return .just(.toNavigate(.createError)) - } - } else { - guard let id = currentState.collection?.collectionId else { return .empty() } - return setCollectionUseCase.execute( - collectionId: id, - name: trimmed - ) - .andThen(.just(.toNavigate(.dismissWithData))) - .catch { _ in - return .just(.toNavigate(.updateError)) - } - } - } - } - - // MARK: - Reduce - public func reduce(state: State, mutation: Mutation) -> State { - var newState = state - - switch mutation { - case .saveInput(let text): - newState.inputText = text - case .setError(let isError): - newState.isError = isError - case .setButtonEnabled(let isEnabled): - newState.isButtonEnabled = isEnabled - case .toNavigate(let route): - newState.route = route - } - - return newState - } -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListReactor.swift deleted file mode 100644 index 38e19e26..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListReactor.swift +++ /dev/null @@ -1,319 +0,0 @@ -import DomainInterface - -import ReactorKit -import RxSwift - -public final class BookmarkListReactor: Reactor { - // MARK: - Type - public enum Route { - case none - case sort(DictionaryType) - case filter(DictionaryType) - case detail(DictionaryType, Int) - case dictionary - case login - case edit - case bookmarkError - } - - public enum UIEvent { - case none - case add(BookmarkResponse) - case delete(BookmarkResponse) - case undo - case login - } - - enum ViewState: Equatable { - case loginWithData - case loginWithoutData - case logout - } - - // MARK: - Action - public enum Action { - case viewWillAppear - case toggleBookmark(Int) - case sortButtonTapped - case filterButtonTapped - case editButtonTapped - case fetchList - case sortOptionSelected(SortType) - case filterOptionSelected(startLevel: Int, endLevel: Int) - case undoLastDeletedBookmark - case dataTapped(Int) - case emptyButtonTapped - case itemFilterOptionSelected([(String, String)]) - case showLogin - } - - // MARK: - Mutation - public enum Mutation { - case setItems([BookmarkResponse]) - case setLoginState(Bool) - case setSort(SortType) - case setFilter(start: Int?, end: Int?) - case setLastDeletedBookmark(BookmarkResponse?) - case navigatTo(Route) - case setJobId([Int]) - case setCategoryId([Int]) - case setEvent(UIEvent) - } - - // MARK: - State - public struct State { - @Pulse var uiEvent: UIEvent = .none - @Pulse var route: Route - var items: [BookmarkResponse] = [] - var type: DictionaryType - var isLogin: Bool - var jobId: [Int]? - var categoryIds: [Int]? - var sort: SortType? - var startLevel: Int? - var endLevel: Int? - var lastDeletedBookmark: BookmarkResponse? - var viewState: ViewState { - if !isLogin { - return .logout - } else if items.isEmpty { - return .loginWithoutData - } else { - return .loginWithData - } - } - } - - public var initialState: State - - // MARK: - UseCases - private let fetchProfileUseCase: FetchProfileUseCase - private let setBookmarkUseCase: SetBookmarkUseCase - - private let fetchTotalBookmarkUseCase: FetchBookmarkUseCase - private let fetchMonsterBookmarkUseCase: FetchMonsterBookmarkUseCase - private let fetchItemBookmarkUseCase: FetchItemBookmarkUseCase - private let fetchNPCBookmarkUseCase: FetchNPCBookmarkUseCase - private let fetchQuestBookmarkUseCase: FetchQuestBookmarkUseCase - private let fetchMapBookmarkUseCase: FetchMapBookmarkUseCase - private let parseItemFilterResultUseCase: ParseItemFilterResultUseCase - - private let disposeBag = DisposeBag() - - // MARK: - Init - public init( - type: DictionaryType, - fetchProfileUseCase: FetchProfileUseCase, - setBookmarkUseCase: SetBookmarkUseCase, - fetchBookmarkUseCase: FetchBookmarkUseCase, - fetchMonsterBookmarkUseCase: FetchMonsterBookmarkUseCase, - fetchItemBookmarkUseCase: FetchItemBookmarkUseCase, - fetchNPCBookmarkUseCase: FetchNPCBookmarkUseCase, - fetchQuestBookmarkUseCase: FetchQuestBookmarkUseCase, - fetchMapBookmarkUseCase: FetchMapBookmarkUseCase, - parseItemFilterResultUseCase: ParseItemFilterResultUseCase - ) { - self.initialState = State(route: .none, type: type, isLogin: false) - self.fetchProfileUseCase = fetchProfileUseCase - self.setBookmarkUseCase = setBookmarkUseCase - self.fetchTotalBookmarkUseCase = fetchBookmarkUseCase - self.fetchMonsterBookmarkUseCase = fetchMonsterBookmarkUseCase - self.fetchItemBookmarkUseCase = fetchItemBookmarkUseCase - self.fetchNPCBookmarkUseCase = fetchNPCBookmarkUseCase - self.fetchQuestBookmarkUseCase = fetchQuestBookmarkUseCase - self.fetchMapBookmarkUseCase = fetchMapBookmarkUseCase - self.parseItemFilterResultUseCase = parseItemFilterResultUseCase - } - - // MARK: - Mutate - public func mutate(action: Action) -> Observable { - switch action { - case .viewWillAppear: - return fetchProfileUseCase.execute() - .flatMap { [weak self] profile -> Observable in - guard let self = self else { return .empty() } - if profile == nil { - return .just(.setLoginState(false)) - } else { - return Observable.concat([ - .just(.setLoginState(true)), - self.fetchList() - ]) - } - } - - case let .toggleBookmark(id): - return handleTogle(id: id) - - case .sortButtonTapped: - return .just(.navigatTo(.sort(currentState.type))) - - case .filterButtonTapped: - return .just(.navigatTo(.filter(currentState.type))) - - case .fetchList: - guard currentState.isLogin else { return .empty() } - return fetchList() - - case let .sortOptionSelected(sort): - return Observable.concat([ - .just(.setSort(sort)), - fetchList(sort: sort) - ]) - - case let .filterOptionSelected(startLevel, endLevel): - return Observable.concat([ - .just(.setFilter(start: startLevel, end: endLevel)), - fetchList() - ]) - - case .undoLastDeletedBookmark: - return handleUndo() - - case let .dataTapped(index): - let item = currentState.items[index] - guard let type = item.type.toDictionaryType else { return .empty() } - return .just(.navigatTo(.detail(type, item.originalId))) - case .emptyButtonTapped: - if currentState.viewState == .logout { - return .just(.navigatTo(.login)) - } else { - return .just(.navigatTo(.dictionary)) - } - case .editButtonTapped: - return .just(.navigatTo(.edit)) - case let .itemFilterOptionSelected(results): - let criteria = parseItemFilterResultUseCase.execute(results: results) - - return .concat([ - .just(.setJobId(criteria.jobIds)), - .just(.setFilter(start: criteria.startLevel, end: criteria.endLevel)), - .just(.setCategoryId(criteria.categoryIds)) - ]) - .concat(Observable.deferred { [weak self] in - guard let self = self else { return .empty() } - return self.fetchList() - }) - case .showLogin: - return .just(.setEvent(.login)) - } - } - - // MARK: - Fetch List - private func fetchList(sort: SortType? = nil) -> Observable { - switch currentState.type { - case .total: - return fetchTotalBookmarkUseCase.execute( - sort: sort ?? currentState.sort - ).map { .setItems($0) } - - case .monster: - return fetchMonsterBookmarkUseCase.execute( - minLevel: currentState.startLevel ?? 1, - maxLevel: currentState.endLevel ?? 200, - sort: sort ?? currentState.sort - ).map { .setItems($0) } - - case .item: - return fetchItemBookmarkUseCase.execute( - jobId: nil, - minLevel: currentState.startLevel, - maxLevel: currentState.endLevel, - categoryIds: nil, - sort: sort ?? currentState.sort - ).map { .setItems($0) } - - case .npc: - return fetchNPCBookmarkUseCase.execute(sort: sort ?? currentState.sort) - .map { .setItems($0) } - - case .quest: - return fetchQuestBookmarkUseCase.execute(sort: sort ?? currentState.sort) - .map { .setItems($0) } - - case .map: - return fetchMapBookmarkUseCase.execute(sort: sort ?? currentState.sort) - .map { .setItems($0) } - - default: - return .empty() - } - } - - // MARK: - Reduce - public func reduce(state: State, mutation: Mutation) -> State { - var newState = state - - switch mutation { - case let .setItems(response): - newState.items = response - case let .setLoginState(isLogin): - newState.isLogin = isLogin - case let .setSort(sort): - newState.sort = sort - case let .setFilter(start, end): - newState.startLevel = start - newState.endLevel = end - case let .setLastDeletedBookmark(item): - newState.lastDeletedBookmark = item - case let .navigatTo(route): - newState.route = route - case let .setJobId(ids): - newState.jobId = ids - case let .setCategoryId(ids): - newState.categoryIds = ids - case let .setEvent(event): - newState.uiEvent = event - } - - return newState - } -} - -private extension BookmarkListReactor { - func handleTogle(id: Int) -> Observable { - guard let index = currentState.items.firstIndex(where: { $0.originalId == id }) else { - return .empty() - } - - let targetItem = currentState.items[index] - - return setBookmarkUseCase.execute(bookmarkId: targetItem.bookmarkId, isBookmark: .delete) - .flatMap { _ -> Observable in - let lastItem = Mutation.setLastDeletedBookmark(targetItem) - - let eventMutation = Mutation.setEvent(.delete(targetItem)) - - return Observable.concat([ - .from([lastItem, eventMutation]), - self.fetchList() - ]) - } - .catch { _ in - .just(.navigatTo(.bookmarkError)) - } - } - - func handleUndo() -> Observable { - guard let lastDeleted = currentState.lastDeletedBookmark else { return .empty() } - - return setBookmarkUseCase.execute( - bookmarkId: lastDeleted.originalId, - isBookmark: .set(lastDeleted.type) - ) - .flatMap { _ -> Observable in - let lastItem = Mutation.setLastDeletedBookmark(nil) - - let event: UIEvent = .add(lastDeleted) - let eventMutation = Mutation.setEvent(event) - - return Observable.concat([ - .from([lastItem, eventMutation]), - self.fetchList() - ]) - } - .catch { _ in - .just(.navigatTo(.bookmarkError)) - } - } -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkMain/BookmarkMainReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkMain/BookmarkMainReactor.swift deleted file mode 100644 index 1c9d5da2..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkMain/BookmarkMainReactor.swift +++ /dev/null @@ -1,86 +0,0 @@ -import DomainInterface - -import ReactorKit - -public final class BookmarkMainReactor: Reactor { - public enum Route { - case none - case search - case onBoarding - case notification - case edit - case login - } - - public enum Action { - case viewWillAppear - case searchButtonTapped - case notificationButtonTapped - case loginButtonTapped - } - - public enum Mutation { - case navigateTo(Route) - case setLogin(Bool) - } - - public struct State { - @Pulse var route: Route - let type = DictionaryMainViewType.bookmark - var sections: [String] { - return type.pageTabList.map { $0.title } - } - - var isLogin = false - } - - // MARK: - Properties - private let setBookmarkUseCase: SetBookmarkUseCase - private let checkLoginUseCase: CheckLoginUseCase - private let fetchVisitBookmarkUseCase: FetchVisitBookmarkUseCase - - public var initialState: State - - private let disposeBag = DisposeBag() - - public init(setBookmarkUseCase: SetBookmarkUseCase, checkLoginUseCase: CheckLoginUseCase, fetchVisitBookmarkUseCase: FetchVisitBookmarkUseCase) { - self.initialState = State(route: .none) - self.setBookmarkUseCase = setBookmarkUseCase - self.checkLoginUseCase = checkLoginUseCase - self.fetchVisitBookmarkUseCase = fetchVisitBookmarkUseCase - } - - public func mutate(action: Action) -> Observable { - switch action { - case .viewWillAppear: - let onboardingMutation = fetchVisitBookmarkUseCase.execute() - .flatMap { hasVisited -> Observable in - if hasVisited { - return .empty() - } else { - return .just(.navigateTo(.onBoarding)) - } - } - let loginMutation = checkLoginUseCase.execute() - .map { Mutation.setLogin($0) } - return .concat([onboardingMutation, loginMutation]) - case .searchButtonTapped: - return Observable.just(.navigateTo(.search)) - case .notificationButtonTapped: - return Observable.just(.navigateTo(.notification)) - case .loginButtonTapped: - return .just(.navigateTo(.login)) - } - } - - public func reduce(state: State, mutation: Mutation) -> State { - var newState = state - switch mutation { - case let .navigateTo(route): - newState.route = route - case let .setLogin(isLogin): - newState.isLogin = isLogin - } - return newState - } -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkMain/BookmarkMainView.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkMain/BookmarkMainView.swift deleted file mode 100644 index f58e0cfa..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkMain/BookmarkMainView.swift +++ /dev/null @@ -1,101 +0,0 @@ -import BaseFeature -import DesignSystem -import DomainInterface -import SnapKit -import UIKit - -final class BookmarkMainView: UIView { - enum Constant { - static let topMargin: CGFloat = 20 - static let pageTabHeight: CGFloat = 40 - static let bottomTabHeight: CGFloat = 64 - } - - // MARK: - Components - public let headerView = Header(style: .main, title: "북마크") - public let searchBar = SearchBar() - - public let tabCollectionView: UICollectionView = { - let layout = UICollectionViewLayout() - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) - collectionView.isScrollEnabled = false - return collectionView - }() - - public let pageViewController = UIPageViewController( - transitionStyle: .scroll, - navigationOrientation: .horizontal - ) - - public let emptyView = ToLoginView() - - // MARK: - Init - public init(type: DictionaryMainViewType) { - super.init(frame: .zero) - setupBaseLayout(type: type) - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Base Layout -private extension BookmarkMainView { - func setupBaseLayout(type: DictionaryMainViewType) { - switch type { - case .search: - addSubview(searchBar) - searchBar.snp.makeConstraints { make in - make.top.equalTo(safeAreaLayoutGuide) - make.horizontalEdges.equalToSuperview() - } - default: - addSubview(headerView) - headerView.snp.makeConstraints { make in - make.top.equalTo(safeAreaLayoutGuide) - make.horizontalEdges.equalToSuperview() - } - - addSubview(tabCollectionView) - addSubview(pageViewController.view) - addSubview(emptyView) - - tabCollectionView.snp.makeConstraints { make in - make.top.equalTo(headerView.snp.bottom).offset(Constant.topMargin) - make.horizontalEdges.equalToSuperview() - make.height.equalTo(Constant.pageTabHeight) - } - - pageViewController.view.snp.makeConstraints { make in - make.top.equalTo(tabCollectionView.snp.bottom) - make.horizontalEdges.equalTo(safeAreaLayoutGuide) - make.bottom.equalToSuperview().inset(Constant.bottomTabHeight) - } - - emptyView.snp.makeConstraints { make in - make.top.equalTo(headerView.snp.bottom).offset(Constant.topMargin) - make.horizontalEdges.equalToSuperview() - make.bottom.equalToSuperview().inset(Constant.bottomTabHeight) - } - - tabCollectionView.isHidden = true - pageViewController.view.isHidden = true - emptyView.isHidden = false - } - } -} - -// MARK: - Public Update -extension BookmarkMainView { - public func updateLoginState(isLogin: Bool) { - tabCollectionView.isHidden = !isLogin - pageViewController.view.isHidden = !isLogin - - emptyView.isHidden = isLogin - - tabCollectionView.isUserInteractionEnabled = isLogin - pageViewController.view.isUserInteractionEnabled = isLogin - } -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift deleted file mode 100644 index 669604f3..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift +++ /dev/null @@ -1,30 +0,0 @@ -import BaseFeature -import BookmarkFeatureInterface -import DomainInterface - -public final class BookmarkModalFactoryImpl: BookmarkModalFactory { - private let addCollectionFactory: AddCollectionFactory - private let fetchCollectionListUseCase: FetchCollectionListUseCase - private let addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase - - public init(addCollectionFactory: AddCollectionFactory, fetchCollectionListUseCase: FetchCollectionListUseCase, addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase) { - self.addCollectionFactory = addCollectionFactory - self.fetchCollectionListUseCase = fetchCollectionListUseCase - self.addCollectionAndBookmarkUseCase = addCollectionAndBookmarkUseCase - } - - public func make(bookmarkIds: [Int]) -> BaseViewController { - let reactor = BookmarkModalReactor(bookmarkIds: bookmarkIds, fetchCollectionListUseCase: fetchCollectionListUseCase, addCollectionAndBookmarkUseCase: addCollectionAndBookmarkUseCase) - let viewController = BookmarkModalViewController(addCollectionFactory: addCollectionFactory) - viewController.reactor = reactor - return viewController - } - - public func make(bookmarkIds: [Int], onComplete: ((Bool) -> Void)? = nil) -> BaseViewController { - let viewController = make(bookmarkIds: bookmarkIds) - if let viewController = viewController as? BookmarkModalViewController { - viewController.onCompleted = onComplete - } - return viewController - } -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditReactor.swift deleted file mode 100644 index c9616086..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditReactor.swift +++ /dev/null @@ -1,73 +0,0 @@ -import UIKit - -import BookmarkFeatureInterface -import DomainInterface - -import ReactorKit - -public final class CollectionEditReactor: Reactor { - public enum Route { - case none - case dismiss - case collcectionList - } - - public enum Action { - case backButtonTapped - case addCollectionButtonTapped - case itemTapped(Int) - } - - public enum Mutation { - case navigateTo(Route) - case checkBookMarks([BookmarkResponse]) - case checkCollections([CollectionResponse]) - } - - public struct State { - @Pulse var route: Route - var bookmarks: [BookmarkResponse] - var selectedItems = [BookmarkResponse]() - var selectedCollections = [CollectionResponse]() - } - - // MARK: - Properties - public var initialState: State - - private let disposeBag = DisposeBag() - - public init(bookmarks: [BookmarkResponse]) { - self.initialState = State(route: .none, bookmarks: bookmarks) - } - - public func mutate(action: Action) -> Observable { - switch action { - case .backButtonTapped: - return .just(.navigateTo(.dismiss)) - case .addCollectionButtonTapped: - return .just(.navigateTo(.collcectionList)) - case .itemTapped(let index): - let item = currentState.bookmarks[index] - var newItems = currentState.selectedItems - if let index = newItems.firstIndex(where: { $0.originalId == item.originalId }) { - newItems.remove(at: index) - } else { - newItems.append(item) - } - return .just(.checkBookMarks(newItems)) - } - } - - public func reduce(state: State, mutation: Mutation) -> State { - var newState = state - switch mutation { - case .navigateTo(let route): - newState.route = route - case .checkBookMarks(let bookmarks): - newState.selectedItems = bookmarks - case .checkCollections(let collections): - newState.selectedCollections = collections - } - return newState - } -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListCell.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListCell.swift deleted file mode 100644 index 6a19c9cd..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListCell.swift +++ /dev/null @@ -1,79 +0,0 @@ -import BaseFeature -import UIKit - -import DesignSystem - -public final class CollectionListCell: UICollectionViewCell { - // MARK: - Properties - - // MARK: - Components - public let cellView = CollectionList() - - // MARK: - init - override init(frame: CGRect) { - super.init(frame: frame) - - addViews() - setupContstraints() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension CollectionListCell { - func addViews() { - contentView.addSubview(cellView) - } - - func setupContstraints() { - cellView.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - } -} - -public extension CollectionListCell { - struct Input { - let title: String - let count: Int - let images: [String?] - - public init(title: String, count: Int, images: [String?]) { - self.title = title - self.count = count - self.images = images - } - } - - func inject(input: Input) { - loadImages(from: input.images) { [weak self] images in - print("이미지:\(images)") - self?.cellView.setImages(images: images) - } - cellView.setTitle(text: input.title) - cellView.setSubtitle(text: "\(String(input.count))개") - } -} - -private func loadImages(from urls: [String?], completion: @escaping ([UIImage?]) -> Void) { - - var results = [UIImage?](repeating: nil, count: urls.count) - let dispatchGroup = DispatchGroup() - - for (index, urlString) in urls.enumerated() { - dispatchGroup.enter() - - ImageLoader.shared.loadImage(stringURL: urlString) { image in - results[index] = image - dispatchGroup.leave() - } - } - - dispatchGroup.notify(queue: .main) { - completion(results) - } -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListFactoryImpl.swift deleted file mode 100644 index 6d0d0513..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListFactoryImpl.swift +++ /dev/null @@ -1,25 +0,0 @@ -import BaseFeature -import BookmarkFeatureInterface -import DictionaryFeatureInterface -import DomainInterface - -public final class CollectionListFactoryImpl: CollectionListFactory { - private let fetchCollectionListUseCase: FetchCollectionListUseCase - private let addCollectionFactory: AddCollectionFactory - private let bookmarkDetailFactory: CollectionDetailFactory - private let sortedBottomSheetFactory: SortedBottomSheetFactory - - public init(fetchCollectionListUseCase: FetchCollectionListUseCase, addCollectionFactory: AddCollectionFactory, bookmarkDetailFactory: CollectionDetailFactory, sortedBottomSheetFactory: SortedBottomSheetFactory) { - self.fetchCollectionListUseCase = fetchCollectionListUseCase - self.addCollectionFactory = addCollectionFactory - self.bookmarkDetailFactory = bookmarkDetailFactory - self.sortedBottomSheetFactory = sortedBottomSheetFactory - } - - public func make() -> BaseViewController { - let reactor = CollectionListReactor(fetchCollectionListUseCase: fetchCollectionListUseCase) - let viewController = CollectionListViewController(addCollectionFactory: addCollectionFactory, detailFactory: bookmarkDetailFactory, sortedBottomSheetFactory: sortedBottomSheetFactory) - viewController.reactor = reactor - return viewController - } -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionSettingSheet/CollectionSettingReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionSettingSheet/CollectionSettingReactor.swift deleted file mode 100644 index a593f52f..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionSettingSheet/CollectionSettingReactor.swift +++ /dev/null @@ -1,66 +0,0 @@ -import UIKit - -import BookmarkFeatureInterface - -import ReactorKit -import RxCocoa -import RxSwift - -public final class CollectionSettingReactor: Reactor { - public enum Route { - case none - case dismiss - case dismissWithMenu(CollectionSettingMenu) - } - - // MARK: - Reactor - public enum Action { - case cancelButtonTapped - case editBookmarkButtonTapped - case editNameButtonTapped - case deleteButtonTapped - } - - public enum Mutation { - case navigateTo(route: Route) - } - - public struct State { - @Pulse var route: Route = .none - var menu = CollectionSettingMenu.allCases - } - - // MARK: - properties - public var initialState: State - var disposeBag = DisposeBag() - - // MARK: - init - public init() { - self.initialState = State() - } - - // MARK: - Reactor Methods - public func mutate(action: Action) -> Observable { - switch action { - case .cancelButtonTapped: - return Observable.just(.navigateTo(route: .dismiss)) - case .editBookmarkButtonTapped: - return Observable.just(.navigateTo(route: .dismissWithMenu(.editBookmark))) - case .editNameButtonTapped: - return Observable.just(.navigateTo(route: .dismissWithMenu(.editName))) - case .deleteButtonTapped: - return Observable.just(.navigateTo(route: .dismissWithMenu(.delete))) - } - } - - public func reduce(state: State, mutation: Mutation) -> State { - var newState = state - - switch mutation { - case .navigateTo(let route): - newState.route = route - } - - return newState - } -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionSettingSheet/CollectionSettingViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionSettingSheet/CollectionSettingViewController.swift deleted file mode 100644 index 30f06bb2..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionSettingSheet/CollectionSettingViewController.swift +++ /dev/null @@ -1,130 +0,0 @@ -import UIKit - -import BaseFeature -import BookmarkFeatureInterface - -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit - -public final class CollectionSettingViewController: BaseViewController, ModalPresentable, View { - public var modalHeight: CGFloat? = 284 - - public typealias Reactor = CollectionSettingReactor - - // MARK: - Properties - public var disposeBag = DisposeBag() - - public var setMenu: ((CollectionSettingMenu) -> Void)? - - private var mainView = CollectionSettingView() -} - -// MARK: - Life Cycle -public extension CollectionSettingViewController { - override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - } -} - -// MARK: - SetUp -private extension CollectionSettingViewController { - func addViews() { - view.addSubview(mainView) - } - - func setupConstraints() { - mainView.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - } - - func configureUI() { - mainView.menuTableView.delegate = self - mainView.menuTableView.dataSource = self - } -} - -extension CollectionSettingViewController { - public func bind(reactor: Reactor) { - bindUserActions(reactor: reactor) - bindViewState(reactor: reactor) - } - - func bindUserActions(reactor: Reactor) { - mainView.header.firstIconButton.rx.tap - .map { Reactor.Action.cancelButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - } - - func bindViewState(reactor: Reactor) { - rx.viewDidAppear - .take(1) - .flatMapLatest { _ in reactor.pulse(\.$route) } - .withUnretained(self) - .subscribe { owner, route in - switch route { - case .dismiss: - owner.dismissCurrentModal() - case .dismissWithMenu(let menu): - owner.setMenu?(menu) - owner.dismissCurrentModal() - default: - break - } - } - .disposed(by: disposeBag) - } -} - -extension CollectionSettingViewController: UITableViewDelegate, UITableViewDataSource { - public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - guard let reactor = reactor else { return 0 } - return reactor.currentState.menu.count - } - - public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = UITableViewCell() - guard let reactor = reactor else { return cell } - let item = reactor.currentState.menu[indexPath.row] - cell.textLabel?.textAlignment = .center - cell.textLabel?.autoresizingMask = [.flexibleWidth, .flexibleHeight] - cell.textLabel?.attributedText = .makeStyledString(font: .b_m_r, text: item.title, color: item.titleColor) - if indexPath.row < reactor.currentState.menu.count - 1 { - let divider = UIView() - divider.backgroundColor = .lightGray.withAlphaComponent(0.3) - cell.contentView.addSubview(divider) - divider.snp.makeConstraints { make in - make.horizontalEdges.bottom.equalToSuperview() - make.height.equalTo(1) - } - } - cell.selectionStyle = .none - return cell - } - - public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - guard let reactor = reactor else { return } - let item = reactor.currentState.menu[indexPath.row] - switch item { - case .editBookmark: - reactor.action.onNext(.editBookmarkButtonTapped) - case .editName: - reactor.action.onNext(.editNameButtonTapped) - case .delete: - reactor.action.onNext(.deleteButtonTapped) - case .cancel: - reactor.action.onNext(.cancelButtonTapped) - } - } - - public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - return 54 - } -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/AppDelegate.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/AppDelegate.swift deleted file mode 100644 index 3dfa35d1..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/AppDelegate.swift +++ /dev/null @@ -1,405 +0,0 @@ -// swiftlint:disable function_body_length -// swiftlint:disable line_length - -import UIKit - -import AuthFeature -import AuthFeatureInterface -import BaseFeature -import BookmarkFeature -import BookmarkFeatureInterface -import Core -import Data -import DataMock -import DesignSystem -import DictionaryFeature -import DictionaryFeatureInterface -import Domain -import DomainInterface -import MyPageFeature -import MyPageFeatureInterface - -@main -class AppDelegate: UIResponder, UIApplicationDelegate { - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - ImageLoader.shared.configure.diskCacheCountLimit = 10 - FontManager.registerFonts() - registerDependencies() - return true - } - - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) - } - - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {} -} - -private extension AppDelegate { - func registerDependencies() { - registerProvider() - registerRepository() - registerUseCase() - registerFactory() - } - - func registerProvider() { - DIContainer.register(type: NetworkProvider.self) { - NetworkProviderImpl() - } - DIContainer.register(type: SocialAuthenticatableProvider.self, name: "kakao") { - KakaoLoginProviderMock() - } - DIContainer.register(type: SocialAuthenticatableProvider.self, name: "apple") { - AppleLoginProviderMock() - } - } - - func registerRepository() { - DIContainer.register(type: AuthAPIRepository.self) { - AuthAPIRepositoryMock(provider: DIContainer.resolve(type: NetworkProvider.self)) - } - DIContainer.register(type: TokenRepository.self) { - KeyChainRepositoryImpl() - } - DIContainer.register(type: DictionaryListAPIRepository.self) { - return DictionaryListAPIRepositoryImpl(provider: DIContainer.resolve(type: NetworkProvider.self), tokenInterceptor: TokenInterceptor(fetchTokenUseCase: DIContainer.resolve(type: FetchTokenFromLocalUseCase.self))) - } - DIContainer.register(type: BookmarkRepository.self) { - return BookmarkRepositoryImpl(provider: DIContainer.resolve(type: NetworkProvider.self), interceptor: TokenInterceptor(fetchTokenUseCase: DIContainer.resolve(type: FetchTokenFromLocalUseCase.self))) - } - DIContainer.register(type: DictionaryDetailAPIRepository.self) { - return DictionaryDetailAPIRepositoryImpl(provider: DIContainer.resolve(type: NetworkProvider.self), tokenInterceptor: TokenInterceptor(fetchTokenUseCase: DIContainer.resolve(type: FetchTokenFromLocalUseCase.self))) - } - DIContainer.register(type: UserDefaultsRepository.self) { - UserDefaultsRepositoryImpl() - } - } - - func registerUseCase() { - DIContainer.register(type: FetchSocialCredentialUseCase.self, name: "kakao") { - let provider = DIContainer.resolve(type: SocialAuthenticatableProvider.self, name: "kakao") - return SocialLoginUseCaseImpl(provider: provider) - } - DIContainer.register(type: FetchSocialCredentialUseCase.self, name: "apple") { - let provider = DIContainer.resolve(type: SocialAuthenticatableProvider.self, name: "apple") - return SocialLoginUseCaseImpl(provider: provider) - } - DIContainer.register(type: CheckEmptyLevelAndRoleUseCase.self) { - CheckEmptyLevelAndRoleUseCaseImpl() - } - DIContainer.register(type: CheckValidLevelUseCase.self) { - CheckValidLevelUseCaseImpl() - } - DIContainer.register(type: FetchJobListUseCase.self) { - FetchJobListUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: LoginWithAppleUseCase.self) { - LoginWithAppleUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: LoginWithKakaoUseCase.self) { - LoginWithKakaoUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: SignUpWithAppleUseCase.self) { - SignUpWithAppleUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: SignUpWithKakaoUseCase.self) { - SignUpWithKakaoUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: UpdateUserInfoUseCase.self) { - UpdateUserInfoUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: FetchTokenFromLocalUseCase.self) { - FetchTokenFromLocalUseCaseImpl(repository: DIContainer.resolve(type: TokenRepository.self)) - } - DIContainer.register(type: SaveTokenToLocalUseCase.self) { - SaveTokenToLocalUseCaseImpl(repository: DIContainer.resolve(type: TokenRepository.self)) - } - DIContainer.register(type: DeleteTokenFromLocalUseCase.self) { - DeleteTokenFromLocalUseCaseImpl(repository: DIContainer.resolve(type: TokenRepository.self)) - } - DIContainer.register(type: FetchNotificationUseCase.self) { - FetchNotificationUseCaseImpl() - } - DIContainer.register(type: SetBookmarkUseCase.self) { - SetBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) - } - DIContainer.register(type: CheckLoginUseCase.self) { - CheckLoginUseCaseImpl(authRepository: DIContainer.resolve(type: AuthAPIRepository.self), tokenRepository: DIContainer.resolve(type: TokenRepository.self)) - } - DIContainer.register(type: CheckNotificationPermissionUseCase.self) { - return CheckNotificationPermissionUseCaseImpl() - } - DIContainer.register(type: OpenNotificationSettingUseCase.self) { - return OpenNotificationSettingUseCaseImpl() - } - DIContainer.register(type: UpdateNotificationAgreementUseCase.self) { - return UpdateNotificationAgreementUseCaseImpl(authRepository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: UpdateMarketingAgreementUseCase.self) { - return UpdateMarketingAgreementUseCaseImpl(authRepository: DIContainer.resolve(type: AuthAPIRepository.self), tokenRepository: DIContainer.resolve(type: TokenRepository.self)) - } - DIContainer.register(type: FetchDictionaryMapListUseCase.self) { - return FetchDictionaryMapListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryItemListUseCase.self) { - return FetchDictionaryItemListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryQuestListUseCase.self) { - return FetchDictionaryQuestListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryNpcListUseCase.self) { - return FetchDictionaryNpcListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryMonsterListUseCase.self) { - return FetchDictionaryMonsterListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMonsterUseCase.self) { - return FetchDictionaryDetailMonsterUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMonsterItemsUseCase.self) { - return FetchDictionaryDetailMonsterDropItemUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMonsterMapUseCase.self) { - return FetchDictionaryDetailMonsterMapUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailNpcUseCase.self) { - return FetchDictionaryDetailNpcUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailNpcQuestUseCase.self) { - return FetchDictionaryDetailNpcQuestUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailNpcMapUseCase.self) { - return FetchDictionaryDetailNpcMapUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailItemUseCase.self) { - return FetchDictionaryDetailItemUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailItemDropMonsterUseCase.self) { - return FetchDictionaryDetailItemDropMonsterUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailQuestUseCase.self) { - return FetchDictionaryDetailQuestUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailQuestLinkedQuestsUseCase.self) { - return FetchDictionaryDetailQuestLinkedQuestsUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMapUseCase.self) { - return FetchDictionaryDetailMapUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMapSpawnMonsterUseCase.self) { - return FetchDictionaryDetailMapSpawnMonsterUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMapNpcUseCase.self) { - return FetchDictionaryDetailMapNpcUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchMonsterBookmarkUseCase.self) { - FetchMonsterBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) - } - DIContainer.register(type: FetchItemBookmarkUseCase.self) { - FetchItemBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) - } - DIContainer.register(type: FetchMapBookmarkUseCase.self) { - FetchMapBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) - } - DIContainer.register(type: FetchQuestBookmarkUseCase.self) { - FetchQuestBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) - } - DIContainer.register(type: FetchNPCBookmarkUseCase.self) { - FetchNPCBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) - } - DIContainer.register(type: FetchBookmarkUseCase.self) { - FetchBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) - } - DIContainer.register(type: FetchDictionaryAllListUseCase.self) { - FetchDictionaryAllListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: RecentSearchRemoveUseCase.self) { - RecentSearchRemoveUseCaseImpl(repository: DIContainer.resolve(type: UserDefaultsRepository.self)) - } - DIContainer.register(type: RecentSearchAddUseCase.self) { - RecentSearchAddUseCaseImpl(repository: DIContainer.resolve(type: UserDefaultsRepository.self)) - } - DIContainer.register(type: FetchDictionaryListCountUseCase.self) { - FetchDictionaryListCountUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionarySearchListUseCase.self) { - FetchDictionarySearchListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: RecentSearchFetchUseCase.self) { - RecentSearchFetchUseCaseImpl(repository: DIContainer.resolve(type: UserDefaultsRepository.self)) - } - } - - func registerFactory() { - DIContainer.register(type: ItemFilterBottomSheetFactory.self) { - ItemFilterBottomSheetFactoryImpl() - } - DIContainer.register(type: MonsterFilterBottomSheetFactory.self) { - MonsterFilterBottomSheetFactoryImpl() - } - DIContainer.register(type: SortedBottomSheetFactory.self) { - SortedBottomSheetFactoryImpl() - } - DIContainer.register(type: AddCollectionFactory.self) { - AddCollectionFactoryImpl() - } - DIContainer.register(type: BookmarkModalFactory.self) { - BookmarkModalFactoryImpl(addCollectionFactory: DIContainer.resolve(type: AddCollectionFactory.self)) - } - DIContainer.register(type: DictionaryDetailFactory.self) { - return DictionaryDetailFactoryImpl(dictionaryDetailMapUseCase: DIContainer.resolve(type: FetchDictionaryDetailMapUseCase.self), dictionaryDetailMapSpawnMonsterUseCase: DIContainer.resolve(type: FetchDictionaryDetailMapSpawnMonsterUseCase.self), dictionaryDetailMapNpcUseCase: DIContainer.resolve(type: FetchDictionaryDetailMapNpcUseCase.self), dictionaryDetailQuestLinkedQuestsUseCase: DIContainer.resolve(type: FetchDictionaryDetailQuestLinkedQuestsUseCase.self), dictionaryDetailQuestUseCase: DIContainer.resolve(type: FetchDictionaryDetailQuestUseCase.self), dictionaryDetailItemDropMonsterUseCase: DIContainer.resolve(type: FetchDictionaryDetailItemDropMonsterUseCase.self), dictionaryDetailItemUseCase: DIContainer.resolve(type: FetchDictionaryDetailItemUseCase.self), dictionaryDetailNpcUseCase: DIContainer.resolve(type: FetchDictionaryDetailNpcUseCase.self), dictionaryDetailNpcQuestUseCase: DIContainer.resolve(type: FetchDictionaryDetailNpcQuestUseCase.self), dictionaryDetailNpcMapUseCase: DIContainer.resolve(type: FetchDictionaryDetailNpcMapUseCase.self), dictionaryDetailMonsterUseCase: DIContainer.resolve(type: FetchDictionaryDetailMonsterUseCase.self), dictionaryDetailMonsterDropItemUseCase: DIContainer.resolve(type: FetchDictionaryDetailMonsterItemsUseCase.self), dictionaryDetailMonsterMapUseCase: DIContainer.resolve(type: FetchDictionaryDetailMonsterMapUseCase.self)) - } - DIContainer.register(type: DictionaryMainListFactory.self) { - DictionaryListFactoryImpl( - checkLoginUseCase: DIContainer.resolve(type: CheckLoginUseCase.self), - dictionaryAllListItemUseCase: DIContainer.resolve(type: FetchDictionaryAllListUseCase.self), - dictionaryMapListItemUseCase: DIContainer.resolve(type: FetchDictionaryMapListUseCase.self), - dictionaryItemListItemUseCase: DIContainer.resolve(type: FetchDictionaryItemListUseCase.self), - dictionaryQuestListItemUseCase: DIContainer.resolve(type: FetchDictionaryQuestListUseCase.self), - dictionaryNpcListItemUseCase: DIContainer.resolve(type: FetchDictionaryNpcListUseCase.self), - dictionaryListItemUseCase: DIContainer.resolve(type: FetchDictionaryMonsterListUseCase.self), - setBookmarkUseCase: DIContainer.resolve(type: SetBookmarkUseCase.self), - itemFilterFactory: DIContainer.resolve(type: ItemFilterBottomSheetFactory.self), - monsterFilterFactory: DIContainer.resolve(type: MonsterFilterBottomSheetFactory.self), - sortedFactory: DIContainer.resolve(type: SortedBottomSheetFactory.self), - bookmarkModalFactory: DIContainer.resolve(type: BookmarkModalFactory.self), - detailFactory: DIContainer.resolve(type: DictionaryDetailFactory.self) - ) - } - DIContainer.register(type: DictionarySearchResultFactory.self) { - DictionarySearchResultFactoryImpl( - dictionaryListCountUseCase: DIContainer.resolve(type: FetchDictionaryListCountUseCase.self), - dictionaryMainListFactory: DIContainer - .resolve(type: DictionaryMainListFactory.self), - dictionarySearchListUseCase: DIContainer.resolve(type: FetchDictionarySearchListUseCase.self) - ) - } - DIContainer.register(type: DictionarySearchFactory.self) { - DictionarySearchFactoryImpl(recentSearchRemoveUseCase: DIContainer.resolve(type: RecentSearchRemoveUseCase.self), - recentSearchAddUseCase: DIContainer.resolve(type: RecentSearchAddUseCase.self), - searchResultFactory: DIContainer - .resolve(type: DictionarySearchResultFactory.self), recentSearchFetchUseCase: DIContainer.resolve(type: RecentSearchFetchUseCase.self) - ) - } - DIContainer.register(type: NotificationSettingFactory.self) { - NotificationSettingFactoryImpl(checkNotificationPermissionUseCase: DIContainer.resolve(type: CheckNotificationPermissionUseCase.self), updateNotificationAgreementUseCase: DIContainer.resolve(type: UpdateNotificationAgreementUseCase.self)) - } - DIContainer.register(type: DictionaryNotificationFactory.self) { - DictionaryNotificationFactoryImpl( - fetchNotificationUseCase: DIContainer - .resolve(type: FetchNotificationUseCase.self), - notificationSettingFactory: DIContainer - .resolve(type: NotificationSettingFactory.self) - ) - } - DIContainer.register(type: DictionaryMainViewFactory.self) { - DictionaryMainViewFactoryImpl( - dictionaryMainListFactory: DIContainer - .resolve(type: DictionaryMainListFactory.self), - searchFactory: DIContainer.resolve(type: DictionarySearchFactory.self), - notificationFactory: DIContainer - .resolve(type: DictionaryNotificationFactory.self), checkLoginUseCase: DIContainer.resolve(type: CheckLoginUseCase.self)) - } - DIContainer.register(type: BookmarkOnBoardingFactory.self) { - BookmarkOnBoardingFactoryImpl() - } - DIContainer.register(type: OnBoardingNotificationSheetFactory.self) { - return OnBoardingNotificationSheetFactoryImpl(checkNotificationPermissionUseCase: DIContainer.resolve(type: CheckNotificationPermissionUseCase.self), openNotificationSettingUseCase: DIContainer.resolve(type: OpenNotificationSettingUseCase.self), updateNotificationAgreementUseCase: DIContainer.resolve(type: UpdateNotificationAgreementUseCase.self), updateUserInfoUseCase: DIContainer.resolve(type: UpdateUserInfoUseCase.self), dictionaryMainViewFactory: DIContainer.resolve(type: DictionaryMainViewFactory.self)) - } - DIContainer.register(type: OnBoardingNotificationFactory.self) { - return OnBoardingNotificationFactoryImpl(onBoardingNotificationSheetFactory: DIContainer.resolve(type: OnBoardingNotificationSheetFactory.self)) - } - DIContainer.register(type: OnBoardingInputFactory.self) { - return OnBoardingInputFactoryImpl( - checkEmptyUseCase: DIContainer - .resolve(type: CheckEmptyLevelAndRoleUseCase.self), - checkValidLevelUseCase: DIContainer - .resolve(type: CheckValidLevelUseCase.self), - fetchJobListUseCase: DIContainer - .resolve(type: FetchJobListUseCase.self), updateUserInfoUseCase: DIContainer.resolve(type: UpdateUserInfoUseCase.self), - onBoardingNotificationFactory: DIContainer - .resolve(type: OnBoardingNotificationFactory.self) - ) - } - DIContainer.register(type: OnBoardingQuestionFactory.self) { - OnBoardingQuestionFactoryImpl(onBoardingInputFactory: DIContainer.resolve(type: OnBoardingInputFactory.self)) - } - DIContainer.register(type: TermsAgreementFactory.self) { - TermsAgreementFactoryImpl( - onBoardingQuestionFactory: DIContainer - .resolve(type: OnBoardingQuestionFactory.self), - signUpWithKakaoUseCase: DIContainer - .resolve(type: SignUpWithKakaoUseCase.self), - signUpWithAppleUseCase: DIContainer - .resolve(type: SignUpWithAppleUseCase.self), - saveTokenUseCase: DIContainer - .resolve(type: SaveTokenToLocalUseCase.self), - fetchTokenUseCase: DIContainer - .resolve(type: FetchTokenFromLocalUseCase.self), updateMarketingAgreementUseCase: DIContainer.resolve(type: UpdateMarketingAgreementUseCase.self) - ) - } - DIContainer.register(type: PutFCMTokenUseCase.self) { - PutFCMTokenUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: LoginFactory.self) { - LoginFactoryImpl( - termsAgreementsFactory: DIContainer - .resolve(type: TermsAgreementFactory.self), - appleLoginUseCase: DIContainer - .resolve(type: FetchSocialCredentialUseCase.self, name: "apple"), - kakaoLoginUseCase: DIContainer - .resolve(type: FetchSocialCredentialUseCase.self, name: "kakao"), - loginWithAppleUseCase: DIContainer - .resolve(type: LoginWithAppleUseCase.self), - loginWithKakaoUseCase: DIContainer - .resolve(type: LoginWithKakaoUseCase.self), - fetchTokenUseCase: DIContainer.resolve(type: FetchTokenFromLocalUseCase.self), - putFCMTokenUseCase: DIContainer.resolve(type: PutFCMTokenUseCase.self) - ) - } - DIContainer.register(type: BookmarkListFactory.self) { - BookmarkListFactoryImpl(itemFilterFactory: DIContainer.resolve(type: ItemFilterBottomSheetFactory.self), monsterFilterFactory: DIContainer.resolve(type: MonsterFilterBottomSheetFactory.self), sortedFactory: DIContainer.resolve(type: SortedBottomSheetFactory.self), bookmarkModalFactory: DIContainer.resolve(type: BookmarkModalFactory.self), loginFactory: DIContainer.resolve(type: LoginFactory.self), dictionaryDetailFactory: DIContainer.resolve(type: DictionaryDetailFactory.self), setBookmarkUseCase: DIContainer.resolve(type: SetBookmarkUseCase.self), checkLoginUseCase: DIContainer.resolve(type: CheckLoginUseCase.self), fetchBookmarkUseCase: DIContainer.resolve(type: FetchBookmarkUseCase.self), fetchMonsterBookmarkUseCase: DIContainer.resolve(type: FetchMonsterBookmarkUseCase.self), fetchItemBookmarkUseCase: DIContainer.resolve(type: FetchItemBookmarkUseCase.self), fetchNPCBookmarkUseCase: DIContainer.resolve(type: FetchNPCBookmarkUseCase.self), fetchQuestBookmarkUseCase: DIContainer.resolve(type: FetchQuestBookmarkUseCase.self), fetchMapBookmarkUseCase: DIContainer.resolve(type: FetchMapBookmarkUseCase.self)) - } - DIContainer.register(type: CollectionSettingFactory.self) { - CollectionSettingFactoryImpl() - } - DIContainer.register(type: CollectionEditFactory.self) { - CollectionEditFactoryImpl(setBookmarkUseCase: DIContainer.resolve(type: SetBookmarkUseCase.self), bookmarkModalFactory: DIContainer.resolve(type: BookmarkModalFactory.self)) - } - DIContainer.register(type: CollectionDetailFactory.self) { - CollectionDetailFactoryImpl( - setBookmarkUseCase: DIContainer - .resolve(type: SetBookmarkUseCase.self), - bookmarkModalFactory: DIContainer - .resolve(type: BookmarkModalFactory.self), - collectionSettingFactory: DIContainer - .resolve(type: CollectionSettingFactory.self), - addCollectionFactory: DIContainer - .resolve(type: AddCollectionFactory.self), - collectionEditFactory: DIContainer - .resolve(type: CollectionEditFactory.self) - ) - } - DIContainer.register(type: CollectionListFactory.self) { - CollectionListFactoryImpl(addCollectionFactory: DIContainer.resolve(type: AddCollectionFactory.self), bookmarkDetailFactory: DIContainer.resolve(type: CollectionDetailFactory.self)) - } - DIContainer.register(type: BookmarkMainFactory.self) { - BookmarkMainFactoryImpl( - setBookmarkUseCase: DIContainer - .resolve(type: SetBookmarkUseCase.self), - onBoardingFactory: DIContainer - .resolve(type: BookmarkOnBoardingFactory.self), - bookmarkListFactory: DIContainer - .resolve(type: BookmarkListFactory.self), - collectionListFactory: DIContainer - .resolve(type: CollectionListFactory.self), - searchFactory: DIContainer - .resolve(type: DictionarySearchFactory.self), - notificationFactory: DIContainer.resolve( - type: DictionaryNotificationFactory.self - ) - ) - } - } -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Assets.xcassets/AppIcon.appiconset/icon.png b/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Assets.xcassets/AppIcon.appiconset/icon.png deleted file mode 100644 index 6f9fcee3..00000000 Binary files a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/Assets.xcassets/AppIcon.appiconset/icon.png and /dev/null differ diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/SceneDelegate.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/SceneDelegate.swift deleted file mode 100644 index 2e5908b1..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/SceneDelegate.swift +++ /dev/null @@ -1,23 +0,0 @@ -import UIKit - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - var window: UIWindow? - - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - guard let windowScene = (scene as? UIWindowScene) else { return } - window = UIWindow(windowScene: windowScene) - let startVC = ViewController() - window?.rootViewController = UINavigationController(rootViewController: startVC) - window?.makeKeyAndVisible() - } - - func sceneDidDisconnect(_ scene: UIScene) {} - - func sceneDidBecomeActive(_ scene: UIScene) {} - - func sceneWillResignActive(_ scene: UIScene) {} - - func sceneWillEnterForeground(_ scene: UIScene) {} - - func sceneDidEnterBackground(_ scene: UIScene) {} -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/ViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/ViewController.swift deleted file mode 100644 index d62fd169..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureDemo/ViewController.swift +++ /dev/null @@ -1,69 +0,0 @@ -import UIKit - -import BaseFeature -import BookmarkFeatureInterface -import Core -import DesignSystem -import DictionaryFeatureInterface - -import SnapKit - -class ViewController: UIViewController { - let tableView: UITableView = { - let view = UITableView(frame: .zero, style: .plain) - return view - }() - - lazy var views: [[UIViewController]] = { - let dictionaryMainVC = DIContainer.resolve(type: DictionaryMainViewFactory.self).make() - - let bookmarkMainVC = DIContainer.resolve(type: BookmarkMainFactory.self).make() - - let dictView = BottomTabBarController(viewControllers: [ - dictionaryMainVC, - bookmarkMainVC, - BaseViewController() - ]) - dictView.title = "도감 메인" - - return [ - [dictView] - ] - }() - - override func viewDidLoad() { - super.viewDidLoad() - view.backgroundColor = .systemBackground - tableView.dataSource = self - tableView.delegate = self - navigationItem.title = "MLS Feature System" - - view.addSubview(tableView) - - tableView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -extension ViewController: UITableViewDataSource, UITableViewDelegate { - func numberOfSections(in tableView: UITableView) -> Int { - return views.count - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return views[section].count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = UITableViewCell() - cell.textLabel?.text = views[indexPath.section][indexPath.row].title - cell.selectionStyle = .none - return cell - } - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let nextController = views[indexPath.section][indexPath.row] - navigationController?.pushViewController(nextController, animated: true) - } -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkMainFactory.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkMainFactory.swift deleted file mode 100644 index 0b8deb2b..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkMainFactory.swift +++ /dev/null @@ -1,5 +0,0 @@ -import BaseFeature - -public protocol BookmarkMainFactory { - func make() -> BaseViewController -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionEditMenu.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionEditMenu.swift deleted file mode 100644 index 08b4b81e..00000000 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionEditMenu.swift +++ /dev/null @@ -1,30 +0,0 @@ -import UIKit - -public enum CollectionSettingMenu: CaseIterable { - case editBookmark - case editName - case delete - case cancel - - public var title: String { - switch self { - case .editBookmark: - return "컬렉션 북마크 수정" - case .editName: - return "컬렉션 이름 수정" - case .delete: - return "컬렉션 삭제" - case .cancel: - return "취소" - } - } - - public var titleColor: UIColor { - switch self { - case .delete: - return .red - default: - return .textColor - } - } -} diff --git a/MLS/Presentation/DesignSystem/.DS_Store b/MLS/Presentation/DesignSystem/.DS_Store deleted file mode 100644 index ce009ba5..00000000 Binary files a/MLS/Presentation/DesignSystem/.DS_Store and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/BookmarkModalFactory.swift b/MLS/Presentation/DesignSystem/BookmarkModalFactory.swift deleted file mode 100644 index 90b5b5b0..00000000 --- a/MLS/Presentation/DesignSystem/BookmarkModalFactory.swift +++ /dev/null @@ -1,5 +0,0 @@ -import BaseFeature - -public protocol BookmarkModalFactory { - func make(onDismissWithMessage: @escaping (String) -> Void) -> BaseViewController -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem.xcodeproj/project.pbxproj b/MLS/Presentation/DesignSystem/DesignSystem.xcodeproj/project.pbxproj deleted file mode 100644 index d3ede243..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem.xcodeproj/project.pbxproj +++ /dev/null @@ -1,704 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXBuildFile section */ - 08686A3D2DD1C8F400868C70 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 08686A3C2DD1C8F400868C70 /* SnapKit */; }; - 08686A702DD1E70500868C70 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 08686A6F2DD1E70500868C70 /* SnapKit */; }; - 08E424E32DF0B9D800D6ACD3 /* BaseFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 776AD6822DE985C300991745 /* BaseFeature.framework */; }; - 08E424E42DF0B9D800D6ACD3 /* BaseFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 776AD6822DE985C300991745 /* BaseFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 08E424E62DF0B9D900D6ACD3 /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 778F55F62DE8D7E500684E34 /* Core.framework */; }; - 08E424E72DF0B9D900D6ACD3 /* Core.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 778F55F62DE8D7E500684E34 /* Core.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 08E424E82DF0B9DB00D6ACD3 /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 778F55FC2DE8D87000684E34 /* Data.framework */; }; - 08E424E92DF0B9DB00D6ACD3 /* Data.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 778F55FC2DE8D87000684E34 /* Data.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 08E424EC2DF0B9DD00D6ACD3 /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 778F55F82DE8D7E500684E34 /* Domain.framework */; }; - 08E424ED2DF0B9DD00D6ACD3 /* Domain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 778F55F82DE8D7E500684E34 /* Domain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 08E424EE2DF0B9DE00D6ACD3 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 778F55FA2DE8D7E500684E34 /* DomainInterface.framework */; }; - 08E424EF2DF0B9DE00D6ACD3 /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 778F55FA2DE8D7E500684E34 /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 08E424F42DF0BA2F00D6ACD3 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0858ABAE2DCFD6940060EBCA /* DesignSystem.framework */; }; - 08E424F52DF0BA2F00D6ACD3 /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0858ABAE2DCFD6940060EBCA /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 08ED4DD22DCFF895002C21A2 /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 08ED4DD12DCFF895002C21A2 /* RxRelay */; }; - 08ED4DD42DCFF899002C21A2 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08ED4DD32DCFF899002C21A2 /* RxSwift */; }; - 778F56002DE8D8D900684E34 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 778F55FF2DE8D8D900684E34 /* ReactorKit */; }; - 77A33E952DD89FB800F14EC1 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 77A33E942DD89FB800F14EC1 /* RxCocoa */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 0858ABE12DCFD7700060EBCA /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0858ABA52DCFD6940060EBCA /* Project object */; - proxyType = 1; - remoteGlobalIDString = 0858ABAD2DCFD6940060EBCA; - remoteInfo = DesignSystem; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 08E424E52DF0B9D800D6ACD3 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 08E424EF2DF0B9DE00D6ACD3 /* DomainInterface.framework in Embed Frameworks */, - 08E424F52DF0BA2F00D6ACD3 /* DesignSystem.framework in Embed Frameworks */, - 08E424ED2DF0B9DD00D6ACD3 /* Domain.framework in Embed Frameworks */, - 08E424E92DF0B9DB00D6ACD3 /* Data.framework in Embed Frameworks */, - 08E424E42DF0B9D800D6ACD3 /* BaseFeature.framework in Embed Frameworks */, - 08E424E72DF0B9D900D6ACD3 /* Core.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 0858ABAE2DCFD6940060EBCA /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 0858ABC92DCFD7560060EBCA /* DesignSystemDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DesignSystemDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 776AD6822DE985C300991745 /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 778F55F22DE8D7E500684E34 /* AuthFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 778F55F42DE8D7E500684E34 /* AuthFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 778F55F62DE8D7E500684E34 /* Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 778F55F82DE8D7E500684E34 /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 778F55FA2DE8D7E500684E34 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 778F55FC2DE8D87000684E34 /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 0858ABDA2DCFD7580060EBCA /* Exceptions for "DesignSystemDemo" folder in "DesignSystemDemo" target */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Info.plist, - ); - target = 0858ABC82DCFD7560060EBCA /* DesignSystemDemo */; - }; - 0858ABF72DCFDBB90060EBCA /* Exceptions for "DesignSystem" folder in "DesignSystemDemo" target */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Resource/Image.xcassets, - ); - target = 0858ABC82DCFD7560060EBCA /* DesignSystemDemo */; - }; - 08ED4E072DD07FC5002C21A2 /* Exceptions for "DesignSystem" folder in "DesignSystem" target */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Info.plist, - ); - target = 0858ABAD2DCFD6940060EBCA /* DesignSystem */; - }; -/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 0858ABB02DCFD6940060EBCA /* DesignSystem */ = { - isa = PBXFileSystemSynchronizedRootGroup; - exceptions = ( - 08ED4E072DD07FC5002C21A2 /* Exceptions for "DesignSystem" folder in "DesignSystem" target */, - 0858ABF72DCFDBB90060EBCA /* Exceptions for "DesignSystem" folder in "DesignSystemDemo" target */, - ); - path = DesignSystem; - sourceTree = ""; - }; - 0858ABCA2DCFD7560060EBCA /* DesignSystemDemo */ = { - isa = PBXFileSystemSynchronizedRootGroup; - exceptions = ( - 0858ABDA2DCFD7580060EBCA /* Exceptions for "DesignSystemDemo" folder in "DesignSystemDemo" target */, - ); - path = DesignSystemDemo; - sourceTree = ""; - }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - 0858ABAB2DCFD6940060EBCA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 08686A3D2DD1C8F400868C70 /* SnapKit in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0858ABC62DCFD7560060EBCA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 08E424E32DF0B9D800D6ACD3 /* BaseFeature.framework in Frameworks */, - 08ED4DD42DCFF899002C21A2 /* RxSwift in Frameworks */, - 77A33E952DD89FB800F14EC1 /* RxCocoa in Frameworks */, - 08E424E62DF0B9D900D6ACD3 /* Core.framework in Frameworks */, - 08E424F42DF0BA2F00D6ACD3 /* DesignSystem.framework in Frameworks */, - 08E424E82DF0B9DB00D6ACD3 /* Data.framework in Frameworks */, - 08ED4DD22DCFF895002C21A2 /* RxRelay in Frameworks */, - 08E424EE2DF0B9DE00D6ACD3 /* DomainInterface.framework in Frameworks */, - 08E424EC2DF0B9DD00D6ACD3 /* Domain.framework in Frameworks */, - 778F56002DE8D8D900684E34 /* ReactorKit in Frameworks */, - 08686A702DD1E70500868C70 /* SnapKit in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 0858ABA42DCFD6940060EBCA = { - isa = PBXGroup; - children = ( - 0858ABB02DCFD6940060EBCA /* DesignSystem */, - 0858ABCA2DCFD7560060EBCA /* DesignSystemDemo */, - 0858ABDE2DCFD7700060EBCA /* Frameworks */, - 0858ABAF2DCFD6940060EBCA /* Products */, - ); - sourceTree = ""; - }; - 0858ABAF2DCFD6940060EBCA /* Products */ = { - isa = PBXGroup; - children = ( - 0858ABAE2DCFD6940060EBCA /* DesignSystem.framework */, - 0858ABC92DCFD7560060EBCA /* DesignSystemDemo.app */, - ); - name = Products; - sourceTree = ""; - }; - 0858ABDE2DCFD7700060EBCA /* Frameworks */ = { - isa = PBXGroup; - children = ( - 776AD6822DE985C300991745 /* BaseFeature.framework */, - 778F55FC2DE8D87000684E34 /* Data.framework */, - 778F55F22DE8D7E500684E34 /* AuthFeature.framework */, - 778F55F42DE8D7E500684E34 /* AuthFeatureInterface.framework */, - 778F55F62DE8D7E500684E34 /* Core.framework */, - 778F55F82DE8D7E500684E34 /* Domain.framework */, - 778F55FA2DE8D7E500684E34 /* DomainInterface.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 0858ABA92DCFD6940060EBCA /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 0858ABAD2DCFD6940060EBCA /* DesignSystem */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0858ABB42DCFD6940060EBCA /* Build configuration list for PBXNativeTarget "DesignSystem" */; - buildPhases = ( - 0858ABA92DCFD6940060EBCA /* Headers */, - 0858ABAA2DCFD6940060EBCA /* Sources */, - 0858ABAB2DCFD6940060EBCA /* Frameworks */, - 0858ABAC2DCFD6940060EBCA /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - 0858ABB02DCFD6940060EBCA /* DesignSystem */, - ); - name = DesignSystem; - packageProductDependencies = ( - 08686A3C2DD1C8F400868C70 /* SnapKit */, - ); - productName = DesignSystem; - productReference = 0858ABAE2DCFD6940060EBCA /* DesignSystem.framework */; - productType = "com.apple.product-type.framework"; - }; - 0858ABC82DCFD7560060EBCA /* DesignSystemDemo */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0858ABDB2DCFD7580060EBCA /* Build configuration list for PBXNativeTarget "DesignSystemDemo" */; - buildPhases = ( - 0858ABC52DCFD7560060EBCA /* Sources */, - 0858ABC62DCFD7560060EBCA /* Frameworks */, - 0858ABC72DCFD7560060EBCA /* Resources */, - 08E424E52DF0B9D800D6ACD3 /* Embed Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 0858ABE22DCFD7700060EBCA /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 0858ABCA2DCFD7560060EBCA /* DesignSystemDemo */, - ); - name = DesignSystemDemo; - packageProductDependencies = ( - 08ED4DD12DCFF895002C21A2 /* RxRelay */, - 08ED4DD32DCFF899002C21A2 /* RxSwift */, - 08686A6F2DD1E70500868C70 /* SnapKit */, - 77A33E942DD89FB800F14EC1 /* RxCocoa */, - 778F55FF2DE8D8D900684E34 /* ReactorKit */, - ); - productName = DesignSystemDemo; - productReference = 0858ABC92DCFD7560060EBCA /* DesignSystemDemo.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 0858ABA52DCFD6940060EBCA /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1620; - LastUpgradeCheck = 1620; - TargetAttributes = { - 0858ABAD2DCFD6940060EBCA = { - CreatedOnToolsVersion = 16.2; - LastSwiftMigration = 1620; - }; - 0858ABC82DCFD7560060EBCA = { - CreatedOnToolsVersion = 16.2; - }; - }; - }; - buildConfigurationList = 0858ABA82DCFD6940060EBCA /* Build configuration list for PBXProject "DesignSystem" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 0858ABA42DCFD6940060EBCA; - minimizedProjectReferenceProxies = 1; - packageReferences = ( - 0858ABE62DCFD8610060EBCA /* XCRemoteSwiftPackageReference "SnapKit" */, - 08ED4DB82DCFE91F002C21A2 /* XCRemoteSwiftPackageReference "RxSwift" */, - 778F55FE2DE8D8D900684E34 /* XCRemoteSwiftPackageReference "ReactorKit" */, - ); - preferredProjectObjectVersion = 77; - productRefGroup = 0858ABAF2DCFD6940060EBCA /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 0858ABAD2DCFD6940060EBCA /* DesignSystem */, - 0858ABC82DCFD7560060EBCA /* DesignSystemDemo */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 0858ABAC2DCFD6940060EBCA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0858ABC72DCFD7560060EBCA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 0858ABAA2DCFD6940060EBCA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0858ABC52DCFD7560060EBCA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 0858ABE22DCFD7700060EBCA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 0858ABAD2DCFD6940060EBCA /* DesignSystem */; - targetProxy = 0858ABE12DCFD7700060EBCA /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 0858ABB52DCFD6940060EBCA /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = DesignSystem/Info.plist; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.DesignSystem; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 0858ABB62DCFD6940060EBCA /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = DesignSystem/Info.plist; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.DesignSystem; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; - 0858ABB72DCFD6940060EBCA /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 0858ABB82DCFD6940060EBCA /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 0858ABDC2DCFD7580060EBCA /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = DesignSystemDemo/Info.plist; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLS.DesignSystemDemo; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = MLSDesignSystemProvisioningProfileDev; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 0858ABDD2DCFD7580060EBCA /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = DesignSystemDemo/Info.plist; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLS.DesignSystemDemo; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = MLSDesignSystemProvisioningProfileDev; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 0858ABA82DCFD6940060EBCA /* Build configuration list for PBXProject "DesignSystem" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0858ABB72DCFD6940060EBCA /* Debug */, - 0858ABB82DCFD6940060EBCA /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 0858ABB42DCFD6940060EBCA /* Build configuration list for PBXNativeTarget "DesignSystem" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0858ABB52DCFD6940060EBCA /* Debug */, - 0858ABB62DCFD6940060EBCA /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 0858ABDB2DCFD7580060EBCA /* Build configuration list for PBXNativeTarget "DesignSystemDemo" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0858ABDC2DCFD7580060EBCA /* Debug */, - 0858ABDD2DCFD7580060EBCA /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - 0858ABE62DCFD8610060EBCA /* XCRemoteSwiftPackageReference "SnapKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/SnapKit/SnapKit.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 5.7.1; - }; - }; - 08ED4DB82DCFE91F002C21A2 /* XCRemoteSwiftPackageReference "RxSwift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactiveX/RxSwift"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 6.9.0; - }; - }; - 778F55FE2DE8D8D900684E34 /* XCRemoteSwiftPackageReference "ReactorKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactorKit/ReactorKit.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 3.2.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - 08686A3C2DD1C8F400868C70 /* SnapKit */ = { - isa = XCSwiftPackageProductDependency; - package = 0858ABE62DCFD8610060EBCA /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = SnapKit; - }; - 08686A6F2DD1E70500868C70 /* SnapKit */ = { - isa = XCSwiftPackageProductDependency; - package = 0858ABE62DCFD8610060EBCA /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = SnapKit; - }; - 08ED4DD12DCFF895002C21A2 /* RxRelay */ = { - isa = XCSwiftPackageProductDependency; - package = 08ED4DB82DCFE91F002C21A2 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxRelay; - }; - 08ED4DD32DCFF899002C21A2 /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 08ED4DB82DCFE91F002C21A2 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; - 778F55FF2DE8D8D900684E34 /* ReactorKit */ = { - isa = XCSwiftPackageProductDependency; - package = 778F55FE2DE8D8D900684E34 /* XCRemoteSwiftPackageReference "ReactorKit" */; - productName = ReactorKit; - }; - 77A33E942DD89FB800F14EC1 /* RxCocoa */ = { - isa = XCSwiftPackageProductDependency; - package = 08ED4DB82DCFE91F002C21A2 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxCocoa; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = 0858ABA52DCFD6940060EBCA /* Project object */; -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MLS/Presentation/DesignSystem/DesignSystem.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/.DS_Store b/MLS/Presentation/DesignSystem/DesignSystem/.DS_Store deleted file mode 100644 index 530d0a5a..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/.DS_Store and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/AuthGuideAlert.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/AuthGuideAlert.swift deleted file mode 100644 index f619a3de..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/AuthGuideAlert.swift +++ /dev/null @@ -1,130 +0,0 @@ -import UIKit - -import SnapKit - -public class AuthGuideAlert: GuideAlert { - // MARK: - Type - private enum Constant { - static let iconSize: CGFloat = 24 - static let topSpacing: CGFloat = 14 - static let stackViewTopSpacing: CGFloat = stackViewBottomSpacing * 2 - static let stackViewBottomSpacing: CGFloat = 8 - static let iconSpacing: CGFloat = 5 - } - - public enum AuthGuideAlertType { - case logout - case withdraw - - var mainText: String { - switch self { - case .logout: - "정말 로그아웃 하시겠어요?" - case .withdraw: - "정말 탈퇴 하시겠어요?" - } - } - - var subText: String { - switch self { - case .logout: - "로그아웃하면 저장한 캐릭터,\n북마크한 아이템 정보를 볼 수 없어요." - case .withdraw: - "탈퇴 시, 아래 정보가 삭제 되어 복구가 불가능해요." - } - } - - var ctaText: String { - switch self { - case .logout: - "로그아웃 하기" - case .withdraw: - "탈퇴하기" - } - } - } - - // MARK: - Components - private let contentStackView: UIStackView = { - let view = UIStackView() - view.axis = .vertical - return view - }() - - private let subTextLabel: UILabel = { - let label = UILabel() - label.numberOfLines = 2 - return label - }() - - private lazy var bookmarkStackView = makeStack(icon: .checkMarkFill, text: "북마크 컬렉션") - private lazy var characterStackView = makeStack(icon: .checkMarkFill, text: "캐릭터 정보") - - // MARK: - init - public init(type: AuthGuideAlertType) { - super.init(mainText: type.mainText, ctaText: type.ctaText, cancelText: "취소") - addViews(type: type) - setupConstraints(type: type) - configureUI(type: type) - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension AuthGuideAlert { - func addViews(type: AuthGuideAlertType) { - addSubview(contentStackView) - - contentStackView.addArrangedSubview(subTextLabel) - if type == .withdraw { - contentStackView.addArrangedSubview(bookmarkStackView) - contentStackView.addArrangedSubview(characterStackView) - } - } - - func setupConstraints(type: AuthGuideAlertType) { - contentStackView.snp.makeConstraints { make in - make.top.equalTo(mainTextLabel.snp.bottom).offset(Constant.topSpacing) - make.horizontalEdges.equalToSuperview().inset(GuideAlert.Constant.horizontalInset) - } - - if type == .withdraw { - contentStackView.setCustomSpacing(Constant.stackViewTopSpacing, after: subTextLabel) - contentStackView.setCustomSpacing(Constant.stackViewBottomSpacing, after: bookmarkStackView) - } - - buttonStackView.snp.remakeConstraints { make in - make.top.equalTo(contentStackView.snp.bottom).offset(GuideAlert.Constant.verticalSpacing) - make.horizontalEdges.equalToSuperview().inset(GuideAlert.Constant.horizontalInset) - make.bottom.equalToSuperview().inset(GuideAlert.Constant.verticalInset) - make.height.equalTo(GuideAlert.Constant.buttonHeight) - } - } - - func configureUI(type: AuthGuideAlertType) { - subTextLabel.attributedText = .makeStyledString(font: .b_s_r, text: type.subText, color: .neutral700) - } - - func makeStack(icon: UIImage, text: String) -> UIStackView { - let iconView = UIImageView(image: icon) - - let label: UILabel = { - let label = UILabel() - label.attributedText = .makeStyledString(font: .cp_s_r, text: text, color: .neutral700, alignment: .left) - return label - }() - - iconView.snp.makeConstraints { make in - make.size.equalTo(Constant.iconSize) - } - - let stackView = UIStackView(arrangedSubviews: [iconView, label]) - stackView.axis = .horizontal - stackView.spacing = Constant.stackViewBottomSpacing - return stackView - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/Badge.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/Badge.swift deleted file mode 100644 index 6d7ff623..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/Badge.swift +++ /dev/null @@ -1,102 +0,0 @@ -import UIKit - -import SnapKit - -public final class Badge: UIView { - // MARK: - Type - public enum BadgeStyle { - case element(String) - case preQuest - case currentQuest - case nextQuest - - var font: UIFont? { - switch self { - case .element: - return .cp_s_sb - case .currentQuest: - return .cp_xs_sb - default: - return .cp_xs_r - } - } - - var fontColor: UIColor { - switch self { - case .element, .currentQuest: - return .primary700 - default: - return .neutral600 - } - } - - var backgroundColor: UIColor { - switch self { - case .element, .currentQuest: - return .primary25 - default: - return .neutral100 - } - } - - var text: String { - switch self { - case .element(let text): - return text - case .preQuest: - return "이전 퀘스트" - case .currentQuest: - return "현재 퀘스트" - case .nextQuest: - return "다음 퀘스트" - } - } - } - - private enum Constant { - static let radius: CGFloat = 4 - static let contentInsets: CGFloat = 6 - } - - // MARK: - Components - private let textLabel = UILabel() - - // MARK: - init - public init(style: BadgeStyle) { - super.init(frame: .zero) - - addViews() - setupConstraints() - configureUI(style: style) - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension Badge { - func addViews() { - addSubview(textLabel) - } - - func setupConstraints() { - textLabel.snp.makeConstraints { make in - make.edges.equalToSuperview().inset(Constant.contentInsets) - } - } - - func configureUI(style: BadgeStyle) { - layer.cornerRadius = Constant.radius - backgroundColor = style.backgroundColor - textLabel.attributedText = .makeStyledString(font: style.font, text: style.text, color: style.fontColor, lineHeight: 1) - } -} - -public extension Badge { - func update(style: BadgeStyle) { - configureUI(style: style) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/CardList.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/CardList.swift deleted file mode 100644 index 312ac972..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/CardList.swift +++ /dev/null @@ -1,264 +0,0 @@ -import UIKit - -import SnapKit - -public final class CardList: UIView { - // MARK: - Type - public enum CardListType { - case bookmark - case checkbox - case detailStack - case detailStackText // 몬스터 카드일 경우 드롭률 - case detailStackBadge(Badge.BadgeStyle) - - var icon: UIImage? { - switch self { - case .bookmark: - return .bookmarkBorderList - case .checkbox: - return .checkSquare - case .detailStack, .detailStackText, .detailStackBadge: - return nil - } - } - - var selectedIcon: UIImage? { - switch self { - case .bookmark: - return .bookmarkList - case .checkbox: - return .checkSquareFill - case .detailStack, .detailStackText, .detailStackBadge: - return nil - } - } - } - - enum Constant { - static let cardRadius: CGFloat = 16 - static let cardLeadingInset: CGFloat = 12 - static let cardTrailingInset: CGFloat = 16 - static let infoLabelInset: CGFloat = 16 - static let imageRadius: CGFloat = 8 - static let imageInset: CGFloat = 10 - static let imageContentViewSize: CGFloat = 80 - static let stackViewSpacing: CGFloat = 4 - static let iconSize: CGFloat = 24 - static let mapImageSize: CGFloat = 40 - } - - // MARK: - Properties - private var type = CardListType.bookmark - private var icon = UIImage() - private var selectedIcon = UIImage() - - public var isIconSelected: Bool = false { - didSet { - updateIcon() - } - } - - public var mainText: String? { - didSet { - updateMainText() - } - } - - public var subText: String? { - didSet { - updateSubText() - } - } - - public var onIconTapped: (() -> Void)? - - // MARK: - Components - public let imageView = ItemImageView(image: nil, cornerRadius: Constant.imageRadius, inset: Constant.imageInset, backgroundColor: .listMap) - - private lazy var textLabelStackView: UIStackView = { - let view = UIStackView(arrangedSubviews: [mainTextLabel, subTextLabel]) - view.axis = .vertical - view.spacing = Constant.stackViewSpacing - return view - }() - - private let mainTextLabel: UILabel = { - let label = UILabel() - label.numberOfLines = 2 - label.lineBreakMode = .byTruncatingTail - label.lineBreakStrategy = .pushOut - return label - }() - - private let subTextLabel: UILabel = { - let label = UILabel() - label.numberOfLines = 1 - return label - }() - - private let iconButton: UIButton = { - let button = UIButton() - button.setImage(.bookmarkBorder, for: .normal) - return button - }() - - // 드롭률 표시용 라벨 2개 - private let dropTitleLabel: UILabel = { - let label = UILabel() - label.numberOfLines = 1 - return label - }() - - private let dropValueLabel: UILabel = { - let label = UILabel() - label.numberOfLines = 1 - return label - }() - - // 2개 라벨을 세로로 묶는 스택뷰 - private lazy var dropInfoStack: UIStackView = { - let stack = UIStackView(arrangedSubviews: [dropTitleLabel, dropValueLabel]) - stack.axis = .vertical - stack.alignment = .trailing - stack.isHidden = true // 기본은 숨김 - return stack - }() - - private let badge = Badge(style: .currentQuest) - - public init() { - super.init(frame: .zero) - addViews() - setupConstraints() - configureUI() - bindButton() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension CardList { - func addViews() { - addSubview(imageView) - addSubview(textLabelStackView) - addSubview(iconButton) - addSubview(dropInfoStack) - addSubview(badge) - } - - func setupConstraints() { - imageView.snp.makeConstraints { make in - make.top.leading.bottom.equalToSuperview().inset(Constant.cardLeadingInset) - make.size.equalTo(Constant.imageContentViewSize) - } - - textLabelStackView.snp.makeConstraints { make in - make.leading.equalTo(imageView.snp.trailing).offset(Constant.cardLeadingInset) - make.centerY.equalToSuperview() - make.trailing.lessThanOrEqualToSuperview().inset(Constant.cardTrailingInset) - } - - dropInfoStack.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.trailing.equalToSuperview().inset(Constant.cardTrailingInset) - } - - badge.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.trailing.equalToSuperview().inset(Constant.cardTrailingInset) - } - - iconButton.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.trailing.equalToSuperview().inset(Constant.cardTrailingInset) - make.size.equalTo(Constant.iconSize) - } - - textLabelStackView.snp.makeConstraints { make in - make.trailing.lessThanOrEqualTo(dropInfoStack.snp.leading).offset(-Constant.cardLeadingInset) - make.trailing.lessThanOrEqualTo(badge.snp.leading).offset(-Constant.cardLeadingInset) - make.trailing.lessThanOrEqualTo(iconButton.snp.leading).offset(-Constant.cardLeadingInset) - } - } - - func configureUI() { - backgroundColor = .whiteMLS - layer.cornerRadius = Constant.cardRadius - } - - func bindButton() { - iconButton.addAction(UIAction(handler: { [weak self] _ in - guard let self = self else { return } - self.onIconTapped?() - }), for: .touchUpInside) - } - - func updateMainText() { - mainTextLabel.attributedText = .makeStyledString(font: .sub_m_sb, text: mainText, alignment: .left) - } - - func updateSubText() { - subTextLabel.attributedText = .makeStyledString(font: .cp_s_r, text: subText, color: .neutral500, alignment: .left) - } - - func updateIcon() { - iconButton.setImage(isIconSelected ? selectedIcon : icon, for: .normal) - } -} - -public extension CardList { - func setMainText(text: String) { - mainText = text - } - - func setSubText(text: String?) { - subText = text - } - - func setImage(image: UIImage, backgroundColor: UIColor) { - imageView.setImage(image: image, backgroundColor: backgroundColor) - } - - func setMapImage(image: UIImage, backgroundColor: UIColor) { - imageView.setMapImage(image: image, backgroundColor: backgroundColor) - } - - func setSelected(isSelected: Bool) { - isIconSelected = isSelected - } - - func setType(type: CardListType) { - self.type = type - icon = type.icon ?? UIImage() - selectedIcon = type.selectedIcon ?? UIImage() - - switch type { - case .detailStack: - iconButton.isHidden = true - dropInfoStack.isHidden = true - badge.isHidden = true - case .detailStackText: - iconButton.isHidden = true - dropInfoStack.isHidden = false - badge.isHidden = true - case .detailStackBadge(let type): - iconButton.isHidden = true - dropInfoStack.isHidden = false - badge.isHidden = false - badge.update(style: type) - default: - iconButton.isHidden = false - dropInfoStack.isHidden = true - badge.isHidden = true - } - } - - func setDropInfoText(title: String, value: String?) { - dropTitleLabel.attributedText = .makeStyledString(font: .cp_s_r, text: title, color: .neutral700) - dropValueLabel.attributedText = .makeStyledString(font: .sub_m_b, text: value, color: .primary700) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/CheckBoxButton.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/CheckBoxButton.swift deleted file mode 100644 index 96f8553f..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/CheckBoxButton.swift +++ /dev/null @@ -1,253 +0,0 @@ -import UIKit - -import SnapKit - -public final class CheckBoxButton: UIButton { - // MARK: - Types - public enum CheckBoxButtonStyle { - case normal - case listSmall - case listMedium - case listLarge - - public var font: UIFont? { - switch self { - case .normal: - return .sub_m_b - case .listSmall, .listMedium: - return .b_s_r - case .listLarge: - return .b_m_r - } - } - - public var verticalInset: CGFloat { - switch self { - case .normal, .listLarge: - return 16 - case .listSmall: - return 4 - case .listMedium: - return 10 - } - } - - public var subtitleIsHidden: Bool { - switch self { - case .normal: - return false - default: - return true - } - } - - public var rightButtonIsHidden: Bool { - switch self { - case .listMedium: - return false - default: - return true - } - } - - public var backgroundColor: UIColor { - switch self { - case .normal: - return .neutral100 - default: - return .clearMLS - } - } - - public var textColor: UIColor { - switch self { - case .listSmall: - return .neutral700 - default: - return .textColor - } - } - - public var selecteTextColor: UIColor { - switch self { - case .listSmall: - return .primary700 - default: - return .textColor - } - } - - public var height: CGFloat? { - switch self { - case .normal: - return 56 - case .listSmall: - return 32 - case .listMedium: - return 44 - case .listLarge: - return nil - } - } - } - - private enum Constant { - static let imageSize: CGFloat = 24 - static let horizontalInset: CGFloat = 20 - static let labelSpacing: CGFloat = 4 - static let spacing: CGFloat = 8 - static let radius: CGFloat = 8 - } - - // MARK: - Properties - private let style: CheckBoxButtonStyle - - public var mainTitle: String? { - didSet { - updateText() - } - } - - public var subTitle: String? { - didSet { - updateText() - } - } - - override public var isSelected: Bool { - didSet { - updateTintColor() - } - } - - private lazy var contentStackView: UIStackView = { [weak self] in - let view = UIStackView() - view.isUserInteractionEnabled = false - view.spacing = Constant.spacing - return view - }() - - private let labelTrailingView: UIView = .init() - - private let checkIconImageView: UIImageView = { - let image = DesignSystemAsset.image(named: "checkCircleFill")?.withRenderingMode(.alwaysTemplate) - let view = UIImageView(image: image) - return view - }() - - private let buttonTitleLabel: UILabel = .init() - - private let buttonSubTitleLabel: UILabel = .init() - - public let rightButton: UIButton = { - let button = UIButton() - let image = DesignSystemAsset.image(named: "arrowForwardSmall")?.withRenderingMode(.alwaysTemplate) - button.setImage(image, for: .normal) - button.tintColor = .textColor - return button - }() - - // MARK: - init - public init(style: CheckBoxButtonStyle, mainTitle: String?, subTitle: String?) { - self.style = style - self.mainTitle = mainTitle - self.subTitle = subTitle - super.init(frame: .zero) - addViews() - setupConstraints() - configureUI() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } - - override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - guard !isHidden, isUserInteractionEnabled, alpha > 0.01 else { return nil } - let view = super.hitTest(point, with: event) - if view != self { return view } - let rightPoint = convert(point, to: rightButton) - if rightButton.bounds.contains(rightPoint) && !rightButton.isHidden && rightButton.isUserInteractionEnabled { - return rightButton - } - return view - } -} - -// MARK: - SetUp -private extension CheckBoxButton { - func addViews() { - addSubview(contentStackView) - labelTrailingView.addSubview(buttonTitleLabel) - labelTrailingView.addSubview(buttonSubTitleLabel) - - if style == .listLarge { - contentStackView.addArrangedSubview(labelTrailingView) - contentStackView.addArrangedSubview(UIView()) - contentStackView.addArrangedSubview(checkIconImageView) - } else { - contentStackView.addArrangedSubview(checkIconImageView) - contentStackView.addArrangedSubview(labelTrailingView) - contentStackView.addArrangedSubview(UIView()) - contentStackView.addArrangedSubview(rightButton) - } - } - - func setupConstraints() { - snp.makeConstraints { make in - if let height = style.height { - make.height.equalTo(height) - } - } - - contentStackView.snp.makeConstraints { make in - if style == .listSmall { - make.horizontalEdges.equalToSuperview() - } else { - make.horizontalEdges.equalToSuperview().inset(Constant.horizontalInset) - } - make.verticalEdges.equalToSuperview().inset(style.verticalInset) - } - - checkIconImageView.snp.makeConstraints { make in - make.size.equalTo(Constant.imageSize) - } - - buttonTitleLabel.snp.makeConstraints { make in - make.leading.verticalEdges.equalToSuperview() - make.centerY.equalToSuperview() - } - - buttonSubTitleLabel.snp.makeConstraints { make in - make.leading.equalTo(buttonTitleLabel.snp.trailing).offset(Constant.labelSpacing) - make.trailing.equalToSuperview() - make.centerY.equalToSuperview() - } - - rightButton.snp.makeConstraints { make in - make.size.equalTo(Constant.imageSize) - } - } - - func configureUI() { - updateTintColor() - updateText() - - buttonTitleLabel.font = style.font - buttonSubTitleLabel.isHidden = style.subtitleIsHidden - backgroundColor = style.backgroundColor - layer.cornerRadius = Constant.radius - rightButton.isHidden = style.rightButtonIsHidden - } - - func updateTintColor() { - checkIconImageView.tintColor = isSelected ? .primary700 : .neutral300 - buttonTitleLabel.attributedText = .makeStyledString(font: style.font, text: mainTitle, color: isSelected ? style.selecteTextColor : style.textColor) - } - - func updateText() { - buttonTitleLabel.attributedText = .makeStyledString(font: style.font, text: mainTitle, color: style.textColor) - buttonSubTitleLabel.attributedText = .makeStyledString(font: .b_m_r, text: subTitle) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/CollectionList.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/CollectionList.swift deleted file mode 100644 index b6543477..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/CollectionList.swift +++ /dev/null @@ -1,143 +0,0 @@ -import UIKit - -import SnapKit - -public final class CollectionList: UIView { - private enum Constant { - static let imageSize: CGFloat = 36 - static let imageInset: CGFloat = 4 - static let imageSpacing: CGFloat = 4 - static let imageRadius: CGFloat = 6 - static let contentInset: CGFloat = 10 - static let labelLeadingMargin: CGFloat = 20 - static let labelTrailingMargin: CGFloat = 64 - static let iconSize: CGFloat = 24 - static let radius: CGFloat = 12 - } - - // MARK: - Components - private lazy var imageViews: [ItemImageView] = (0 ..< 4).map { _ in - let view = ItemImageView( - image: nil, - cornerRadius: Constant.imageRadius, - inset: Constant.imageInset, - backgroundColor: .neutral200 - ) - return view - } - - private lazy var imageGridView: UIStackView = { - let topRow = UIStackView(arrangedSubviews: Array(imageViews[0...1])) - topRow.axis = .horizontal - topRow.spacing = Constant.imageSpacing - topRow.distribution = .fillEqually - - let bottomRow = UIStackView(arrangedSubviews: Array(imageViews[2...3])) - bottomRow.axis = .horizontal - bottomRow.spacing = Constant.imageSpacing - bottomRow.distribution = .fillEqually - - let stack = UIStackView(arrangedSubviews: [topRow, bottomRow]) - stack.axis = .vertical - stack.spacing = Constant.imageSpacing - return stack - }() - - private let titleLabel: UILabel = { - let label = UILabel() - label.numberOfLines = 1 - label.lineBreakMode = .byTruncatingTail - return label - }() - - private let subtitleLabel: UILabel = { - let label = UILabel() - label.numberOfLines = 1 - return label - }() - - private lazy var textStackView: UIStackView = { - let stack = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel]) - stack.axis = .vertical - stack.spacing = 4 - return stack - }() - - private let clickIcon: UIImageView = { - let view = UIImageView(image: .arrowForwardSmall) - view.tintColor = .black - return view - }() - - // MARK: - Init - override public init(frame: CGRect) { - super.init(frame: frame) - addViews() - setupConstraints() - configureUI() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension CollectionList { - func addViews() { - addSubview(imageGridView) - addSubview(textStackView) - addSubview(clickIcon) - } - - func setupConstraints() { - imageGridView.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(Constant.contentInset) - make.verticalEdges.equalToSuperview().inset(Constant.contentInset) - make.width.height.equalTo((Constant.imageSize * 2) + Constant.imageSpacing) - } - - textStackView.snp.makeConstraints { make in - make.leading.equalTo(imageGridView.snp.trailing).offset(Constant.labelLeadingMargin) - make.centerY.equalToSuperview() - } - - clickIcon.snp.makeConstraints { make in - make.leading.equalTo(textStackView.snp.trailing).offset(Constant.labelTrailingMargin) - make.trailing.equalToSuperview().inset(Constant.contentInset) - make.centerY.equalToSuperview() - make.width.height.equalTo(Constant.iconSize) - } - - imageViews.forEach { - $0.snp.makeConstraints { make in - make.width.height.equalTo(Constant.imageSize) - } - } - } - - func configureUI() { - backgroundColor = .whiteMLS - layer.cornerRadius = Constant.radius - clipsToBounds = true - } -} - -public extension CollectionList { - func setTitle(text: String) { - titleLabel.attributedText = .makeStyledString(font: .b_s_m, text: text, alignment: .left) - } - - func setSubtitle(text: String) { - subtitleLabel.attributedText = .makeStyledString(font: .cp_xs_r, text: text, color: .neutral500, alignment: .left) - } - - func setImages(images: [UIImage?]) { - for (index, view) in imageViews.enumerated() { - let imageView = view.subviews.compactMap { $0 as? UIImageView }.first - print("이미지 뷰 설정") - imageView?.image = index < images.count ? images[index] : nil - } - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/CommonButton.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/CommonButton.swift deleted file mode 100644 index 738acf56..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/CommonButton.swift +++ /dev/null @@ -1,193 +0,0 @@ -import UIKit - -import SnapKit - -public final class CommonButton: UIButton { - // MARK: - Type - public enum CommonButtonStyle { - case normal - case text - case border - - public var height: CGFloat { - switch self { - case .normal, .border: - return 54 - case .text: - return 44 - } - } - - public var backgroundColor: UIColor { - switch self { - case .normal: - .primary700 - case .text, .border: - .clearMLS - } - } - - public var borderColor: UIColor { - switch self { - case .normal, .text: - UIColor.clearMLS - case .border: - UIColor.neutral300 - } - } - - public var textColor: UIColor { - switch self { - case .normal: - .whiteMLS - case .text, .border: - .textColor - } - } - - public var font: UIFont? { - switch self { - case .normal: - .btn_m_b - case .text: - .btn_s_r - case .border: - .btn_m_r - } - } - } - - private enum Constant { - static let height: CGFloat = 54 - static let normalStyleCornerRadius: CGFloat = 8 - static let textLineHeight: CGFloat = 1.2 - static let buttonInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20) - } - - // MARK: - Properties - private let style: CommonButtonStyle - private var title: String? - private var disabledTitle: String? - - override public var isEnabled: Bool { - didSet { - configureUI() - } - } - - // MARK: - init - public init(style: CommonButtonStyle, title: String?, disabledTitle: String?) { - self.style = style - self.title = title - self.disabledTitle = disabledTitle - super.init(frame: .zero) - configureUI() - } - - public init() { - self.style = .normal - self.title = nil - self.disabledTitle = nil - super.init(frame: .zero) - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension CommonButton { - func configureUI() { - var config = UIButton.Configuration.plain() - config.contentInsets = Constant.buttonInsets - - switch style { - case .normal, .border: - config.background.backgroundColor = isEnabled ? style.backgroundColor : .neutral300 - config.background.cornerRadius = Constant.normalStyleCornerRadius - let currentTitle = isEnabled ? title : disabledTitle - config.attributedTitle = AttributedString(.makeStyledString(font: style.font, text: currentTitle, color: isEnabled ? style.textColor : .whiteMLS) ?? .init()) - config.baseForegroundColor = isEnabled ? style.textColor : .whiteMLS - switch style { - case .border: - config.background.strokeColor = isEnabled ? style.borderColor : .neutral300 - config.background.strokeWidth = 1 - default: - break - } - configuration = config - snp.makeConstraints { make in - make.height.equalTo(style.height) - } - case .text: - config.background.backgroundColor = .clear - let currentTitle = isEnabled ? title : disabledTitle - if let textButtonTitle = currentTitle, - let lineHeight = style.font?.lineHeight { - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.minimumLineHeight = lineHeight * Constant.textLineHeight - paragraphStyle.maximumLineHeight = lineHeight * Constant.textLineHeight - paragraphStyle.alignment = .center - - let enabledAttributedString = NSAttributedString( - string: textButtonTitle, - attributes: [ - .foregroundColor: isEnabled ? style.textColor : .neutral700, - .underlineStyle: NSUnderlineStyle.single.rawValue, - .underlineColor: isEnabled ? style.textColor : .neutral700, - .paragraphStyle: paragraphStyle, - .font: style.font ?? UIFont() - ] - ) - config.attributedTitle = AttributedString(enabledAttributedString) - } - configuration = config - } - - configurationUpdateHandler = { [weak self] button in - guard let self = self else { return } - var updatedConfig = button.configuration - switch button.state { - case .highlighted: - switch style { - case .normal: - updatedConfig?.background.backgroundColor = self.style.backgroundColor.withAlphaComponent(0.9) - case .border: - updatedConfig?.background.backgroundColor = UIColor.neutral100 - default: - break - } - default: - updatedConfig?.background.backgroundColor = self.isEnabled ? self.style.backgroundColor : .neutral300 - } - button.configuration = updatedConfig - } - } -} - -public extension CommonButton { - func updateTitle(title: String, disabledTitle: String? = nil) { - self.title = title - if let disabledTitle = disabledTitle { - self.disabledTitle = disabledTitle - } - configureUI() - } - - func updateTitleColor(color: UIColor, for state: UIControl.State = .normal) { - var config = configuration ?? UIButton.Configuration.plain() - - if var attributedTitle = config.attributedTitle { - var container = AttributeContainer() - container.foregroundColor = color - attributedTitle.mergeAttributes(container, mergePolicy: .keepNew) - config.attributedTitle = attributedTitle - } else if let title = title(for: state) { - config.attributedTitle = AttributedString(title, attributes: AttributeContainer([.foregroundColor: color])) - } - - configuration = config - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/DictionaryDetailListView.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/DictionaryDetailListView.swift deleted file mode 100644 index e46953ba..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/DictionaryDetailListView.swift +++ /dev/null @@ -1,132 +0,0 @@ -import SnapKit -import UIKit - -public final class DictionaryDetailListView: UIStackView { - // MARK: - Type - private enum Constant { - static let height: CGFloat = 50 - static let horizontalInset: CGFloat = 7 - static let iconSize: CGFloat = 24 - static let spacing: CGFloat = 9 - } - - // MARK: - Components - private let mainLabel = UILabel() - private let mainButtonLabel = UILabel() - private lazy var mainButton = makeButton(label: mainButtonLabel) - - private let leftSpacer = UIView() - private let rightSpacer = UIView() - - private let mainAdditionalLabel = UILabel() - private let spacer = UIView() - - private let subLabel = UILabel() - private let subButtonLabel = UILabel() - private lazy var subButton = makeButton(label: subButtonLabel) - - private let underLine: UIView = { - let view = UIView() - view.backgroundColor = .neutral200 - return view - }() - - // MARK: - init - public init() { - super.init(frame: .zero) - axis = .horizontal - spacing = Constant.spacing - alignment = .center - - addBaseViews() - setupConstraints() - } - - required init(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -private extension DictionaryDetailListView { - func addBaseViews() { - addArrangedSubview(leftSpacer) - addArrangedSubview(spacer) - addArrangedSubview(rightSpacer) - addSubview(underLine) - } - - func setupConstraints() { - snp.makeConstraints { make in - make.height.equalTo(Constant.height) - } - - leftSpacer.snp.makeConstraints { make in - make.width.equalTo(Constant.horizontalInset) - } - - rightSpacer.snp.makeConstraints { make in - make.width.equalTo(Constant.horizontalInset) - } - - underLine.snp.makeConstraints { make in - make.horizontalEdges.bottom.equalToSuperview() - make.height.equalTo(1) - } - } - - func makeButton(label: UILabel) -> UIButton { - let button = UIButton() - let icon = UIImageView(image: .rightArrow) - - button.addSubview(label) - button.addSubview(icon) - - label.snp.makeConstraints { make in - make.leading.verticalEdges.equalToSuperview() - } - - icon.snp.makeConstraints { make in - make.leading.equalTo(label.snp.trailing) - make.trailing.centerY.equalToSuperview() - make.size.equalTo(Constant.iconSize) - } - - return button - } -} - -extension DictionaryDetailListView { - public func update(mainText: String? = nil, clickableMainText: String? = nil, additionalText: String? = nil, subText: String? = nil, clickableSubText: String? = nil) { - let fixedViews: Set = [leftSpacer, spacer, rightSpacer] - arrangedSubviews - .filter { !fixedViews.contains($0) } - .forEach { removeArrangedSubview($0); $0.removeFromSuperview() } - - insertArrangedSubview(leftSpacer, at: 0) - - if let mainText = mainText { - mainLabel.attributedText = .makeStyledString(font: .sub_m_sb, text: mainText) - insertArrangedSubview(mainLabel, at: 1) - } - - if let clickableMainText = clickableMainText { - mainButtonLabel.attributedText = .makeStyledUnderlinedString(font: .sub_m_sb, text: clickableMainText) - insertArrangedSubview(mainButton, at: arrangedSubviews.firstIndex(of: spacer) ?? 0) - } - - if let additionalText = additionalText { - mainAdditionalLabel.attributedText = .makeStyledString(font: .sub_m_sb, text: additionalText) - insertArrangedSubview(mainAdditionalLabel, at: arrangedSubviews.firstIndex(of: spacer) ?? 0) - } - - if let subText = subText { - subLabel.attributedText = .makeStyledString(font: .btn_s_r, text: subText) - insertArrangedSubview(subLabel, at: arrangedSubviews.firstIndex(of: rightSpacer) ?? 0) - } - - if let clickableSubText = clickableSubText { - subButtonLabel.attributedText = .makeStyledUnderlinedString(font: .btn_s_r, text: clickableSubText) - insertArrangedSubview(subButton, at: arrangedSubviews.firstIndex(of: rightSpacer) ?? 0) - } - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/DividerView.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/DividerView.swift deleted file mode 100644 index 8f636bae..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/DividerView.swift +++ /dev/null @@ -1,19 +0,0 @@ -import UIKit - -import SnapKit - -public final class DividerView: UIView { - // MARK: - init - public init() { - super.init(frame: .zero) - self.backgroundColor = .neutral200 - self.snp.makeConstraints { make in - make.height.equalTo(1) - } - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/DropDownBox/DropDownBox.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/DropDownBox/DropDownBox.swift deleted file mode 100644 index 6e3dbd30..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/DropDownBox/DropDownBox.swift +++ /dev/null @@ -1,184 +0,0 @@ -import UIKit - -import SnapKit - -public final class DropDownBox: UIStackView { - // MARK: - Properties - private var isExpanded = false - private var tableViewHeightConstraint: Constraint? - private var selectedIndex: Int? { - didSet { - tableView.reloadData() - } - } - - public var selectedItem: Item? { - guard let index = selectedIndex, items.indices.contains(index) else { - return nil - } - return items[index] - } - - // 선택 이벤트 콜백 - public var onItemSelected: ((Item) -> Void)? - - public var items = [Item]() { - didSet { - tableView.reloadData() - } - } - - // MARK: - Components - public let inputBox = InputBox() - - private let iconButton: UIButton = { - let view = UIButton() - view.setImage(.arrowDropdown, for: .normal) - return view - }() - - public let tableView: UITableView = { - let tableView = UITableView() - tableView.isHidden = true - tableView.layer.borderWidth = 1 - tableView.layer.cornerRadius = 8 - tableView.layer.borderColor = UIColor.neutral300.cgColor - tableView.separatorStyle = .none - tableView.isScrollEnabled = false - tableView.contentInset = UIEdgeInsets(top: 4, left: 0, bottom: 4, right: 0) - tableView.register(DropDownBoxCell.self, forCellReuseIdentifier: "DropDownCell") - return tableView - }() - - // MARK: - Init - public init(label: String? = nil, placeHodler: String? = nil, items: [Item]) { - self.items = items - super.init(frame: .zero) - - inputBox.label.attributedText = .makeStyledString(font: .b_s_r, text: label, color: .neutral700, alignment: .left) - inputBox.textField.attributedPlaceholder = .makeStyledString(font: .b_m_r, text: placeHodler, color: .neutral500, alignment: .left) - - setupStackView() - setupInputBox() - setupTableView() - configureTap() - } - - @available(*, unavailable) - required init(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Setup -private extension DropDownBox { - func setupStackView() { - axis = .vertical - spacing = 4 - alignment = .fill - addArrangedSubview(inputBox) - addArrangedSubview(tableView) - } - - func setupInputBox() { - inputBox.borderView.addSubview(iconButton) - - inputBox.textField.snp.remakeConstraints { make in - make.verticalEdges.equalToSuperview().inset(16) - make.leading.equalToSuperview().inset(20) - } - - iconButton.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalTo(inputBox.textField.snp.trailing).offset(8) - make.trailing.equalToSuperview().inset(20) - make.size.equalTo(24) - } - - inputBox.borderView.layer.borderColor = UIColor.neutral300.cgColor - inputBox.textField.isUserInteractionEnabled = false - - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap)) - inputBox.borderView.addGestureRecognizer(tapGesture) - inputBox.borderView.isUserInteractionEnabled = true - } - - func setupTableView() { - tableView.delegate = self - tableView.dataSource = self - - tableView.snp.makeConstraints { make in - make.horizontalEdges.equalTo(inputBox) - tableViewHeightConstraint = make.height.equalTo(0).constraint - } - } - - func configureTap() { - let action = UIAction { [weak self] _ in - self?.toggleDropdown() - } - iconButton.addAction(action, for: .touchUpInside) - } - - func toggleDropdown() { - isExpanded.toggle() - tableView.isHidden = !isExpanded - iconButton.setImage(isExpanded ? .arrowDropUp : .arrowDropdown, for: .normal) - let height = CGFloat(items.count) * 44 + tableView.contentInset.top + tableView.contentInset.bottom - tableViewHeightConstraint?.update(offset: isExpanded ? height : 0) - } - - func removeKeyboard() { - UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) - } - - @objc private func handleTap() { - toggleDropdown() - removeKeyboard() - } -} - -// MARK: - UITableView -extension DropDownBox: UITableViewDataSource, UITableViewDelegate { - public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return items.count - } - - public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: "DropDownCell", for: indexPath) as? DropDownBoxCell else { - return UITableViewCell() - } - let isSelected = selectedIndex == indexPath.row - cell.injection(with: items[indexPath.row].name, isSelected: isSelected) - cell.selectionStyle = .none - return cell - } - - public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - selectedIndex = indexPath.row - let selectedItem = items[indexPath.row] - inputBox.textField.attributedText = .makeStyledString(font: .b_m_r, text: selectedItem.name, alignment: .left, lineHeight: 1.0) - inputBox.textField.sendActions(for: .editingChanged) - toggleDropdown() - - onItemSelected?(selectedItem) - } - - public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - return 44 - } -} - -// MARK: Model -extension DropDownBox { - public struct Item { - public let name: String - public let id: Int - - public init(name: String, id: Int) { - self.name = name - self.id = id - } - } - -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/DropDownBox/DropDownBoxCell.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/DropDownBox/DropDownBoxCell.swift deleted file mode 100644 index 42c9ee32..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/DropDownBox/DropDownBoxCell.swift +++ /dev/null @@ -1,61 +0,0 @@ -import UIKit - -import SnapKit - -final class DropDownBoxCell: UITableViewCell { - // MARK: - Type - private enum Constant { - static var horizontalInset: CGFloat = 20 - static var verticalInset: CGFloat = 10 - static var cellInset: CGFloat = 4 - static var radius: CGFloat = 8 - } - - // MARK: - Components - private let titleLabel = UILabel() - - private let backgroundColorView: UIView = { - let view = UIView() - view.layer.cornerRadius = Constant.radius - return view - }() - - // MARK: - init - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - addViews() - setupContstraints() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension DropDownBoxCell { - func addViews() { - contentView.addSubview(backgroundColorView) - backgroundColorView.addSubview(titleLabel) - } - - func setupContstraints() { - titleLabel.snp.makeConstraints { make in - make.horizontalEdges.equalToSuperview().inset(Constant.horizontalInset) - make.verticalEdges.equalToSuperview().inset(Constant.verticalInset) - } - - backgroundColorView.snp.makeConstraints { make in - make.horizontalEdges.equalToSuperview().inset(Constant.cellInset) - make.verticalEdges.equalToSuperview() - } - } -} - -extension DropDownBoxCell { - func injection(with input: String, isSelected: Bool) { - titleLabel.attributedText = .makeStyledString(font: .b_m_r, text: input, color: isSelected ? .textColor : .neutral500, alignment: .left) - backgroundColorView.backgroundColor = isSelected ? .neutral100 : .clearMLS - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/ErrorMessage.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/ErrorMessage.swift deleted file mode 100644 index fbd63cca..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/ErrorMessage.swift +++ /dev/null @@ -1,69 +0,0 @@ -import UIKit - -import SnapKit - -public final class ErrorMessage: UIView { - private enum Constant { - static let verticalEdgesInset: CGFloat = 8 - static let horizontalEdges: CGFloat = 20 - static let cornerRadius: CGFloat = 18 - static let spacing: CGFloat = 4 - static let height: CGFloat = 36 - static let iconSize: CGFloat = 16 - } - - // MARK: - Properties - private let iconView: UIImageView = { - let view = UIImageView() - view.image = .error - return view - }() - - public let label = UILabel() - - // MARK: - init - public init(message: String?) { - super.init(frame: .zero) - addViews() - setupConstraints() - configureUI(message: message) - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension ErrorMessage { - func addViews() { - addSubview(iconView) - addSubview(label) - } - - func setupConstraints() { - snp.makeConstraints { make in - make.height.equalTo(Constant.height) - } - - iconView.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(Constant.horizontalEdges) - make.size.equalTo(Constant.iconSize) - } - - label.snp.makeConstraints { make in - make.verticalEdges.equalToSuperview().inset(Constant.verticalEdgesInset) - make.leading.equalTo(self.iconView.snp.trailing).offset(Constant.spacing) - make.trailing.equalToSuperview().inset(Constant.horizontalEdges) - } - } - - func configureUI(message: String?) { - self.backgroundColor = .error100 - self.layer.cornerRadius = Constant.cornerRadius - self.clipsToBounds = true - self.label.attributedText = .makeStyledString(font: .b_s_r, text: message, color: .error900) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/FloatingActionButton.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/FloatingActionButton.swift deleted file mode 100644 index d59f7b17..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/FloatingActionButton.swift +++ /dev/null @@ -1,34 +0,0 @@ -import UIKit - -import SnapKit - -public final class FloatingActionButton: UIButton { - // MARK: - Properties - private var action: (() -> Void)? - - // MARK: - LifeCycle - public init(action: @escaping () -> Void) { - self.action = action - super.init(frame: .zero) - configureUI() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension FloatingActionButton { - func configureUI() { - setImage(.fab, for: .normal) - layer.cornerRadius = 24 - clipsToBounds = true - addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) - } - - @objc private func buttonTapped() { - action?() - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/GuideAlert.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/GuideAlert.swift deleted file mode 100644 index 0674eddf..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/GuideAlert.swift +++ /dev/null @@ -1,110 +0,0 @@ -import UIKit - -import SnapKit - -public class GuideAlert: UIView { - // MARK: - Type - enum Constant { - static let verticalInset: CGFloat = 20 - static let horizontalInset: CGFloat = 20 - static let iconSize: CGFloat = 48 - static let verticalSpacing: CGFloat = 24 - static let buttonSpacing: CGFloat = 4 - static let buttonHeight: CGFloat = 48 - static let alertWidth: CGFloat = 327 - static let radius: CGFloat = 24 - } - - // MARK: - Components - private let warningIconView: UIImageView = { - let view = UIImageView() - view.image = .warning - return view - }() - - public let mainTextLabel = UILabel() - public let buttonStackView: UIStackView = { - let view = UIStackView() - view.spacing = Constant.buttonSpacing - return view - }() - - public var ctaButton: CommonButton - public var cancelButton: CommonButton? - - // MARK: - init - public init(mainText: String, ctaText: String, cancelText: String?, ctaRatio: Double = 0.7) { - mainTextLabel.attributedText = .makeStyledString(font: .sub_l_b, text: mainText) - self.ctaButton = CommonButton(style: .normal, title: ctaText, disabledTitle: nil) - self.cancelButton = cancelText.map { CommonButton(style: .border, title: $0, disabledTitle: nil) } - mainTextLabel.numberOfLines = 0 - super.init(frame: .zero) - - addViews(cancelText: cancelText) - setupConstraints(ctaRatio: ctaRatio) - configureUI() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension GuideAlert { - func addViews(cancelText: String?) { - addSubview(warningIconView) - addSubview(mainTextLabel) - addSubview(buttonStackView) - - if let cancelButton = cancelButton { - buttonStackView.addArrangedSubview(cancelButton) - } - buttonStackView.addArrangedSubview(ctaButton) - } - - func setupConstraints(ctaRatio: Double) { - snp.makeConstraints { make in - make.width.equalTo(Constant.alertWidth) - } - - warningIconView.snp.makeConstraints { make in - make.top.equalToSuperview().inset(Constant.verticalInset) - make.centerX.equalToSuperview() - make.size.equalTo(Constant.iconSize) - } - - mainTextLabel.snp.makeConstraints { make in - make.top.equalTo(warningIconView.snp.bottom).offset(Constant.verticalInset) - make.horizontalEdges.equalToSuperview().inset(Constant.horizontalInset) - } - - buttonStackView.snp.makeConstraints { make in - make.top.equalTo(mainTextLabel.snp.bottom).offset(Constant.verticalSpacing) - make.horizontalEdges.equalToSuperview().inset(Constant.horizontalInset) - make.bottom.equalToSuperview().inset(Constant.verticalInset) - make.height.equalTo(Constant.buttonHeight) - } - - if let cancelButton = cancelButton { - let cancelRatio = 1 - ctaRatio - - cancelButton.snp.makeConstraints { make in - make.width.equalTo(buttonStackView.snp.width).multipliedBy(cancelRatio).offset(-Constant.buttonSpacing) - } - ctaButton.snp.makeConstraints { make in - make.width.equalTo(buttonStackView.snp.width).multipliedBy(ctaRatio).offset(-Constant.buttonSpacing) - } - } else { - ctaButton.snp.makeConstraints { make in - make.width.equalTo(buttonStackView.snp.width) - } - } - } - - func configureUI() { - backgroundColor = .whiteMLS - layer.cornerRadius = Constant.radius - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/Header.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/Header.swift deleted file mode 100644 index c0705ed2..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/Header.swift +++ /dev/null @@ -1,102 +0,0 @@ -import UIKit - -import SnapKit - -public final class Header: UIStackView { - // MARK: - Type - public enum HeaderStyle { - case main - case filter - - public var titleFont: UIFont? { - switch self { - case .main: - return .h_xxxl_sb - case .filter: - return .h_xl_sb - } - } - - var icons: [UIImage] { - switch self { - case .main: - return [.search, .bell] - case .filter: - return [.largeX] - } - } - } - - private enum Constant { - static let iconSize: CGFloat = 24 - static let spacing: CGFloat = 16 - static let mainTypeHeight: CGFloat = 44 - } - - // MARK: - Properties - public let style: HeaderStyle - - // MARK: - Components - public let titleLabel = UILabel() - private let spacer = UIView() - public let firstIconButton = UIButton() - public let secondIconButton = UIButton() - - // MARK: - init - public init(style: HeaderStyle, title: String) { - titleLabel.attributedText = .makeStyledString(font: .h_xxxl_sb, text: title) - self.style = style - super.init(frame: .zero) - - addViews() - setupConstraints() - configureUI() - } - - @available(*, unavailable) - required init(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension Header { - func addViews() { - addArrangedSubview(titleLabel) - addArrangedSubview(spacer) - addArrangedSubview(firstIconButton) - if style == .main { - addArrangedSubview(secondIconButton) - } - } - - func setupConstraints() { - if style == .main { - snp.makeConstraints { make in - make.height.equalTo(Constant.mainTypeHeight) - } - } - - firstIconButton.snp.makeConstraints { make in - make.size.equalTo(Constant.iconSize) - } - - secondIconButton.snp.makeConstraints { make in - make.size.equalTo(Constant.iconSize) - } - } - - func configureUI() { - alignment = .center - isLayoutMarginsRelativeArrangement = true - layoutMargins = UIEdgeInsets(top: 0, left: Constant.spacing, bottom: 0, right: Constant.spacing) - axis = .horizontal - spacing = Constant.spacing - titleLabel.font = style.titleFont - titleLabel.textColor = .textColor - firstIconButton.setImage(style.icons[0], for: .normal) - if style == .main { - secondIconButton.setImage(style.icons[1], for: .normal) - } - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/InputBox.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/InputBox.swift deleted file mode 100644 index 9ee776a5..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/InputBox.swift +++ /dev/null @@ -1,105 +0,0 @@ -import UIKit - -import SnapKit - -public final class InputBox: UIStackView { - // MARK: - Properties - private var type: InputBoxType = .edit { - didSet { - setBorderColor() - } - } - - // MARK: - Components - public let label = UILabel() - public let textField: UITextField = { - let textField = UITextField() - textField.clearButtonMode = .whileEditing - return textField - }() - - public lazy var borderView: UIView = { - let view = UIView() - view.layer.cornerRadius = 8 - view.layer.borderWidth = 1 - view.layer.borderColor = UIColor.neutral300.cgColor - - view.addSubview(textField) - - textField.snp.makeConstraints { make in - make.verticalEdges.equalToSuperview().inset(16) - make.leading.equalToSuperview().inset(20) - make.trailing.equalToSuperview().inset(10) - } - return view - }() - - // MARK: - Init - public init(label: String? = nil, placeHodler: String? = nil) { - super.init(frame: .zero) - configureUI(label: label, placeHodler: placeHodler) - } - - @available(*, unavailable) - required init(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension InputBox { - func setupStackView() { - addArrangedSubview(label) - addArrangedSubview(borderView) - - spacing = 4 - axis = .vertical - alignment = .leading - } - - func setupLabel(label: String?) { - self.label.attributedText = .makeStyledString(font: .b_s_r, text: label, color: .neutral700, alignment: .left) - } - - func setupTextField(placeHolder: String?) { - textField.attributedPlaceholder = .makeStyledString(font: .b_m_r, text: placeHolder, color: .neutral500, alignment: .left) - } - - func setupConstaraints() { - borderView.snp.makeConstraints { make in - make.horizontalEdges.equalToSuperview() - } - } - - func configureUI(label: String?, placeHodler: String?) { - setupStackView() - setupLabel(label: label) - setupTextField(placeHolder: placeHodler) - setupConstaraints() - } - - func setBorderColor() { - borderView.layer.borderColor = type.borderColor.cgColor - } -} - -// MARK: - Mothods -public extension InputBox { - func setType(type: InputBoxType) { - self.type = type - } -} - -public enum InputBoxType { - case edit - case error - - var borderColor: UIColor { - switch self { - case .edit: - return .neutral300 - case .error: - return .error900 - } - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/ItemImageView.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/ItemImageView.swift deleted file mode 100644 index 57a15624..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/ItemImageView.swift +++ /dev/null @@ -1,54 +0,0 @@ -import UIKit - -import SnapKit - -public final class ItemImageView: UIView { - private let imageView = UIImageView() - - init(image: UIImage?, cornerRadius: CGFloat, inset: CGFloat, backgroundColor: UIColor) { - super.init(frame: .zero) - addViews() - setUpConstraints(inset: inset) - configureUI(radius: cornerRadius) - setImage(image: image, backgroundColor: backgroundColor) - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension ItemImageView { - func addViews() { - addSubview(imageView) - } - - func setUpConstraints(inset: CGFloat) { - imageView.snp.makeConstraints { make in - make.edges.equalToSuperview().inset(inset) - } - } - - func configureUI(radius: CGFloat) { - layer.cornerRadius = radius - imageView.contentMode = .scaleAspectFit - imageView.backgroundColor = .clearMLS - } -} - -public extension ItemImageView { - func setImage(image: UIImage?, backgroundColor: UIColor) { - imageView.image = image - self.backgroundColor = backgroundColor - } - - func setMapImage(image: UIImage?, backgroundColor: UIColor) { - setImage(image: image, backgroundColor: backgroundColor) - imageView.snp.remakeConstraints { make in - make.center.equalToSuperview() - make.size.equalTo(40) - } - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/NavigationBar.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/NavigationBar.swift deleted file mode 100644 index a57506b9..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/NavigationBar.swift +++ /dev/null @@ -1,182 +0,0 @@ -import UIKit - -import SnapKit - -public final class NavigationBar: UIView { - // MARK: - Types - public enum NavigationType { - case withUnderLine(String) - case arrowRightLeft - case arrowLeft - case withString(String) - case collection(String) - } - - private enum Constant { - static let spacing: CGFloat = 8 - static let imageSize: CGFloat = 44 - static let rightInset: CGFloat = 16 - static let lineHeight: CGFloat = 1.17 - } - - // MARK: - Properties - private let contentStackView: UIStackView = { - let view = UIStackView() - view.alignment = .center - view.spacing = Constant.spacing - return view - }() - - public let leftButton: UIButton = { - let button = UIButton(type: .system) - button.setImage(.arrowBack.withRenderingMode(.alwaysTemplate), for: .normal) - button.tintColor = .textColor - return button - }() - - public let rightButton: UIButton = { - let button = UIButton(type: .system) - button.setImage(.arrowForward.withRenderingMode(.alwaysTemplate), for: .normal) - button.tintColor = .textColor - return button - }() - - public let underlineTextButton: UIButton = { - let button = UIButton() - button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10) - return button - }() - - public let boldTextButton: UIButton = { - let button = UIButton() - button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10) - return button - }() - - private let collectionTitleLabel = UILabel() - public let editButton: UIButton = { - let button = UIButton() - button.setImage(.edit, for: .normal) - return button - }() - - public let addButton: UIButton = { - let button = UIButton() - button.setImage(.addIcon, for: .normal) - return button - }() - - // MARK: - init - public init(type: NavigationType) { - super.init(frame: .zero) - addViews(type: type) - setupConstraints(type: type) - configureUI(type: type) - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension NavigationBar { - func addViews(type: NavigationType) { - addSubview(contentStackView) - - switch type { - case .withUnderLine: - contentStackView.addArrangedSubview(leftButton) - contentStackView.addArrangedSubview(UIView()) - contentStackView.addArrangedSubview(underlineTextButton) - case .arrowRightLeft: - contentStackView.addArrangedSubview(leftButton) - contentStackView.addArrangedSubview(UIView()) - contentStackView.addArrangedSubview(rightButton) - case .arrowLeft: - contentStackView.addArrangedSubview(leftButton) - contentStackView.addArrangedSubview(UIView()) - case .withString: - contentStackView.addArrangedSubview(leftButton) - contentStackView.addArrangedSubview(UIView()) - contentStackView.addArrangedSubview(boldTextButton) - case .collection: - contentStackView.addArrangedSubview(leftButton) - contentStackView.addArrangedSubview(collectionTitleLabel) - contentStackView.addArrangedSubview(UIView()) - contentStackView.addArrangedSubview(editButton) - contentStackView.addArrangedSubview(addButton) - } - } - - func setupConstraints(type: NavigationType) { - contentStackView.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - leftButton.snp.makeConstraints { make in - make.size.equalTo(Constant.imageSize) - } - switch type { - case .withUnderLine, .arrowRightLeft: - rightButton.snp.makeConstraints { make in - make.size.equalTo(Constant.imageSize) - } - case .withString: - boldTextButton.snp.makeConstraints { make in - make.trailing.equalToSuperview().inset(10) - } - case .collection: - editButton.snp.makeConstraints { make in - make.size.equalTo(Constant.imageSize) - } - addButton.snp.makeConstraints { make in - make.size.equalTo(Constant.imageSize) - } - default: - break - } - } - - func configureUI(type: NavigationType) { - switch type { - case .withUnderLine(let title): - guard let lineHeight = UIFont.b_s_r?.lineHeight, - let font = UIFont.b_m_r else { return } - - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.minimumLineHeight = lineHeight * Constant.lineHeight - paragraphStyle.maximumLineHeight = lineHeight * Constant.lineHeight - paragraphStyle.alignment = .center - - let attributedString = NSAttributedString( - string: title, - attributes: [ - .font: font, - .foregroundColor: UIColor.neutral700, - .underlineStyle: NSUnderlineStyle.single.rawValue, - .underlineColor: UIColor.neutral700, - .paragraphStyle: paragraphStyle - ] - ) - underlineTextButton.setAttributedTitle(attributedString, for: .normal) - case .withString(let title): - boldTextButton.setAttributedTitle( - .makeStyledString(font: .btn_m_b, text: title), - for: .normal - ) - case .collection(let title): - collectionTitleLabel.text = title - collectionTitleLabel.font = .b_m_r - collectionTitleLabel.textColor = .textColor - case .arrowRightLeft, .arrowLeft: - break - } - } -} - -public extension NavigationBar { - func setTitle(title: String) { - collectionTitleLabel.text = title - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/SearchBar.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/SearchBar.swift deleted file mode 100644 index 67008b75..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/SearchBar.swift +++ /dev/null @@ -1,163 +0,0 @@ -import UIKit - -import SnapKit - -public protocol SearchBarDelegate: AnyObject { - func searchBarDidReturn(_ searchBar: SearchBar, text: String) -} - -public final class SearchBar: UIView { - // MARK: - Properties - public weak var searchDelegate: SearchBarDelegate? - - // MARK: - Components - public let backButton: UIButton = { - let button = UIButton(type: .system) - let image = DesignSystemAsset.image(named: "arrowBack")?.withRenderingMode(.alwaysTemplate) - button.setImage(image, for: .normal) - button.tintColor = .textColor - return button - }() - - public let textField: UITextField = { - let textField = UITextField() - textField.font = .b_l_r - textField.textColor = .textColor - textField.attributedPlaceholder = .makeStyledString(font: .b_l_r, text: "찾는 정보를 검색해 보세요", color: .neutral300) - textField.textAlignment = .left - textField.tintColor = .primary300 - textField.returnKeyType = .search - return textField - }() - - private let contentStackView: UIStackView = { - let view = UIStackView() - view.axis = .horizontal - view.alignment = .center - view.spacing = 8 - return view - }() - - public let searchButton: UIButton = { - let button = UIButton(type: .system) - let image = DesignSystemAsset.image(named: "search")?.withRenderingMode(.alwaysTemplate) - button.setImage(image, for: .normal) - button.tintColor = .textColor - return button - }() - - public let clearButton: UIButton = { - let button = UIButton(type: .custom) - let image = DesignSystemAsset.image(named: "textFieldClear") - button.setImage(image, for: .normal) - button.isHidden = true - return button - }() - - private let lineView: UIView = { - let view = UIView() - view.backgroundColor = .neutral300 - return view - }() - - private let fillLineView: UIView = { - let view = UIView() - view.backgroundColor = .primary700 - view.transform = CGAffineTransform(scaleX: 0, y: 1) - return view - }() - - // MARK: - init - public init() { - super.init(frame: .zero) - - addViews() - setupConstraints() - configureUI() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension SearchBar { - func addViews() { - addSubview(contentStackView) - addSubview(lineView) - lineView.addSubview(fillLineView) - - contentStackView.addArrangedSubview(backButton) - contentStackView.addArrangedSubview(textField) - contentStackView.addArrangedSubview(clearButton) - contentStackView.addArrangedSubview(searchButton) - } - - func setupConstraints() { - contentStackView.snp.makeConstraints { make in - make.verticalEdges.leading.equalToSuperview() - make.trailing.equalToSuperview().inset(10) - } - lineView.snp.makeConstraints { make in - make.horizontalEdges.bottom.equalToSuperview() - make.height.equalTo(2) - } - backButton.snp.makeConstraints { make in - make.size.equalTo(44) - } - searchButton.snp.makeConstraints { make in - make.size.equalTo(44) - } - textField.snp.makeConstraints { make in - make.height.equalTo(25) - } - clearButton.snp.makeConstraints { make in - make.size.equalTo(19) - } - fillLineView.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - } - - func configureUI() { - textField.delegate = self - clearButton.addAction(.init(handler: { [weak self] _ in self?.textField.text = "" }), for: .touchUpInside) - searchButton.addAction(.init(handler: { [weak self] _ in self?.endEditing(true) }), for: .touchUpInside) - } -} - -extension SearchBar: UITextFieldDelegate { - public func textFieldDidBeginEditing(_ textField: UITextField) { - clearButton.isHidden = (textField.text ?? "").isEmpty - UIView.animate(withDuration: 0.35, delay: 0, options: [.curveEaseOut]) { - self.fillLineView.transform = CGAffineTransform.identity - } - } - - public func textFieldDidEndEditing(_ textField: UITextField) { - clearButton.isHidden = true - UIView.animate(withDuration: 0.35, delay: 0, options: [.curveEaseIn]) { - self.fillLineView.transform = CGAffineTransform(scaleX: 0.001, y: 1) - } completion: { _ in - self.fillLineView.transform = CGAffineTransform(scaleX: 0, y: 1) - } - } - - public func textFieldShouldReturn(_ textField: UITextField) -> Bool { - searchDelegate?.searchBarDidReturn(self, text: textField.text ?? "") - - endEditing(true) - clearButton.isHidden = true - return true - } - - public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { - let currentText = textField.text ?? "" - guard let textRange = Range(range, in: currentText) else { return true } - let updatedText = currentText.replacingCharacters(in: textRange, with: string) - clearButton.isHidden = updatedText.isEmpty || !textField.isFirstResponder - return true - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/SnackBar.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/SnackBar.swift deleted file mode 100644 index d093cf82..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/SnackBar.swift +++ /dev/null @@ -1,115 +0,0 @@ -import UIKit - -import SnapKit - -public final class SnackBar: UIView { - private enum Constant { - static let imageSize: CGFloat = 32 - static let imageInset: CGFloat = 5 - static let horizontalInset: CGFloat = 16 - static let width: CGFloat = 343 - static let height: CGFloat = 48 - static let spacing: CGFloat = 8 - static let radius: CGFloat = 8 - } - - // MARK: - ProPerties - private let type: SnackBarType - - // MARK: - Components - public let imageView: ItemImageView - private let label = UILabel() - private let button = UIButton() - - // MARK: - init - public init(type: SnackBarType, image: UIImage?, imageBackgroundColor: UIColor, text: String, buttonText: String, buttonAction: (() -> Void)?) { - self.type = type - imageView = ItemImageView(image: image, cornerRadius: 3.432, inset: 5, backgroundColor: imageBackgroundColor) - super.init(frame: .zero) - - addViews() - setupConstraints() - configureUI(text: text, buttonText: buttonText, buttonAction: buttonAction) - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension SnackBar { - func addViews() { - addSubview(imageView) - addSubview(label) - addSubview(button) - } - - func setupConstraints() { - snp.makeConstraints { make in - make.width.equalTo(Constant.width) - make.height.equalTo(Constant.height) - } - - imageView.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(Constant.horizontalInset) - make.size.equalTo(Constant.imageSize) - } - - label.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalTo(imageView.snp.trailing).offset(Constant.spacing) - } - - button.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.leading.equalTo(label.snp.trailing) - make.trailing.equalToSuperview().inset(Constant.horizontalInset) - } - - label.setContentHuggingPriority(.defaultLow, for: .horizontal) - label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) - - button.setContentHuggingPriority(.required, for: .horizontal) - button.setContentCompressionResistancePriority(.required, for: .horizontal) - } - - func configureUI(text: String, buttonText: String, buttonAction: (() -> Void)?) { - layer.cornerRadius = Constant.radius - clipsToBounds = true - - label.adjustsFontSizeToFitWidth = true - label.attributedText = .makeStyledString(font: .b_s_r, text: text, color: .whiteMLS, alignment: .left) - - let attributedTitle = NSAttributedString( - string: buttonText, - attributes: [ - .font: UIFont.btn_xs_r ?? UIFont.systemFont(ofSize: 12), - .foregroundColor: UIColor.whiteMLS, - .underlineStyle: NSUnderlineStyle.single.rawValue, - .underlineColor: UIColor.whiteMLS - ] - ) - button.setAttributedTitle(attributedTitle, for: .normal) - button.backgroundColor = .clear - button.addAction(UIAction { _ in - buttonAction?() - }, for: .touchUpInside) - - switch type { - case .normal: - backgroundColor = .neutral900 - case .delete: - backgroundColor = .error900 - } - } -} - -public extension SnackBar { - enum SnackBarType { - case normal - case delete - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/StepIndicator.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/StepIndicator.swift deleted file mode 100644 index 06ffaa7d..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/StepIndicator.swift +++ /dev/null @@ -1,57 +0,0 @@ -import UIKit - -import SnapKit - -public final class StepIndicator: UIStackView { - // MARK: - Type - private enum Constant { - static let circleSize: CGFloat = 8 - static let spacing: CGFloat = 8 - } - - // MARK: - Components - - // MARK: - init - public init(circleCount: Int) { - super.init(frame: .zero) - configureUI(circleCount: circleCount) - } - - @available(*, unavailable) - required init(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension StepIndicator { - func configureUI(circleCount: Int) { - axis = .horizontal - distribution = .fillEqually - spacing = Constant.spacing - - for _ in 0 ..< circleCount { - let view = UIImageView(image: DesignSystemAsset.image(named: "circle")?.withRenderingMode(.alwaysTemplate)) - view.contentMode = .scaleAspectFit - view.tintColor = .neutral300 - addArrangedSubview(view) - } - - arrangedSubviews.forEach { view in - view.snp.makeConstraints { make in - make.size.equalTo(Constant.circleSize) - } - } - } -} - -public extension StepIndicator { - func selectIndicator(index: Int) { - guard index >= 0, index < arrangedSubviews.count else { return } - - arrangedSubviews.enumerated().forEach { circleIndex, view in - guard let circle = view as? UIImageView else { return } - circle.tintColor = (index == circleIndex) ? .primary700 : .neutral300 - } - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/Tabbar/BottomTabBar.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/Tabbar/BottomTabBar.swift deleted file mode 100644 index bf1c2a71..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/Tabbar/BottomTabBar.swift +++ /dev/null @@ -1,100 +0,0 @@ -import UIKit - -import SnapKit - -// MARK: - Model -public struct TabItem { - var title: String - var icon: UIImage -} - -public final class BottomTabBar: UIStackView { - // MARK: - Type - private enum Constant { - static let height: CGFloat = 64 - static let buttonSize: CGFloat = 64 - } - - // MARK: - Properties - public var onTabSelected: ((Int) -> Void)? - - private var tabButtons: [TabButton] = [] - private var selectedIndex: Int = 0 { - didSet { - selectIndex() - } - } - - // MARK: - Init - public init(tabItems: [TabItem], selectedIndex: Int) { - self.selectedIndex = selectedIndex - super.init(frame: .zero) - setUpConstraints() - configureUI(tabItems: tabItems) - setupStackView() - selectIndex() - } - - @available(*, unavailable) - required init(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: - Setup - - private func setUpConstraints() { - snp.makeConstraints { make in - make.height.equalTo(Constant.height) - } - } - - private func configureUI(tabItems: [TabItem]) { - tabButtons.forEach { $0.removeFromSuperview() } - tabButtons.removeAll() - arrangedSubviews.forEach { removeArrangedSubview($0); $0.removeFromSuperview() } - - for (index, item) in tabItems.enumerated() { - if index > 0 { - let spacer = UIView() - spacer.setContentHuggingPriority(.defaultLow, for: .horizontal) - spacer.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) - addArrangedSubview(spacer) - } - - let button = TabButton(icon: item.icon, text: item.title) - button.tag = index - button.addTarget(self, action: #selector(tabButtonTapped(_:)), for: .touchUpInside) - addArrangedSubview(button) - tabButtons.append(button) - - button.snp.makeConstraints { make in - make.width.equalTo(Constant.buttonSize) - } - } - } - - private func setupStackView() { - backgroundColor = .systemBackground - axis = .horizontal - distribution = .equalSpacing - spacing = 0 - } - - @objc private func tabButtonTapped(_ sender: TabButton) { - let newIndex = sender.tag - guard newIndex != selectedIndex else { return } - selectedIndex = newIndex - onTabSelected?(newIndex) - } - - private func selectIndex() { - for (index, button) in tabButtons.enumerated() { - button.isSelected = (index == selectedIndex) - } - } - - public func selectTab(index: Int) { - guard index != selectedIndex else { return } - selectedIndex = index - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/Tabbar/BottomTabBarController.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/Tabbar/BottomTabBarController.swift deleted file mode 100644 index 1a89a6b0..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/Tabbar/BottomTabBarController.swift +++ /dev/null @@ -1,105 +0,0 @@ -import UIKit - -import SnapKit - -public final class BottomTabBarController: UITabBarController { - // MARK: - Type - private enum Constant { - static let horizontalInset: CGFloat = 24 - } - - // MARK: - Components - private let divider = DividerView() - private let tabItems: [TabItem] - private let customTabBar: BottomTabBar - - // MARK: - Init - public init(viewControllers: [UIViewController], initialIndex: Int = 0) { - tabItems = [ - TabItem(title: "도감", icon: .dictionary), - TabItem(title: "북마크", icon: .bookmarkList), - TabItem(title: "MY", icon: .mypage) - ] - customTabBar = BottomTabBar(tabItems: tabItems, selectedIndex: initialIndex) - super.init(nibName: nil, bundle: nil) - configureUI(controllers: viewControllers) - selectedIndex = initialIndex - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override public func viewDidLoad() { - super.viewDidLoad() - addViews() - setupConstraints() - } -} - -// MARK: - SetUp -private extension BottomTabBarController { - func addViews() { - view.addSubview(customTabBar) - view.addSubview(divider) - } - - func setupConstraints() { - divider.snp.makeConstraints { make in - make.horizontalEdges.equalToSuperview() - make.bottom.equalTo(customTabBar.snp.top) - } - - customTabBar.snp.makeConstraints { make in - make.horizontalEdges.equalToSuperview().inset(Constant.horizontalInset) - make.bottom.equalTo(view.safeAreaLayoutGuide) - } - } - - func configureUI(controllers: [UIViewController]) { - viewControllers = controllers.map { - if $0 is UINavigationController { - return $0 - } else { - return UINavigationController(rootViewController: $0) - } - } - tabBar.isHidden = true - - customTabBar.onTabSelected = { [weak self] index in - UIView.performWithoutAnimation { - self?.selectedIndex = index - self?.customTabBar.selectTab(index: index) - } - } - } -} - -public extension BottomTabBarController { - func setHidden(hidden: Bool, animated: Bool = false) { - guard customTabBar.isHidden != hidden else { return } - - if animated { - UIView.animate(withDuration: 0.3) { - self.customTabBar.alpha = hidden ? 0 : 1 - self.divider.alpha = hidden ? 0 : 1 - } completion: { _ in - self.customTabBar.isHidden = hidden - self.divider.isHidden = hidden - } - } else { - customTabBar.isHidden = hidden - customTabBar.alpha = hidden ? 0 : 1 - divider.isHidden = hidden - divider.alpha = hidden ? 0 : 1 - } - } - - func selectTab(index: Int, animated: Bool = false) { - UIView.performWithoutAnimation { - selectedIndex = index - customTabBar.selectTab(index: index) - } - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/Tabbar/TabButton.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/Tabbar/TabButton.swift deleted file mode 100644 index 664c2ea1..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/Tabbar/TabButton.swift +++ /dev/null @@ -1,73 +0,0 @@ -import UIKit - -import SnapKit - -public final class TabButton: UIButton { - // MARK: - Type - private enum Constant { - static let spacing: CGFloat = 4 - static let iconSize: CGFloat = 24 - } - - // MARK: - Properties - override public var isSelected: Bool { - didSet { - updateUI() - } - } - - // MARK: - Components - private lazy var contentView: UIStackView = { - let view = UIStackView(arrangedSubviews: [iconView, textLabel]) - view.axis = .vertical - view.spacing = Constant.spacing - view.alignment = .center - view.isUserInteractionEnabled = false - return view - }() - - private let iconView = UIImageView() - private let textLabel: UILabel = { - let label = UILabel() - label.textAlignment = .center - return label - }() - - // MARK: - Init - public init(icon: UIImage, text: String) { - super.init(frame: .zero) - iconView.image = icon.withRenderingMode(.alwaysTemplate) - textLabel.text = text - addViews() - setupConstraints() - updateUI() - } - - @available(*, unavailable) - required init(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension TabButton { - func addViews() { - addSubview(contentView) - } - - func setupConstraints() { - contentView.snp.makeConstraints { make in - make.center.equalToSuperview() - } - - iconView.snp.makeConstraints { make in - make.size.equalTo(Constant.iconSize) - } - } - - func updateUI() { - iconView.tintColor = isSelected ? .primary700 : .neutral300 - textLabel.textColor = isSelected ? .primary700 : .neutral700 - textLabel.font = .systemFont(ofSize: 11, weight: isSelected ? .semibold : .regular) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/TagChip.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/TagChip.swift deleted file mode 100644 index 3bb5c967..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/TagChip.swift +++ /dev/null @@ -1,144 +0,0 @@ -import UIKit - -import SnapKit - -public final class TagChip: UIButton { - // MARK: - Type - public enum TagChipStyle { - case normal - case search - - var borderWidth: CGFloat { - switch self { - case .normal: - return 0 - case .search: - return 1 - } - } - - var borderColor: CGColor { - switch self { - case .normal: - return UIColor.clearMLS.cgColor - case .search: - return UIColor.neutral300.cgColor - } - } - - var fontColor: UIColor { - switch self { - case .normal: - return .primary700 - case .search: - return .textColor - } - } - - var backgroundColor: UIColor { - switch self { - case .normal: - return .primary50 - case .search: - return .clearMLS - } - } - - var radius: CGFloat { - switch self { - case .normal: - return 16 - case .search: - return 8 - } - } - - var contentInsets: NSDirectionalEdgeInsets { - switch self { - case .normal: - return .init(top: 4, leading: 12, bottom: 4, trailing: 8) - case .search: - return .init(top: 4, leading: 10, bottom: 4, trailing: 10) - } - } - } - - private enum Constant { - static let height: CGFloat = 32 - static let imageSize: CGFloat = 24 - } - - // MARK: - Properties - public var style: TagChipStyle { - didSet { - updateUI() - } - } - - public var text: String? { - didSet { - updateUI() - } - } - - public let mainTitleLabel: UILabel = { - let label = UILabel() - return label - }() - - public let cancelButton: UIButton = { - let button = UIButton(type: .custom) - return button - }() - - // MARK: - init - public init(style: TagChipStyle, text: String?) { - self.style = style - self.text = text - super.init(frame: .zero) - - setupConstraints() - configureUI() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension TagChip { - func setupConstraints() { - addSubview(mainTitleLabel) - addSubview(cancelButton) - snp.makeConstraints { make in - make.height.equalTo(Constant.height) - } - mainTitleLabel.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(style.contentInsets.leading) - make.centerY.equalToSuperview() - } - cancelButton.snp.makeConstraints { make in - make.leading.equalTo(mainTitleLabel.snp.trailing) - make.verticalEdges.equalToSuperview().inset(style.contentInsets.top) - make.size.equalTo(24) - make.trailing.equalToSuperview().inset(style.contentInsets.trailing) - } - } - - func configureUI() { - let image = UIImage.smallX.withRenderingMode(.alwaysTemplate) - cancelButton.setImage(image, for: .normal) - cancelButton.tintColor = style.fontColor - } - - func updateUI() { - backgroundColor = style.backgroundColor - mainTitleLabel.attributedText = .makeStyledString(font: .cp_s_r, text: text, color: style.fontColor) - layer.borderColor = style.borderColor - layer.borderWidth = style.borderWidth - layer.cornerRadius = style.radius - cancelButton.tintColor = style.fontColor - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/TapButton.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/TapButton.swift deleted file mode 100644 index 629fd51b..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/TapButton.swift +++ /dev/null @@ -1,75 +0,0 @@ -import UIKit - -import SnapKit - -public final class TapButton: UIButton { - // MARK: - Type - private enum Constant { - static let height: CGFloat = 34 - static let borderWidth: CGFloat = 1 - static let radius: CGFloat = 17 - static let contentInsets: NSDirectionalEdgeInsets = .init(top: 10, leading: 16, bottom: 10, trailing: 16) - } - - public let mainTitleLabel: UILabel = { - let label = UILabel() - return label - }() - - // MARK: - Properties - override public var isSelected: Bool { - didSet { - updateUI() - } - } - - public var text: String? { - didSet { - updateUI() - } - } - - // MARK: - init - public init(text: String? = nil) { - self.text = text - super.init(frame: .zero) - - setupConstraints() - configureUI() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension TapButton { - func setupConstraints() { - snp.makeConstraints { make in - make.height.equalTo(Constant.height) - } - addSubview(mainTitleLabel) - mainTitleLabel.snp.makeConstraints { make in - make.horizontalEdges.equalToSuperview().inset(Constant.contentInsets.leading) - make.verticalEdges.equalToSuperview().inset(Constant.contentInsets.top) - } - } - - func configureUI() { - backgroundColor = .clear - layer.borderWidth = Constant.borderWidth - layer.cornerRadius = Constant.radius - layer.borderColor = isSelected ? UIColor.primary700.cgColor : UIColor.neutral200.cgColor - } - - func updateUI() { - mainTitleLabel.attributedText = .makeStyledString( - font: isSelected ? .cp_s_sb : .cp_s_r, - text: text, - color: isSelected ? .primary700 : .neutral700 - ) - layer.borderColor = isSelected ? UIColor.primary700.cgColor : UIColor.neutral200.cgColor - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/TextButton.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/TextButton.swift deleted file mode 100644 index fa57221a..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/TextButton.swift +++ /dev/null @@ -1,75 +0,0 @@ -import UIKit - -import SnapKit - -public final class TextButton: UIButton { - // MARK: - Type - - private enum Constant { - static let height: CGFloat = 32 - static let iconSize: CGFloat = 16 - static let horizontalInset: CGFloat = 12 - static let spacing: CGFloat = 4 - static let radius: CGFloat = 16 - } - - // MARK: - Properties - public let iconView: UIImageView = { - let view = UIImageView() - view.image = .edit.withRenderingMode(.alwaysTemplate) - view.tintColor = .neutral700 - return view - }() - - public let textLabel: UILabel = { - let label = UILabel() - label.attributedText = .makeStyledString(font: .btn_s_r, text: "편집", color: .neutral700) - return label - }() - - // MARK: - init - public init() { - super.init(frame: .zero) - - addViews() - setupConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension TextButton { - func addViews() { - addSubview(iconView) - addSubview(textLabel) - } - - func setupConstraints() { - snp.makeConstraints { make in - make.height.equalTo(Constant.height) - } - - iconView.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(Constant.horizontalInset) - make.centerY.equalToSuperview() - make.size.equalTo(Constant.iconSize) - } - - textLabel.snp.makeConstraints { make in - make.leading.equalTo(iconView.snp.trailing).offset(Constant.spacing) - make.centerY.equalToSuperview() - make.trailing.equalToSuperview().inset(Constant.horizontalInset) - } - } - - func configureUI() { - backgroundColor = .whiteMLS - layer.cornerRadius = Constant.radius - layer.borderWidth = 1 - layer.borderColor = UIColor.neutral300.cgColor - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/Toast.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/Toast.swift deleted file mode 100644 index a889b4c6..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/Toast.swift +++ /dev/null @@ -1,59 +0,0 @@ -import UIKit - -import SnapKit - -public final class Toast: UIView { - private enum Constant { - static let verticalEdgesInset: CGFloat = 16 - static let horizontalEdges: CGFloat = 16 - static let cornerRadius: CGFloat = 8 - } - - // MARK: - Properties - private let toastContentView: UIView = { - let view = UIView() - view.backgroundColor = .neutral900 - return view - }() - - private let label: UILabel = .init() - - // MARK: - init - public init(message: String?) { - super.init(frame: .zero) - - self.addViews() - self.setupConstraints() - self.configureUI(message: message) - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension Toast { - func addViews() { - addSubview(self.toastContentView) - toastContentView.addSubview(self.label) - } - - func setupConstraints() { - toastContentView.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - - label.snp.makeConstraints { make in - make.verticalEdges.equalToSuperview().inset(Constant.verticalEdgesInset) - make.horizontalEdges.equalToSuperview().inset(Constant.horizontalEdges) - } - } - - func configureUI(message: String?) { - layer.cornerRadius = Constant.cornerRadius - clipsToBounds = true - label.attributedText = .makeStyledString(font: .b_s_r, text: message, color: .whiteMLS) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/ToggleBox.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/ToggleBox.swift deleted file mode 100644 index 890de020..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/ToggleBox.swift +++ /dev/null @@ -1,69 +0,0 @@ -import UIKit - -import SnapKit - -public final class ToggleBox: UIView { - // MARK: - Type - enum Constant { - static let margin: CGFloat = 20 - static let toggleWidth: CGFloat = 51 - static let toggleHeight: CGFloat = 31 - static let radius: CGFloat = 8 - static let height: CGFloat = 60 - } - - // MARK: - Components - private let textLabel = UILabel() - - public let toggle: UISwitch = { - let button = UISwitch() - button.thumbTintColor = .whiteMLS - button.onTintColor = .primary700 - button.isOn = false - return button - }() - - public init(text: String?) { - super.init(frame: .zero) - addViews() - setupConstraints() - configureUI(text: text) - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension ToggleBox { - func addViews() { - addSubview(textLabel) - addSubview(toggle) - } - - func setupConstraints() { - snp.makeConstraints { make in - make.height.equalTo(Constant.height) - } - - textLabel.snp.makeConstraints { make in - make.verticalEdges.equalToSuperview().inset(Constant.margin) - make.leading.equalToSuperview().inset(Constant.margin) - } - - toggle.snp.makeConstraints { make in - make.leading.equalTo(textLabel.snp.trailing) - make.trailing.centerY.equalToSuperview().inset(Constant.margin) - make.width.equalTo(Constant.toggleWidth) - make.height.equalTo(Constant.toggleHeight) - } - } - - func configureUI(text: String?) { - textLabel.attributedText = .makeStyledString(font: .sub_m_b, text: text, alignment: .left) - backgroundColor = .neutral100 - layer.cornerRadius = Constant.radius - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Info.plist b/MLS/Presentation/DesignSystem/DesignSystem/Info.plist deleted file mode 100644 index fee620f3..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Info.plist +++ /dev/null @@ -1,15 +0,0 @@ - - - - - ITSAppUsesNonExemptEncryption - - UIAppFonts - - Resource/Fonts/Pretendard-Bold.ttf - Resource/Fonts/Pretendard-SemiBold.ttf - Resource/Fonts/Pretendard-Medium.ttf - Resource/Fonts/Pretendard-Regular.ttf - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Fonts/Pretendard-Bold.ttf b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Fonts/Pretendard-Bold.ttf deleted file mode 100644 index fb07fc65..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Fonts/Pretendard-Bold.ttf and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Fonts/Pretendard-Medium.ttf b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Fonts/Pretendard-Medium.ttf deleted file mode 100644 index 1db67c68..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Fonts/Pretendard-Medium.ttf and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Fonts/Pretendard-Regular.ttf b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Fonts/Pretendard-Regular.ttf deleted file mode 100644 index 01147e99..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Fonts/Pretendard-Regular.ttf and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Fonts/Pretendard-SemiBold.ttf b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Fonts/Pretendard-SemiBold.ttf deleted file mode 100644 index 9f2690f0..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Fonts/Pretendard-SemiBold.ttf and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/AppleLogo.imageset/AppleLogo.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/AppleLogo.imageset/AppleLogo.svg deleted file mode 100644 index 39e20f52..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/AppleLogo.imageset/AppleLogo.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/AppleLogo.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/AppleLogo.imageset/Contents.json deleted file mode 100644 index 52803e85..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/AppleLogo.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "AppleLogo.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideAlert.imageset/guideAlert.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideAlert.imageset/guideAlert.png deleted file mode 100644 index c7fa2f9f..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideAlert.imageset/guideAlert.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideArrow1.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideArrow1.imageset/Contents.json deleted file mode 100644 index c540110a..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideArrow1.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "guideArrow1.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideArrow1.imageset/guideArrow1.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideArrow1.imageset/guideArrow1.png deleted file mode 100644 index 07e674cb..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideArrow1.imageset/guideArrow1.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideArrow2.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideArrow2.imageset/Contents.json deleted file mode 100644 index 4e3272ad..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideArrow2.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "guideArrow2.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideArrow2.imageset/guideArrow2.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideArrow2.imageset/guideArrow2.png deleted file mode 100644 index 2b33ee8b..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideArrow2.imageset/guideArrow2.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideIcon.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideIcon.imageset/Contents.json deleted file mode 100644 index 6d1fa50c..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideIcon.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "guideIcon.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideIcon.imageset/guideIcon.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideIcon.imageset/guideIcon.png deleted file mode 100644 index d92f19e2..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/BookmarkOnboarding/guideIcon.imageset/guideIcon.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/DropDown.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/DropDown.imageset/Contents.json deleted file mode 100644 index cd7ddaee..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/DropDown.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "dropDown.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/DropDown.imageset/dropDown.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/DropDown.imageset/dropDown.svg deleted file mode 100644 index 67225443..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/DropDown.imageset/dropDown.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/Fab.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/Fab.imageset/Contents.json deleted file mode 100644 index 4b7e95d7..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/Fab.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "Fab.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/Fab.imageset/Fab.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/Fab.imageset/Fab.svg deleted file mode 100644 index b3840368..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/Fab.imageset/Fab.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/addIcon.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/addIcon.imageset/Contents.json deleted file mode 100644 index b4988ec2..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/addIcon.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "add.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/addIcon.imageset/add.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/addIcon.imageset/add.svg deleted file mode 100644 index 0530ce26..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/addIcon.imageset/add.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowBack.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowBack.imageset/Contents.json deleted file mode 100644 index fa1b15e0..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowBack.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "arrowBack.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowBack.imageset/arrowBack.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowBack.imageset/arrowBack.svg deleted file mode 100644 index 690091c6..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowBack.imageset/arrowBack.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowDropUp.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowDropUp.imageset/Contents.json deleted file mode 100644 index 9df632b0..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowDropUp.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "arrowDropUp.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowDropUp.imageset/arrowDropUp.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowDropUp.imageset/arrowDropUp.svg deleted file mode 100644 index 20352b31..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowDropUp.imageset/arrowDropUp.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowDropdown.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowDropdown.imageset/Contents.json deleted file mode 100644 index 62f4cae1..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowDropdown.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "arrowDropdown.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowDropdown.imageset/arrowDropdown.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowDropdown.imageset/arrowDropdown.svg deleted file mode 100644 index bc11335c..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowDropdown.imageset/arrowDropdown.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowForward.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowForward.imageset/Contents.json deleted file mode 100644 index 21e06750..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowForward.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "arrwoForward.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowForward.imageset/arrwoForward.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowForward.imageset/arrwoForward.svg deleted file mode 100644 index 93f8e30d..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowForward.imageset/arrwoForward.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowForwardSmall.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowForwardSmall.imageset/Contents.json deleted file mode 100644 index 2d79acc7..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowForwardSmall.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "arrowForwordSmall.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowForwardSmall.imageset/arrowForwordSmall.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowForwardSmall.imageset/arrowForwordSmall.svg deleted file mode 100644 index f0d29991..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/arrowForwardSmall.imageset/arrowForwordSmall.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bell.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bell.imageset/Contents.json deleted file mode 100644 index b0db2947..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bell.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "bell.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bell.imageset/bell.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bell.imageset/bell.svg deleted file mode 100644 index 389cbbae..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bell.imageset/bell.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/blurImage.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/blurImage.imageset/Contents.json deleted file mode 100644 index e5e139a0..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/blurImage.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "blurImage.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/blurImage.imageset/blurImage.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/blurImage.imageset/blurImage.png deleted file mode 100644 index c40f1df1..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/blurImage.imageset/blurImage.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmark.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmark.imageset/Contents.json deleted file mode 100644 index b9a0914c..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmark.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "bookmark.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmark.imageset/bookmark.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmark.imageset/bookmark.svg deleted file mode 100644 index 4df98315..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmark.imageset/bookmark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkBorder.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkBorder.imageset/Contents.json deleted file mode 100644 index 718b074b..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkBorder.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "bookmark_border.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkBorder.imageset/bookmark_border.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkBorder.imageset/bookmark_border.svg deleted file mode 100644 index bf456cff..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkBorder.imageset/bookmark_border.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkBorderList.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkBorderList.imageset/Contents.json deleted file mode 100644 index 684a4e90..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkBorderList.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "bookmarkBorderList.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkBorderList.imageset/bookmarkBorderList.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkBorderList.imageset/bookmarkBorderList.png deleted file mode 100644 index 9fe9661a..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkBorderList.imageset/bookmarkBorderList.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkGrayBorder.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkGrayBorder.imageset/Contents.json deleted file mode 100644 index dda7fb83..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkGrayBorder.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "bookmarkBorder.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkGrayBorder.imageset/bookmarkBorder.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkGrayBorder.imageset/bookmarkBorder.svg deleted file mode 100644 index c3875680..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkGrayBorder.imageset/bookmarkBorder.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkGrayBorderList.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkGrayBorderList.imageset/Contents.json deleted file mode 100644 index e279269b..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkGrayBorderList.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "bookmarkGrayBorderList.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkGrayBorderList.imageset/bookmarkGrayBorderList.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkGrayBorderList.imageset/bookmarkGrayBorderList.svg deleted file mode 100644 index d5ace6ae..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkGrayBorderList.imageset/bookmarkGrayBorderList.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkList.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkList.imageset/Contents.json deleted file mode 100644 index 78cce243..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkList.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "bookmarkList.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkList.imageset/bookmarkList.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkList.imageset/bookmarkList.svg deleted file mode 100644 index dc45cef0..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/bookmarkList.imageset/bookmarkList.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkCircle.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkCircle.imageset/Contents.json deleted file mode 100644 index ce4dc702..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkCircle.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "checkCircle.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkCircle.imageset/checkCircle.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkCircle.imageset/checkCircle.svg deleted file mode 100644 index fd0968fd..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkCircle.imageset/checkCircle.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkCircleFill.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkCircleFill.imageset/Contents.json deleted file mode 100644 index e2e75972..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkCircleFill.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "checkCircleFill.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkCircleFill.imageset/checkCircleFill.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkCircleFill.imageset/checkCircleFill.svg deleted file mode 100644 index cc39a399..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkCircleFill.imageset/checkCircleFill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkMark.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkMark.imageset/Contents.json deleted file mode 100644 index 7f105edc..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkMark.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "checkMark.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkMark.imageset/checkMark.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkMark.imageset/checkMark.svg deleted file mode 100644 index d6f5372f..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkMark.imageset/checkMark.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkMarkFill.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkMarkFill.imageset/Contents.json deleted file mode 100644 index 9976bef5..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkMarkFill.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "checkMarkFill.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkMarkFill.imageset/checkMarkFill.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkMarkFill.imageset/checkMarkFill.svg deleted file mode 100644 index b8409aae..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkMarkFill.imageset/checkMarkFill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkSquare.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkSquare.imageset/Contents.json deleted file mode 100644 index c9e9b7f5..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkSquare.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "checkSquare.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkSquare.imageset/checkSquare.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkSquare.imageset/checkSquare.svg deleted file mode 100644 index 4707e286..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkSquare.imageset/checkSquare.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkSquareFill.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkSquareFill.imageset/Contents.json deleted file mode 100644 index f5c7ed27..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkSquareFill.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "checkSquareFill.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkSquareFill.imageset/checkSquareFill.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkSquareFill.imageset/checkSquareFill.svg deleted file mode 100644 index 7e06b1ad..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/checkSquareFill.imageset/checkSquareFill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/circle.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/circle.imageset/Contents.json deleted file mode 100644 index acd6921d..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/circle.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "icon_circle.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/circle.imageset/icon_circle.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/circle.imageset/icon_circle.svg deleted file mode 100644 index 54655880..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/circle.imageset/icon_circle.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/dictionary.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/dictionary.imageset/Contents.json deleted file mode 100644 index 2475455d..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/dictionary.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "dictionary.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/dictionary.imageset/dictionary.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/dictionary.imageset/dictionary.svg deleted file mode 100644 index f09313b4..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/dictionary.imageset/dictionary.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/edit.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/edit.imageset/Contents.json deleted file mode 100644 index 13f9713f..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/edit.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "edit.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/edit.imageset/edit.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/edit.imageset/edit.svg deleted file mode 100644 index 35c2ed7d..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/edit.imageset/edit.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/error.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/error.imageset/Contents.json deleted file mode 100644 index 8d95c25b..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/error.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "error.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/error.imageset/error.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/error.imageset/error.svg deleted file mode 100644 index 049788c6..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/error.imageset/error.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/errorBlack.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/errorBlack.imageset/Contents.json deleted file mode 100644 index 8d95c25b..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/errorBlack.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "error.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/errorBlack.imageset/error.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/errorBlack.imageset/error.svg deleted file mode 100644 index e74491e2..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/errorBlack.imageset/error.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/errorImage.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/errorImage.imageset/Contents.json deleted file mode 100644 index 5179fc52..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/errorImage.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "errorImage.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/errorImage.imageset/errorImage.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/errorImage.imageset/errorImage.png deleted file mode 100644 index e5204398..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/errorImage.imageset/errorImage.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/favorite.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/favorite.imageset/Contents.json deleted file mode 100644 index 33e62908..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/favorite.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "favorite_true.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/favorite.imageset/favorite_true.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/favorite.imageset/favorite_true.svg deleted file mode 100644 index d47569a5..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/favorite.imageset/favorite_true.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/filter.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/filter.imageset/Contents.json deleted file mode 100644 index 01d6c7dd..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/filter.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "filter.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/filter.imageset/filter.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/filter.imageset/filter.svg deleted file mode 100644 index 131b98da..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/filter.imageset/filter.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/blueSnail.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/blueSnail.imageset/Contents.json deleted file mode 100644 index f0818241..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/blueSnail.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "blueSnail.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/blueSnail.imageset/blueSnail.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/blueSnail.imageset/blueSnail.png deleted file mode 100644 index 50788978..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/blueSnail.imageset/blueSnail.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/blueSnailSelected.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/blueSnailSelected.imageset/Contents.json deleted file mode 100644 index b936cfb1..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/blueSnailSelected.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "blueSnailSelected.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/blueSnailSelected.imageset/blueSnailSelected.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/blueSnailSelected.imageset/blueSnailSelected.png deleted file mode 100644 index ccac9342..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/blueSnailSelected.imageset/blueSnailSelected.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/juniorYeti.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/juniorYeti.imageset/Contents.json deleted file mode 100644 index d25c043c..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/juniorYeti.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "juniorYeti.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/juniorYeti.imageset/juniorYeti.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/juniorYeti.imageset/juniorYeti.png deleted file mode 100644 index 7d692953..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/juniorYeti.imageset/juniorYeti.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/juniorYetiSelected.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/juniorYetiSelected.imageset/Contents.json deleted file mode 100644 index 52ceecf0..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/juniorYetiSelected.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "juniorYetiSelected.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/juniorYetiSelected.imageset/juniorYetiSelected.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/juniorYetiSelected.imageset/juniorYetiSelected.png deleted file mode 100644 index c81772ef..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/juniorYetiSelected.imageset/juniorYetiSelected.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroom.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroom.imageset/Contents.json deleted file mode 100644 index 3a330589..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroom.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "mushroom.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroom.imageset/mushroom.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroom.imageset/mushroom.png deleted file mode 100644 index 0773ae4f..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroom.imageset/mushroom.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroomSelected.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroomSelected.imageset/Contents.json deleted file mode 100644 index be7cc9af..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroomSelected.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "mushroomSelected.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroomSelected.imageset/mushroomSelected.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroomSelected.imageset/mushroomSelected.png deleted file mode 100644 index a502b616..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroomSelected.imageset/mushroomSelected.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroomTest.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroomTest.imageset/Contents.json deleted file mode 100644 index a6efc45b..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroomTest.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "mushroomTest.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroomTest.imageset/mushroomTest.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroomTest.imageset/mushroomTest.png deleted file mode 100644 index 9a90b6c9..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/mushroomTest.imageset/mushroomTest.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/pepe.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/pepe.imageset/Contents.json deleted file mode 100644 index b50a2622..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/pepe.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "pepe.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/pepe.imageset/pepe.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/pepe.imageset/pepe.png deleted file mode 100644 index 69653047..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/pepe.imageset/pepe.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/pepeSelected.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/pepeSelected.imageset/Contents.json deleted file mode 100644 index d2fd897d..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/pepeSelected.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "pepeSelected.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/pepeSelected.imageset/pepeSelected.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/pepeSelected.imageset/pepeSelected.png deleted file mode 100644 index 3657aa09..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/pepeSelected.imageset/pepeSelected.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/rash.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/rash.imageset/Contents.json deleted file mode 100644 index 9c8da93a..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/rash.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "rash.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/rash.imageset/rash.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/rash.imageset/rash.png deleted file mode 100644 index 5f095627..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/rash.imageset/rash.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/rashSelected.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/rashSelected.imageset/Contents.json deleted file mode 100644 index 11fa673c..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/rashSelected.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "rashSelected.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/rashSelected.imageset/rashSelected.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/rashSelected.imageset/rashSelected.png deleted file mode 100644 index f63ae757..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/rashSelected.imageset/rashSelected.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/slime.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/slime.imageset/Contents.json deleted file mode 100644 index 4ced3bf3..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/slime.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "slime.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/slime.imageset/slime.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/slime.imageset/slime.png deleted file mode 100644 index 4278e35a..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/slime.imageset/slime.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/slimeSelected.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/slimeSelected.imageset/Contents.json deleted file mode 100644 index 5722705d..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/slimeSelected.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "slimeSelected.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/slimeSelected.imageset/slimeSelected.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/slimeSelected.imageset/slimeSelected.png deleted file mode 100644 index fdb91b6f..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/slimeSelected.imageset/slimeSelected.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/starPixie.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/starPixie.imageset/Contents.json deleted file mode 100644 index 70545b1b..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/starPixie.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "starpixie.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/starPixie.imageset/starpixie.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/starPixie.imageset/starpixie.png deleted file mode 100644 index 6cd9d7de..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/starPixie.imageset/starpixie.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/starPixieSelected.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/starPixieSelected.imageset/Contents.json deleted file mode 100644 index e68397ab..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/starPixieSelected.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "starPixieSelected.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/starPixieSelected.imageset/starPixieSelected.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/starPixieSelected.imageset/starPixieSelected.png deleted file mode 100644 index 09fd755c..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/starPixieSelected.imageset/starPixieSelected.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/wraith.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/wraith.imageset/Contents.json deleted file mode 100644 index 18c2f1df..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/wraith.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "wraith.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/wraith.imageset/wraith.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/wraith.imageset/wraith.png deleted file mode 100644 index b6c4015e..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/wraith.imageset/wraith.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/wraithSelected.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/wraithSelected.imageset/Contents.json deleted file mode 100644 index 170ee7e2..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/wraithSelected.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "wraithSelected.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/wraithSelected.imageset/wraithSelected.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/wraithSelected.imageset/wraithSelected.png deleted file mode 100644 index 335885e3..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/wraithSelected.imageset/wraithSelected.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/yeti.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/yeti.imageset/Contents.json deleted file mode 100644 index 01fd28ee..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/yeti.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "yeti.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/yeti.imageset/yeti.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/yeti.imageset/yeti.png deleted file mode 100644 index 190ceb41..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/yeti.imageset/yeti.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/yetiSelected.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/yetiSelected.imageset/Contents.json deleted file mode 100644 index 439a9018..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/yetiSelected.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "yetiSelected.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/yetiSelected.imageset/yetiSelected.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/yetiSelected.imageset/yetiSelected.png deleted file mode 100644 index 4db0974f..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/illustration/yetiSelected.imageset/yetiSelected.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/Login_KV_img.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/Login_KV_img.imageset/Contents.json deleted file mode 100644 index 0ec129e8..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/Login_KV_img.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "Login_KV_img.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/Login_KV_img.imageset/Login_KV_img.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/Login_KV_img.imageset/Login_KV_img.png deleted file mode 100644 index 1e6d15d3..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/Login_KV_img.imageset/Login_KV_img.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/addToCollection.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/addToCollection.imageset/Contents.json deleted file mode 100644 index ed70f822..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/addToCollection.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "addToCollection.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/addToCollection.imageset/addToCollection.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/addToCollection.imageset/addToCollection.png deleted file mode 100644 index 4c38fbf8..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/addToCollection.imageset/addToCollection.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/appleImage.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/appleImage.imageset/Contents.json deleted file mode 100644 index b3712ae4..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/appleImage.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "appleImage.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/appleImage.imageset/appleImage.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/appleImage.imageset/appleImage.png deleted file mode 100644 index e6038fb5..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/appleImage.imageset/appleImage.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/connectionError.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/connectionError.imageset/Contents.json deleted file mode 100644 index fac1a53c..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/connectionError.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "connectionError.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/connectionError.imageset/connectionError.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/connectionError.imageset/connectionError.png deleted file mode 100644 index 75604d9b..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/connectionError.imageset/connectionError.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/fabHint.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/fabHint.imageset/Contents.json deleted file mode 100644 index e1484a1b..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/fabHint.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "fabHint.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/fabHint.imageset/fabHint.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/fabHint.imageset/fabHint.png deleted file mode 100644 index 82a2ea35..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/fabHint.imageset/fabHint.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/getNotify.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/getNotify.imageset/Contents.json deleted file mode 100644 index d14b19a0..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/getNotify.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "getNotify.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/getNotify.imageset/getNotify.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/getNotify.imageset/getNotify.png deleted file mode 100644 index 9fee5ecf..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/getNotify.imageset/getNotify.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/kakaoImage.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/kakaoImage.imageset/Contents.json deleted file mode 100644 index 8298ca5d..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/kakaoImage.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "kakaoImage.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/kakaoImage.imageset/kakaoImage.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/kakaoImage.imageset/kakaoImage.png deleted file mode 100644 index ef883fa6..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/kakaoImage.imageset/kakaoImage.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/noResult.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/noResult.imageset/Contents.json deleted file mode 100644 index e0b54f5a..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/noResult.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "noResult.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/noResult.imageset/noResult.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/noResult.imageset/noResult.png deleted file mode 100644 index 574e37d7..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/noResult.imageset/noResult.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/noShowList.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/noShowList.imageset/Contents.json deleted file mode 100644 index 402e6280..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/noShowList.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "noShowList.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/noShowList.imageset/noShowList.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/noShowList.imageset/noShowList.png deleted file mode 100644 index e90300c0..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/noShowList.imageset/noShowList.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/onBoardingBookmark.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/onBoardingBookmark.imageset/Contents.json deleted file mode 100644 index ed18840a..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/onBoardingBookmark.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "bookmarkList.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/onBoardingBookmark.imageset/bookmarkList.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/onBoardingBookmark.imageset/bookmarkList.png deleted file mode 100644 index 988fc8e7..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/onBoardingBookmark.imageset/bookmarkList.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/questionNotify.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/questionNotify.imageset/Contents.json deleted file mode 100644 index a71a5df4..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/questionNotify.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "questionNotify.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/questionNotify.imageset/questionNotify.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/questionNotify.imageset/questionNotify.png deleted file mode 100644 index d7c51a5d..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/questionNotify.imageset/questionNotify.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/recentLoginLogo.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/recentLoginLogo.imageset/Contents.json deleted file mode 100644 index c1fb5668..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/recentLoginLogo.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "recentLoginLogo.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/recentLoginLogo.imageset/recentLoginLogo.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/recentLoginLogo.imageset/recentLoginLogo.png deleted file mode 100644 index 4afde34f..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/recentLoginLogo.imageset/recentLoginLogo.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/settingsHint.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/settingsHint.imageset/Contents.json deleted file mode 100644 index 4c241a10..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/settingsHint.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "settingsHint.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/settingsHint.imageset/settingsHint.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/settingsHint.imageset/settingsHint.png deleted file mode 100644 index 76be1394..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/image/settingsHint.imageset/settingsHint.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/kakaoLogo.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/kakaoLogo.imageset/Contents.json deleted file mode 100644 index 332f97e7..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/kakaoLogo.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "kakaoLogo.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/kakaoLogo.imageset/kakaoLogo.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/kakaoLogo.imageset/kakaoLogo.svg deleted file mode 100644 index 43ac1413..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/kakaoLogo.imageset/kakaoLogo.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/largeX.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/largeX.imageset/Contents.json deleted file mode 100644 index f93a7f69..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/largeX.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "x-black_big.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/largeX.imageset/x-black_big.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/largeX.imageset/x-black_big.svg deleted file mode 100644 index d793ff8a..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/largeX.imageset/x-black_big.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/lineArrowDown.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/lineArrowDown.imageset/Contents.json deleted file mode 100644 index 1012cc97..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/lineArrowDown.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "lineArrowDown.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/lineArrowDown.imageset/lineArrowDown.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/lineArrowDown.imageset/lineArrowDown.svg deleted file mode 100644 index 67225443..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/lineArrowDown.imageset/lineArrowDown.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/logo.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/logo.imageset/Contents.json deleted file mode 100644 index 5f670ca8..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/logo.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "logo.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/logo.imageset/logo.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/logo.imageset/logo.png deleted file mode 100644 index 0c524885..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/logo.imageset/logo.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/mypage.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/mypage.imageset/Contents.json deleted file mode 100644 index 171be441..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/mypage.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "mypage_true.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/mypage.imageset/mypage_true.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/mypage.imageset/mypage_true.svg deleted file mode 100644 index 89cd6bd5..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/mypage.imageset/mypage_true.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/plusIcon.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/plusIcon.imageset/Contents.json deleted file mode 100644 index ccdb0f7e..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/plusIcon.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "plusicon.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/plusIcon.imageset/plusicon.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/plusIcon.imageset/plusicon.svg deleted file mode 100644 index c1ecbac2..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/plusIcon.imageset/plusicon.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/rightArrow.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/rightArrow.imageset/Contents.json deleted file mode 100644 index bfef48d9..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/rightArrow.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "rightArrow.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/rightArrow.imageset/rightArrow.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/rightArrow.imageset/rightArrow.svg deleted file mode 100644 index d722791e..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/rightArrow.imageset/rightArrow.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/search.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/search.imageset/Contents.json deleted file mode 100644 index e4d0b2c4..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/search.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "seach.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/search.imageset/seach.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/search.imageset/seach.svg deleted file mode 100644 index e1f774eb..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/search.imageset/seach.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/smallX.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/smallX.imageset/Contents.json deleted file mode 100644 index e29acd20..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/smallX.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "x-black.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/smallX.imageset/x-black.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/smallX.imageset/x-black.svg deleted file mode 100644 index e530a67c..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/smallX.imageset/x-black.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/testImage.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/testImage.imageset/Contents.json deleted file mode 100644 index 6a9ec0f9..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/testImage.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "testImage.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/testImage.imageset/testImage.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/testImage.imageset/testImage.png deleted file mode 100644 index 95973c2a..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/testImage.imageset/testImage.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/testImage2.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/testImage2.imageset/Contents.json deleted file mode 100644 index d114ab7e..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/testImage2.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "image.png", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/testImage2.imageset/image.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/testImage2.imageset/image.png deleted file mode 100644 index 0fc3ba70..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/testImage2.imageset/image.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/textFieldClear.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/textFieldClear.imageset/Contents.json deleted file mode 100644 index 7080485b..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/textFieldClear.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "textFieldClear.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/textFieldClear.imageset/textFieldClear.png b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/textFieldClear.imageset/textFieldClear.png deleted file mode 100644 index 5be83d97..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/textFieldClear.imageset/textFieldClear.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/warning.imageset/Contents.json b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/warning.imageset/Contents.json deleted file mode 100644 index d07182d3..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/warning.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "warning.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/warning.imageset/warning.svg b/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/warning.imageset/warning.svg deleted file mode 100644 index e6980bf6..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Resource/Image.xcassets/warning.imageset/warning.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Utills/DesignSystemAsset.swift b/MLS/Presentation/DesignSystem/DesignSystem/Utills/DesignSystemAsset.swift deleted file mode 100644 index 44dbad79..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Utills/DesignSystemAsset.swift +++ /dev/null @@ -1,50 +0,0 @@ -import UIKit - -public enum DesignSystemAsset { - static let bundle: Bundle = { - // 모듈의 번들을 특정 클래스 기반으로 가져옴 - return Bundle(for: DesignSystemMarker.self) - }() - - public static func image(named name: String) -> UIImage? { - return UIImage(named: name, in: bundle, compatibleWith: nil) - } -} - -/// Marker 클래스 - 번들 식별용 -private class DesignSystemMarker {} - -public enum MapleIllustration { - case mushroom - case slime - case blueSnail - case juniorYeti - case yeti - case pepe - case wraith - case starPixie - case rash - - public var url: String { - switch self { - case .mushroom: - "https://maple-db-team-s3.s3.ap-northeast-2.amazonaws.com/profile-images/profile_1.jpg" - case .slime: - "https://maple-db-team-s3.s3.ap-northeast-2.amazonaws.com/profile-images/profile_2.jpg" - case .blueSnail: - "https://maple-db-team-s3.s3.ap-northeast-2.amazonaws.com/profile-images/profile_3.jpg" - case .juniorYeti: - "https://maple-db-team-s3.s3.ap-northeast-2.amazonaws.com/profile-images/profile_4.jpg" - case .yeti: - "https://maple-db-team-s3.s3.ap-northeast-2.amazonaws.com/profile-images/profile_5.jpg" - case .pepe: - "https://maple-db-team-s3.s3.ap-northeast-2.amazonaws.com/profile-images/profile_6.jpg" - case .wraith: - "https://maple-db-team-s3.s3.ap-northeast-2.amazonaws.com/profile-images/profile_7.jpg" - case .starPixie: - "https://maple-db-team-s3.s3.ap-northeast-2.amazonaws.com/profile-images/profile_8.jpg" - case .rash: - "https://maple-db-team-s3.s3.ap-northeast-2.amazonaws.com/profile-images/profile_9.jpg" - } - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Utills/Extension/UIButton+.swift b/MLS/Presentation/DesignSystem/DesignSystem/Utills/Extension/UIButton+.swift deleted file mode 100644 index 5c6822be..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Utills/Extension/UIButton+.swift +++ /dev/null @@ -1,21 +0,0 @@ -import UIKit - -extension UIButton { - func setUnderlinedTitle(title: String, font: UIFont?, state: UIControl.State = .normal, textInsets: UIEdgeInsets = .zero) { - guard let font = font else { return } - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.minimumLineHeight = 0 - paragraphStyle.lineBreakMode = .byTruncatingTail - paragraphStyle.maximumLineHeight = font.lineHeight * 1.17 - - let attributes: [NSAttributedString.Key: Any] = [ - .underlineStyle: NSUnderlineStyle.single.rawValue, - .font: font, - .paragraphStyle: paragraphStyle - ] - - let attributedString = NSAttributedString(string: title, attributes: attributes) - setAttributedTitle(attributedString, for: state) - titleEdgeInsets = textInsets - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Utills/Extension/UIColor+.swift b/MLS/Presentation/DesignSystem/DesignSystem/Utills/Extension/UIColor+.swift deleted file mode 100644 index b3c62f6e..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Utills/Extension/UIColor+.swift +++ /dev/null @@ -1,58 +0,0 @@ -import UIKit - -public extension UIColor { - static let primary900 = UIColor(hexCode: "EE500C") - static let primary700 = UIColor(hexCode: "FF5C00") - static let primary500 = UIColor(hexCode: "FF7D33") - static let primary300 = UIColor(hexCode: "FF9D66") - static let primary100 = UIColor(hexCode: "FFBE9A") - static let primary50 = UIColor(hexCode: "FFE6D8") - static let primary25 = UIColor(hexCode: "FFF1E9") - - static let secondary = UIColor(hexCode: "FFAA00") - static let textColor = UIColor(hexCode: "1D1D1F") - - static let neutral900 = UIColor(hexCode: "313131") - static let neutral700 = UIColor(hexCode: "575757") - static let neutral600 = UIColor(hexCode: "848484") - static let neutral500 = UIColor(hexCode: "AFAFAF") - static let neutral300 = UIColor(hexCode: "CFCFCF") - static let neutral200 = UIColor(hexCode: "E9E9E9") - static let neutral100 = UIColor(hexCode: "F5F5F5") - - static let whiteMLS = UIColor(hexCode: "FFFFFF") - static let clearMLS = UIColor(hexCode: "FFFFFF", alpha: 0) - - static let error900 = UIColor(hexCode: "FF4B4B") - static let error100 = UIColor(hexCode: "FFEFEF") - - static let success = UIColor(hexCode: "15CC00") - static let redMLS = UIColor(hexCode: "FF0000") - static let overlays = UIColor(hexCode: "000000", alpha: 0.2) - - static let listMonster = UIColor(hexCode: "FFEFCE") - static let listItem = UIColor(hexCode: "F1EEFC") - static let listMap = UIColor(hexCode: "EEF9E0") - static let listNPC = UIColor(hexCode: "E0EFF9") - static let listQuest = UIColor(hexCode: "FFECEF") - - convenience init(hexCode: String, alpha: CGFloat = 1.0) { - var hexFormatted: String = hexCode.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).uppercased() - - if hexFormatted.hasPrefix("#") { - hexFormatted = String(hexFormatted.dropFirst()) - } - - assert(hexFormatted.count == 6, "Invalid hex code used.") - - var rgbValue: UInt64 = 0 - Scanner(string: hexFormatted).scanHexInt64(&rgbValue) - - self.init( - red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, - green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, - blue: CGFloat(rgbValue & 0x0000FF) / 255.0, - alpha: alpha - ) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Utills/Extension/UIFont+.swift b/MLS/Presentation/DesignSystem/DesignSystem/Utills/Extension/UIFont+.swift deleted file mode 100644 index 3873351e..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Utills/Extension/UIFont+.swift +++ /dev/null @@ -1,48 +0,0 @@ -// swiftlint:disable all - -import UIKit - -public extension UIFont { - static let h_xxxl_b = korFont(style: .bold, size: 24) - static let h_xxxl_sb = korFont(style: .semiBold, size: 24) - - static let h_xxl_b = korFont(style: .bold, size: 22) - - static let h_xl_b = korFont(style: .bold, size: 20) - static let h_xl_sb = korFont(style: .semiBold, size: 20) - static let h_xl_r = korFont(style: .regular, size: 20) - - static let b_l_r = korFont(style: .regular, size: 18) - static let b_m_r = korFont(style: .regular, size: 16) - static let b_s_sb = korFont(style: .semiBold, size: 14) - static let b_s_m = korFont(style: .medium, size: 14) - static let b_s_r = korFont(style: .regular, size: 14) - - static let sub_l_b = korFont(style: .bold, size: 18) - static let sub_l_m = korFont(style: .medium, size: 18) - static let sub_m_b = korFont(style: .bold, size: 16) - static let sub_m_sb = korFont(style: .semiBold, size: 16) - static let sub_m_m = korFont(style: .medium, size: 16) - - static let cp_s_sb = korFont(style: .semiBold, size: 14) - static let cp_s_m = korFont(style: .medium, size: 14) - static let cp_s_r = korFont(style: .regular, size: 14) - static let cp_xs_sb = korFont(style: .semiBold, size: 12) - static let cp_xs_r = korFont(style: .regular, size: 12) - - static let btn_m_b = korFont(style: .bold, size: 16) - static let btn_m_r = korFont(style: .regular, size: 16) - static let btn_s_r = korFont(style: .regular, size: 14) - static let btn_xs_r = korFont(style: .regular, size: 12) - - static func korFont(style: FontStyle, size: CGFloat) -> UIFont? { - return UIFont(name: "Pretendard\(style.rawValue)", size: size) - } - - enum FontStyle: String { - case bold = "-Bold" - case semiBold = "-SemiBold" - case medium = "-Medium" - case regular = "-Regular" - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Utills/Extension/UIImage+.swift b/MLS/Presentation/DesignSystem/DesignSystem/Utills/Extension/UIImage+.swift deleted file mode 100644 index 90112d88..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Utills/Extension/UIImage+.swift +++ /dev/null @@ -1,29 +0,0 @@ -import UIKit - -extension UIImage { - static func fromColor(_ color: UIColor?) -> UIImage { - guard let color else { return UIImage() } - let rect = CGRect(x: 0, y: 0, width: 1, height: 1) - UIGraphicsBeginImageContext(rect.size) - color.setFill() - UIRectFill(rect) - let image = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage() - UIGraphicsEndImageContext() - return image - } - - func resizeImage(to targetSize: CGSize, preserveAspectRatio: Bool = true) -> UIImage? { - let size: CGSize - if preserveAspectRatio { - let aspectRatio = min(targetSize.width / self.size.width, targetSize.height / self.size.height) - size = CGSize(width: self.size.width * aspectRatio, height: self.size.height * aspectRatio) - } else { - size = targetSize - } - - let renderer = UIGraphicsImageRenderer(size: size) - return renderer.image { _ in - self.draw(in: CGRect(origin: .zero, size: size)) - } - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Utills/FontManager.swift b/MLS/Presentation/DesignSystem/DesignSystem/Utills/FontManager.swift deleted file mode 100644 index 35c89907..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Utills/FontManager.swift +++ /dev/null @@ -1,36 +0,0 @@ -import os -import UIKit - -public class FontManager { - /// 폰트를 등록하는 메서드 - public static func registerFonts() { - let fontNames = [ - "Pretendard-Bold", - "Pretendard-SemiBold", - "Pretendard-Medium", - "Pretendard-Regular" - ] - - fontNames.forEach { fontName in - guard let fontURL = Bundle.designSystem.url(forResource: fontName, withExtension: "ttf") else { - os_log(.error, "Font file not found: \(fontName)") - return - } - - var error: Unmanaged? - CTFontManagerRegisterFontsForURL(fontURL as CFURL, .process, &error) - - if let error = error { - os_log(.error, "Error registering font: \(error.takeUnretainedValue())") - } else { - os_log(.error, "\(fontName) registered successfully") - } - } - } -} - -extension Bundle { - static var designSystem: Bundle { - return Bundle(for: FontManager.self) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Utills/NSAttributedString.swift b/MLS/Presentation/DesignSystem/DesignSystem/Utills/NSAttributedString.swift deleted file mode 100644 index a7d131bb..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystem/Utills/NSAttributedString.swift +++ /dev/null @@ -1,60 +0,0 @@ -import UIKit - -public extension NSAttributedString { - static func makeStyledString( - font: UIFont?, - text: String?, - color: UIColor? = .textColor, - alignment: NSTextAlignment = .center, - lineHeight: CGFloat = 1.17 - ) -> NSAttributedString? { - guard let text, let color, let font else { return nil } - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.minimumLineHeight = 0 - paragraphStyle.lineBreakMode = .byTruncatingTail - paragraphStyle.lineHeightMultiple = lineHeight - paragraphStyle.alignment = alignment - - let actualLineHeight = font.lineHeight * lineHeight - let baselineOffset = (actualLineHeight - font.lineHeight) / 2 - - let attributedString = NSAttributedString( - string: text, - attributes: [ - .font: font, - .foregroundColor: color, - .paragraphStyle: paragraphStyle, - .baselineOffset: baselineOffset - ] - ) - return attributedString - } - - static func makeStyledUnderlinedString( - font: UIFont?, - text: String?, - color: UIColor? = .textColor, - alignment: NSTextAlignment = .center, - lineHeight: CGFloat = 1.17, - underlineStyle: NSUnderlineStyle = .single - ) -> NSAttributedString? { - guard let text, let color, let font else { return nil } - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.minimumLineHeight = 0 - paragraphStyle.lineBreakMode = .byTruncatingTail - paragraphStyle.maximumLineHeight = font.lineHeight * lineHeight - paragraphStyle.alignment = alignment - - let attributedString = NSAttributedString( - string: text, - attributes: [ - .font: font, - .foregroundColor: color, - .paragraphStyle: paragraphStyle, - .underlineStyle: underlineStyle.rawValue - ] - ) - - return attributedString - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/.DS_Store b/MLS/Presentation/DesignSystem/DesignSystemDemo/.DS_Store deleted file mode 100644 index df07e1cb..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystemDemo/.DS_Store and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/Application/AppDelegate.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/Application/AppDelegate.swift deleted file mode 100644 index cbff7ead..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/Application/AppDelegate.swift +++ /dev/null @@ -1,50 +0,0 @@ -import UIKit - -import Core -import Data -import DesignSystem -import Domain -import DomainInterface - -@main -class AppDelegate: UIResponder, UIApplicationDelegate { - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - FontManager.registerFonts() - registerDependencies() - return true - } - - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) - } - - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {} -} - -private extension AppDelegate { - func registerDependencies() { - registerProvider() - registerUseCase() - } - - func registerProvider() { - DIContainer.register(type: NetworkProvider.self) { - NetworkProviderImpl() - } - DIContainer.register(type: SocialAuthenticatableProvider.self, name: "kakao") { - KakaoLoginProviderImpl() - } - DIContainer.register(type: SocialAuthenticatableProvider.self, name: "apple") { - AppleLoginProviderImpl() - } - } - - func registerUseCase() { - DIContainer.register(type: CheckEmptyLevelAndRoleUseCase.self) { - CheckEmptyLevelAndRoleUseCaseImpl() - } - DIContainer.register(type: CheckValidLevelUseCase.self) { - CheckValidLevelUseCaseImpl() - } - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/Application/SceneDelegate.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/Application/SceneDelegate.swift deleted file mode 100644 index 6ef7f387..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/Application/SceneDelegate.swift +++ /dev/null @@ -1,28 +0,0 @@ -import UIKit - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - - var window: UIWindow? - - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - guard let windowScene = (scene as? UIWindowScene) else { return } - window = UIWindow(windowScene: windowScene) - window?.rootViewController = UINavigationController(rootViewController: ViewController()) - window?.makeKeyAndVisible() - } - - func sceneDidDisconnect(_ scene: UIScene) { - } - - func sceneDidBecomeActive(_ scene: UIScene) { - } - - func sceneWillResignActive(_ scene: UIScene) { - } - - func sceneWillEnterForeground(_ scene: UIScene) { - } - - func sceneDidEnterBackground(_ scene: UIScene) { - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/Assets.xcassets/AppIcon.appiconset/DesignSystemLogo.png b/MLS/Presentation/DesignSystem/DesignSystemDemo/Assets.xcassets/AppIcon.appiconset/DesignSystemLogo.png deleted file mode 100644 index 5d3e8292..00000000 Binary files a/MLS/Presentation/DesignSystem/DesignSystemDemo/Assets.xcassets/AppIcon.appiconset/DesignSystemLogo.png and /dev/null differ diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/Assets.xcassets/Contents.json b/MLS/Presentation/DesignSystem/DesignSystemDemo/Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/BadgeTestController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/BadgeTestController.swift deleted file mode 100644 index 7ffd7866..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/BadgeTestController.swift +++ /dev/null @@ -1,70 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift - -final class BadgeTestController: UIViewController { - - // MARK: - Properties - var disposeBag = DisposeBag() - private let currentBadge = Badge(style: .currentQuest) - private let preBadge = Badge(style: .preQuest) - private let nextBadge = Badge(style: .nextQuest) - private let elementBadge = Badge(style: .element("불 약점")) - - init() { - super.init(nibName: nil, bundle: nil) - title = "Badge" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension BadgeTestController { - override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - } -} - -// MARK: - SetUp -private extension BadgeTestController { - func addViews() { - view.addSubview(currentBadge) - view.addSubview(preBadge) - view.addSubview(nextBadge) - view.addSubview(elementBadge) - } - - func setupConstraints() { - currentBadge.snp.makeConstraints { make in - make.center.equalToSuperview() - } - - preBadge.snp.makeConstraints { make in - make.top.equalTo(currentBadge.snp.bottom).offset(16) - make.centerX.equalToSuperview() - } - - nextBadge.snp.makeConstraints { make in - make.top.equalTo(preBadge.snp.bottom).offset(16) - make.centerX.equalToSuperview() - } - - elementBadge.snp.makeConstraints { make in - make.top.equalTo(nextBadge.snp.bottom).offset(16) - make.centerX.equalToSuperview() - } - } - - func configureUI() { - view.backgroundColor = .systemBackground - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/CardListTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/CardListTestViewController.swift deleted file mode 100644 index f993a6ba..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/CardListTestViewController.swift +++ /dev/null @@ -1,146 +0,0 @@ -import UIKit - -import BaseFeature -import DesignSystem - -import RxSwift - -final class CardListTestViewController: UIViewController { - - // MARK: - Properties - var disposeBag = DisposeBag() - private let cardList = CardList() - - private let mainTextTextField: UITextField = { - let view = UITextField() - view.placeholder = "main" - view.text = "text" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - return view - }() - - private let mainTextTextLabel: UILabel = { - let label = UILabel() - label.text = "main" - return label - }() - - private let subTextTextField: UITextField = { - let view = UITextField() - view.placeholder = "sub" - view.text = "text" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - return view - }() - - private let subTextTextLabel: UILabel = { - let label = UILabel() - label.text = "sub" - return label - }() - - private let cardListToggle = ToggleBox(text: "isBookmark") - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "CardList" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension CardListTestViewController { - override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - bind() - } -} - -// MARK: - SetUp -private extension CardListTestViewController { - func addViews() { - view.addSubview(cardList) - view.addSubview(mainTextTextLabel) - view.addSubview(mainTextTextField) - view.addSubview(subTextTextLabel) - view.addSubview(subTextTextField) - view.addSubview(cardListToggle) - } - - func setupConstraints() { - cardList.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).inset(16) - make.horizontalEdges.equalToSuperview().inset(16) - } - - mainTextTextLabel.snp.makeConstraints { make in - make.top.equalTo(cardList.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - mainTextTextField.snp.makeConstraints { make in - make.top.equalTo(mainTextTextLabel.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - - subTextTextLabel.snp.makeConstraints { make in - make.top.equalTo(mainTextTextField.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - subTextTextField.snp.makeConstraints { make in - make.top.equalTo(subTextTextLabel.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - - cardListToggle.snp.makeConstraints { make in - make.top.equalTo(subTextTextField.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - } - - func configureUI() { - view.backgroundColor = .systemBackground - ImageLoader.shared.loadImage(stringURL: "https://maplestory.io/api/gms/62/mob/2100101/render/stand") { [weak self] image in - if let image { - DispatchQueue.main.async { - self?.cardList.setImage(image: image, backgroundColor: .listMap) - } - } else { - print("fail to load image") - } - } - } - - func bind() { - mainTextTextField.rx.text - .withUnretained(self) - .subscribe { (owner, text) in - owner.cardList.mainText = text - } - .disposed(by: disposeBag) - - subTextTextField.rx.text - .withUnretained(self) - .subscribe { (owner, text) in - owner.cardList.subText = text - } - .disposed(by: disposeBag) - - cardListToggle.toggle.rx.isOn - .withUnretained(self) - .subscribe { (owner, isOn) in - owner.cardList.isIconSelected = isOn - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/CheckBoxButtonTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/CheckBoxButtonTestViewController.swift deleted file mode 100644 index 2fea084a..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/CheckBoxButtonTestViewController.swift +++ /dev/null @@ -1,211 +0,0 @@ -import UIKit - -import DesignSystem - -import RxCocoa -import RxSwift -import SnapKit - -final class CheckBoxButtonTestViewController: UIViewController { - // MARK: - Properties - private var disposeBag = DisposeBag() - private var normalButton = CheckBoxButton(style: .normal, mainTitle: nil, subTitle: nil) - private var smallButton = CheckBoxButton(style: .listSmall, mainTitle: nil, subTitle: nil) - private var mediumButton = CheckBoxButton(style: .listMedium, mainTitle: nil, subTitle: nil) - private var largeButton = CheckBoxButton(style: .listLarge, mainTitle: nil, subTitle: nil) - - private let typeSegmentControl: UISegmentedControl = { - let items = ["normal", "listSmall", "listMedium", "listLarge"] - let control = UISegmentedControl(items: items) - control.selectedSegmentIndex = 0 - return control - }() - - private let mainTitleTextField: UITextField = { - let view = UITextField() - view.placeholder = "MainTitle" - view.text = "MainTitle" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - return view - }() - - private let subTitleTextField: UITextField = { - let view = UITextField() - view.placeholder = "SubTitle" - view.text = "SubTitle" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - return view - }() - - private let mainTitleTextLabel: UILabel = { - let label = UILabel() - label.text = "MainTitle" - return label - }() - - private let subTitleTextLabel: UILabel = { - let label = UILabel() - label.text = "SubTitle" - return label - }() - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "CheckBoxButton" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - -} - -// MARK: - Life Cycle -extension CheckBoxButtonTestViewController { - override func viewDidLoad() { - super.viewDidLoad() - self.addViews() - self.setupConstraints() - self.configureUI() - self.bind() - } -} - -// MARK: - SetUp -private extension CheckBoxButtonTestViewController { - func addViews() { - view.addSubview(normalButton) - view.addSubview(smallButton) - view.addSubview(mediumButton) - view.addSubview(largeButton) - view.addSubview(typeSegmentControl) - view.addSubview(mainTitleTextLabel) - view.addSubview(mainTitleTextField) - view.addSubview(subTitleTextLabel) - view.addSubview(subTitleTextField) - } - - func setupConstraints() { - normalButton.snp.makeConstraints { make in - make.horizontalEdges.top.equalTo(view.safeAreaLayoutGuide).inset(16) - } - - smallButton.snp.makeConstraints { make in - make.horizontalEdges.top.equalTo(view.safeAreaLayoutGuide).inset(16) - } - - mediumButton.snp.makeConstraints { make in - make.horizontalEdges.top.equalTo(view.safeAreaLayoutGuide).inset(16) - } - - largeButton.snp.makeConstraints { make in - make.horizontalEdges.top.equalTo(view.safeAreaLayoutGuide).inset(16) - } - - typeSegmentControl.snp.makeConstraints { make in - make.top.equalTo(normalButton.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - mainTitleTextLabel.snp.makeConstraints { make in - make.top.equalTo(typeSegmentControl.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - mainTitleTextField.snp.makeConstraints { make in - make.top.equalTo(mainTitleTextLabel.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - - subTitleTextLabel.snp.makeConstraints { make in - make.top.equalTo(mainTitleTextField.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - subTitleTextField.snp.makeConstraints { make in - make.top.equalTo(subTitleTextLabel.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - } - - func configureUI() { - view.backgroundColor = .systemBackground - } - - func bind() { - normalButton.rx.tap - .withUnretained(self) - .subscribe { (owner, _) in - owner.normalButton.isSelected.toggle() - } - .disposed(by: disposeBag) - - smallButton.rx.tap - .withUnretained(self) - .subscribe { (owner, _) in - owner.smallButton.isSelected.toggle() - } - .disposed(by: disposeBag) - - mediumButton.rx.tap - .withUnretained(self) - .subscribe { (owner, _) in - owner.mediumButton.isSelected.toggle() - } - .disposed(by: disposeBag) - - largeButton.rx.tap - .withUnretained(self) - .subscribe { (owner, _) in - owner.largeButton.isSelected.toggle() - } - .disposed(by: disposeBag) - - typeSegmentControl.rx.selectedSegmentIndex - .withUnretained(self) - .subscribe { (owner, index) in - switch index { - case 0: - owner.normalButton.isHidden = false - owner.smallButton.isHidden = true - owner.mediumButton.isHidden = true - owner.largeButton.isHidden = true - case 1: - owner.normalButton.isHidden = true - owner.smallButton.isHidden = false - owner.mediumButton.isHidden = true - owner.largeButton.isHidden = true - case 2: - owner.normalButton.isHidden = true - owner.smallButton.isHidden = true - owner.mediumButton.isHidden = false - owner.largeButton.isHidden = true - default: - owner.normalButton.isHidden = true - owner.smallButton.isHidden = true - owner.mediumButton.isHidden = true - owner.largeButton.isHidden = false - } - } - .disposed(by: disposeBag) - - mainTitleTextField.rx.text - .withUnretained(self) - .subscribe { (owner, text) in - owner.normalButton.mainTitle = text - owner.smallButton.mainTitle = text - owner.mediumButton.mainTitle = text - owner.largeButton.mainTitle = text - } - .disposed(by: disposeBag) - - subTitleTextField.rx.text - .withUnretained(self) - .subscribe { (owner, text) in - owner.normalButton.subTitle = text - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/CollectionListTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/CollectionListTestViewController.swift deleted file mode 100644 index db3a20cc..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/CollectionListTestViewController.swift +++ /dev/null @@ -1,54 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift -import SnapKit - -final class CollectionListTestViewController: UIViewController { - - // MARK: - Properties - var disposeBag = DisposeBag() - - let collection = CollectionList() - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "CollectionList" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension CollectionListTestViewController { - override func viewDidLoad() { - super.viewDidLoad() - - self.addViews() - self.setupConstraints() - self.configureUI() - } -} - -// MARK: - SetUp -private extension CollectionListTestViewController { - func addViews() { - self.view.addSubview(collection) - } - - func setupConstraints() { - collection.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).inset(16) - make.horizontalEdges.equalToSuperview().inset(16) - } - } - - func configureUI() { - self.view.backgroundColor = .neutral200 - collection.setTitle(text: "글자수는 10글자 이후부터 생략입니다.") - collection.setSubtitle(text: "$n개") - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/CommonButtonTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/CommonButtonTestViewController.swift deleted file mode 100644 index 66247791..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/CommonButtonTestViewController.swift +++ /dev/null @@ -1,117 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift - -final class CommonButtonTestViewController: UIViewController { - - // MARK: - Properties - var disposeBag = DisposeBag() - private let commonButton = CommonButton(style: .normal, title: "NormalTitle", disabledTitle: "DisabledTitle") - private let textButton = CommonButton(style: .text, title: "NormalTitle", disabledTitle: "DisabledTitle") - private let borderButton = CommonButton(style: .border, title: "NormalTitle", disabledTitle: "DisabledTitle") - - private let typeSegmentControl: UISegmentedControl = { - let items = ["normal", "text", "border"] - let control = UISegmentedControl(items: items) - control.selectedSegmentIndex = 0 - return control - }() - - private let buttonStateToggle = ToggleBox(text: "isEnabled") - init() { - super.init(nibName: nil, bundle: nil) - self.title = "CommonButton" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension CommonButtonTestViewController { - override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - bind() - } -} - -// MARK: - SetUp -private extension CommonButtonTestViewController { - func addViews() { - view.addSubview(commonButton) - view.addSubview(textButton) - view.addSubview(borderButton) - view.addSubview(typeSegmentControl) - view.addSubview(buttonStateToggle) - } - - func setupConstraints() { - commonButton.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).inset(16) - make.horizontalEdges.equalToSuperview().inset(16) - } - - textButton.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).inset(16) - make.centerX.equalToSuperview() - } - - borderButton.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).inset(16) - make.horizontalEdges.equalToSuperview().inset(16) - } - - typeSegmentControl.snp.makeConstraints { make in - make.top.equalTo(textButton.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - buttonStateToggle.snp.makeConstraints { make in - make.top.equalTo(typeSegmentControl.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - } - - func configureUI() { - self.view.backgroundColor = .systemBackground - self.title = "CommonButton" - } - - func bind() { - self.typeSegmentControl.rx.selectedSegmentIndex - .withUnretained(self) - .subscribe { owner, selectedIndex in - switch selectedIndex { - case 0: - owner.commonButton.isHidden = false - owner.textButton.isHidden = true - owner.borderButton.isHidden = true - case 1: - owner.commonButton.isHidden = true - owner.textButton.isHidden = false - owner.borderButton.isHidden = true - default: - owner.commonButton.isHidden = true - owner.textButton.isHidden = true - owner.borderButton.isHidden = false - } - } - .disposed(by: disposeBag) - - self.buttonStateToggle.toggle.rx.isOn - .withUnretained(self) - .subscribe { (owner, isOn) in - owner.commonButton.isEnabled = isOn - owner.textButton.isEnabled = isOn - owner.borderButton.isEnabled = isOn - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/DictionaryDetailViewTestController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/DictionaryDetailViewTestController.swift deleted file mode 100644 index ad87f7a9..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/DictionaryDetailViewTestController.swift +++ /dev/null @@ -1,74 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift - -final class DictionaryDetailViewTestController: UIViewController { - - // MARK: - Properties - var disposeBag = DisposeBag() - private let first = DictionaryDetailListView() - private let second = DictionaryDetailListView() - private let third = DictionaryDetailListView() - private let forth = DictionaryDetailListView() - - init() { - super.init(nibName: nil, bundle: nil) - title = "DictionaryDetailView" - first.update(clickableMainText: "mainText", additionalText: "text", clickableSubText: "text") - second.update(mainText: "mainText", clickableSubText: "text") - third.update(mainText: "mainText", subText: "text") - forth.update(clickableMainText: "mainText", clickableSubText: "text") - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension DictionaryDetailViewTestController { - override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - } -} - -// MARK: - SetUp -private extension DictionaryDetailViewTestController { - func addViews() { - view.addSubview(first) - view.addSubview(second) - view.addSubview(third) - view.addSubview(forth) - } - - func setupConstraints() { - first.snp.makeConstraints { make in - make.horizontalEdges.centerY.equalToSuperview() - } - - second.snp.makeConstraints { make in - make.top.equalTo(first.snp.bottom).offset(16) - make.horizontalEdges.equalToSuperview() - } - - third.snp.makeConstraints { make in - make.top.equalTo(second.snp.bottom).offset(16) - make.horizontalEdges.equalToSuperview() - } - - forth.snp.makeConstraints { make in - make.top.equalTo(third.snp.bottom).offset(16) - make.horizontalEdges.equalToSuperview() - } - } - - func configureUI() { - view.backgroundColor = .systemBackground - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/DropDwonBoxTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/DropDwonBoxTestViewController.swift deleted file mode 100644 index 7ab543ec..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/DropDwonBoxTestViewController.swift +++ /dev/null @@ -1,161 +0,0 @@ -import UIKit - -import DesignSystem - -import RxCocoa -import RxSwift -import SnapKit - -final class DropDownBoxTextViewController: UIViewController { - // MARK: - Properties - private var disposeBag = DisposeBag() - private var dropDownBox = DropDownBox(menus: ["1", "2"]) - private lazy var inputBox = dropDownBox.inputBox - - private let labelTextField: UITextField = { - let view = UITextField() - view.placeholder = "label" - view.text = "label" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - return view - }() - - private let placeHolderTextField: UITextField = { - let view = UITextField() - view.placeholder = "placeHolder" - view.text = "placeHolder" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - return view - }() - - private let countTextField: UITextField = { - let view = UITextField() - view.placeholder = "count" - view.text = "4" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - view.keyboardType = .numberPad - return view - }() - - private let labelTextLabel: UILabel = { - let label = UILabel() - label.text = "label" - return label - }() - - private let placeHolderTextLabel: UILabel = { - let label = UILabel() - label.text = "placeHolder" - return label - }() - - private let countTextLabel: UILabel = { - let label = UILabel() - label.text = "메뉴개수" - return label - }() - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "DropDownBox" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - -} - -// MARK: - Life Cycle -extension DropDownBoxTextViewController { - override func viewDidLoad() { - super.viewDidLoad() - self.addViews() - self.setupConstraints() - self.configureUI() - self.bind() - } -} - -// MARK: - SetUp -private extension DropDownBoxTextViewController { - func addViews() { - view.addSubview(labelTextLabel) - view.addSubview(labelTextField) - view.addSubview(placeHolderTextLabel) - view.addSubview(placeHolderTextField) - view.addSubview(countTextLabel) - view.addSubview(countTextField) - view.addSubview(dropDownBox) - } - - func setupConstraints() { - dropDownBox.snp.makeConstraints { make in - make.horizontalEdges.top.equalTo(view.safeAreaLayoutGuide).inset(16) - } - - labelTextLabel.snp.makeConstraints { make in - make.top.equalTo(dropDownBox.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - - labelTextField.snp.makeConstraints { make in - make.top.equalTo(labelTextLabel.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - - placeHolderTextLabel.snp.makeConstraints { make in - make.top.equalTo(labelTextField.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - placeHolderTextField.snp.makeConstraints { make in - make.top.equalTo(placeHolderTextLabel.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - - countTextLabel.snp.makeConstraints { make in - make.top.equalTo(placeHolderTextField.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - countTextField.snp.makeConstraints { make in - make.top.equalTo(countTextLabel.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - } - - func configureUI() { - view.backgroundColor = .systemBackground - } - - func bind() { - labelTextField.rx.text - .withUnretained(self) - .subscribe { owner, text in - owner.inputBox.label.attributedText = .makeStyledString(font: .b_s_r, text: text, color: .neutral700, alignment: .left) - } - .disposed(by: disposeBag) - - placeHolderTextField.rx.text - .withUnretained(self) - .subscribe { owner, text in - owner.inputBox.textField.attributedPlaceholder = .makeStyledString(font: .b_m_r, text: text, color: .neutral500, alignment: .left) - } - .disposed(by: disposeBag) - - countTextField.rx.text - .withUnretained(self) - .subscribe { (owner, count) in - guard let count = Int(count ?? "") else { return } - owner.dropDownBox.menus = [] - for index in 1...count { - owner.dropDownBox.menus.append("메뉴\(index)") - } - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/ErrorMessageTextViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/ErrorMessageTextViewController.swift deleted file mode 100644 index a3e3b278..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/ErrorMessageTextViewController.swift +++ /dev/null @@ -1,88 +0,0 @@ -import UIKit - -import DesignSystem - -import RxCocoa -import RxSwift -import SnapKit - -final class ErrorMessageTextViewController: UIViewController { - // MARK: - Properties - private var disposeBag = DisposeBag() - private var errorMessage = ErrorMessage(message: nil) - - private let messageTextField: UITextField = { - let view = UITextField() - view.placeholder = "message" - view.text = "error" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - return view - }() - - private let messageTextLabel: UILabel = { - let label = UILabel() - label.text = "message" - return label - }() - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "ErrorMessage" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - -} - -// MARK: - Life Cycle -extension ErrorMessageTextViewController { - override func viewDidLoad() { - super.viewDidLoad() - self.addViews() - self.setupConstraints() - self.configureUI() - self.bind() - } -} - -// MARK: - SetUp -private extension ErrorMessageTextViewController { - func addViews() { - view.addSubview(errorMessage) - view.addSubview(messageTextLabel) - view.addSubview(messageTextField) - } - - func setupConstraints() { - errorMessage.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).inset(16) - make.centerX.equalToSuperview() - } - - messageTextLabel.snp.makeConstraints { make in - make.top.equalTo(errorMessage.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - messageTextField.snp.makeConstraints { make in - make.top.equalTo(messageTextLabel.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - } - - func configureUI() { - view.backgroundColor = .systemBackground - } - - func bind() { - messageTextField.rx.text - .withUnretained(self) - .subscribe { owner, message in - owner.errorMessage.label.attributedText = .makeStyledString(font: .b_s_r, text: message, color: .error900) - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/GuideAlertTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/GuideAlertTestViewController.swift deleted file mode 100644 index 60431590..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/GuideAlertTestViewController.swift +++ /dev/null @@ -1,105 +0,0 @@ -import UIKit - -import BaseFeature -import DesignSystem - -import RxSwift -import SnapKit - -final class GuideAlertTestViewController: UIViewController { - // MARK: - Properties - var disposeBag = DisposeBag() - - let oneButton = CommonButton(style: .normal, title: "oneButtonModal", disabledTitle: nil) - let twoButton = CommonButton(style: .normal, title: "twoButtonModal", disabledTitle: nil) - let logoutButton = CommonButton(style: .normal, title: "logoutButtonModal", disabledTitle: nil) - let withdrawButton = CommonButton(style: .normal, title: "withdrawButtonModal", disabledTitle: nil) - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "GuideAlert" - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension GuideAlertTestViewController { - override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - bind() - } -} - -// MARK: - SetUp -private extension GuideAlertTestViewController { - func addViews() { - view.addSubview(oneButton) - view.addSubview(twoButton) - view.addSubview(logoutButton) - view.addSubview(withdrawButton) - } - - func setupConstraints() { - oneButton.snp.makeConstraints { make in - make.bottom.equalToSuperview().inset(16) - make.centerX.equalToSuperview() - } - - twoButton.snp.makeConstraints { make in - make.bottom.equalTo(oneButton.snp.top).offset(-16) - make.centerX.equalToSuperview() - } - - logoutButton.snp.makeConstraints { make in - make.bottom.equalTo(twoButton.snp.top).offset(-16) - make.centerX.equalToSuperview() - } - - withdrawButton.snp.makeConstraints { make in - make.bottom.equalTo(logoutButton.snp.top).offset(-16) - make.centerX.equalToSuperview() - } - } - - func configureUI() { - view.backgroundColor = .systemBackground - } - - func bind() { - oneButton.rx.tap - .withUnretained(self) - .subscribe { _, _ in - GuideAlertFactory.show(mainText: "버튼 하나", ctaText: "확인", ctaAction: {}) - } - .disposed(by: disposeBag) - - twoButton.rx.tap - .withUnretained(self) - .subscribe { _, _ in - GuideAlertFactory.show(mainText: "버튼 두개", ctaText: "확인", cancelText: "취소", ctaAction: {}) - } - .disposed(by: disposeBag) - - logoutButton.rx.tap - .withUnretained(self) - .subscribe { _, _ in - GuideAlertFactory.showAuthAlert(type: .logout, ctaAction: {}) - } - .disposed(by: disposeBag) - - withdrawButton.rx.tap - .withUnretained(self) - .subscribe { _, _ in - GuideAlertFactory.showAuthAlert(type: .withdraw, ctaAction: {}) - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/HeaderTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/HeaderTestViewController.swift deleted file mode 100644 index 8f23db73..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/HeaderTestViewController.swift +++ /dev/null @@ -1,155 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift - -final class HeaderTestViewController: UIViewController { - - // MARK: - Properties - var disposeBag = DisposeBag() - private let mainHeader = Header(style: .main, title: "메인") - private let filterHeader = Header(style: .filter, title: "필터") - - private let typeSegmentControl: UISegmentedControl = { - let items = ["main", "filter"] - let control = UISegmentedControl(items: items) - control.selectedSegmentIndex = 0 - return control - }() - - private let mainTextTextField: UITextField = { - let view = UITextField() - view.placeholder = "text" - view.text = "text" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - return view - }() - - private let mainTextTextLabel: UILabel = { - let label = UILabel() - label.text = "text" - return label - }() - - private let filterTextTextField: UITextField = { - let view = UITextField() - view.placeholder = "text" - view.text = "text" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - return view - }() - - private let filterTextTextLabel: UILabel = { - let label = UILabel() - label.text = "text" - return label - }() - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "Header" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension HeaderTestViewController { - override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - bind() - } -} - -// MARK: - SetUp -private extension HeaderTestViewController { - func addViews() { - view.addSubview(mainHeader) - view.addSubview(filterHeader) - view.addSubview(typeSegmentControl) - view.addSubview(mainTextTextLabel) - view.addSubview(mainTextTextField) - view.addSubview(filterTextTextLabel) - view.addSubview(filterTextTextField) - } - - func setupConstraints() { - mainHeader.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).inset(16) - make.horizontalEdges.equalToSuperview().inset(16) - } - - filterHeader.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).inset(16) - make.horizontalEdges.equalToSuperview().inset(16) - } - - typeSegmentControl.snp.makeConstraints { make in - make.top.equalTo(mainHeader.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - mainTextTextLabel.snp.makeConstraints { make in - make.top.equalTo(typeSegmentControl.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - mainTextTextField.snp.makeConstraints { make in - make.top.equalTo(mainTextTextLabel.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - - filterTextTextLabel.snp.makeConstraints { make in - make.top.equalTo(mainTextTextField.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - filterTextTextField.snp.makeConstraints { make in - make.top.equalTo(filterTextTextLabel.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - } - - func configureUI() { - self.view.backgroundColor = .systemBackground - } - - func bind() { - typeSegmentControl.rx.selectedSegmentIndex - .withUnretained(self) - .subscribe { owner, selectedIndex in - switch selectedIndex { - case 0: - owner.mainHeader.isHidden = false - owner.filterHeader.isHidden = true - default: - owner.mainHeader.isHidden = true - owner.filterHeader.isHidden = false - } - } - .disposed(by: disposeBag) - - mainTextTextField.rx.text - .withUnretained(self) - .subscribe { (owner, text) in - owner.mainHeader.titleLabel.attributedText = .makeStyledString(font: owner.mainHeader.style.titleFont, text: text) - } - .disposed(by: disposeBag) - - filterTextTextField.rx.text - .withUnretained(self) - .subscribe { (owner, text) in - owner.filterHeader.titleLabel.attributedText = .makeStyledString(font: owner.filterHeader.style.titleFont, text: text) - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/InputBoxTextViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/InputBoxTextViewController.swift deleted file mode 100644 index ab32c630..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/InputBoxTextViewController.swift +++ /dev/null @@ -1,179 +0,0 @@ -import UIKit - -import DesignSystem - -import RxCocoa -import RxSwift -import SnapKit - -final class InputBoxTextViewController: UIViewController { - // MARK: - Properties - private var disposeBag = DisposeBag() - private var inputBox = InputBox(label: "label", placeHodler: "placeHolder") - - private let typeSegmentControl: UISegmentedControl = { - let items = ["edit", "error"] - let control = UISegmentedControl(items: items) - control.selectedSegmentIndex = 0 - return control - }() - - private let labelTextField: UITextField = { - let view = UITextField() - view.placeholder = "label" - view.text = "label" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - return view - }() - - private let placeHolderTextField: UITextField = { - let view = UITextField() - view.placeholder = "placeHolder" - view.text = "placeHolder" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - return view - }() - - private let textTextField: UITextField = { - let view = UITextField() - view.placeholder = "text" - view.text = "text" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - return view - }() - - private let labelTextLabel: UILabel = { - let label = UILabel() - label.text = "label" - return label - }() - - private let placeHolderTextLabel: UILabel = { - let label = UILabel() - label.text = "placeHolder" - return label - }() - - private let textTextLabel: UILabel = { - let label = UILabel() - label.text = "text" - return label - }() - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "InputBox" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - -} - -// MARK: - Life Cycle -extension InputBoxTextViewController { - override func viewDidLoad() { - super.viewDidLoad() - self.addViews() - self.setupConstraints() - self.configureUI() - self.bind() - } -} - -// MARK: - SetUp -private extension InputBoxTextViewController { - func addViews() { - view.addSubview(inputBox) - view.addSubview(typeSegmentControl) - view.addSubview(labelTextLabel) - view.addSubview(labelTextField) - view.addSubview(placeHolderTextLabel) - view.addSubview(placeHolderTextField) - view.addSubview(textTextLabel) - view.addSubview(textTextField) - } - - func setupConstraints() { - inputBox.snp.makeConstraints { make in - make.horizontalEdges.top.equalTo(view.safeAreaLayoutGuide).inset(16) - } - - typeSegmentControl.snp.makeConstraints { make in - make.top.equalTo(inputBox.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - labelTextLabel.snp.makeConstraints { make in - make.top.equalTo(typeSegmentControl.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - labelTextField.snp.makeConstraints { make in - make.top.equalTo(labelTextLabel.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - - placeHolderTextLabel.snp.makeConstraints { make in - make.top.equalTo(labelTextField.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - placeHolderTextField.snp.makeConstraints { make in - make.top.equalTo(placeHolderTextLabel.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - - textTextLabel.snp.makeConstraints { make in - make.top.equalTo(placeHolderTextField.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - textTextField.snp.makeConstraints { make in - make.top.equalTo(textTextLabel.snp.bottom).offset(10) - make.horizontalEdges.equalToSuperview().inset(16) - } - } - - func configureUI() { - view.backgroundColor = .systemBackground - } - - func bind() { - typeSegmentControl.rx.selectedSegmentIndex - .withUnretained(self) - .subscribe { (owner, index) in - if index == 0 { - owner.inputBox.setType(type: .edit) - } else { - owner.inputBox.setType(type: .error) - } - } - .disposed(by: disposeBag) - - labelTextField.rx.text - .withUnretained(self) - .subscribe { owner, text in - owner.inputBox.label.attributedText = .makeStyledString(font: .b_s_r, text: text, color: .neutral700, alignment: .left) - } - .disposed(by: disposeBag) - - placeHolderTextField.rx.text - .withUnretained(self) - .subscribe { owner, text in - owner.inputBox.textField.attributedPlaceholder = .makeStyledString(font: .b_m_r, text: text, color: .neutral500, alignment: .left) - } - .disposed(by: disposeBag) - - textTextField.rx.text - .withUnretained(self) - .subscribe { (owner, text) in - owner.inputBox.textField.attributedText = .makeStyledString(font: .b_m_r, text: text, alignment: .left) - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/NavigationBarTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/NavigationBarTestViewController.swift deleted file mode 100644 index b6e76241..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/NavigationBarTestViewController.swift +++ /dev/null @@ -1,99 +0,0 @@ -import UIKit - -import DesignSystem - -import RxCocoa -import RxSwift -import SnapKit - -final class NavigationBarTestViewController: UIViewController { - - // MARK: - Properties - var disposeBag = DisposeBag() - - private let headerView1: NavigationBar = { - let view = NavigationBar(type: .withUnderLine("null")) - return view - }() - - private let headerView2: NavigationBar = { - let view = NavigationBar(type: .arrowRightLeft) - return view - }() - - private let headerView3: NavigationBar = { - let view = NavigationBar(type: .arrowLeft) - return view - }() - - private let headerView4: NavigationBar = { - let view = NavigationBar(type: .withString("null")) - return view - }() - - private let headerView5: NavigationBar = { - let view = NavigationBar(type: .collection("컬렉션 이름")) - return view - }() - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "NavigationBar" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension NavigationBarTestViewController { - override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - } -} - -// MARK: - SetUp -private extension NavigationBarTestViewController { - func addViews() { - view.addSubview(headerView1) - view.addSubview(headerView2) - view.addSubview(headerView3) - view.addSubview(headerView4) - view.addSubview(headerView5) - } - - func setupConstraints() { - headerView1.snp.makeConstraints { make in - make.top.horizontalEdges.equalTo(view.safeAreaLayoutGuide) - } - - headerView2.snp.makeConstraints { make in - make.top.equalTo(headerView1.snp.bottom).offset(16) - make.horizontalEdges.equalTo(view.safeAreaLayoutGuide) - } - - headerView3.snp.makeConstraints { make in - make.top.equalTo(headerView2.snp.bottom).offset(16) - make.horizontalEdges.equalTo(view.safeAreaLayoutGuide) - } - - headerView4.snp.makeConstraints { make in - make.top.equalTo(headerView3.snp.bottom).offset(16) - make.horizontalEdges.equalTo(view.safeAreaLayoutGuide) - } - - headerView5.snp.makeConstraints { make in - make.top.equalTo(headerView4.snp.bottom).offset(16) - make.horizontalEdges.equalTo(view.safeAreaLayoutGuide) - } - } - - func configureUI() { - view.backgroundColor = .systemBackground - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/SearchBarTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/SearchBarTestViewController.swift deleted file mode 100644 index bbfd740b..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/SearchBarTestViewController.swift +++ /dev/null @@ -1,61 +0,0 @@ -import UIKit - -import DesignSystem - -import RxCocoa -import RxSwift -import SnapKit - -final class SearchBarTestViewController: UIViewController { - - // MARK: - Properties - var disposeBag = DisposeBag() - - let searchBar: SearchBar = SearchBar() - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "SearchBar" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension SearchBarTestViewController { - override func viewDidLoad() { - super.viewDidLoad() - - self.addViews() - self.setupConstraints() - self.configureUI() - } -} - -// MARK: - SetUp -private extension SearchBarTestViewController { - func addViews() { - view.addSubview(searchBar) - } - - func setupConstraints() { - searchBar.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).inset(16) - make.horizontalEdges.equalToSuperview() - } - } - - func configureUI() { - self.view.backgroundColor = .systemBackground - let tapGesture = UITapGestureRecognizer() - view.addGestureRecognizer(tapGesture) - - tapGesture.rx.event - .bind { [weak self] _ in - self?.view.endEditing(true) - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/SnackBarTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/SnackBarTestViewController.swift deleted file mode 100644 index dd040d12..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/SnackBarTestViewController.swift +++ /dev/null @@ -1,61 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift -import SnapKit - -final class SnackBarTestViewController: UIViewController { - - // MARK: - Properties - var disposeBag = DisposeBag() - - let normalSnackBar = SnackBar(type: .normal, image: .appleLogo, imageBackgroundColor: .listNPC, text: "제목제목", buttonText: "되돌리기", buttonAction: nil) - - let deleteSnackBar = SnackBar(type: .delete, image: DesignSystemAsset.image(named: "testImage"), imageBackgroundColor: .listNPC, text: "제목제목", buttonText: "되돌리기", buttonAction: nil) - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "SnackBar" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension SnackBarTestViewController { - override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - } -} - -// MARK: - SetUp -private extension SnackBarTestViewController { - func addViews() { - view.addSubview(normalSnackBar) - view.addSubview(deleteSnackBar) - } - - func setupConstraints() { - normalSnackBar.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).inset(16) - make.centerX.equalToSuperview() - } - - deleteSnackBar.snp.makeConstraints { make in - make.top.equalTo(normalSnackBar.snp.bottom).offset(16) - make.centerX.equalToSuperview() - } - } - - func configureUI() { - self.view.backgroundColor = .systemBackground - - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/StepIndicatorTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/StepIndicatorTestViewController.swift deleted file mode 100644 index e2a5e294..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/StepIndicatorTestViewController.swift +++ /dev/null @@ -1,70 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift -import SnapKit - -final class StepIndicatorTestViewController: UIViewController { - - // MARK: - Properties - var disposeBag = DisposeBag() - - let firstIndicator = StepIndicator(circleCount: 3) - let secondIndicator = StepIndicator(circleCount: 3) - let thirdIndicator = StepIndicator(circleCount: 3) - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "StepIndicator" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension StepIndicatorTestViewController { - override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - } -} - -// MARK: - SetUp -private extension StepIndicatorTestViewController { - func addViews() { - view.addSubview(firstIndicator) - view.addSubview(secondIndicator) - view.addSubview(thirdIndicator) - } - - func setupConstraints() { - firstIndicator.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).offset(16) - make.centerX.equalToSuperview() - } - - secondIndicator.snp.makeConstraints { make in - make.top.equalTo(firstIndicator.snp.bottom).offset(16) - make.centerX.equalToSuperview() - } - - thirdIndicator.snp.makeConstraints { make in - make.top.equalTo(secondIndicator.snp.bottom).offset(16) - make.centerX.equalToSuperview() - } - } - - func configureUI() { - view.backgroundColor = .systemBackground - - firstIndicator.selectIndicator(index: 0) - secondIndicator.selectIndicator(index: 1) - thirdIndicator.selectIndicator(index: 2) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/TagChipTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/TagChipTestViewController.swift deleted file mode 100644 index 8721e54e..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/TagChipTestViewController.swift +++ /dev/null @@ -1,155 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift - -final class TagChipTestViewController: UIViewController { - - // MARK: - Properties - var disposeBag = DisposeBag() - private let normalTagChip = TagChip(style: .normal, text: "text") - private let searchTagChip = TagChip(style: .search, text: "text") - - private let typeSegmentControl: UISegmentedControl = { - let items = ["normal", "search"] - let control = UISegmentedControl(items: items) - control.selectedSegmentIndex = 0 - return control - }() - - private let normalTextLabel: UILabel = { - let label = UILabel() - label.text = "normal" - return label - }() - - private let normalTextField: UITextField = { - let view = UITextField() - view.placeholder = "text" - view.text = "text" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - return view - }() - - private let searchTextLabel: UILabel = { - let label = UILabel() - label.text = "search" - return label - }() - - private let searchTextField: UITextField = { - let view = UITextField() - view.placeholder = "text" - view.text = "text" - view.layer.borderColor = UIColor.gray.cgColor - view.layer.borderWidth = 1 - return view - }() - - init() { - super.init(nibName: nil, bundle: nil) - title = "TagChip" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension TagChipTestViewController { - override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - bind() - } -} - -// MARK: - SetUp -private extension TagChipTestViewController { - func addViews() { - view.addSubview(normalTagChip) - view.addSubview(searchTagChip) - view.addSubview(typeSegmentControl) - view.addSubview(normalTextLabel) - view.addSubview(normalTextField) - view.addSubview(searchTextLabel) - view.addSubview(searchTextField) - } - - func setupConstraints() { - normalTagChip.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).inset(16) - make.centerX.equalToSuperview() - } - - searchTagChip.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).inset(16) - make.centerX.equalToSuperview() - } - - typeSegmentControl.snp.makeConstraints { make in - make.top.equalTo(normalTagChip.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - normalTextLabel.snp.makeConstraints { make in - make.top.equalTo(typeSegmentControl.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - normalTextField.snp.makeConstraints { make in - make.top.equalTo(normalTextLabel.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - searchTextLabel.snp.makeConstraints { make in - make.top.equalTo(normalTextField.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - - searchTextField.snp.makeConstraints { make in - make.top.equalTo(searchTextLabel.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - } - - func configureUI() { - view.backgroundColor = .systemBackground - } - - func bind() { - typeSegmentControl.rx.selectedSegmentIndex - .withUnretained(self) - .subscribe { owner, selectedIndex in - switch selectedIndex { - case 0: - owner.normalTagChip.isHidden = false - owner.searchTagChip.isHidden = true - default: - owner.normalTagChip.isHidden = true - owner.searchTagChip.isHidden = false - } - } - .disposed(by: disposeBag) - - normalTextField.rx.text.orEmpty - .withUnretained(self) - .subscribe { (owner, text) in - owner.normalTagChip.text = text - } - .disposed(by: disposeBag) - - searchTextField.rx.text.orEmpty - .withUnretained(self) - .subscribe { (owner, text) in - owner.searchTagChip.text = text - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/TapButtonTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/TapButtonTestViewController.swift deleted file mode 100644 index b3eb12e0..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/TapButtonTestViewController.swift +++ /dev/null @@ -1,68 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift - -final class TapButtonTestViewController: UIViewController { - - // MARK: - Properties - var disposeBag = DisposeBag() - private let tapButton = TapButton(text: "text") - - private let buttonStateToggle = ToggleBox(text: "isSelected") - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "TapButton" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension TapButtonTestViewController { - override func viewDidLoad() { - super.viewDidLoad() - - addViews() - setupConstraints() - configureUI() - bind() - } -} - -// MARK: - SetUp -private extension TapButtonTestViewController { - func addViews() { - view.addSubview(tapButton) - view.addSubview(buttonStateToggle) - } - - func setupConstraints() { - tapButton.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).inset(16) - make.centerX.equalToSuperview() - } - - buttonStateToggle.snp.makeConstraints { make in - make.top.equalTo(tapButton.snp.bottom).offset(30) - make.horizontalEdges.equalToSuperview().inset(16) - } - } - - func configureUI() { - view.backgroundColor = .systemBackground - } - - func bind() { - buttonStateToggle.toggle.rx.isOn - .withUnretained(self) - .subscribe { (owner, isOn) in - owner.tapButton.isSelected = isOn - } - .disposed(by: disposeBag) - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/TextButtonTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/TextButtonTestViewController.swift deleted file mode 100644 index 4b430cb1..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/TextButtonTestViewController.swift +++ /dev/null @@ -1,47 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift -import SnapKit - -final class TextButtonTestViewController: UIViewController { - - // MARK: - Properties - var disposeBag = DisposeBag() - - let button = TextButton() - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "TextButton" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension TextButtonTestViewController { - override func viewDidLoad() { - super.viewDidLoad() - - view.backgroundColor = .whiteMLS - addViews() - setupConstraints() - } -} - -// MARK: - SetUp -private extension TextButtonTestViewController { - func addViews() { - view.addSubview(button) - } - - func setupConstraints() { - button.snp.makeConstraints { make in - make.center.equalToSuperview() - } - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/ToastMakerTestViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/ToastMakerTestViewController.swift deleted file mode 100644 index d94be686..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ComponentsTest/ToastMakerTestViewController.swift +++ /dev/null @@ -1,47 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift -import SnapKit - -final class ToastMakerTestViewController: UIViewController { - - // MARK: - Properties - var disposeBag = DisposeBag() - - let toast = Toast(message: "토스트 테스트") - - init() { - super.init(nibName: nil, bundle: nil) - self.title = "Toast" - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Life Cycle -extension ToastMakerTestViewController { - override func viewDidLoad() { - super.viewDidLoad() - - view.backgroundColor = .whiteMLS - addViews() - setupConstraints() - } -} - -// MARK: - SetUp -private extension ToastMakerTestViewController { - func addViews() { - view.addSubview(toast) - } - - func setupConstraints() { - toast.snp.makeConstraints { make in - make.center.equalToSuperview() - } - } -} diff --git a/MLS/Presentation/DesignSystem/DesignSystemDemo/ViewController.swift b/MLS/Presentation/DesignSystem/DesignSystemDemo/ViewController.swift deleted file mode 100644 index 14d37034..00000000 --- a/MLS/Presentation/DesignSystem/DesignSystemDemo/ViewController.swift +++ /dev/null @@ -1,112 +0,0 @@ -import UIKit - -import Core -import DesignSystem - -import RxCocoa -import RxSwift -import SnapKit - -class ViewController: UIViewController { - let tableView: UITableView = { - let view = UITableView(frame: .zero, style: .plain) - return view - }() - - let bottomTabBarViewController = BottomTabBarController(viewControllers: [ - CheckBoxButtonTestViewController(), - NavigationBarTestViewController(), - CommonButtonTestViewController(), - InputBoxTextViewController() - ], initialIndex: 1) - - lazy var componentViews: [UIViewController] = [ - CheckBoxButtonTestViewController(), - NavigationBarTestViewController(), - CommonButtonTestViewController(), - InputBoxTextViewController(), - DropDownBoxTextViewController(), - ToastMakerTestViewController(), - ErrorMessageTextViewController(), - StepIndicatorTestViewController(), - HeaderTestViewController(), - TapButtonTestViewController(), - TagChipTestViewController(), - GuideAlertTestViewController(), - CardListTestViewController(), - bottomTabBarViewController, - SearchBarTestViewController(), - CollectionListTestViewController(), - SnackBarTestViewController(), - BadgeTestController(), - DictionaryDetailViewTestController(), - TextButtonTestViewController() - ] - - override func viewDidLoad() { - super.viewDidLoad() - self.view.backgroundColor = .systemBackground - tableView.dataSource = self - tableView.delegate = self - navigationItem.title = "MLS Design System" - bottomTabBarViewController.title = "BottomTabBar" - - view.addSubview(tableView) - tableView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -extension ViewController: UITableViewDataSource, UITableViewDelegate { - func numberOfSections(in tableView: UITableView) -> Int { - return 2 - } - - func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { - switch section { - case 0: - return "Components" - default: - return nil - } - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - switch section { - case 0: - return componentViews.count - default: - return 0 - } - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = UITableViewCell() - let viewController: UIViewController - - switch indexPath.section { - case 0: - viewController = componentViews[indexPath.row] - default: - return cell - } - - cell.textLabel?.text = viewController.title - cell.selectionStyle = .none - return cell - } - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let nextController: UIViewController - - switch indexPath.section { - case 0: - nextController = componentViews[indexPath.row] - default: - return - } - - navigationController?.pushViewController(nextController, animated: true) - } -} diff --git a/MLS/Presentation/DictionaryFeature/.DS_Store b/MLS/Presentation/DictionaryFeature/.DS_Store deleted file mode 100644 index 69668d56..00000000 Binary files a/MLS/Presentation/DictionaryFeature/.DS_Store and /dev/null differ diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature.xcodeproj/project.pbxproj b/MLS/Presentation/DictionaryFeature/DictionaryFeature.xcodeproj/project.pbxproj deleted file mode 100644 index cd987481..00000000 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1048 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXBuildFile section */ - 081D9DC12DF80DF6004F850D /* BaseFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DC02DF80DF6004F850D /* BaseFeature.framework */; }; - 081D9DC52DF80E15004F850D /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DC42DF80E15004F850D /* DomainInterface.framework */; }; - 081D9DC82DF80E1A004F850D /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DC72DF80E1A004F850D /* DesignSystem.framework */; }; - 081D9DCA2DF80E27004F850D /* DictionaryFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9D762DF80CB1004F850D /* DictionaryFeatureInterface.framework */; }; - 081D9DD02DF80E5C004F850D /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 081D9DCF2DF80E5C004F850D /* RxCocoa */; }; - 081D9DD22DF80E5C004F850D /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 081D9DD12DF80E5C004F850D /* RxRelay */; }; - 081D9DD42DF80E5C004F850D /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 081D9DD32DF80E5C004F850D /* RxSwift */; }; - 081D9DD72DF80E69004F850D /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 081D9DD62DF80E69004F850D /* SnapKit */; }; - 081D9DDA2DF80E7E004F850D /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 081D9DD92DF80E7E004F850D /* ReactorKit */; }; - 081D9DDC2DF80EBB004F850D /* BaseFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DDB2DF80EBB004F850D /* BaseFeature.framework */; }; - 081D9DE02DF80EC2004F850D /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DDF2DF80EC2004F850D /* DomainInterface.framework */; }; - 081D9DE22DF80F1D004F850D /* DictionaryFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9D762DF80CB1004F850D /* DictionaryFeatureInterface.framework */; }; - 081D9DE32DF80F1D004F850D /* DictionaryFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9D762DF80CB1004F850D /* DictionaryFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 081D9DE72DF80F20004F850D /* DictionaryFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9C192DF80854004F850D /* DictionaryFeature.framework */; }; - 081D9DE82DF80F20004F850D /* DictionaryFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9C192DF80854004F850D /* DictionaryFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 081D9DEC2DF80F23004F850D /* BaseFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DEB2DF80F23004F850D /* BaseFeature.framework */; }; - 081D9DED2DF80F23004F850D /* BaseFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DEB2DF80F23004F850D /* BaseFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 081D9DEF2DF80F2B004F850D /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DEE2DF80F2B004F850D /* Core.framework */; }; - 081D9DF02DF80F2B004F850D /* Core.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DEE2DF80F2B004F850D /* Core.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 081D9DF22DF80F30004F850D /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DF12DF80F30004F850D /* Data.framework */; }; - 081D9DF32DF80F30004F850D /* Data.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DF12DF80F30004F850D /* Data.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 081D9DF52DF80F34004F850D /* DataMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DF42DF80F34004F850D /* DataMock.framework */; }; - 081D9DF62DF80F34004F850D /* DataMock.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DF42DF80F34004F850D /* DataMock.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 081D9DF82DF80F3C004F850D /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DF72DF80F3C004F850D /* DesignSystem.framework */; }; - 081D9DF92DF80F3C004F850D /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9DF72DF80F3C004F850D /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 081D9DFB2DF80F44004F850D /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 081D9DFA2DF80F44004F850D /* RxCocoa */; }; - 081D9DFD2DF80F47004F850D /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 081D9DFC2DF80F47004F850D /* RxSwift */; }; - 081D9DFF2DF80F4B004F850D /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 081D9DFE2DF80F4B004F850D /* RxRelay */; }; - 081D9E012DF80F53004F850D /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 081D9E002DF80F53004F850D /* SnapKit */; }; - 081D9E032DF80F58004F850D /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 081D9E022DF80F58004F850D /* ReactorKit */; }; - 081D9E052DF80F63004F850D /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9E042DF80F63004F850D /* DomainInterface.framework */; }; - 081D9E062DF80F63004F850D /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9E042DF80F63004F850D /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 081D9E082DF80F68004F850D /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9E072DF80F68004F850D /* Domain.framework */; }; - 081D9E092DF80F68004F850D /* Domain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 081D9E072DF80F68004F850D /* Domain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 772DC9972E55CA93005F92E3 /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 772DC9962E55CA93005F92E3 /* RxKeyboard */; }; - 773E8D462E7AF4A70094714D /* MyPageFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 773E8D452E7AF4A70094714D /* MyPageFeatureInterface.framework */; }; - 773E8D4A2E7AF4D30094714D /* MyPageFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 773E8D492E7AF4D30094714D /* MyPageFeature.framework */; }; - 773E8D4B2E7AF4D30094714D /* MyPageFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 773E8D492E7AF4D30094714D /* MyPageFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 773E8D4D2E7AF4F00094714D /* MyPageFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 773E8D4C2E7AF4F00094714D /* MyPageFeature.framework */; }; - 773E8D4E2E7AF4F00094714D /* MyPageFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 773E8D4C2E7AF4F00094714D /* MyPageFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 773E8D502E7AF4F60094714D /* MyPageFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 773E8D4F2E7AF4F60094714D /* MyPageFeatureInterface.framework */; }; - 773E8D512E7AF4F60094714D /* MyPageFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 773E8D4F2E7AF4F60094714D /* MyPageFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 779A49132E1AD4BC00ABDE4F /* BookmarkFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 779A49122E1AD4BC00ABDE4F /* BookmarkFeature.framework */; }; - 779A49142E1AD4BC00ABDE4F /* BookmarkFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 779A49122E1AD4BC00ABDE4F /* BookmarkFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C755B82E4B70C30081D80F /* AuthFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C755B62E4B70C30081D80F /* AuthFeature.framework */; }; - 77C755B92E4B70C30081D80F /* AuthFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77C755B62E4B70C30081D80F /* AuthFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C755BA2E4B70C30081D80F /* AuthFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C755B72E4B70C30081D80F /* AuthFeatureInterface.framework */; }; - 77C755BB2E4B70C30081D80F /* AuthFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77C755B72E4B70C30081D80F /* AuthFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77C755C12E4B917F0081D80F /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 77C755C02E4B917F0081D80F /* RxKeyboard */; }; - 77C7F4E52ED88D14009559E1 /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 77C7F4E42ED88D14009559E1 /* RxGesture */; }; - 77C974732E376124007198DA /* BookmarkFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C974722E376124007198DA /* BookmarkFeatureInterface.framework */; }; - 77C97DAB2E37D4AC007198DA /* AuthFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97DAA2E37D4AC007198DA /* AuthFeature.framework */; }; - 77DBD8BF2E1AD0C600529428 /* BookmarkFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77DBD8BE2E1AD0C600529428 /* BookmarkFeatureInterface.framework */; }; - 77DBD8C02E1AD0C600529428 /* BookmarkFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77DBD8BE2E1AD0C600529428 /* BookmarkFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77FEEF1D2EC5B0870023197A /* AuthFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77FEEF1C2EC5B0870023197A /* AuthFeatureInterface.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 081D9DCC2DF80E27004F850D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 081D9C102DF80854004F850D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 081D9D752DF80CB1004F850D; - remoteInfo = DictionaryFeatureInterface; - }; - 081D9DE42DF80F1D004F850D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 081D9C102DF80854004F850D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 081D9D752DF80CB1004F850D; - remoteInfo = DictionaryFeatureInterface; - }; - 081D9DE92DF80F20004F850D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 081D9C102DF80854004F850D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 081D9C182DF80854004F850D; - remoteInfo = DictionaryFeature; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 081D9DE62DF80F1D004F850D /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 779A49142E1AD4BC00ABDE4F /* BookmarkFeature.framework in Embed Frameworks */, - 081D9DF92DF80F3C004F850D /* DesignSystem.framework in Embed Frameworks */, - 081D9DE32DF80F1D004F850D /* DictionaryFeatureInterface.framework in Embed Frameworks */, - 773E8D4E2E7AF4F00094714D /* MyPageFeature.framework in Embed Frameworks */, - 081D9DED2DF80F23004F850D /* BaseFeature.framework in Embed Frameworks */, - 081D9E092DF80F68004F850D /* Domain.framework in Embed Frameworks */, - 081D9DE82DF80F20004F850D /* DictionaryFeature.framework in Embed Frameworks */, - 773E8D4B2E7AF4D30094714D /* MyPageFeature.framework in Embed Frameworks */, - 773E8D512E7AF4F60094714D /* MyPageFeatureInterface.framework in Embed Frameworks */, - 77DBD8C02E1AD0C600529428 /* BookmarkFeatureInterface.framework in Embed Frameworks */, - 081D9DF32DF80F30004F850D /* Data.framework in Embed Frameworks */, - 081D9DF62DF80F34004F850D /* DataMock.framework in Embed Frameworks */, - 77C755BB2E4B70C30081D80F /* AuthFeatureInterface.framework in Embed Frameworks */, - 081D9E062DF80F63004F850D /* DomainInterface.framework in Embed Frameworks */, - 081D9DF02DF80F2B004F850D /* Core.framework in Embed Frameworks */, - 77C755B92E4B70C30081D80F /* AuthFeature.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 081D9C192DF80854004F850D /* DictionaryFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DictionaryFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 081D9D762DF80CB1004F850D /* DictionaryFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DictionaryFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 081D9DAA2DF80DBD004F850D /* DictionaryFeatureDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DictionaryFeatureDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 081D9DC02DF80DF6004F850D /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 081D9DC42DF80E15004F850D /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 081D9DC72DF80E1A004F850D /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 081D9DDB2DF80EBB004F850D /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 081D9DDF2DF80EC2004F850D /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 081D9DEB2DF80F23004F850D /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 081D9DEE2DF80F2B004F850D /* Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 081D9DF12DF80F30004F850D /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 081D9DF42DF80F34004F850D /* DataMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DataMock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 081D9DF72DF80F3C004F850D /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 081D9E042DF80F63004F850D /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 081D9E072DF80F68004F850D /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 773E8D452E7AF4A70094714D /* MyPageFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MyPageFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 773E8D492E7AF4D30094714D /* MyPageFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MyPageFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 773E8D4C2E7AF4F00094714D /* MyPageFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MyPageFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 773E8D4F2E7AF4F60094714D /* MyPageFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MyPageFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 775D68052DFD638900DDAD2F /* DataMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DataMock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 775D68092DFD641E00DDAD2F /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 779A49122E1AD4BC00ABDE4F /* BookmarkFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BookmarkFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C755B62E4B70C30081D80F /* AuthFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C755B72E4B70C30081D80F /* AuthFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C974722E376124007198DA /* BookmarkFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BookmarkFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77C97DAA2E37D4AC007198DA /* AuthFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77DBD8BE2E1AD0C600529428 /* BookmarkFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BookmarkFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77FEEF1C2EC5B0870023197A /* AuthFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 081D9DBB2DF80DBE004F850D /* Exceptions for "DictionaryFeatureDemo" folder in "DictionaryFeatureDemo" target */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Info.plist, - ); - target = 081D9DA92DF80DBD004F850D /* DictionaryFeatureDemo */; - }; -/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 081D9C1B2DF80854004F850D /* DictionaryFeature */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = DictionaryFeature; - sourceTree = ""; - }; - 081D9D772DF80CB1004F850D /* DictionaryFeatureInterface */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = DictionaryFeatureInterface; - sourceTree = ""; - }; - 081D9DAB2DF80DBD004F850D /* DictionaryFeatureDemo */ = { - isa = PBXFileSystemSynchronizedRootGroup; - exceptions = ( - 081D9DBB2DF80DBE004F850D /* Exceptions for "DictionaryFeatureDemo" folder in "DictionaryFeatureDemo" target */, - ); - path = DictionaryFeatureDemo; - sourceTree = ""; - }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - 081D9C162DF80854004F850D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 081D9DD22DF80E5C004F850D /* RxRelay in Frameworks */, - 772DC9972E55CA93005F92E3 /* RxKeyboard in Frameworks */, - 081D9DD72DF80E69004F850D /* SnapKit in Frameworks */, - 081D9DC82DF80E1A004F850D /* DesignSystem.framework in Frameworks */, - 081D9DC52DF80E15004F850D /* DomainInterface.framework in Frameworks */, - 081D9DDA2DF80E7E004F850D /* ReactorKit in Frameworks */, - 77C974732E376124007198DA /* BookmarkFeatureInterface.framework in Frameworks */, - 77C7F4E52ED88D14009559E1 /* RxGesture in Frameworks */, - 77FEEF1D2EC5B0870023197A /* AuthFeatureInterface.framework in Frameworks */, - 081D9DD02DF80E5C004F850D /* RxCocoa in Frameworks */, - 77C97DAB2E37D4AC007198DA /* AuthFeature.framework in Frameworks */, - 081D9DC12DF80DF6004F850D /* BaseFeature.framework in Frameworks */, - 081D9DCA2DF80E27004F850D /* DictionaryFeatureInterface.framework in Frameworks */, - 773E8D462E7AF4A70094714D /* MyPageFeatureInterface.framework in Frameworks */, - 081D9DD42DF80E5C004F850D /* RxSwift in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 081D9D732DF80CB1004F850D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 081D9DE02DF80EC2004F850D /* DomainInterface.framework in Frameworks */, - 081D9DDC2DF80EBB004F850D /* BaseFeature.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 081D9DA72DF80DBD004F850D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 081D9DFF2DF80F4B004F850D /* RxRelay in Frameworks */, - 081D9DFD2DF80F47004F850D /* RxSwift in Frameworks */, - 081D9DF82DF80F3C004F850D /* DesignSystem.framework in Frameworks */, - 081D9DE22DF80F1D004F850D /* DictionaryFeatureInterface.framework in Frameworks */, - 77C755B82E4B70C30081D80F /* AuthFeature.framework in Frameworks */, - 081D9DEC2DF80F23004F850D /* BaseFeature.framework in Frameworks */, - 773E8D502E7AF4F60094714D /* MyPageFeatureInterface.framework in Frameworks */, - 773E8D4D2E7AF4F00094714D /* MyPageFeature.framework in Frameworks */, - 77DBD8BF2E1AD0C600529428 /* BookmarkFeatureInterface.framework in Frameworks */, - 081D9DE72DF80F20004F850D /* DictionaryFeature.framework in Frameworks */, - 779A49132E1AD4BC00ABDE4F /* BookmarkFeature.framework in Frameworks */, - 081D9E082DF80F68004F850D /* Domain.framework in Frameworks */, - 081D9DF22DF80F30004F850D /* Data.framework in Frameworks */, - 77C755C12E4B917F0081D80F /* RxKeyboard in Frameworks */, - 081D9E052DF80F63004F850D /* DomainInterface.framework in Frameworks */, - 081D9DF52DF80F34004F850D /* DataMock.framework in Frameworks */, - 081D9DFB2DF80F44004F850D /* RxCocoa in Frameworks */, - 081D9DEF2DF80F2B004F850D /* Core.framework in Frameworks */, - 77C755BA2E4B70C30081D80F /* AuthFeatureInterface.framework in Frameworks */, - 081D9E032DF80F58004F850D /* ReactorKit in Frameworks */, - 773E8D4A2E7AF4D30094714D /* MyPageFeature.framework in Frameworks */, - 081D9E012DF80F53004F850D /* SnapKit in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 081D9C0F2DF80854004F850D = { - isa = PBXGroup; - children = ( - 081D9C1B2DF80854004F850D /* DictionaryFeature */, - 081D9D772DF80CB1004F850D /* DictionaryFeatureInterface */, - 081D9DAB2DF80DBD004F850D /* DictionaryFeatureDemo */, - 081D9DBF2DF80DF6004F850D /* Frameworks */, - 081D9C1A2DF80854004F850D /* Products */, - ); - sourceTree = ""; - }; - 081D9C1A2DF80854004F850D /* Products */ = { - isa = PBXGroup; - children = ( - 081D9C192DF80854004F850D /* DictionaryFeature.framework */, - 081D9D762DF80CB1004F850D /* DictionaryFeatureInterface.framework */, - 081D9DAA2DF80DBD004F850D /* DictionaryFeatureDemo.app */, - ); - name = Products; - sourceTree = ""; - }; - 081D9DBF2DF80DF6004F850D /* Frameworks */ = { - isa = PBXGroup; - children = ( - 77FEEF1C2EC5B0870023197A /* AuthFeatureInterface.framework */, - 773E8D4F2E7AF4F60094714D /* MyPageFeatureInterface.framework */, - 773E8D4C2E7AF4F00094714D /* MyPageFeature.framework */, - 773E8D492E7AF4D30094714D /* MyPageFeature.framework */, - 773E8D452E7AF4A70094714D /* MyPageFeatureInterface.framework */, - 77C755B62E4B70C30081D80F /* AuthFeature.framework */, - 77C755B72E4B70C30081D80F /* AuthFeatureInterface.framework */, - 77C97DAA2E37D4AC007198DA /* AuthFeature.framework */, - 77C974722E376124007198DA /* BookmarkFeatureInterface.framework */, - 779A49122E1AD4BC00ABDE4F /* BookmarkFeature.framework */, - 77DBD8BE2E1AD0C600529428 /* BookmarkFeatureInterface.framework */, - 775D68092DFD641E00DDAD2F /* Domain.framework */, - 775D68052DFD638900DDAD2F /* DataMock.framework */, - 081D9E072DF80F68004F850D /* Domain.framework */, - 081D9E042DF80F63004F850D /* DomainInterface.framework */, - 081D9DF72DF80F3C004F850D /* DesignSystem.framework */, - 081D9DF42DF80F34004F850D /* DataMock.framework */, - 081D9DF12DF80F30004F850D /* Data.framework */, - 081D9DEE2DF80F2B004F850D /* Core.framework */, - 081D9DEB2DF80F23004F850D /* BaseFeature.framework */, - 081D9DDF2DF80EC2004F850D /* DomainInterface.framework */, - 081D9DDB2DF80EBB004F850D /* BaseFeature.framework */, - 081D9DC72DF80E1A004F850D /* DesignSystem.framework */, - 081D9DC42DF80E15004F850D /* DomainInterface.framework */, - 081D9DC02DF80DF6004F850D /* BaseFeature.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 081D9C142DF80854004F850D /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 081D9D712DF80CB1004F850D /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 081D9C182DF80854004F850D /* DictionaryFeature */ = { - isa = PBXNativeTarget; - buildConfigurationList = 081D9C1F2DF80854004F850D /* Build configuration list for PBXNativeTarget "DictionaryFeature" */; - buildPhases = ( - 081D9C142DF80854004F850D /* Headers */, - 081D9C152DF80854004F850D /* Sources */, - 081D9C162DF80854004F850D /* Frameworks */, - 081D9C172DF80854004F850D /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 081D9DCD2DF80E27004F850D /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 081D9C1B2DF80854004F850D /* DictionaryFeature */, - ); - name = DictionaryFeature; - packageProductDependencies = ( - 081D9DCF2DF80E5C004F850D /* RxCocoa */, - 081D9DD12DF80E5C004F850D /* RxRelay */, - 081D9DD32DF80E5C004F850D /* RxSwift */, - 081D9DD62DF80E69004F850D /* SnapKit */, - 081D9DD92DF80E7E004F850D /* ReactorKit */, - 772DC9962E55CA93005F92E3 /* RxKeyboard */, - 77C7F4E42ED88D14009559E1 /* RxGesture */, - ); - productName = DictionaryFeature; - productReference = 081D9C192DF80854004F850D /* DictionaryFeature.framework */; - productType = "com.apple.product-type.framework"; - }; - 081D9D752DF80CB1004F850D /* DictionaryFeatureInterface */ = { - isa = PBXNativeTarget; - buildConfigurationList = 081D9D7D2DF80CB1004F850D /* Build configuration list for PBXNativeTarget "DictionaryFeatureInterface" */; - buildPhases = ( - 081D9D712DF80CB1004F850D /* Headers */, - 081D9D722DF80CB1004F850D /* Sources */, - 081D9D732DF80CB1004F850D /* Frameworks */, - 081D9D742DF80CB1004F850D /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - 081D9D772DF80CB1004F850D /* DictionaryFeatureInterface */, - ); - name = DictionaryFeatureInterface; - packageProductDependencies = ( - ); - productName = DictionaryFeatureInterface; - productReference = 081D9D762DF80CB1004F850D /* DictionaryFeatureInterface.framework */; - productType = "com.apple.product-type.framework"; - }; - 081D9DA92DF80DBD004F850D /* DictionaryFeatureDemo */ = { - isa = PBXNativeTarget; - buildConfigurationList = 081D9DBC2DF80DBE004F850D /* Build configuration list for PBXNativeTarget "DictionaryFeatureDemo" */; - buildPhases = ( - 081D9DA62DF80DBD004F850D /* Sources */, - 081D9DA72DF80DBD004F850D /* Frameworks */, - 081D9DA82DF80DBD004F850D /* Resources */, - 081D9DE62DF80F1D004F850D /* Embed Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 081D9DE52DF80F1D004F850D /* PBXTargetDependency */, - 081D9DEA2DF80F20004F850D /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 081D9DAB2DF80DBD004F850D /* DictionaryFeatureDemo */, - ); - name = DictionaryFeatureDemo; - packageProductDependencies = ( - 081D9DFA2DF80F44004F850D /* RxCocoa */, - 081D9DFC2DF80F47004F850D /* RxSwift */, - 081D9DFE2DF80F4B004F850D /* RxRelay */, - 081D9E002DF80F53004F850D /* SnapKit */, - 081D9E022DF80F58004F850D /* ReactorKit */, - 77C755C02E4B917F0081D80F /* RxKeyboard */, - ); - productName = DictionaryFeatureDemo; - productReference = 081D9DAA2DF80DBD004F850D /* DictionaryFeatureDemo.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 081D9C102DF80854004F850D /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1620; - LastUpgradeCheck = 1620; - TargetAttributes = { - 081D9C182DF80854004F850D = { - CreatedOnToolsVersion = 16.2; - LastSwiftMigration = 1620; - }; - 081D9D752DF80CB1004F850D = { - CreatedOnToolsVersion = 16.2; - LastSwiftMigration = 1620; - }; - 081D9DA92DF80DBD004F850D = { - CreatedOnToolsVersion = 16.2; - }; - }; - }; - buildConfigurationList = 081D9C132DF80854004F850D /* Build configuration list for PBXProject "DictionaryFeature" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 081D9C0F2DF80854004F850D; - minimizedProjectReferenceProxies = 1; - packageReferences = ( - 081D9DCE2DF80E5C004F850D /* XCRemoteSwiftPackageReference "RxSwift" */, - 081D9DD52DF80E69004F850D /* XCRemoteSwiftPackageReference "SnapKit" */, - 081D9DD82DF80E7E004F850D /* XCRemoteSwiftPackageReference "ReactorKit" */, - 77C755BF2E4B917F0081D80F /* XCRemoteSwiftPackageReference "RxKeyboard" */, - 77C7F4E32ED88D14009559E1 /* XCRemoteSwiftPackageReference "RxGesture" */, - ); - preferredProjectObjectVersion = 77; - productRefGroup = 081D9C1A2DF80854004F850D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 081D9C182DF80854004F850D /* DictionaryFeature */, - 081D9D752DF80CB1004F850D /* DictionaryFeatureInterface */, - 081D9DA92DF80DBD004F850D /* DictionaryFeatureDemo */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 081D9C172DF80854004F850D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 081D9D742DF80CB1004F850D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 081D9DA82DF80DBD004F850D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 081D9C152DF80854004F850D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 081D9D722DF80CB1004F850D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 081D9DA62DF80DBD004F850D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 081D9DCD2DF80E27004F850D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 081D9D752DF80CB1004F850D /* DictionaryFeatureInterface */; - targetProxy = 081D9DCC2DF80E27004F850D /* PBXContainerItemProxy */; - }; - 081D9DE52DF80F1D004F850D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 081D9D752DF80CB1004F850D /* DictionaryFeatureInterface */; - targetProxy = 081D9DE42DF80F1D004F850D /* PBXContainerItemProxy */; - }; - 081D9DEA2DF80F20004F850D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 081D9C182DF80854004F850D /* DictionaryFeature */; - targetProxy = 081D9DE92DF80F20004F850D /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 081D9C202DF80854004F850D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.DictionaryFeature; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 081D9C212DF80854004F850D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.DictionaryFeature; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; - 081D9C222DF80854004F850D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 081D9C232DF80854004F850D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 081D9D7A2DF80CB1004F850D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 15.2; - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.DictionaryFeatureInterface; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = auto; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - XROS_DEPLOYMENT_TARGET = 2.2; - }; - name = Debug; - }; - 081D9D7B2DF80CB1004F850D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 15.2; - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.DictionaryFeatureInterface; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = auto; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - XROS_DEPLOYMENT_TARGET = 2.2; - }; - name = Release; - }; - 081D9DBD2DF80DBE004F850D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = DictionaryFeatureDemo/Info.plist; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLS.DictionaryFeatureDemo; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = MLSDictionaryFeatureProvisioningProfileDev; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 081D9DBE2DF80DBE004F850D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = DictionaryFeatureDemo/Info.plist; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLS.DictionaryFeatureDemo; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = MLSDictionaryFeatureProvisioningProfile; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 081D9C132DF80854004F850D /* Build configuration list for PBXProject "DictionaryFeature" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 081D9C222DF80854004F850D /* Debug */, - 081D9C232DF80854004F850D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 081D9C1F2DF80854004F850D /* Build configuration list for PBXNativeTarget "DictionaryFeature" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 081D9C202DF80854004F850D /* Debug */, - 081D9C212DF80854004F850D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 081D9D7D2DF80CB1004F850D /* Build configuration list for PBXNativeTarget "DictionaryFeatureInterface" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 081D9D7A2DF80CB1004F850D /* Debug */, - 081D9D7B2DF80CB1004F850D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 081D9DBC2DF80DBE004F850D /* Build configuration list for PBXNativeTarget "DictionaryFeatureDemo" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 081D9DBD2DF80DBE004F850D /* Debug */, - 081D9DBE2DF80DBE004F850D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - 081D9DCE2DF80E5C004F850D /* XCRemoteSwiftPackageReference "RxSwift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactiveX/RxSwift"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 6.9.0; - }; - }; - 081D9DD52DF80E69004F850D /* XCRemoteSwiftPackageReference "SnapKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/SnapKit/SnapKit.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 5.7.1; - }; - }; - 081D9DD82DF80E7E004F850D /* XCRemoteSwiftPackageReference "ReactorKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactorKit/ReactorKit"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 3.2.0; - }; - }; - 77C755BF2E4B917F0081D80F /* XCRemoteSwiftPackageReference "RxKeyboard" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/RxSwiftCommunity/RxKeyboard"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.0.1; - }; - }; - 77C7F4E32ED88D14009559E1 /* XCRemoteSwiftPackageReference "RxGesture" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/RxSwiftCommunity/RxGesture.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 4.0.4; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - 081D9DCF2DF80E5C004F850D /* RxCocoa */ = { - isa = XCSwiftPackageProductDependency; - package = 081D9DCE2DF80E5C004F850D /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxCocoa; - }; - 081D9DD12DF80E5C004F850D /* RxRelay */ = { - isa = XCSwiftPackageProductDependency; - package = 081D9DCE2DF80E5C004F850D /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxRelay; - }; - 081D9DD32DF80E5C004F850D /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 081D9DCE2DF80E5C004F850D /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; - 081D9DD62DF80E69004F850D /* SnapKit */ = { - isa = XCSwiftPackageProductDependency; - package = 081D9DD52DF80E69004F850D /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = SnapKit; - }; - 081D9DD92DF80E7E004F850D /* ReactorKit */ = { - isa = XCSwiftPackageProductDependency; - package = 081D9DD82DF80E7E004F850D /* XCRemoteSwiftPackageReference "ReactorKit" */; - productName = ReactorKit; - }; - 081D9DFA2DF80F44004F850D /* RxCocoa */ = { - isa = XCSwiftPackageProductDependency; - package = 081D9DCE2DF80E5C004F850D /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxCocoa; - }; - 081D9DFC2DF80F47004F850D /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 081D9DCE2DF80E5C004F850D /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; - 081D9DFE2DF80F4B004F850D /* RxRelay */ = { - isa = XCSwiftPackageProductDependency; - package = 081D9DCE2DF80E5C004F850D /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxRelay; - }; - 081D9E002DF80F53004F850D /* SnapKit */ = { - isa = XCSwiftPackageProductDependency; - package = 081D9DD52DF80E69004F850D /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = SnapKit; - }; - 081D9E022DF80F58004F850D /* ReactorKit */ = { - isa = XCSwiftPackageProductDependency; - package = 081D9DD82DF80E7E004F850D /* XCRemoteSwiftPackageReference "ReactorKit" */; - productName = ReactorKit; - }; - 772DC9962E55CA93005F92E3 /* RxKeyboard */ = { - isa = XCSwiftPackageProductDependency; - package = 77C755BF2E4B917F0081D80F /* XCRemoteSwiftPackageReference "RxKeyboard" */; - productName = RxKeyboard; - }; - 77C755C02E4B917F0081D80F /* RxKeyboard */ = { - isa = XCSwiftPackageProductDependency; - package = 77C755BF2E4B917F0081D80F /* XCRemoteSwiftPackageReference "RxKeyboard" */; - productName = RxKeyboard; - }; - 77C7F4E42ED88D14009559E1 /* RxGesture */ = { - isa = XCSwiftPackageProductDependency; - package = 77C7F4E32ED88D14009559E1 /* XCRemoteSwiftPackageReference "RxGesture" */; - productName = RxGesture; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = 081D9C102DF80854004F850D /* Project object */; -} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MLS/Presentation/DictionaryFeature/DictionaryFeature.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6..00000000 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature.xcodeproj/xcshareddata/xcschemes/DictionaryFeatureDemo.xcscheme b/MLS/Presentation/DictionaryFeature/DictionaryFeature.xcodeproj/xcshareddata/xcschemes/DictionaryFeatureDemo.xcscheme deleted file mode 100644 index 4c48101b..00000000 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature.xcodeproj/xcshareddata/xcschemes/DictionaryFeatureDemo.xcscheme +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/.DS_Store b/MLS/Presentation/DictionaryFeature/DictionaryFeature/.DS_Store deleted file mode 100644 index 3c58da99..00000000 Binary files a/MLS/Presentation/DictionaryFeature/DictionaryFeature/.DS_Store and /dev/null differ diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/.DS_Store b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/.DS_Store deleted file mode 100644 index 9c35ab72..00000000 Binary files a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/.DS_Store and /dev/null differ diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailFactoryImpl.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailFactoryImpl.swift deleted file mode 100644 index c0f1a578..00000000 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailFactoryImpl.swift +++ /dev/null @@ -1,201 +0,0 @@ -import AuthFeatureInterface -import BaseFeature -import BookmarkFeatureInterface -import DictionaryFeatureInterface -import DomainInterface - -import RxCocoa - -public final class DictionaryDetailFactoryImpl: DictionaryDetailFactory { - private let loginFactory: () -> LoginFactory - private let bookmarkModalFactory: BookmarkModalFactory - private let dictionaryDetailFactory: () -> DictionaryDetailFactory - private let detailOnBoardingFactory: DetailOnBoardingFactory - private let appCoordinator: () -> AppCoordinatorProtocol - - private let dictionaryDetailMapUseCase: FetchDictionaryDetailMapUseCase - private let dictionaryDetailMapSpawnMonsterUseCase: FetchDictionaryDetailMapSpawnMonsterUseCase - private let dictionaryDetailMapNpcUseCase: FetchDictionaryDetailMapNpcUseCase - private let dictionaryDetailQuestLinkedQuestsUseCase: FetchDictionaryDetailQuestLinkedQuestsUseCase - private let dictionaryDetailQuestUseCase: FetchDictionaryDetailQuestUseCase - private let dictionaryDetailItemDropMonsterUseCase: FetchDictionaryDetailItemDropMonsterUseCase - private let dictionaryDetailItemUseCase: FetchDictionaryDetailItemUseCase - private let dictionaryDetailNpcUseCase: FetchDictionaryDetailNpcUseCase - private let dictionaryDetailNpcQuestUseCase: FetchDictionaryDetailNpcQuestUseCase - private let dictionaryDetailNpcMapUseCase: - FetchDictionaryDetailNpcMapUseCase - private let dictionaryDetailMonsterUseCase: FetchDictionaryDetailMonsterUseCase - private let dictionaryDetailMonsterDropItemUseCase: FetchDictionaryDetailMonsterItemsUseCase - private let dictionaryDetailMonsterMapUseCase: FetchDictionaryDetailMonsterMapUseCase - private let checkLoginUseCase: CheckLoginUseCase - private let setBookmarkUseCase: SetBookmarkUseCase - private let fetchVisitDictionaryDetailUseCase: FetchVisitDictionaryDetailUseCase - - public init( - loginFactory: @escaping () -> LoginFactory, - bookmarkModalFactory: BookmarkModalFactory, - dictionaryDetailFactory: @escaping () -> DictionaryDetailFactory, - detailOnBoardingFactory: DetailOnBoardingFactory, - appCoordinator: @escaping () -> AppCoordinatorProtocol, - dictionaryDetailMapUseCase: FetchDictionaryDetailMapUseCase, - dictionaryDetailMapSpawnMonsterUseCase: FetchDictionaryDetailMapSpawnMonsterUseCase, - dictionaryDetailMapNpcUseCase: FetchDictionaryDetailMapNpcUseCase, - dictionaryDetailQuestLinkedQuestsUseCase: FetchDictionaryDetailQuestLinkedQuestsUseCase, - dictionaryDetailQuestUseCase: FetchDictionaryDetailQuestUseCase, - dictionaryDetailItemDropMonsterUseCase: FetchDictionaryDetailItemDropMonsterUseCase, - dictionaryDetailItemUseCase: FetchDictionaryDetailItemUseCase, - dictionaryDetailNpcUseCase: FetchDictionaryDetailNpcUseCase, - dictionaryDetailNpcQuestUseCase: FetchDictionaryDetailNpcQuestUseCase, - dictionaryDetailNpcMapUseCase: FetchDictionaryDetailNpcMapUseCase, - dictionaryDetailMonsterUseCase: FetchDictionaryDetailMonsterUseCase, - dictionaryDetailMonsterDropItemUseCase: FetchDictionaryDetailMonsterItemsUseCase, - dictionaryDetailMonsterMapUseCase: FetchDictionaryDetailMonsterMapUseCase, - checkLoginUseCase: CheckLoginUseCase, - setBookmarkUseCase: SetBookmarkUseCase, - fetchVisitDictionaryDetailUseCase: FetchVisitDictionaryDetailUseCase - ) { - self.loginFactory = loginFactory - self.bookmarkModalFactory = bookmarkModalFactory - self.detailOnBoardingFactory = detailOnBoardingFactory - self.dictionaryDetailMapUseCase = dictionaryDetailMapUseCase - self.dictionaryDetailMapSpawnMonsterUseCase = dictionaryDetailMapSpawnMonsterUseCase - self.dictionaryDetailMapNpcUseCase = dictionaryDetailMapNpcUseCase - self.dictionaryDetailQuestLinkedQuestsUseCase = dictionaryDetailQuestLinkedQuestsUseCase - self.dictionaryDetailQuestUseCase = dictionaryDetailQuestUseCase - self.dictionaryDetailItemDropMonsterUseCase = dictionaryDetailItemDropMonsterUseCase - self.dictionaryDetailItemUseCase = dictionaryDetailItemUseCase - self.dictionaryDetailNpcUseCase = dictionaryDetailNpcUseCase - self.dictionaryDetailNpcQuestUseCase = dictionaryDetailNpcQuestUseCase - self.dictionaryDetailNpcMapUseCase = dictionaryDetailNpcMapUseCase - self.dictionaryDetailMonsterUseCase = dictionaryDetailMonsterUseCase - self.dictionaryDetailMonsterDropItemUseCase = dictionaryDetailMonsterDropItemUseCase - self.dictionaryDetailMonsterMapUseCase = dictionaryDetailMonsterMapUseCase - self.checkLoginUseCase = checkLoginUseCase - self.setBookmarkUseCase = setBookmarkUseCase - self.appCoordinator = appCoordinator - self.dictionaryDetailFactory = dictionaryDetailFactory - self.fetchVisitDictionaryDetailUseCase = fetchVisitDictionaryDetailUseCase - } - - public func make(type: DictionaryType, id: Int, bookmarkRelay: PublishRelay<(id: Int, newBookmarkId: Int?)>?, loginRelay: PublishRelay?) -> BaseViewController { - var viewController = BaseViewController() - switch type { - case .total: - break - case .collection: - break - case .item: - viewController = ItemDictionaryDetailViewController( - type: .item, - bookmarkModalFactory: bookmarkModalFactory, - loginFactory: loginFactory(), - dictionaryDetailFactory: dictionaryDetailFactory(), - detailOnBoardingFactory: detailOnBoardingFactory, - appCoordinator: appCoordinator(), fetchVisitDictionaryDetailUseCase: fetchVisitDictionaryDetailUseCase, - bookmarkRelay: bookmarkRelay, - loginRelay: loginRelay - ) - let reactor = ItemDictionaryDetailReactor( - dictionaryDetailItemUseCase: dictionaryDetailItemUseCase, - dictionaryDetailItemDropMonsterUseCase: dictionaryDetailItemDropMonsterUseCase, - checkLoginUseCase: checkLoginUseCase, - setBookmarkUseCase: setBookmarkUseCase, - id: id - ) - if let viewController = viewController as? ItemDictionaryDetailViewController { - viewController.reactor = reactor - } - case .monster: - viewController = MonsterDictionaryDetailViewController( - type: .monster, - bookmarkModalFactory: bookmarkModalFactory, - loginFactory: loginFactory(), - dictionaryDetailFactory: dictionaryDetailFactory(), - detailOnBoardingFactory: detailOnBoardingFactory, - appCoordinator: appCoordinator(), fetchVisitDictionaryDetailUseCase: fetchVisitDictionaryDetailUseCase, - bookmarkRelay: bookmarkRelay, - loginRelay: loginRelay - ) - let reactor = MonsterDictionaryDetailReactor( - dictionaryDetailMonsterUseCase: dictionaryDetailMonsterUseCase, - dictionaryDetailMonsterDropItemUseCase: dictionaryDetailMonsterDropItemUseCase, - dictionaryDetailMonsterMapUseCase: dictionaryDetailMonsterMapUseCase, - checkLoginUseCase: checkLoginUseCase, - setBookmarkUseCase: setBookmarkUseCase, - id: id - ) - if let viewController = viewController as? MonsterDictionaryDetailViewController { - viewController.reactor = reactor - } - case .map: - viewController = MapDictionaryDetailViewController( - type: .map, - bookmarkModalFactory: bookmarkModalFactory, - loginFactory: loginFactory(), - dictionaryDetailFactory: dictionaryDetailFactory(), - detailOnBoardingFactory: detailOnBoardingFactory, - appCoordinator: appCoordinator(), fetchVisitDictionaryDetailUseCase: fetchVisitDictionaryDetailUseCase, - bookmarkRelay: bookmarkRelay, - loginRelay: loginRelay - ) - let reactor = MapDictionaryDetailReactor( - dictionaryDetailMapUseCase: dictionaryDetailMapUseCase, - dictionaryDetailMapSpawnMonsterUseCase: dictionaryDetailMapSpawnMonsterUseCase, - dictionaryDetailMapNpcUseCase: dictionaryDetailMapNpcUseCase, - checkLoginUseCase: checkLoginUseCase, - setBookmarkUseCase: setBookmarkUseCase, - id: id - ) - if let viewController = viewController as? MapDictionaryDetailViewController { - viewController.reactor = reactor - } - case .npc: - viewController = NpcDictionaryDetailViewController( - type: .npc, - bookmarkModalFactory: bookmarkModalFactory, - loginFactory: loginFactory(), - dictionaryDetailFactory: dictionaryDetailFactory(), - detailOnBoardingFactory: detailOnBoardingFactory, - appCoordinator: appCoordinator(), fetchVisitDictionaryDetailUseCase: fetchVisitDictionaryDetailUseCase, - bookmarkRelay: bookmarkRelay, - loginRelay: loginRelay - ) - let reactor = NpcDictionaryDetailReactor( - dictionaryDetailNpcUseCase: dictionaryDetailNpcUseCase, - dictionaryDetailNpcQuestUseCase: dictionaryDetailNpcQuestUseCase, - dictionaryDetailNpcMapUseCase: dictionaryDetailNpcMapUseCase, - checkLoginUseCase: checkLoginUseCase, - setBookmarkUseCase: setBookmarkUseCase, - id: id - ) - if let viewController = viewController as? NpcDictionaryDetailViewController { - viewController.reactor = reactor - } - case .quest: - viewController = QuestDictionaryDetailViewController( - type: .quest, - bookmarkModalFactory: bookmarkModalFactory, - loginFactory: loginFactory(), - dictionaryDetailFactory: dictionaryDetailFactory(), - detailOnBoardingFactory: detailOnBoardingFactory, - appCoordinator: appCoordinator(), fetchVisitDictionaryDetailUseCase: fetchVisitDictionaryDetailUseCase, - bookmarkRelay: bookmarkRelay, - loginRelay: loginRelay - ) - let reactor = QuestDictionaryDetailReactor( - dictionaryDetailQuestUseCase: dictionaryDetailQuestUseCase, - dictionaryDetailQuestLinkedQuestUseCase: dictionaryDetailQuestLinkedQuestsUseCase, - checkLoginUseCase: checkLoginUseCase, - setBookmarkUseCase: setBookmarkUseCase, - id: id - ) - if let viewController = viewController as? QuestDictionaryDetailViewController { - viewController.reactor = reactor - } - } - - // 하단 탭바 히든 - viewController.isBottomTabbarHidden = true - return viewController - } -} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchFactoryImpl.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchFactoryImpl.swift deleted file mode 100644 index 10857c85..00000000 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchFactoryImpl.swift +++ /dev/null @@ -1,24 +0,0 @@ -import BaseFeature -import DictionaryFeatureInterface -import DomainInterface - -public final class DictionarySearchFactoryImpl: DictionarySearchFactory { - private let recentSearchRemoveUseCase: RecentSearchRemoveUseCase - private let recentSearchAddUseCase: RecentSearchAddUseCase - private let recentSearchFetchUseCase: RecentSearchFetchUseCase - private let searchResultFactory: DictionarySearchResultFactory - - public init(recentSearchRemoveUseCase: RecentSearchRemoveUseCase, recentSearchAddUseCase: RecentSearchAddUseCase, searchResultFactory: DictionarySearchResultFactory, recentSearchFetchUseCase: RecentSearchFetchUseCase) { - self.recentSearchRemoveUseCase = recentSearchRemoveUseCase - self.recentSearchAddUseCase = recentSearchAddUseCase - self.recentSearchFetchUseCase = recentSearchFetchUseCase - self.searchResultFactory = searchResultFactory - } - - public func make() -> BaseViewController { - let reactor = DictionarySearchReactor(recentSearchAddUseCase: recentSearchAddUseCase, recentSearchRemoveUseCase: recentSearchRemoveUseCase, recentSearchFetchUseCase: recentSearchFetchUseCase) - let viewController = DictionarySearchViewController(searchResultFactory: searchResultFactory) - viewController.reactor = reactor - return viewController - } -} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultFactoryImpl.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultFactoryImpl.swift deleted file mode 100644 index eb07655e..00000000 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultFactoryImpl.swift +++ /dev/null @@ -1,23 +0,0 @@ -import BaseFeature -import DictionaryFeatureInterface -import DomainInterface - -public final class DictionarySearchResultFactoryImpl: DictionarySearchResultFactory { - private let dictionaryListCountUseCase: FetchDictionaryListCountUseCase - private let dictionaryMainListFactory: DictionaryMainListFactory - private let dictionarySearchListUseCase: FetchDictionarySearchListUseCase - private let recentSearchAddUseCase: RecentSearchAddUseCase - - public init(dictionaryListCountUseCase: FetchDictionaryListCountUseCase, dictionaryMainListFactory: DictionaryMainListFactory, dictionarySearchListUseCase: FetchDictionarySearchListUseCase, recentSearchAddUseCase: RecentSearchAddUseCase) { - self.dictionaryListCountUseCase = dictionaryListCountUseCase - self.dictionaryMainListFactory = dictionaryMainListFactory - self.dictionarySearchListUseCase = dictionarySearchListUseCase - self.recentSearchAddUseCase = recentSearchAddUseCase - } - - public func make(keyword: String?) -> BaseViewController { - let reactor = DictionarySearchResultReactor(keyword: keyword, dictionarySearchUseCase: dictionarySearchListUseCase, dictionarySearchCountUseCase: dictionaryListCountUseCase, recentSearchAddUseCase: recentSearchAddUseCase) - let viewController = DictionarySearchResultViewController(dictionaryListFactory: dictionaryMainListFactory, reactor: reactor) - return viewController - } -} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift deleted file mode 100644 index 1f04fe03..00000000 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift +++ /dev/null @@ -1,416 +0,0 @@ -// swiftlint:disable function_body_length -// swiftlint:disable line_length - -import UIKit - -import AuthFeature -import AuthFeatureInterface -import BaseFeature -import BookmarkFeature -import BookmarkFeatureInterface -import Core -import Data -import DataMock -import DesignSystem -import DictionaryFeature -import DictionaryFeatureInterface -import Domain -import DomainInterface -import MyPageFeature -import MyPageFeatureInterface - -import KakaoSDKCommon - -@main -class AppDelegate: UIResponder, UIApplicationDelegate { - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - ImageLoader.shared.configure.diskCacheCountLimit = 10 - FontManager.registerFonts() - registerDependencies() - return true - } - - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) - } - - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {} -} - -private extension AppDelegate { - func registerDependencies() { - registerProvider() - registerRepository() - registerUseCase() - registerFactory() - } - - func registerProvider() { - DIContainer.register(type: NetworkProvider.self) { - NetworkProviderImpl() - } - DIContainer.register(type: SocialAuthenticatableProvider.self, name: "kakao") { - KakaoLoginProviderMock() - } - DIContainer.register(type: SocialAuthenticatableProvider.self, name: "apple") { - AppleLoginProviderMock() - } - } - - func registerRepository() { - DIContainer.register(type: AuthAPIRepository.self) { - AuthAPIRepositoryMock(provider: DIContainer.resolve(type: NetworkProvider.self)) - } - DIContainer.register(type: TokenRepository.self) { - KeyChainRepositoryImpl() - } - DIContainer.register(type: DictionaryListAPIRepository.self) { - DictionaryListAPIRepositoryImpl(provider: DIContainer.resolve(type: NetworkProvider.self), tokenInterceptor: TokenInterceptor(fetchTokenUseCase: DIContainer.resolve(type: FetchTokenFromLocalUseCase.self))) - } - DIContainer.register(type: DictionaryDetailAPIRepository.self) { - DictionaryDetailAPIRepositoryImpl(provider: DIContainer.resolve(type: NetworkProvider.self), tokenInterceptor: TokenInterceptor(fetchTokenUseCase: DIContainer.resolve(type: FetchTokenFromLocalUseCase.self))) - } - DIContainer.register(type: BookmarkRepository.self) { - BookmarkRepositoryImpl(provider: DIContainer.resolve(type: NetworkProvider.self), interceptor: TokenInterceptor(fetchTokenUseCase: DIContainer.resolve(type: FetchTokenFromLocalUseCase.self))) - } - DIContainer.register(type: UserDefaultsRepository.self) { - UserDefaultsRepositoryImpl() - } - DIContainer.register(type: AlarmAPIRepository.self) { - AlarmAPIRepositoryImpl(provider: DIContainer.resolve(type: NetworkProvider.self), interceptor: TokenInterceptor(fetchTokenUseCase: DIContainer.resolve(type: FetchTokenFromLocalUseCase.self))) - } - } - - func registerUseCase() { - DIContainer.register(type: FetchSocialCredentialUseCase.self, name: "kakao") { - let provider = DIContainer.resolve(type: SocialAuthenticatableProvider.self, name: "kakao") - return SocialLoginUseCaseImpl(provider: provider) - } - DIContainer.register(type: FetchSocialCredentialUseCase.self, name: "apple") { - let provider = DIContainer.resolve(type: SocialAuthenticatableProvider.self, name: "apple") - return SocialLoginUseCaseImpl(provider: provider) - } - DIContainer.register(type: CheckEmptyLevelAndRoleUseCase.self) { - CheckEmptyLevelAndRoleUseCaseImpl() - } - DIContainer.register(type: CheckValidLevelUseCase.self) { - CheckValidLevelUseCaseImpl() - } - DIContainer.register(type: FetchJobListUseCase.self) { - FetchJobListUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: LoginWithAppleUseCase.self) { - LoginWithAppleUseCaseImpl(authRepository: DIContainer.resolve(type: AuthAPIRepository.self), tokenRepository: DIContainer.resolve(type: TokenRepository.self), userDefaultsRepository: DIContainer.resolve(type: UserDefaultsRepository.self)) - } - DIContainer.register(type: LoginWithKakaoUseCase.self) { - LoginWithKakaoUseCaseImpl(authRepository: DIContainer.resolve(type: AuthAPIRepository.self), tokenRepository: DIContainer.resolve(type: TokenRepository.self), userDefaultsRepository: DIContainer.resolve(type: UserDefaultsRepository.self)) - } - DIContainer.register(type: SignUpWithAppleUseCase.self) { - SignUpWithAppleUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: SignUpWithKakaoUseCase.self) { - SignUpWithKakaoUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: UpdateUserInfoUseCase.self) { - UpdateUserInfoUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: FetchTokenFromLocalUseCase.self) { - FetchTokenFromLocalUseCaseImpl(repository: DIContainer.resolve(type: TokenRepository.self)) - } - DIContainer.register(type: SaveTokenToLocalUseCase.self) { - SaveTokenToLocalUseCaseImpl(repository: DIContainer.resolve(type: TokenRepository.self)) - } - DIContainer.register(type: DeleteTokenFromLocalUseCase.self) { - DeleteTokenFromLocalUseCaseImpl(repository: DIContainer.resolve(type: TokenRepository.self)) - } - DIContainer.register(type: FetchDictionaryAllListUseCase.self) { - FetchDictionaryAllListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryMapListUseCase.self) { - FetchDictionaryMapListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryItemListUseCase.self) { - FetchDictionaryItemListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryQuestListUseCase.self) { - FetchDictionaryQuestListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryNpcListUseCase.self) { - FetchDictionaryNpcListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryMonsterListUseCase.self) { - FetchDictionaryMonsterListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMonsterUseCase.self) { - FetchDictionaryDetailMonsterUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMonsterItemsUseCase.self) { - FetchDictionaryDetailMonsterDropItemUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMonsterMapUseCase.self) { - FetchDictionaryDetailMonsterMapUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailNpcUseCase.self) { - FetchDictionaryDetailNpcUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailNpcQuestUseCase.self) { - FetchDictionaryDetailNpcQuestUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailNpcMapUseCase.self) { - FetchDictionaryDetailNpcMapUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailItemUseCase.self) { - FetchDictionaryDetailItemUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailItemDropMonsterUseCase.self) { - FetchDictionaryDetailItemDropMonsterUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailQuestUseCase.self) { - FetchDictionaryDetailQuestUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailQuestLinkedQuestsUseCase.self) { - FetchDictionaryDetailQuestLinkedQuestsUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMapUseCase.self) { - FetchDictionaryDetailMapUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMapSpawnMonsterUseCase.self) { - FetchDictionaryDetailMapSpawnMonsterUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryDetailMapNpcUseCase.self) { - FetchDictionaryDetailMapNpcUseCaseImpl(repository: DIContainer.resolve(type: DictionaryDetailAPIRepository.self)) - } - DIContainer.register(type: CheckLoginUseCase.self) { - CheckLoginUseCaseImpl(authRepository: DIContainer.resolve(type: AuthAPIRepository.self), tokenRepository: DIContainer.resolve(type: TokenRepository.self)) - } - DIContainer.register(type: UpdateMarketingAgreementUseCase.self) { - UpdateMarketingAgreementUseCaseImpl(authRepository: DIContainer.resolve(type: AuthAPIRepository.self), tokenRepository: DIContainer.resolve(type: TokenRepository.self)) - } - DIContainer.register(type: CheckNotificationPermissionUseCase.self) { - CheckNotificationPermissionUseCaseImpl() - } - DIContainer.register(type: OpenNotificationSettingUseCase.self) { - OpenNotificationSettingUseCaseImpl() - } - DIContainer.register(type: UpdateNotificationAgreementUseCase.self) { - UpdateNotificationAgreementUseCaseImpl(authRepository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: SetBookmarkUseCase.self) { - SetBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) - } - DIContainer.register(type: FetchMonsterBookmarkUseCase.self) { - FetchMonsterBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) - } - DIContainer.register(type: FetchItemBookmarkUseCase.self) { - FetchItemBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) - } - DIContainer.register(type: FetchMapBookmarkUseCase.self) { - FetchMapBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) - } - DIContainer.register(type: FetchQuestBookmarkUseCase.self) { - FetchQuestBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) - } - DIContainer.register(type: FetchNPCBookmarkUseCase.self) { - FetchNPCBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) - } - DIContainer.register(type: FetchBookmarkUseCase.self) { - FetchBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) - } - DIContainer.register(type: FetchDictionarySearchListUseCase.self) { - FetchDictionarySearchListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: FetchDictionaryListCountUseCase.self) { - FetchDictionaryListCountUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) - } - DIContainer.register(type: RecentSearchAddUseCase.self) { - RecentSearchAddUseCaseImpl(repository: DIContainer.resolve(type: UserDefaultsRepository.self)) - } - DIContainer.register(type: RecentSearchRemoveUseCase.self) { - RecentSearchRemoveUseCaseImpl(repository: DIContainer.resolve(type: UserDefaultsRepository.self)) - } - DIContainer.register(type: RecentSearchFetchUseCase.self) { - RecentSearchFetchUseCaseImpl(repository: DIContainer.resolve(type: UserDefaultsRepository.self)) - } - DIContainer.register(type: ParseItemFilterResultUseCase.self) { - ParseItemFilterResultUseCaseImpl() - } - DIContainer.register(type: FetchPlatformUseCase.self) { - FetchPlatformUseCaseImpl(repository: DIContainer.resolve(type: UserDefaultsRepository.self)) - } - DIContainer.register(type: FetchProfileUseCase.self) { - FetchProfileUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self), fetchJobUseCase: DIContainer.resolve(type: FetchJobUseCase.self)) - } - DIContainer.register(type: FetchJobUseCase.self) { - FetchJobUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: FetchAllAlarmUseCase.self) { - FetchAllAlarmUseCaseImpl(repository: DIContainer.resolve(type: AlarmAPIRepository.self)) - } - } - func registerFactory() { - DIContainer.register(type: ItemFilterBottomSheetFactory.self) { - ItemFilterBottomSheetFactoryImpl() - } - DIContainer.register(type: MonsterFilterBottomSheetFactory.self) { - MonsterFilterBottomSheetFactoryImpl() - } - DIContainer.register(type: SortedBottomSheetFactory.self) { - SortedBottomSheetFactoryImpl() - } - DIContainer.register(type: BookmarkModalFactory.self) { - BookmarkModalFactoryImpl(addCollectionFactory: DIContainer.resolve(type: AddCollectionFactory.self)) - } - DIContainer.register(type: DictionaryDetailFactory.self) { - DictionaryDetailFactoryImpl(loginFactory: { DIContainer.resolve(type: LoginFactory.self) }, bookmarkModalFactory: DIContainer.resolve(type: BookmarkModalFactory.self), dictionaryDetailMapUseCase: DIContainer.resolve(type: FetchDictionaryDetailMapUseCase.self), dictionaryDetailMapSpawnMonsterUseCase: DIContainer.resolve(type: FetchDictionaryDetailMapSpawnMonsterUseCase.self), dictionaryDetailMapNpcUseCase: DIContainer.resolve(type: FetchDictionaryDetailMapNpcUseCase.self), dictionaryDetailQuestLinkedQuestsUseCase: DIContainer.resolve(type: FetchDictionaryDetailQuestLinkedQuestsUseCase.self), dictionaryDetailQuestUseCase: DIContainer.resolve(type: FetchDictionaryDetailQuestUseCase.self), dictionaryDetailItemDropMonsterUseCase: DIContainer.resolve(type: FetchDictionaryDetailItemDropMonsterUseCase.self), dictionaryDetailItemUseCase: DIContainer.resolve(type: FetchDictionaryDetailItemUseCase.self), dictionaryDetailNpcUseCase: DIContainer.resolve(type: FetchDictionaryDetailNpcUseCase.self), dictionaryDetailNpcQuestUseCase: DIContainer.resolve(type: FetchDictionaryDetailNpcQuestUseCase.self), dictionaryDetailNpcMapUseCase: DIContainer.resolve(type: FetchDictionaryDetailNpcMapUseCase.self), dictionaryDetailMonsterUseCase: DIContainer.resolve(type: FetchDictionaryDetailMonsterUseCase.self), dictionaryDetailMonsterDropItemUseCase: DIContainer.resolve(type: FetchDictionaryDetailMonsterItemsUseCase.self), dictionaryDetailMonsterMapUseCase: DIContainer.resolve(type: FetchDictionaryDetailMonsterMapUseCase.self), checkLoginUseCase: DIContainer.resolve(type: CheckLoginUseCase.self), setBookmarkUseCase: DIContainer.resolve(type: SetBookmarkUseCase.self)) - } - DIContainer.register(type: DictionaryMainListFactory.self) { - DictionaryListFactoryImpl( - checkLoginUseCase: DIContainer.resolve(type: CheckLoginUseCase.self), - dictionaryAllListItemUseCase: DIContainer.resolve(type: FetchDictionaryAllListUseCase.self), - dictionaryMapListItemUseCase: DIContainer.resolve(type: FetchDictionaryMapListUseCase.self), - dictionaryItemListItemUseCase: DIContainer.resolve(type: FetchDictionaryItemListUseCase.self), - dictionaryQuestListItemUseCase: DIContainer.resolve(type: FetchDictionaryQuestListUseCase.self), - dictionaryNpcListItemUseCase: DIContainer.resolve(type: FetchDictionaryNpcListUseCase.self), - dictionaryListItemUseCase: DIContainer.resolve(type: FetchDictionaryMonsterListUseCase.self), - setBookmarkUseCase: DIContainer.resolve(type: SetBookmarkUseCase.self), - parseItemFilterResultUseCase: DIContainer.resolve(type: ParseItemFilterResultUseCase.self), - itemFilterFactory: DIContainer.resolve(type: ItemFilterBottomSheetFactory.self), - monsterFilterFactory: DIContainer.resolve(type: MonsterFilterBottomSheetFactory.self), - sortedFactory: DIContainer.resolve(type: SortedBottomSheetFactory.self), - bookmarkModalFactory: DIContainer.resolve(type: BookmarkModalFactory.self), - detailFactory: DIContainer.resolve(type: DictionaryDetailFactory.self), loginFactory: { DIContainer.resolve(type: LoginFactory.self) } - ) - } - DIContainer.register(type: DictionarySearchResultFactory.self) { - DictionarySearchResultFactoryImpl( - dictionaryListCountUseCase: DIContainer.resolve(type: FetchDictionaryListCountUseCase.self), - dictionaryMainListFactory: DIContainer - .resolve(type: DictionaryMainListFactory.self), - dictionarySearchListUseCase: DIContainer.resolve(type: FetchDictionarySearchListUseCase.self) - ) - } - DIContainer.register(type: DictionarySearchFactory.self) { - DictionarySearchFactoryImpl(recentSearchRemoveUseCase: DIContainer.resolve(type: RecentSearchRemoveUseCase.self), - recentSearchAddUseCase: DIContainer.resolve(type: RecentSearchAddUseCase.self), - searchResultFactory: DIContainer - .resolve(type: DictionarySearchResultFactory.self), recentSearchFetchUseCase: DIContainer.resolve(type: RecentSearchFetchUseCase.self) - ) - } - DIContainer.register(type: NotificationSettingFactory.self) { - NotificationSettingFactoryImpl(checkNotificationPermissionUseCase: DIContainer.resolve(type: CheckNotificationPermissionUseCase.self), updateNotificationAgreementUseCase: DIContainer.resolve(type: UpdateNotificationAgreementUseCase.self)) - } - DIContainer.register(type: DictionaryNotificationFactory.self) { - DictionaryNotificationFactoryImpl(notificationSettingFactory: DIContainer.resolve(type: NotificationSettingFactory.self), fetchAllAlarmUseCase: DIContainer.resolve(type: FetchAllAlarmUseCase.self), fetchProfileUseCase: DIContainer.resolve(type: FetchProfileUseCase.self)) - } - DIContainer.register(type: DictionaryMainViewFactory.self) { - DictionaryMainViewFactoryImpl( - dictionaryMainListFactory: DIContainer - .resolve(type: DictionaryMainListFactory.self), - searchFactory: DIContainer.resolve(type: DictionarySearchFactory.self), - notificationFactory: DIContainer - .resolve(type: DictionaryNotificationFactory.self), checkLoginUseCase: DIContainer.resolve(type: CheckLoginUseCase.self)) - } - DIContainer.register(type: BookmarkOnBoardingFactory.self) { - BookmarkOnBoardingFactoryImpl() - } - DIContainer.register(type: OnBoardingNotificationSheetFactory.self) { - OnBoardingNotificationSheetFactoryImpl(checkNotificationPermissionUseCase: DIContainer.resolve(type: CheckNotificationPermissionUseCase.self), openNotificationSettingUseCase: DIContainer.resolve(type: OpenNotificationSettingUseCase.self), updateNotificationAgreementUseCase: DIContainer.resolve(type: UpdateNotificationAgreementUseCase.self), updateUserInfoUseCase: DIContainer.resolve(type: UpdateUserInfoUseCase.self), dictionaryMainViewFactory: DIContainer.resolve(type: DictionaryMainViewFactory.self)) - } - DIContainer.register(type: OnBoardingNotificationFactory.self) { - OnBoardingNotificationFactoryImpl(onBoardingNotificationSheetFactory: DIContainer.resolve(type: OnBoardingNotificationSheetFactory.self)) - } - DIContainer.register(type: OnBoardingInputFactory.self) { - OnBoardingInputFactoryImpl( - checkEmptyUseCase: DIContainer - .resolve(type: CheckEmptyLevelAndRoleUseCase.self), - checkValidLevelUseCase: DIContainer - .resolve(type: CheckValidLevelUseCase.self), - fetchJobListUseCase: DIContainer - .resolve(type: FetchJobListUseCase.self), updateUserInfoUseCase: DIContainer.resolve(type: UpdateUserInfoUseCase.self), - onBoardingNotificationFactory: DIContainer - .resolve(type: OnBoardingNotificationFactory.self) - ) - } - DIContainer.register(type: OnBoardingQuestionFactory.self) { - OnBoardingQuestionFactoryImpl(onBoardingInputFactory: DIContainer.resolve(type: OnBoardingInputFactory.self)) - } - DIContainer.register(type: TermsAgreementFactory.self) { - TermsAgreementFactoryImpl( - onBoardingQuestionFactory: DIContainer - .resolve(type: OnBoardingQuestionFactory.self), - signUpWithKakaoUseCase: DIContainer - .resolve(type: SignUpWithKakaoUseCase.self), - signUpWithAppleUseCase: DIContainer - .resolve(type: SignUpWithAppleUseCase.self), - saveTokenUseCase: DIContainer - .resolve(type: SaveTokenToLocalUseCase.self), - fetchTokenUseCase: DIContainer - .resolve(type: FetchTokenFromLocalUseCase.self), updateMarketingAgreementUseCase: DIContainer.resolve(type: UpdateMarketingAgreementUseCase.self) - ) - } - DIContainer.register(type: PutFCMTokenUseCase.self) { - PutFCMTokenUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) - } - DIContainer.register(type: LoginFactory.self) { - LoginFactoryImpl( - termsAgreementsFactory: DIContainer - .resolve(type: TermsAgreementFactory.self), - appleLoginUseCase: DIContainer - .resolve(type: FetchSocialCredentialUseCase.self, name: "apple"), - kakaoLoginUseCase: DIContainer - .resolve(type: FetchSocialCredentialUseCase.self, name: "kakao"), - loginWithAppleUseCase: DIContainer - .resolve(type: LoginWithAppleUseCase.self), - loginWithKakaoUseCase: DIContainer - .resolve(type: LoginWithKakaoUseCase.self), - fetchTokenUseCase: DIContainer.resolve(type: FetchTokenFromLocalUseCase.self), - putFCMTokenUseCase: DIContainer.resolve(type: PutFCMTokenUseCase.self), fetchPlatformUseCase: DIContainer.resolve(type: FetchPlatformUseCase.self) - ) - } - DIContainer.register(type: AddCollectionFactory.self) { - AddCollectionFactoryImpl() - } - DIContainer.register(type: BookmarkListFactory.self) { - BookmarkListFactoryImpl(itemFilterFactory: DIContainer.resolve(type: ItemFilterBottomSheetFactory.self), monsterFilterFactory: DIContainer.resolve(type: MonsterFilterBottomSheetFactory.self), sortedFactory: DIContainer.resolve(type: SortedBottomSheetFactory.self), bookmarkModalFactory: DIContainer.resolve(type: BookmarkModalFactory.self), loginFactory: DIContainer.resolve(type: LoginFactory.self), dictionaryDetailFactory: DIContainer.resolve(type: DictionaryDetailFactory.self), setBookmarkUseCase: DIContainer.resolve(type: SetBookmarkUseCase.self), checkLoginUseCase: DIContainer.resolve(type: CheckLoginUseCase.self), fetchBookmarkUseCase: DIContainer.resolve(type: FetchBookmarkUseCase.self), fetchMonsterBookmarkUseCase: DIContainer.resolve(type: FetchMonsterBookmarkUseCase.self), fetchItemBookmarkUseCase: DIContainer.resolve(type: FetchItemBookmarkUseCase.self), fetchNPCBookmarkUseCase: DIContainer.resolve(type: FetchNPCBookmarkUseCase.self), fetchQuestBookmarkUseCase: DIContainer.resolve(type: FetchQuestBookmarkUseCase.self), fetchMapBookmarkUseCase: DIContainer.resolve(type: FetchMapBookmarkUseCase.self), collectionEditFactory: DIContainer.resolve(type: CollectionEditFactory.self)) - } - DIContainer.register(type: CollectionSettingFactory.self) { - CollectionSettingFactoryImpl() - } - DIContainer.register(type: CollectionEditFactory.self) { - CollectionEditFactoryImpl(setBookmarkUseCase: DIContainer.resolve(type: SetBookmarkUseCase.self), bookmarkModalFactory: DIContainer.resolve(type: BookmarkModalFactory.self)) - } - DIContainer.register(type: CollectionDetailFactory.self) { - CollectionDetailFactoryImpl( - setBookmarkUseCase: DIContainer.resolve(type: SetBookmarkUseCase.self), - bookmarkModalFactory: DIContainer - .resolve(type: BookmarkModalFactory.self), - collectionSettingFactory: DIContainer - .resolve(type: CollectionSettingFactory.self), - addCollectionFactory: DIContainer - .resolve(type: AddCollectionFactory.self), - collectionEditFactory: DIContainer - .resolve(type: CollectionEditFactory.self), dictionaryDetailFactory: DIContainer.resolve(type: DictionaryDetailFactory.self) - ) - } - DIContainer.register(type: CollectionListFactory.self) { - CollectionListFactoryImpl(addCollectionFactory: DIContainer.resolve(type: AddCollectionFactory.self), bookmarkDetailFactory: DIContainer.resolve(type: CollectionDetailFactory.self)) - } - DIContainer.register(type: BookmarkMainFactory.self) { - BookmarkMainFactoryImpl( - setBookmarkUseCase: DIContainer - .resolve(type: SetBookmarkUseCase.self), - onBoardingFactory: DIContainer - .resolve(type: BookmarkOnBoardingFactory.self), - bookmarkListFactory: DIContainer - .resolve(type: BookmarkListFactory.self), - collectionListFactory: DIContainer - .resolve(type: CollectionListFactory.self), - searchFactory: DIContainer - .resolve(type: DictionarySearchFactory.self), - notificationFactory: DIContainer.resolve( - type: DictionaryNotificationFactory.self - ) - ) - } - } -} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/AppIcon.appiconset/appIcon.png b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/AppIcon.appiconset/appIcon.png deleted file mode 100644 index b759ed9c..00000000 Binary files a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/AppIcon.appiconset/appIcon.png and /dev/null differ diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/Contents.json b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/image.imageset/Contents.json b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/image.imageset/Contents.json deleted file mode 100644 index 8f75fcb4..00000000 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/image.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "image.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/image.imageset/image.svg b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/image.imageset/image.svg deleted file mode 100644 index 7b8caba9..00000000 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Assets.xcassets/image.imageset/image.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Base.lproj/LaunchScreen.storyboard b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 865e9329..00000000 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/SceneDelegate.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/SceneDelegate.swift deleted file mode 100644 index 93fa62bb..00000000 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/SceneDelegate.swift +++ /dev/null @@ -1,26 +0,0 @@ -import UIKit - -import DesignSystem -import DictionaryFeature - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - var window: UIWindow? - - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - guard let windowScene = (scene as? UIWindowScene) else { return } - window = UIWindow(windowScene: windowScene) - let startVC = ViewController() - window?.rootViewController = UINavigationController(rootViewController: startVC) - window?.makeKeyAndVisible() - } - - func sceneDidDisconnect(_ scene: UIScene) {} - - func sceneDidBecomeActive(_ scene: UIScene) {} - - func sceneWillResignActive(_ scene: UIScene) {} - - func sceneWillEnterForeground(_ scene: UIScene) {} - - func sceneDidEnterBackground(_ scene: UIScene) {} -} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/ViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/ViewController.swift deleted file mode 100644 index 28d2d119..00000000 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/ViewController.swift +++ /dev/null @@ -1,100 +0,0 @@ -import UIKit - -import BaseFeature -import BookmarkFeatureInterface -import Core -import DesignSystem -import DictionaryFeatureInterface -import Domain -import DomainInterface - -import RxCocoa -import RxSwift -import SnapKit - -class ViewController: UIViewController { - let tableView: UITableView = { - let view = UITableView(frame: .zero, style: .plain) - return view - }() - - lazy var views: [[UIViewController]] = { - let itemFilterBottomSheetVC = DIContainer.resolve(type: ItemFilterBottomSheetFactory.self).make { _ in } - itemFilterBottomSheetVC.title = "아이템 필터 바텀시트" - - let monsterBottomSheetVC = DIContainer.resolve(type: MonsterFilterBottomSheetFactory.self).make(startLevel: 0, endLevel: 200) { _, _ in } - monsterBottomSheetVC.title = "몬스터 필터 바텀시트" - - let sortedBottomSheetVC = DIContainer.resolve(type: SortedBottomSheetFactory.self).make(sortedOptions: [ - .korean - ], selectedIndex: 0, onSelectedIndex: {_ in }) - sortedBottomSheetVC.title = "정렬 바텀시트" - - let modalVC = [itemFilterBottomSheetVC, monsterBottomSheetVC, sortedBottomSheetVC] - - let dictionaryMainVC = DIContainer.resolve(type: DictionaryMainViewFactory.self).make() - - let bookmarkMainVC = DIContainer.resolve(type: BookmarkMainFactory.self).make() - - let dictView = BottomTabBarController(viewControllers: [ - dictionaryMainVC, - bookmarkMainVC, - BaseViewController() - ]) - dictView.title = "도감 메인" - - return [ - modalVC, - [dictView] - ] - }() - - override func viewDidLoad() { - super.viewDidLoad() - view.backgroundColor = .systemBackground - tableView.dataSource = self - tableView.delegate = self - navigationItem.title = "MLS Feature System" - - view.addSubview(tableView) - - tableView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -extension ViewController: UITableViewDataSource, UITableViewDelegate { - func numberOfSections(in tableView: UITableView) -> Int { - return views.count - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return views[section].count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = UITableViewCell() - cell.textLabel?.text = views[indexPath.section][indexPath.row].title - cell.selectionStyle = .none - return cell - } - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let nextController = views[indexPath.section][indexPath.row] - if indexPath.section == 0 { - if nextController.title == "몬스터 필터 바텀시트" || nextController.title == "정렬 바텀시트" { - if let nextVC = nextController as? (UIViewController & ModalPresentable) { - presentModal(nextVC) - } - } else { - navigationController?.present(nextController, animated: true) - } - } else if indexPath.section == 1 { - nextController.modalPresentationStyle = .fullScreen - navigationController?.present(nextController, animated: true) - } else { - navigationController?.pushViewController(nextController, animated: true) - } - } -} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/NotificationSettingFactory.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/NotificationSettingFactory.swift deleted file mode 100644 index 87a43ec9..00000000 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/NotificationSettingFactory.swift +++ /dev/null @@ -1,5 +0,0 @@ -import BaseFeature - -// public protocol NotificationSettingFactory { -// func make() -> BaseViewController -// } diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature.xcodeproj/project.pbxproj b/MLS/Presentation/MyPageFeature/MyPageFeature.xcodeproj/project.pbxproj deleted file mode 100644 index dae3ecdd..00000000 --- a/MLS/Presentation/MyPageFeature/MyPageFeature.xcodeproj/project.pbxproj +++ /dev/null @@ -1,958 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXBuildFile section */ - 773A3BEB2E716FD800F75B30 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 773A3BEA2E716FD800F75B30 /* SnapKit */; }; - 773A3BEE2E716FEF00F75B30 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 773A3BED2E716FEF00F75B30 /* RxCocoa */; }; - 773A3BF02E716FEF00F75B30 /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 773A3BEF2E716FEF00F75B30 /* RxRelay */; }; - 773A3BF22E716FEF00F75B30 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 773A3BF12E716FEF00F75B30 /* RxSwift */; }; - 773A3BF52E716FFD00F75B30 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 773A3BF42E716FFD00F75B30 /* ReactorKit */; }; - 773A3BF82E71701D00F75B30 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 773A3BF72E71701D00F75B30 /* RxCocoa */; }; - 773A3BFA2E71701D00F75B30 /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 773A3BF92E71701D00F75B30 /* RxRelay */; }; - 773A3BFC2E71701D00F75B30 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 773A3BFB2E71701D00F75B30 /* RxSwift */; }; - 773A3BFE2E71702300F75B30 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 773A3BFD2E71702300F75B30 /* SnapKit */; }; - 773A3BFF2E7170A700F75B30 /* MyPageFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 773A38202E71591B00F75B30 /* MyPageFeatureInterface.framework */; }; - 773A3C002E7170A700F75B30 /* MyPageFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 773A38202E71591B00F75B30 /* MyPageFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 773A3C092E71712300F75B30 /* MyPageFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 773A38202E71591B00F75B30 /* MyPageFeatureInterface.framework */; }; - 773A3F662E71731700F75B30 /* MyPageFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 773A38062E7158B400F75B30 /* MyPageFeature.framework */; }; - 773A3F672E71731700F75B30 /* MyPageFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 773A38062E7158B400F75B30 /* MyPageFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 773A3F6B2E71734600F75B30 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 773A3F6A2E71734600F75B30 /* DesignSystem.framework */; }; - 773A3F6C2E71734600F75B30 /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 773A3F6A2E71734600F75B30 /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 773A3F6E2E71736300F75B30 /* BaseFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 773A3F6D2E71736300F75B30 /* BaseFeature.framework */; }; - 773A3F6F2E71736300F75B30 /* BaseFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 773A3F6D2E71736300F75B30 /* BaseFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 773A3F712E71736A00F75B30 /* BaseFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 773A3F702E71736A00F75B30 /* BaseFeature.framework */; }; - 773A42D02E717B8900F75B30 /* BaseFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 773A42CF2E717B8900F75B30 /* BaseFeature.framework */; }; - 7746ABAA2E84225A0046F603 /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7746ABA92E84225A0046F603 /* Core.framework */; }; - 7746ABAB2E84225A0046F603 /* Core.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7746ABA92E84225A0046F603 /* Core.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7746ABAD2E84227A0046F603 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7746ABAC2E84227A0046F603 /* DomainInterface.framework */; }; - 7746ABB12E84245D0046F603 /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 7746ABB02E84245D0046F603 /* RxGesture */; }; - 7746ABB32E8425E00046F603 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7746ABB22E8425E00046F603 /* DesignSystem.framework */; }; - 7746ABB72E8427F80046F603 /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 7746ABB62E8427F80046F603 /* RxKeyboard */; }; - 775966F32EC30B3A00CC389B /* AuthFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 775966F22EC30B3A00CC389B /* AuthFeatureInterface.framework */; }; - 777C28142E7D87B8000765F2 /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 777C28132E7D87B8000765F2 /* RxKeyboard */; }; - 77BE55BC2E78596900522216 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77BE55B92E78596900522216 /* DomainInterface.framework */; }; - 77BE55BD2E78596900522216 /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77BE55B92E78596900522216 /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 77D0AD422F97DA78008B0C57 /* AuthFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77D0AD412F97DA78008B0C57 /* AuthFeatureInterface.framework */; }; - 77D0AD432F97DA78008B0C57 /* AuthFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77D0AD412F97DA78008B0C57 /* AuthFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D22195C52E83FA81001203D4 /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = D22195C42E83FA81001203D4 /* RxGesture */; }; - D2DA8C322E798917009C53BA /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = D2227A7C2E76A84A0095343A /* ReactorKit */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 773A3C012E7170A700F75B30 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 773A37FD2E7158B400F75B30 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 773A381F2E71591B00F75B30; - remoteInfo = MyPageFeatureInterface; - }; - 773A3C0B2E71712300F75B30 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 773A37FD2E7158B400F75B30 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 773A381F2E71591B00F75B30; - remoteInfo = MyPageFeatureInterface; - }; - 773A3F682E71731700F75B30 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 773A37FD2E7158B400F75B30 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 773A38052E7158B400F75B30; - remoteInfo = MyPageFeature; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 773A3C032E7170A700F75B30 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 77D0AD432F97DA78008B0C57 /* AuthFeatureInterface.framework in Embed Frameworks */, - 77BE55BD2E78596900522216 /* DomainInterface.framework in Embed Frameworks */, - 773A3F6F2E71736300F75B30 /* BaseFeature.framework in Embed Frameworks */, - 773A3F6C2E71734600F75B30 /* DesignSystem.framework in Embed Frameworks */, - 773A3F672E71731700F75B30 /* MyPageFeature.framework in Embed Frameworks */, - 7746ABAB2E84225A0046F603 /* Core.framework in Embed Frameworks */, - 773A3C002E7170A700F75B30 /* MyPageFeatureInterface.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 773A38062E7158B400F75B30 /* MyPageFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MyPageFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 773A38202E71591B00F75B30 /* MyPageFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MyPageFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 773A38772E7159CA00F75B30 /* MyPageFeatureDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MyPageFeatureDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 773A3F6A2E71734600F75B30 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 773A3F6D2E71736300F75B30 /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 773A3F702E71736A00F75B30 /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 773A42CF2E717B8900F75B30 /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7746A7EE2E841C9C0046F603 /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7746A7F12E841CEA0046F603 /* DataMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DataMock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7746ABA92E84225A0046F603 /* Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7746ABAC2E84227A0046F603 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7746ABB22E8425E00046F603 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 775966F22EC30B3A00CC389B /* AuthFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 776FEA332EA4D29B0039ACE2 /* AuthFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77BE55B82E78596900522216 /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77BE55B92E78596900522216 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77D0AD412F97DA78008B0C57 /* AuthFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AuthFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D2227A762E76A7E70095343A /* Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 773A38882E7159CB00F75B30 /* Exceptions for "MyPageFeatureDemo" folder in "MyPageFeatureDemo" target */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Info.plist, - ); - target = 773A38762E7159CA00F75B30 /* MyPageFeatureDemo */; - }; -/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 773A38082E7158B400F75B30 /* MyPageFeature */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = MyPageFeature; - sourceTree = ""; - }; - 773A38212E71591B00F75B30 /* MyPageFeatureInterface */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = MyPageFeatureInterface; - sourceTree = ""; - }; - 773A38782E7159CA00F75B30 /* MyPageFeatureDemo */ = { - isa = PBXFileSystemSynchronizedRootGroup; - exceptions = ( - 773A38882E7159CB00F75B30 /* Exceptions for "MyPageFeatureDemo" folder in "MyPageFeatureDemo" target */, - ); - path = MyPageFeatureDemo; - sourceTree = ""; - }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - 773A38032E7158B400F75B30 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 7746ABAD2E84227A0046F603 /* DomainInterface.framework in Frameworks */, - 773A3BF02E716FEF00F75B30 /* RxRelay in Frameworks */, - D22195C52E83FA81001203D4 /* RxGesture in Frameworks */, - 773A3BEE2E716FEF00F75B30 /* RxCocoa in Frameworks */, - 773A3BF22E716FEF00F75B30 /* RxSwift in Frameworks */, - 773A3BF52E716FFD00F75B30 /* ReactorKit in Frameworks */, - 7746ABB32E8425E00046F603 /* DesignSystem.framework in Frameworks */, - 773A3C092E71712300F75B30 /* MyPageFeatureInterface.framework in Frameworks */, - 777C28142E7D87B8000765F2 /* RxKeyboard in Frameworks */, - 775966F32EC30B3A00CC389B /* AuthFeatureInterface.framework in Frameworks */, - 773A3BEB2E716FD800F75B30 /* SnapKit in Frameworks */, - 773A3F712E71736A00F75B30 /* BaseFeature.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 773A381D2E71591B00F75B30 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 773A42D02E717B8900F75B30 /* BaseFeature.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 773A38742E7159CA00F75B30 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D2DA8C322E798917009C53BA /* ReactorKit in Frameworks */, - 77BE55BC2E78596900522216 /* DomainInterface.framework in Frameworks */, - 773A3F6E2E71736300F75B30 /* BaseFeature.framework in Frameworks */, - 773A3F6B2E71734600F75B30 /* DesignSystem.framework in Frameworks */, - 7746ABB72E8427F80046F603 /* RxKeyboard in Frameworks */, - 773A3F662E71731700F75B30 /* MyPageFeature.framework in Frameworks */, - 7746ABB12E84245D0046F603 /* RxGesture in Frameworks */, - 77D0AD422F97DA78008B0C57 /* AuthFeatureInterface.framework in Frameworks */, - 7746ABAA2E84225A0046F603 /* Core.framework in Frameworks */, - 773A3BFC2E71701D00F75B30 /* RxSwift in Frameworks */, - 773A3BFE2E71702300F75B30 /* SnapKit in Frameworks */, - 773A3BFA2E71701D00F75B30 /* RxRelay in Frameworks */, - 773A3BF82E71701D00F75B30 /* RxCocoa in Frameworks */, - 773A3BFF2E7170A700F75B30 /* MyPageFeatureInterface.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 773A37FC2E7158B400F75B30 = { - isa = PBXGroup; - children = ( - 773A38082E7158B400F75B30 /* MyPageFeature */, - 773A38212E71591B00F75B30 /* MyPageFeatureInterface */, - 773A38782E7159CA00F75B30 /* MyPageFeatureDemo */, - 773A3BF62E71701D00F75B30 /* Frameworks */, - 773A38072E7158B400F75B30 /* Products */, - ); - sourceTree = ""; - }; - 773A38072E7158B400F75B30 /* Products */ = { - isa = PBXGroup; - children = ( - 773A38062E7158B400F75B30 /* MyPageFeature.framework */, - 773A38202E71591B00F75B30 /* MyPageFeatureInterface.framework */, - 773A38772E7159CA00F75B30 /* MyPageFeatureDemo.app */, - ); - name = Products; - sourceTree = ""; - }; - 773A3BF62E71701D00F75B30 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 77D0AD412F97DA78008B0C57 /* AuthFeatureInterface.framework */, - 775966F22EC30B3A00CC389B /* AuthFeatureInterface.framework */, - 776FEA332EA4D29B0039ACE2 /* AuthFeatureInterface.framework */, - 7746ABB22E8425E00046F603 /* DesignSystem.framework */, - 7746ABAC2E84227A0046F603 /* DomainInterface.framework */, - 7746ABA92E84225A0046F603 /* Core.framework */, - 7746A7F12E841CEA0046F603 /* DataMock.framework */, - 7746A7EE2E841C9C0046F603 /* Data.framework */, - 77BE55B82E78596900522216 /* Domain.framework */, - 77BE55B92E78596900522216 /* DomainInterface.framework */, - D2227A762E76A7E70095343A /* Core.framework */, - 773A42CF2E717B8900F75B30 /* BaseFeature.framework */, - 773A3F702E71736A00F75B30 /* BaseFeature.framework */, - 773A3F6D2E71736300F75B30 /* BaseFeature.framework */, - 773A3F6A2E71734600F75B30 /* DesignSystem.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 773A38012E7158B400F75B30 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 773A381B2E71591B00F75B30 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 773A38052E7158B400F75B30 /* MyPageFeature */ = { - isa = PBXNativeTarget; - buildConfigurationList = 773A380E2E7158B400F75B30 /* Build configuration list for PBXNativeTarget "MyPageFeature" */; - buildPhases = ( - 773A38012E7158B400F75B30 /* Headers */, - 773A38022E7158B400F75B30 /* Sources */, - 773A38032E7158B400F75B30 /* Frameworks */, - 773A38042E7158B400F75B30 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 773A3C0C2E71712300F75B30 /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 773A38082E7158B400F75B30 /* MyPageFeature */, - ); - name = MyPageFeature; - packageProductDependencies = ( - 773A3BEA2E716FD800F75B30 /* SnapKit */, - 773A3BED2E716FEF00F75B30 /* RxCocoa */, - 773A3BEF2E716FEF00F75B30 /* RxRelay */, - 773A3BF12E716FEF00F75B30 /* RxSwift */, - 773A3BF42E716FFD00F75B30 /* ReactorKit */, - 777C28132E7D87B8000765F2 /* RxKeyboard */, - D22195C42E83FA81001203D4 /* RxGesture */, - ); - productName = MyPageFeature; - productReference = 773A38062E7158B400F75B30 /* MyPageFeature.framework */; - productType = "com.apple.product-type.framework"; - }; - 773A381F2E71591B00F75B30 /* MyPageFeatureInterface */ = { - isa = PBXNativeTarget; - buildConfigurationList = 773A38272E71591B00F75B30 /* Build configuration list for PBXNativeTarget "MyPageFeatureInterface" */; - buildPhases = ( - 773A381B2E71591B00F75B30 /* Headers */, - 773A381C2E71591B00F75B30 /* Sources */, - 773A381D2E71591B00F75B30 /* Frameworks */, - 773A381E2E71591B00F75B30 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - 773A38212E71591B00F75B30 /* MyPageFeatureInterface */, - ); - name = MyPageFeatureInterface; - packageProductDependencies = ( - ); - productName = MyPageFeatureInterface; - productReference = 773A38202E71591B00F75B30 /* MyPageFeatureInterface.framework */; - productType = "com.apple.product-type.framework"; - }; - 773A38762E7159CA00F75B30 /* MyPageFeatureDemo */ = { - isa = PBXNativeTarget; - buildConfigurationList = 773A38892E7159CB00F75B30 /* Build configuration list for PBXNativeTarget "MyPageFeatureDemo" */; - buildPhases = ( - 773A38732E7159CA00F75B30 /* Sources */, - 773A38742E7159CA00F75B30 /* Frameworks */, - 773A38752E7159CA00F75B30 /* Resources */, - 773A3C032E7170A700F75B30 /* Embed Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 773A3C022E7170A700F75B30 /* PBXTargetDependency */, - 773A3F692E71731700F75B30 /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 773A38782E7159CA00F75B30 /* MyPageFeatureDemo */, - ); - name = MyPageFeatureDemo; - packageProductDependencies = ( - 773A3BF72E71701D00F75B30 /* RxCocoa */, - 773A3BF92E71701D00F75B30 /* RxRelay */, - 773A3BFB2E71701D00F75B30 /* RxSwift */, - 773A3BFD2E71702300F75B30 /* SnapKit */, - D2227A7C2E76A84A0095343A /* ReactorKit */, - 7746ABB02E84245D0046F603 /* RxGesture */, - 7746ABB62E8427F80046F603 /* RxKeyboard */, - ); - productName = MyPageFeatureDemo; - productReference = 773A38772E7159CA00F75B30 /* MyPageFeatureDemo.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 773A37FD2E7158B400F75B30 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1620; - LastUpgradeCheck = 1620; - TargetAttributes = { - 773A38052E7158B400F75B30 = { - CreatedOnToolsVersion = 16.2; - LastSwiftMigration = 1620; - }; - 773A381F2E71591B00F75B30 = { - CreatedOnToolsVersion = 16.2; - LastSwiftMigration = 1620; - }; - 773A38762E7159CA00F75B30 = { - CreatedOnToolsVersion = 16.2; - }; - }; - }; - buildConfigurationList = 773A38002E7158B400F75B30 /* Build configuration list for PBXProject "MyPageFeature" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 773A37FC2E7158B400F75B30; - minimizedProjectReferenceProxies = 1; - packageReferences = ( - 773A3BE92E716FD800F75B30 /* XCRemoteSwiftPackageReference "SnapKit" */, - 773A3BEC2E716FEF00F75B30 /* XCRemoteSwiftPackageReference "RxSwift" */, - 773A3BF32E716FFD00F75B30 /* XCRemoteSwiftPackageReference "ReactorKit" */, - 777C28122E7D87B8000765F2 /* XCRemoteSwiftPackageReference "RxKeyboard" */, - D22195C32E83FA81001203D4 /* XCRemoteSwiftPackageReference "RxGesture" */, - ); - preferredProjectObjectVersion = 77; - productRefGroup = 773A38072E7158B400F75B30 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 773A38052E7158B400F75B30 /* MyPageFeature */, - 773A381F2E71591B00F75B30 /* MyPageFeatureInterface */, - 773A38762E7159CA00F75B30 /* MyPageFeatureDemo */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 773A38042E7158B400F75B30 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 773A381E2E71591B00F75B30 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 773A38752E7159CA00F75B30 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 773A38022E7158B400F75B30 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 773A381C2E71591B00F75B30 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 773A38732E7159CA00F75B30 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 773A3C022E7170A700F75B30 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 773A381F2E71591B00F75B30 /* MyPageFeatureInterface */; - targetProxy = 773A3C012E7170A700F75B30 /* PBXContainerItemProxy */; - }; - 773A3C0C2E71712300F75B30 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 773A381F2E71591B00F75B30 /* MyPageFeatureInterface */; - targetProxy = 773A3C0B2E71712300F75B30 /* PBXContainerItemProxy */; - }; - 773A3F692E71731700F75B30 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 773A38052E7158B400F75B30 /* MyPageFeature */; - targetProxy = 773A3F682E71731700F75B30 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 773A380F2E7158B400F75B30 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MyPageFeature; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 773A38102E7158B400F75B30 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MyPageFeature; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - 773A38112E7158B400F75B30 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 773A38122E7158B400F75B30 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 773A38282E71591B00F75B30 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MyPageFeatureInterface; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 773A38292E71591B00F75B30 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MyPageFeatureInterface; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - 773A388A2E7159CB00F75B30 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = MyPageFeatureDemo/Info.plist; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLS.MyPageFeatureDemo2; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = MLSMyPageFeatureProvisioningProfileDev; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 773A388B2E7159CB00F75B30 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = MyPageFeatureDemo/Info.plist; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.donggle.MLS.MyPageFeatureDemo2; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = MLSMyPageFeatureProvisioningProfile; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 773A38002E7158B400F75B30 /* Build configuration list for PBXProject "MyPageFeature" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 773A38112E7158B400F75B30 /* Debug */, - 773A38122E7158B400F75B30 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 773A380E2E7158B400F75B30 /* Build configuration list for PBXNativeTarget "MyPageFeature" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 773A380F2E7158B400F75B30 /* Debug */, - 773A38102E7158B400F75B30 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 773A38272E71591B00F75B30 /* Build configuration list for PBXNativeTarget "MyPageFeatureInterface" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 773A38282E71591B00F75B30 /* Debug */, - 773A38292E71591B00F75B30 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 773A38892E7159CB00F75B30 /* Build configuration list for PBXNativeTarget "MyPageFeatureDemo" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 773A388A2E7159CB00F75B30 /* Debug */, - 773A388B2E7159CB00F75B30 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - 773A3BE92E716FD800F75B30 /* XCRemoteSwiftPackageReference "SnapKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/SnapKit/SnapKit"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 5.7.1; - }; - }; - 773A3BEC2E716FEF00F75B30 /* XCRemoteSwiftPackageReference "RxSwift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactiveX/RxSwift"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 6.9.0; - }; - }; - 773A3BF32E716FFD00F75B30 /* XCRemoteSwiftPackageReference "ReactorKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactorKit/ReactorKit.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 3.2.0; - }; - }; - 777C28122E7D87B8000765F2 /* XCRemoteSwiftPackageReference "RxKeyboard" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/RxSwiftCommunity/RxKeyboard"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.0.1; - }; - }; - D22195C32E83FA81001203D4 /* XCRemoteSwiftPackageReference "RxGesture" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/RxSwiftCommunity/RxGesture.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 4.0.4; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - 773A3BEA2E716FD800F75B30 /* SnapKit */ = { - isa = XCSwiftPackageProductDependency; - package = 773A3BE92E716FD800F75B30 /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = SnapKit; - }; - 773A3BED2E716FEF00F75B30 /* RxCocoa */ = { - isa = XCSwiftPackageProductDependency; - package = 773A3BEC2E716FEF00F75B30 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxCocoa; - }; - 773A3BEF2E716FEF00F75B30 /* RxRelay */ = { - isa = XCSwiftPackageProductDependency; - package = 773A3BEC2E716FEF00F75B30 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxRelay; - }; - 773A3BF12E716FEF00F75B30 /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 773A3BEC2E716FEF00F75B30 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; - 773A3BF42E716FFD00F75B30 /* ReactorKit */ = { - isa = XCSwiftPackageProductDependency; - package = 773A3BF32E716FFD00F75B30 /* XCRemoteSwiftPackageReference "ReactorKit" */; - productName = ReactorKit; - }; - 773A3BF72E71701D00F75B30 /* RxCocoa */ = { - isa = XCSwiftPackageProductDependency; - package = 773A3BEC2E716FEF00F75B30 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxCocoa; - }; - 773A3BF92E71701D00F75B30 /* RxRelay */ = { - isa = XCSwiftPackageProductDependency; - package = 773A3BEC2E716FEF00F75B30 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxRelay; - }; - 773A3BFB2E71701D00F75B30 /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 773A3BEC2E716FEF00F75B30 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; - 773A3BFD2E71702300F75B30 /* SnapKit */ = { - isa = XCSwiftPackageProductDependency; - package = 773A3BE92E716FD800F75B30 /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = SnapKit; - }; - 7746ABB02E84245D0046F603 /* RxGesture */ = { - isa = XCSwiftPackageProductDependency; - package = D22195C32E83FA81001203D4 /* XCRemoteSwiftPackageReference "RxGesture" */; - productName = RxGesture; - }; - 7746ABB62E8427F80046F603 /* RxKeyboard */ = { - isa = XCSwiftPackageProductDependency; - package = 777C28122E7D87B8000765F2 /* XCRemoteSwiftPackageReference "RxKeyboard" */; - productName = RxKeyboard; - }; - 777C28132E7D87B8000765F2 /* RxKeyboard */ = { - isa = XCSwiftPackageProductDependency; - package = 777C28122E7D87B8000765F2 /* XCRemoteSwiftPackageReference "RxKeyboard" */; - productName = RxKeyboard; - }; - D22195C42E83FA81001203D4 /* RxGesture */ = { - isa = XCSwiftPackageProductDependency; - package = D22195C32E83FA81001203D4 /* XCRemoteSwiftPackageReference "RxGesture" */; - productName = RxGesture; - }; - D2227A7C2E76A84A0095343A /* ReactorKit */ = { - isa = XCSwiftPackageProductDependency; - package = 773A3BF32E716FFD00F75B30 /* XCRemoteSwiftPackageReference "ReactorKit" */; - productName = ReactorKit; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = 773A37FD2E7158B400F75B30 /* Project object */; -} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/CustomerSupportBaseViewFactoryImpl.swift b/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/CustomerSupportBaseViewFactoryImpl.swift deleted file mode 100644 index 3507e671..00000000 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/CustomerSupportBaseViewFactoryImpl.swift +++ /dev/null @@ -1,56 +0,0 @@ -import BaseFeature -import DomainInterface -import MyPageFeatureInterface - -public final class CustomerSupportBaseViewFactoryImpl: CustomerSupportFactory { - private let policyFactory: PolicyFactory - - private let fetchNoticesUseCase: FetchNoticesUseCase - private let fetchOngoingEventsUseCase: FetchOngoingEventsUseCase - private let fetchOutdatedEventsUseCase: FetchOutdatedEventsUseCase - private let fetchPatchNotesUseCase: FetchPatchNotesUseCase - private let setReadUseCase: SetReadUseCase - - public init( - policyFactory: PolicyFactory, - fetchNoticesUseCase: FetchNoticesUseCase, - fetchOngoingEventsUseCase: FetchOngoingEventsUseCase, - fetchOutdatedEventsUseCase: FetchOutdatedEventsUseCase, - fetchPatchNotesUseCase: FetchPatchNotesUseCase, - setReadUseCase: SetReadUseCase - ) { - self.policyFactory = policyFactory - self.fetchNoticesUseCase = fetchNoticesUseCase - self.fetchOngoingEventsUseCase = fetchOngoingEventsUseCase - self.fetchOutdatedEventsUseCase = fetchOutdatedEventsUseCase - self.fetchPatchNotesUseCase = fetchPatchNotesUseCase - self.setReadUseCase = setReadUseCase - } - - public func make(type: CustomerSupportType) -> BaseViewController { - var viewController = BaseViewController() - - switch type { - case .event: - viewController = EventViewController(type: .event) - if let viewController = viewController as? EventViewController { - viewController.reactor = EventReactor(fetchOngoingEventsUseCase: fetchOngoingEventsUseCase, fetchOutdatedEventsUseCase: fetchOutdatedEventsUseCase, setReadUseCase: setReadUseCase) - } - case .announcement: - viewController = AnnouncementViewController(type: .announcement) - if let viewController = viewController as? AnnouncementViewController { - viewController.reactor = AnnouncementReactor(fetchNoticesUseCase: fetchNoticesUseCase, setReadUseCase: setReadUseCase) - } - case .patchNote: - viewController = PatchNoteViewController(type: .patchNote) - if let viewController = viewController as? PatchNoteViewController { - viewController.reactor = PatchNoteReactor(fetchPatchNotesUseCase: fetchPatchNotesUseCase, setReadUseCase: setReadUseCase) - } - case .terms: - viewController = TermsViewController(type: .terms, policyFactory: policyFactory) - } - - viewController.isBottomTabbarHidden = true - return viewController - } -} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingFactoryImpl.swift b/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingFactoryImpl.swift deleted file mode 100644 index 10548ef8..00000000 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingFactoryImpl.swift +++ /dev/null @@ -1,26 +0,0 @@ -import BaseFeature -import DomainInterface -import MyPageFeatureInterface - -public final class NotificationSettingFactoryImpl: NotificationSettingFactory { - private let checkNotificationPermissionUseCase: CheckNotificationPermissionUseCase - private let updateNotificationAgreementUseCase: UpdateNotificationAgreementUseCase - - public init(checkNotificationPermissionUseCase: CheckNotificationPermissionUseCase, updateNotificationAgreementUseCase: UpdateNotificationAgreementUseCase) { - self.checkNotificationPermissionUseCase = checkNotificationPermissionUseCase - self.updateNotificationAgreementUseCase = updateNotificationAgreementUseCase - } - - public func make(isAgreeEventNotification: Bool, isAgreeNoticeNotification: Bool, isAgreePatchNoteNotification: Bool) -> BaseViewController { - let viewController = NotificationSettingViewController( - reactor: NotificationSettingReactor( - checkNotificationPermissionUseCase: checkNotificationPermissionUseCase, - updateNotificationAgreementUseCase: updateNotificationAgreementUseCase, - isAgreeEventNotification: isAgreeEventNotification, - isAgreeNoticeNotification: isAgreeNoticeNotification, - isAgreePatchNoteNotification: isAgreePatchNoteNotification - ) - ) - return viewController - } -} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageFactoryImpl.swift b/MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageFactoryImpl.swift deleted file mode 100644 index 63540822..00000000 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SelectImage/SelectImageFactoryImpl.swift +++ /dev/null @@ -1,18 +0,0 @@ -import BaseFeature -import DomainInterface -import MyPageFeatureInterface - -public struct SelectImageFactoryImpl: SelectImageFactory { - private let updateProfileImageUseCase: UpdateProfileImageUseCase - - public init(updateProfileImageUseCase: UpdateProfileImageUseCase) { - self.updateProfileImageUseCase = updateProfileImageUseCase - } - - public func make() -> BaseViewController & ModalPresentable { - let viewController = SelectImageViewContoller() - viewController.reactor = SelectImageReactor(updateProfileImageUseCase: updateProfileImageUseCase) - viewController.isBottomTabbarHidden = true - return viewController - } -} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/AppDelegate.swift b/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/AppDelegate.swift deleted file mode 100644 index d71869df..00000000 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/AppDelegate.swift +++ /dev/null @@ -1,166 +0,0 @@ -// swiftlint:disable line_length - -import UIKit - -import AuthFeatureInterface -import BaseFeature -import Core -import DesignSystem -import DomainInterface -import MyPageFeature -import MyPageFeatureInterface - -@main -class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - ImageLoader.shared.configure.diskCacheCountLimit = 10 - FontManager.registerFonts() - registerDependencies() - - let center = UNUserNotificationCenter.current() - center.delegate = self - - center.getNotificationSettings { settings in - switch settings.authorizationStatus { - case .notDetermined: - center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in - if let error = error { - print("알림 권한 요청 실패: \(error)") - } else { - print("알림 권한 허용 여부: \(granted)") - } - } - case .denied: - print("사용자가 알림 권한 거부") - case .authorized, .provisional, .ephemeral: - print("알림 권한 이미 허용됨") - @unknown default: - break - } - } - - return true - } - - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) - } - - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {} -} - -private extension AppDelegate { - func registerDependencies() { - registerUseCase() - registerFactory() - } - - func registerUseCase() { - /// 테스트 코드에 맞게 등록 -// DIContainer.register(type: CheckNickNameUseCase.self) { -// CheckNickNameUseCaseImpl() -// } -// DIContainer.register(type: CheckEmptyLevelAndRoleUseCase.self) { -// CheckEmptyLevelAndRoleUseCaseImpl() -// } -// DIContainer.register(type: CheckValidLevelUseCase.self) { -// CheckValidLevelUseCaseImpl() -// } -// DIContainer.register(type: FetchJobListUseCase.self) { -// FetchJobListUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) -// } -// DIContainer.register(type: UpdateUserInfoUseCase.self) { -// UpdateUserInfoUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) -// } -// DIContainer.register(type: UpdateNickNameUseCase.self) { -// UpdateNickNameUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) -// } -// DIContainer.register(type: LogoutUseCase.self) { -// LogoutUseCaseImpl(repository: DIContainer.resolve(type: TokenRepository.self)) -// } -// DIContainer.register(type: WithdrawUseCase.self) { -// WithdrawUseCaseImpl(authRepository: DIContainer.resolve(type: AuthAPIRepository.self), tokenRepository: DIContainer.resolve(type: TokenRepository.self)) -// } -// DIContainer.register(type: FetchTokenFromLocalUseCase.self) { -// FetchTokenFromLocalUseCaseImpl(repository: DIContainer.resolve(type: TokenRepository.self)) -// } -// DIContainer.register(type: FetchNoticesUseCase.self) { -// FetchNoticesUseCaseImpl(repository: DIContainer.resolve(type: AlarmAPIRepository.self)) -// } -// DIContainer.register(type: FetchOngoingEventsUseCase.self) { -// FetchOngoingEventsUseCaseImpl(repository: DIContainer.resolve(type: AlarmAPIRepository.self)) -// } -// DIContainer.register(type: FetchOutdatedEventsUseCase.self) { -// FetchOutdatedEventsUseCaseImpl(repository: DIContainer.resolve(type: AlarmAPIRepository.self)) -// } -// DIContainer.register(type: FetchPatchNotesUseCase.self) { -// FetchPatchNotesUseCaseImpl(repository: DIContainer.resolve(type: AlarmAPIRepository.self)) -// } -// DIContainer.register(type: SetReadUseCase.self) { -// SetReadUseCaseImpl(repository: DIContainer.resolve(type: AlarmAPIRepository.self)) -// } -// DIContainer.register(type: CheckNotificationPermissionUseCase.self) { -// CheckNotificationPermissionUseCaseImpl() -// } -// DIContainer.register(type: UpdateNotificationAgreementUseCase.self) { -// UpdateNotificationAgreementUseCaseImpl(authRepository: DIContainer.resolve(type: AuthAPIRepository.self)) -// } -// DIContainer.register(type: UpdateProfileImageUseCase.self) { -// UpdateProfileImageUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) -// } -// DIContainer.register(type: FetchJobUseCase.self) { -// FetchJobUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self)) -// } -// DIContainer.register(type: FetchProfileUseCase.self) { -// FetchProfileUseCaseImpl(repository: DIContainer.resolve(type: AuthAPIRepository.self), fetchJobUseCase: DIContainer.resolve(type: FetchJobUseCase.self)) -// } - } - - func registerFactory() { - DIContainer.register(type: SelectImageFactory.self) { - SelectImageFactoryImpl(updateProfileImageUseCase: DIContainer.resolve(type: UpdateProfileImageUseCase.self)) - } - - DIContainer.register(type: SetProfileFactory.self) { - SetProfileFactoryImpl(selectImageFactory: DIContainer.resolve(type: SelectImageFactory.self), checkNickNameUseCase: DIContainer.resolve(type: CheckNickNameUseCase.self), updateNickNameUseCase: DIContainer.resolve(type: UpdateNickNameUseCase.self), logoutUseCase: DIContainer.resolve(type: LogoutUseCase.self), withdrawUseCase: DIContainer.resolve(type: WithdrawUseCase.self), fetchProfileUseCase: DIContainer.resolve(type: FetchProfileUseCase.self)) - } - - DIContainer.register(type: SetCharacterFactory.self) { - SetCharacterFactoryImpl( - checkEmptyUseCase: DIContainer - .resolve(type: CheckEmptyLevelAndRoleUseCase.self), - checkValidLevelUseCase: DIContainer - .resolve(type: CheckValidLevelUseCase.self), - fetchJobListUseCase: DIContainer - .resolve(type: FetchJobListUseCase.self), - updateUserInfoUseCase: DIContainer - .resolve(type: UpdateUserInfoUseCase.self) - ) - } - - DIContainer.register(type: MyPageMainFactory.self) { - MyPageMainFactoryImpl( - loginFactory: DIContainer.resolve(type: LoginFactory.self), setProfileFactory: DIContainer - .resolve(type: SetProfileFactory.self), - customerSupportFactory: DIContainer - .resolve(type: CustomerSupportFactory.self), - notificationSettingFactory: DIContainer - .resolve(type: NotificationSettingFactory.self), - setCharacterFactory: DIContainer - .resolve(type: SetCharacterFactory.self), fetchProfileUseCase: DIContainer.resolve(type: FetchProfileUseCase.self) - ) - } - - DIContainer.register(type: CustomerSupportFactory.self) { - CustomerSupportBaseViewFactoryImpl(policyFactory: DIContainer.resolve(type: PolicyFactory.self), fetchNoticesUseCase: DIContainer.resolve(type: FetchNoticesUseCase.self), fetchOngoingEventsUseCase: DIContainer.resolve(type: FetchOngoingEventsUseCase.self), fetchOutdatedEventsUseCase: DIContainer.resolve(type: FetchOutdatedEventsUseCase.self), fetchPatchNotesUseCase: DIContainer.resolve(type: FetchPatchNotesUseCase.self), setReadUseCase: DIContainer.resolve(type: SetReadUseCase.self)) - } - - DIContainer.register(type: NotificationSettingFactory.self) { - NotificationSettingFactoryImpl(checkNotificationPermissionUseCase: DIContainer.resolve(type: CheckNotificationPermissionUseCase.self), updateNotificationAgreementUseCase: DIContainer.resolve(type: UpdateNotificationAgreementUseCase.self)) - } - - DIContainer.register(type: LoginFactory.self) { - MockLoginFactory() - } - } -} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Assets.xcassets/AccentColor.colorset/Contents.json b/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb878970..00000000 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Assets.xcassets/AppIcon.appiconset/AppIcon.png b/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Assets.xcassets/AppIcon.appiconset/AppIcon.png deleted file mode 100644 index 58815bf5..00000000 Binary files a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Assets.xcassets/AppIcon.appiconset/AppIcon.png and /dev/null differ diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index ce8e776e..00000000 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "images" : [ - { - "filename" : "AppIcon.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "tinted" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Assets.xcassets/Contents.json b/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Base.lproj/LaunchScreen.storyboard b/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 865e9329..00000000 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/SceneDelegate.swift b/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/SceneDelegate.swift deleted file mode 100644 index 2e5908b1..00000000 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/SceneDelegate.swift +++ /dev/null @@ -1,23 +0,0 @@ -import UIKit - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - var window: UIWindow? - - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - guard let windowScene = (scene as? UIWindowScene) else { return } - window = UIWindow(windowScene: windowScene) - let startVC = ViewController() - window?.rootViewController = UINavigationController(rootViewController: startVC) - window?.makeKeyAndVisible() - } - - func sceneDidDisconnect(_ scene: UIScene) {} - - func sceneDidBecomeActive(_ scene: UIScene) {} - - func sceneWillResignActive(_ scene: UIScene) {} - - func sceneWillEnterForeground(_ scene: UIScene) {} - - func sceneDidEnterBackground(_ scene: UIScene) {} -} diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/ViewController.swift b/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/ViewController.swift deleted file mode 100644 index d1f3a414..00000000 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/ViewController.swift +++ /dev/null @@ -1,85 +0,0 @@ -import UIKit - -import Core -import DesignSystem -import MyPageFeatureInterface - -import SnapKit - -class ViewController: UIViewController { - let tableView: UITableView = { - let view = UITableView(frame: .zero, style: .plain) - return view - }() - - lazy var views: [[UIViewController]] = { - let mainView = BottomTabBarController(viewControllers: [ - DIContainer.resolve(type: MyPageMainFactory.self).make() - ]) - let announceView = BottomTabBarController(viewControllers: [ - DIContainer.resolve(type: CustomerSupportFactory.self).make(type: .announcement) - ]) - - let eventView = BottomTabBarController(viewControllers: [ - DIContainer.resolve(type: CustomerSupportFactory.self).make(type: .event) - ]) - - let patchView = BottomTabBarController(viewControllers: [ - DIContainer.resolve(type: CustomerSupportFactory.self).make(type: .patchNote) - ]) - - let termsView = BottomTabBarController(viewControllers: [ - DIContainer.resolve(type: CustomerSupportFactory.self).make(type: .terms) - ]) - - let notiView = BottomTabBarController(viewControllers: [ - DIContainer.resolve(type: NotificationSettingFactory.self).make(isAgreeEventNotification: false, isAgreeNoticeNotification: false, isAgreePatchNoteNotification: false) - ]) - - mainView.title = "마이페이지 메인" - announceView.title = "공지사항" - eventView.title = "이벤트" - patchView.title = "패치 노트" - termsView.title = "약관" - notiView.title = "알림설정" - - return [ - [mainView, announceView, eventView, patchView, termsView, notiView] - ] - }() - - override func viewDidLoad() { - super.viewDidLoad() - view.backgroundColor = .systemBackground - tableView.dataSource = self - tableView.delegate = self - navigationItem.title = "MLS Feature System" - view.addSubview(tableView) - - tableView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -extension ViewController: UITableViewDataSource, UITableViewDelegate { - func numberOfSections(in tableView: UITableView) -> Int { - return views.count - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return views[section].count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = UITableViewCell() - cell.textLabel?.text = views[indexPath.section][indexPath.row].title - cell.selectionStyle = .none - return cell - } - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let nextController = views[indexPath.section][indexPath.row] - navigationController?.pushViewController(nextController, animated: true) - } -}