diff --git a/Core/AppDI/AppDI.xcodeproj/project.pbxproj b/Core/AppDI/AppDI.xcodeproj/project.pbxproj new file mode 100644 index 0000000..1304dbc --- /dev/null +++ b/Core/AppDI/AppDI.xcodeproj/project.pbxproj @@ -0,0 +1,369 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXFileReference section */ + 57588CDA2DD9B4D500DBD9A7 /* AppDI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppDI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 57588CDC2DD9B4D500DBD9A7 /* Sources */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = Sources; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 57588CD72DD9B4D500DBD9A7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 57588CD02DD9B4D500DBD9A7 = { + isa = PBXGroup; + children = ( + 57588CDC2DD9B4D500DBD9A7 /* Sources */, + 57588CDB2DD9B4D500DBD9A7 /* Products */, + ); + sourceTree = ""; + }; + 57588CDB2DD9B4D500DBD9A7 /* Products */ = { + isa = PBXGroup; + children = ( + 57588CDA2DD9B4D500DBD9A7 /* AppDI.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 57588CD52DD9B4D500DBD9A7 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 57588CD92DD9B4D500DBD9A7 /* AppDI */ = { + isa = PBXNativeTarget; + buildConfigurationList = 57588CE12DD9B4D500DBD9A7 /* Build configuration list for PBXNativeTarget "AppDI" */; + buildPhases = ( + 57588CD52DD9B4D500DBD9A7 /* Headers */, + 57588CD62DD9B4D500DBD9A7 /* Sources */, + 57588CD72DD9B4D500DBD9A7 /* Frameworks */, + 57588CD82DD9B4D500DBD9A7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 57588CDC2DD9B4D500DBD9A7 /* Sources */, + ); + name = AppDI; + packageProductDependencies = ( + ); + productName = AppDI; + productReference = 57588CDA2DD9B4D500DBD9A7 /* AppDI.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 57588CD12DD9B4D500DBD9A7 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1630; + LastUpgradeCheck = 1630; + TargetAttributes = { + 57588CD92DD9B4D500DBD9A7 = { + CreatedOnToolsVersion = 16.3; + }; + }; + }; + buildConfigurationList = 57588CD42DD9B4D500DBD9A7 /* Build configuration list for PBXProject "AppDI" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 57588CD02DD9B4D500DBD9A7; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = 57588CDB2DD9B4D500DBD9A7 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 57588CD92DD9B4D500DBD9A7 /* AppDI */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 57588CD82DD9B4D500DBD9A7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 57588CD62DD9B4D500DBD9A7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 57588CDF2DD9B4D500DBD9A7 /* 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; + DEVELOPMENT_TEAM = 5DFZR8RCQR; + 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; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 57588CE02DD9B4D500DBD9A7 /* 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"; + DEVELOPMENT_TEAM = 5DFZR8RCQR; + 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; + SWIFT_COMPILATION_MODE = wholemodule; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 57588CE22DD9B4D500DBD9A7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 5DFZR8RCQR; + 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 = 18.4; + 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.4; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = kr.co.ios.swift.apple.AppDI; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 2.4; + }; + name = Debug; + }; + 57588CE32DD9B4D500DBD9A7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 5DFZR8RCQR; + 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 = 18.4; + 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.4; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = kr.co.ios.swift.apple.AppDI; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 2.4; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 57588CD42DD9B4D500DBD9A7 /* Build configuration list for PBXProject "AppDI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 57588CDF2DD9B4D500DBD9A7 /* Debug */, + 57588CE02DD9B4D500DBD9A7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 57588CE12DD9B4D500DBD9A7 /* Build configuration list for PBXNativeTarget "AppDI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 57588CE22DD9B4D500DBD9A7 /* Debug */, + 57588CE32DD9B4D500DBD9A7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 57588CD12DD9B4D500DBD9A7 /* Project object */; +} diff --git a/Core/AppDI/Sources/DIContainer.swift b/Core/AppDI/Sources/DIContainer.swift new file mode 100644 index 0000000..65e66e9 --- /dev/null +++ b/Core/AppDI/Sources/DIContainer.swift @@ -0,0 +1,40 @@ +// +// DIContainer.swift +// DIContainer +// +// Created by 이정동 on 5/18/25. +// + +import Foundation + +public final class DIContainer { + public static let shared = DIContainer() + private var dependencies: [String: Any] = [:] + + private init() {} + + public func register(_ dependency: T) { + let key = String(describing: T.self) + dependencies[key] = dependency + } + + public func resolve() -> T { + let key = String(describing: T.self) + let dependency = dependencies[key] + + guard let dependency = dependency as? T else { + fatalError("\(key)는 register되지 않았어어요. resolve 부르기전에 register 해주세요") + } + + return dependency + } +} + +@propertyWrapper +public struct Dependency { + public let wrappedValue: T + + public init() { + self.wrappedValue = DIContainer.shared.resolve() + } +} diff --git a/Core/DesignSystem/Sources/Font.swift b/Core/DesignSystem/Sources/Font.swift index 943176f..4b596bf 100644 --- a/Core/DesignSystem/Sources/Font.swift +++ b/Core/DesignSystem/Sources/Font.swift @@ -5,8 +5,8 @@ // Created by 이정동 on 4/8/25. // -import Foundation import CoreText +import Foundation import SwiftUI public extension Font { diff --git a/Core/LinkMetadataKit/Sources/LinkMetadataProvider.swift b/Core/LinkMetadataKit/Sources/LinkMetadataProvider.swift index 17d6c0c..9b9e0e4 100644 --- a/Core/LinkMetadataKit/Sources/LinkMetadataProvider.swift +++ b/Core/LinkMetadataKit/Sources/LinkMetadataProvider.swift @@ -11,11 +11,12 @@ import LinkPresentation import LinkMetadataKitInterface public struct LinkMetadataProviderImpl: LinkMetadataProvider { - private let metadataProvider = LPMetadataProvider() public init() {} public func fetchMetadata(urlString: String) async -> Result { + let metadataProvider = LPMetadataProvider() + /// URL 인코딩 guard let encoded = urlString.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed), let url = URL(string: encoded) diff --git a/Mark-In.xcodeproj/project.pbxproj b/Mark-In.xcodeproj/project.pbxproj index bd57b7b..63f3220 100644 --- a/Mark-In.xcodeproj/project.pbxproj +++ b/Mark-In.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 57588B452DD640C100DBD9A7 /* ReducerKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57588B412DD63D9700DBD9A7 /* ReducerKit.framework */; }; 57588B462DD640C100DBD9A7 /* ReducerKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 57588B412DD63D9700DBD9A7 /* ReducerKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 57588CEC2DD9B66C00DBD9A7 /* AppDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57588CE92DD9B4D600DBD9A7 /* AppDI.framework */; }; + 57588CED2DD9B66C00DBD9A7 /* AppDI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 57588CE92DD9B4D600DBD9A7 /* AppDI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 57944D0C2DC52AEF00EF3D9A /* GoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = 57944D0B2DC52AEF00EF3D9A /* GoogleSignIn */; }; 57AC56A82DA5120600BA84BD /* Util.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57AC56732DA511E900BA84BD /* Util.framework */; }; 57AC56A92DA5120600BA84BD /* Util.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 57AC56732DA511E900BA84BD /* Util.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -26,6 +28,13 @@ remoteGlobalIDString = 57606D5B2DD63B14005EBE3D; remoteInfo = ReducerKit; }; + 57588CE82DD9B4D600DBD9A7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 57588CE42DD9B4D500DBD9A7 /* AppDI.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 57588CDA2DD9B4D500DBD9A7; + remoteInfo = AppDI; + }; 57AC56722DA511E900BA84BD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 57AC53092DA4FC1800BA84BD /* Util.xcodeproj */; @@ -43,6 +52,7 @@ dstSubfolderSpec = 10; files = ( 57AC56A92DA5120600BA84BD /* Util.framework in Embed Frameworks */, + 57588CED2DD9B66C00DBD9A7 /* AppDI.framework in Embed Frameworks */, 57588B462DD640C100DBD9A7 /* ReducerKit.framework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -51,6 +61,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 57588CE42DD9B4D500DBD9A7 /* AppDI.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = AppDI.xcodeproj; path = AppDI/AppDI.xcodeproj; sourceTree = ""; }; 57606D652DD63B14005EBE3D /* ReducerKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReducerKit.xcodeproj; path = ReducerKit/ReducerKit.xcodeproj; sourceTree = ""; }; 57AC52EF2DA4FBC900BA84BD /* DesignSystem.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = DesignSystem.xcodeproj; path = DesignSystem/DesignSystem.xcodeproj; sourceTree = ""; }; 57AC53092DA4FC1800BA84BD /* Util.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Util.xcodeproj; path = Util/Util.xcodeproj; sourceTree = ""; }; @@ -85,6 +96,7 @@ buildActionMask = 2147483647; files = ( 57AC56A82DA5120600BA84BD /* Util.framework in Frameworks */, + 57588CEC2DD9B66C00DBD9A7 /* AppDI.framework in Frameworks */, 57944D0C2DC52AEF00EF3D9A /* GoogleSignIn in Frameworks */, 57E1F9852DA8F9B40033F30C /* FirebaseAuth in Frameworks */, 57E1F9872DA8F9B40033F30C /* FirebaseCore in Frameworks */, @@ -105,6 +117,14 @@ name = Products; sourceTree = ""; }; + 57588CE52DD9B4D500DBD9A7 /* Products */ = { + isa = PBXGroup; + children = ( + 57588CE92DD9B4D600DBD9A7 /* AppDI.framework */, + ); + name = Products; + sourceTree = ""; + }; 57AC55422DA507EC00BA84BD /* Frameworks */ = { isa = PBXGroup; children = ( @@ -115,6 +135,7 @@ 57AC565F2DA511A900BA84BD /* Core */ = { isa = PBXGroup; children = ( + 57588CE42DD9B4D500DBD9A7 /* AppDI.xcodeproj */, 57AC52EF2DA4FBC900BA84BD /* DesignSystem.xcodeproj */, 57AC53232DA4FC4900BA84BD /* LinkMetadataKit.xcodeproj */, 57606D652DD63B14005EBE3D /* ReducerKit.xcodeproj */, @@ -222,6 +243,10 @@ productRefGroup = 57BFD8C42DA4E19600648AD4 /* Products */; projectDirPath = ""; projectReferences = ( + { + ProductGroup = 57588CE52DD9B4D500DBD9A7 /* Products */; + ProjectRef = 57588CE42DD9B4D500DBD9A7 /* AppDI.xcodeproj */; + }, { ProductGroup = 57588A542DD63B5900DBD9A7 /* Products */; ProjectRef = 57606D652DD63B14005EBE3D /* ReducerKit.xcodeproj */; @@ -246,6 +271,13 @@ remoteRef = 57588B402DD63D9700DBD9A7 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 57588CE92DD9B4D600DBD9A7 /* AppDI.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = AppDI.framework; + remoteRef = 57588CE82DD9B4D600DBD9A7 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 57AC56732DA511E900BA84BD /* Util.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; diff --git a/Mark-In/Sources/App/AuthUserManager.swift b/Mark-In/Sources/App/AuthUserManager.swift index ce639e2..d1af254 100644 --- a/Mark-In/Sources/App/AuthUserManager.swift +++ b/Mark-In/Sources/App/AuthUserManager.swift @@ -53,3 +53,7 @@ final class AuthUserManagerImpl: AuthUserManager { user = nil } } + +enum AuthError: Error { + case unauthenticated +} diff --git a/Mark-In/Sources/App/DIContainer.swift b/Mark-In/Sources/App/DIContainer+.swift similarity index 59% rename from Mark-In/Sources/App/DIContainer.swift rename to Mark-In/Sources/App/DIContainer+.swift index f353f61..b2f26d6 100644 --- a/Mark-In/Sources/App/DIContainer.swift +++ b/Mark-In/Sources/App/DIContainer+.swift @@ -7,78 +7,75 @@ import Foundation +import AppDI import LinkMetadataKit import LinkMetadataKitInterface -// TODO: 코어 모듈로 이전 -final class DIContainer { - static let shared = DIContainer() - private var dependencies: [String: Any] = [:] - - private init() {} - - func register(_ dependency: T) { - let key = String(describing: T.self) - dependencies[key] = dependency - } - - func resolve() -> T { - let key = String(describing: T.self) - let dependency = dependencies[key] - - guard let dependency = dependency as? T else { - fatalError("\(key)는 register되지 않았어어요. resolve 부르기전에 register 해주세요") - } - - return dependency - } -} extension DIContainer { func registerDependencies() { + /// Core-Level 의존성 등록 + registerCoreDependencies() - /// Core + /// Domain-Level 의존성 등록 + registerRepositoryDependencies() + registerUseCaseDependencies() + } +} + +private extension DIContainer { + // MARK: - Core + func registerCoreDependencies() { let keychainStore: KeychainStore = KeychainStoreImpl() let linkMetadataProvider: LinkMetadataProvider = LinkMetadataProviderImpl() let authUserManager: AuthUserManager = AuthUserManagerImpl( keychainStore: keychainStore ) + register(keychainStore) register(linkMetadataProvider) register(authUserManager) - - /// Repository + } + + // MARK: - Domain - Repository + func registerRepositoryDependencies() { let folderRepository: FolderRepository = FolderRepositoryImpl() let linkRepository: LinkRepository = LinkRepositoryImpl( - linkMetadataProvider: linkMetadataProvider + linkMetadataProvider: resolve() ) register(folderRepository) register(linkRepository) - - /// UseCase + } + + // MARK: - Domain - UseCase + func registerUseCaseDependencies() { let fetchLinkListUseCase: FetchLinkListUseCase = FetchLinkListUseCaseImpl( - linkRepository: linkRepository + authUserManager: resolve(), + linkRepository: resolve() ) let fetchFolderListUseCase: FetchFolderListUseCase = FetchFolderListUseCaseImpl( - folderRepository: folderRepository + authUserManager: resolve(), + folderRepository: resolve() ) let generateLinkUseCase: GenerateLinkUseCase = GenerateLinkUseCaseImpl( - linkRepository: linkRepository + authUserManager: resolve(), + linkRepository: resolve() ) let generateFolderUseCase: GenerateFolderUseCase = GenerateFolderUseCaseImpl( - folderRepository: folderRepository + authUserManager: resolve(), + folderRepository: resolve() ) let signInUseCase: SignInUseCase = SignInUseCaseImpl( - keychainStore: keychainStore, - authUserManager: authUserManager + keychainStore: resolve(), + authUserManager: resolve() ) let signOutUseCase: SignOutUseCase = SignOutUseCaseImpl( - authUserManager: authUserManager + authUserManager: resolve() ) let withdrawalUseCase: WithdrawalUseCase = WithdrawalUseCaseImpl( - keychainStore: keychainStore, - authUserManager: authUserManager + keychainStore: resolve(), + authUserManager: resolve() ) register(fetchLinkListUseCase) diff --git a/Mark-In/Sources/App/Mark_InApp.swift b/Mark-In/Sources/App/Mark_InApp.swift index c22a66f..7dfecc9 100644 --- a/Mark-In/Sources/App/Mark_InApp.swift +++ b/Mark-In/Sources/App/Mark_InApp.swift @@ -10,6 +10,7 @@ import SwiftUI import FirebaseCore import GoogleSignIn +import AppDI import DesignSystem @main diff --git a/Mark-In/Sources/App/RootView.swift b/Mark-In/Sources/App/RootView.swift index 9c758de..7e4f2da 100644 --- a/Mark-In/Sources/App/RootView.swift +++ b/Mark-In/Sources/App/RootView.swift @@ -7,6 +7,7 @@ import SwiftUI +import AppDI import DesignSystem struct RootView: View { diff --git a/Mark-In/Sources/Data/DTOs/LinkDTO.swift b/Mark-In/Sources/Data/DTOs/WebLinkDTO.swift similarity index 89% rename from Mark-In/Sources/Data/DTOs/LinkDTO.swift rename to Mark-In/Sources/Data/DTOs/WebLinkDTO.swift index 21b8f1c..087d46e 100644 --- a/Mark-In/Sources/Data/DTOs/LinkDTO.swift +++ b/Mark-In/Sources/Data/DTOs/WebLinkDTO.swift @@ -7,7 +7,7 @@ import Foundation -struct LinkDTO: Codable { +struct WebLinkDTO: Codable { var id: String var url: String var title: String? @@ -18,8 +18,8 @@ struct LinkDTO: Codable { var lastAccessedAt: Date? var folderID: String? - func toEntity() -> Link { - Link( + func toEntity() -> WebLink { + WebLink( id: self.id, url: self.url, title: self.title, diff --git a/Mark-In/Sources/Data/FirebaseEndpoint.swift b/Mark-In/Sources/Data/FirebaseEndpoint.swift new file mode 100644 index 0000000..77860e2 --- /dev/null +++ b/Mark-In/Sources/Data/FirebaseEndpoint.swift @@ -0,0 +1,50 @@ +// +// FirebaseEndpoint.swift +// Mark-In +// +// Created by 이정동 on 4/30/25. +// + +import Foundation + +enum FirebaseEndpoint { + enum FirestoreDB { + case links(userID: String) + case link(userID: String, linkID: String) + case folders(userID: String) + case folder(userID: String, folderID: String) + + var path: String { + switch self { + case .links(let userID): + "users/\(userID)/links" + case let .link(userID, linkID): + "users/\(userID)/links/\(linkID)" + case .folders(let userID): + "users/\(userID)/folders" + case let .folder(userID, folderID): + "users/\(userID)/folders/\(folderID)" + } + } + } + + enum Storage { + case thumbnails(userID: String) + case thumbnail(userID: String, thumbnailID: String) + case favicons(userID: String) + case favicon(userID: String, faviconID: String) + + var path: String { + switch self { + case .thumbnails(let userID): + "users/\(userID)/thumbnails" + case let .thumbnail(userID, thumbnailID): + "users/\(userID)/thumbnails/\(thumbnailID)" + case .favicons(let userID): + "users/\(userID)/favicons" + case let .favicon(userID, faviconID): + "users/\(userID)/favicons/\(faviconID)" + } + } + } +} diff --git a/Mark-In/Sources/Data/FirebasePath.swift b/Mark-In/Sources/Data/FirebasePath.swift deleted file mode 100644 index c1887d8..0000000 --- a/Mark-In/Sources/Data/FirebasePath.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// FirebasePath.swift -// Mark-In -// -// Created by 이정동 on 4/30/25. -// - -import Foundation - -enum FirebasePath { - case links(userID: String) - case folders(userID: String) - case thumbnails(userID: String) - case favicons(userID: String) - - var path: String { - switch self { - case .links(let userID): - "users/\(userID)/links" - case .folders(let userID): - "users/\(userID)/folders" - case .thumbnails(let userID): - "users/\(userID)/thumbnails" - case .favicons(let userID): - "users/\(userID)/favicons" - } - } -} diff --git a/Mark-In/Sources/Data/Repositories/FolderRepositoryImpl.swift b/Mark-In/Sources/Data/Repositories/FolderRepositoryImpl.swift index dfbe31f..7c87bb8 100644 --- a/Mark-In/Sources/Data/Repositories/FolderRepositoryImpl.swift +++ b/Mark-In/Sources/Data/Repositories/FolderRepositoryImpl.swift @@ -17,7 +17,7 @@ struct FolderRepositoryImpl: FolderRepository { func create(userID: String, folder: WriteFolder) async throws -> Folder { /// 1. Folder 문서 참조 생성 - let path = FirebasePath.folders(userID: userID).path + let path = FirebaseEndpoint.FirestoreDB.folders(userID: userID).path let folderDocRef = db.collection(path).document() /// 2. Firestore에 저장할 DTO 객체 생성 @@ -45,7 +45,7 @@ struct FolderRepositoryImpl: FolderRepository { func fetchAll(userID: String) async throws -> [Folder] { /// 1. Folders 컬렉션 참조 생성 - let path = FirebasePath.folders(userID: userID).path + let path = FirebaseEndpoint.FirestoreDB.folders(userID: userID).path let folderColRef = db.collection(path) /// 2. 컬렉션의 모든 문서 가져오기 @@ -59,8 +59,8 @@ struct FolderRepositoryImpl: FolderRepository { func update(userID: String, folder: Folder) async throws { /// 1. Folder 문서 참조 생성 - let folderID = folder.id ?? "" - let path = FirebasePath.folders(userID: userID).path + "/\(folderID)" + guard let folderID = folder.id else { return } + let path = FirebaseEndpoint.FirestoreDB.folder(userID: userID, folderID: folderID).path let folderDocRef = db.document(path) /// 2. Entity를 DTO로 변환 @@ -85,8 +85,8 @@ struct FolderRepositoryImpl: FolderRepository { func delete(userID: String, folder: Folder) async throws { /// 1. Folder 문서 참조 생성 - let folderID = folder.id ?? "" - let path = FirebasePath.folders(userID: userID).path + "/\(folderID)" + guard let folderID = folder.id else { return } + let path = FirebaseEndpoint.FirestoreDB.folder(userID: userID, folderID: folderID).path let folderDocRef = db.document(path) /// 2. Folder 삭제 diff --git a/Mark-In/Sources/Data/Repositories/LinkRepositoryImpl.swift b/Mark-In/Sources/Data/Repositories/LinkRepositoryImpl.swift index 692e308..ca8e109 100644 --- a/Mark-In/Sources/Data/Repositories/LinkRepositoryImpl.swift +++ b/Mark-In/Sources/Data/Repositories/LinkRepositoryImpl.swift @@ -25,9 +25,9 @@ struct LinkRepositoryImpl: LinkRepository { self.linkMetadataProvider = linkMetadataProvider } - func create(userID: String, link: WriteLink) async throws -> Link { + func create(userID: String, link: WriteLink) async throws -> WebLink { /// 1. Link 문서 참조 생성 - let path = FirebasePath.links(userID: userID).path + let path = FirebaseEndpoint.FirestoreDB.links(userID: userID).path let linkDocRef = db.collection(path).document() /// 2. URL의 메타데이터 가져오기 @@ -41,7 +41,7 @@ struct LinkRepositoryImpl: LinkRepository { ) /// 4. Firestore에 저장할 DTO 객체 생성 - let linkDTO = LinkDTO( + let linkDTO = WebLinkDTO( id: linkDocRef.documentID, url: link.url, title: link.title ?? metadata.title, @@ -69,9 +69,9 @@ struct LinkRepositoryImpl: LinkRepository { return linkDTO.toEntity() } - func fetchAll(userID: String) async throws -> [Link] { + func fetchAll(userID: String) async throws -> [WebLink] { /// 1. Links 컬렉션 참조 생성 - let path = FirebasePath.links(userID: userID).path + let path = FirebaseEndpoint.FirestoreDB.links(userID: userID).path let linkColRef = db.collection(path) /// 2. 컬렉션의 모든 문서 가져오기 @@ -79,17 +79,17 @@ struct LinkRepositoryImpl: LinkRepository { /// 3. 문서를 DTO로 변환 후 다시 Entity로 변환 return snapshot.documents.compactMap { - try? $0.data(as: LinkDTO.self).toEntity() + try? $0.data(as: WebLinkDTO.self).toEntity() } } - func update(userID: String, link: Link) async throws { + func update(userID: String, link: WebLink) async throws { /// 1. Link 문서 참조 생성 - let path = FirebasePath.links(userID: userID).path + "/\(link.id)" + let path = FirebaseEndpoint.FirestoreDB.link(userID: userID, linkID: link.id).path let linkDocRef = db.document(path) /// 2. Entity를 DTO로 변환 - let linkDTO = LinkDTO( + let linkDTO = WebLinkDTO( id: link.id, url: link.url, title: link.title, @@ -114,9 +114,9 @@ struct LinkRepositoryImpl: LinkRepository { } } - func delete(userID: String, link: Link) async throws { + func delete(userID: String, link: WebLink) async throws { /// 1. Link 문서 참조 생성 - let path = FirebasePath.links(userID: userID).path + "/\(link.id)" + let path = FirebaseEndpoint.FirestoreDB.link(userID: userID, linkID: link.id).path let linkDocRef = db.document(path) /// 2. 이미지 데이터 삭제 @@ -137,8 +137,8 @@ private extension LinkRepositoryImpl { ) async throws -> ImageUrls { /// 1. 썸네일, 파비콘 이미지 데이터를 저장할 storage 주소 생성 - let thumbnailPath = FirebasePath.thumbnails(userID: userID).path + "/\(fileID)" - let faviconPath = FirebasePath.favicons(userID: userID).path + "/\(fileID)" + let thumbnailPath = FirebaseEndpoint.Storage.thumbnail(userID: userID, thumbnailID: fileID).path + let faviconPath = FirebaseEndpoint.Storage.favicon(userID: userID, faviconID: fileID).path let thumbnailRef = storage.child(thumbnailPath) let faviconRef = storage.child(faviconPath) @@ -164,8 +164,8 @@ private extension LinkRepositoryImpl { func deleteImageData(userID: String, fileID: String) async throws { /// 1. 썸네일, 파비콘 이미지 참조 주소 생성 - let thumbnailPath = FirebasePath.thumbnails(userID: userID).path + "/\(fileID)" - let faviconPath = FirebasePath.favicons(userID: userID).path + "/\(fileID)" + let thumbnailPath = FirebaseEndpoint.Storage.thumbnail(userID: userID, thumbnailID: fileID).path + let faviconPath = FirebaseEndpoint.Storage.favicon(userID: userID, faviconID: fileID).path let thumbnailRef = storage.child(thumbnailPath) let faviconRef = storage.child(faviconPath) diff --git a/Mark-In/Sources/Domain/Entities/Link.swift b/Mark-In/Sources/Domain/Entities/WebLink.swift similarity index 91% rename from Mark-In/Sources/Domain/Entities/Link.swift rename to Mark-In/Sources/Domain/Entities/WebLink.swift index fd736d4..531f6f9 100644 --- a/Mark-In/Sources/Domain/Entities/Link.swift +++ b/Mark-In/Sources/Domain/Entities/WebLink.swift @@ -7,7 +7,7 @@ import Foundation -struct Link: Hashable { +struct WebLink: Hashable { var id: String var url: String var title: String? diff --git a/Mark-In/Sources/Domain/Interfaces/LinkRepository.swift b/Mark-In/Sources/Domain/Interfaces/LinkRepository.swift index f04c987..32c665f 100644 --- a/Mark-In/Sources/Domain/Interfaces/LinkRepository.swift +++ b/Mark-In/Sources/Domain/Interfaces/LinkRepository.swift @@ -8,8 +8,8 @@ import Foundation protocol LinkRepository { - func create(userID: String, link: WriteLink) async throws -> Link - func fetchAll(userID: String) async throws -> [Link] - func update(userID: String, link: Link) async throws - func delete(userID: String, link: Link) async throws + func create(userID: String, link: WriteLink) async throws -> WebLink + func fetchAll(userID: String) async throws -> [WebLink] + func update(userID: String, link: WebLink) async throws + func delete(userID: String, link: WebLink) async throws } diff --git a/Mark-In/Sources/Domain/UseCases/Implements/FetchFolderListUseCaseImpl.swift b/Mark-In/Sources/Domain/UseCases/Implements/FetchFolderListUseCaseImpl.swift index 0346210..166c70a 100644 --- a/Mark-In/Sources/Domain/UseCases/Implements/FetchFolderListUseCaseImpl.swift +++ b/Mark-In/Sources/Domain/UseCases/Implements/FetchFolderListUseCaseImpl.swift @@ -9,13 +9,19 @@ import Foundation struct FetchFolderListUseCaseImpl: FetchFolderListUseCase { + private let authUserManager: AuthUserManager private let folderRepository: FolderRepository - init(folderRepository: FolderRepository) { + init( + authUserManager: AuthUserManager, + folderRepository: FolderRepository + ) { + self.authUserManager = authUserManager self.folderRepository = folderRepository } - func execute(userID: String) async throws -> [Folder] { - try await folderRepository.fetchAll(userID: userID) + func execute() async throws -> [Folder] { + guard let user = authUserManager.user else { throw AuthError.unauthenticated } + return try await folderRepository.fetchAll(userID: user.id) } } diff --git a/Mark-In/Sources/Domain/UseCases/Implements/FetchLinkListUseCaseImpl.swift b/Mark-In/Sources/Domain/UseCases/Implements/FetchLinkListUseCaseImpl.swift index fa994d8..df14f46 100644 --- a/Mark-In/Sources/Domain/UseCases/Implements/FetchLinkListUseCaseImpl.swift +++ b/Mark-In/Sources/Domain/UseCases/Implements/FetchLinkListUseCaseImpl.swift @@ -9,13 +9,19 @@ import Foundation struct FetchLinkListUseCaseImpl: FetchLinkListUseCase { + private let authUserManager: AuthUserManager private let linkRepository: LinkRepository - init(linkRepository: LinkRepository) { + init( + authUserManager: AuthUserManager, + linkRepository: LinkRepository + ) { + self.authUserManager = authUserManager self.linkRepository = linkRepository } - func execute(userID: String) async throws -> [Link] { - try await linkRepository.fetchAll(userID: userID) + func execute() async throws -> [WebLink] { + guard let user = authUserManager.user else { throw AuthError.unauthenticated } + return try await linkRepository.fetchAll(userID: user.id) } } diff --git a/Mark-In/Sources/Domain/UseCases/Implements/GenerateFolderUseCaseImpl.swift b/Mark-In/Sources/Domain/UseCases/Implements/GenerateFolderUseCaseImpl.swift index 96aba3f..e5ff84d 100644 --- a/Mark-In/Sources/Domain/UseCases/Implements/GenerateFolderUseCaseImpl.swift +++ b/Mark-In/Sources/Domain/UseCases/Implements/GenerateFolderUseCaseImpl.swift @@ -9,18 +9,22 @@ import Foundation struct GenerateFolderUseCaseImpl: GenerateFolderUseCase { + private let authUserManager: AuthUserManager private let folderRepository: FolderRepository - init(folderRepository: FolderRepository) { + init( + authUserManager: AuthUserManager, + folderRepository: FolderRepository + ) { + self.authUserManager = authUserManager self.folderRepository = folderRepository } func execute(writeFolder: WriteFolder) async throws -> Folder { - // TODO: #29번 PR 머지 후 AuthManager를 통해 현재 로그인 유저 정보 가져옴 - let user = "123" + guard let user = authUserManager.user else { throw AuthError.unauthenticated } - let newFolder = try await folderRepository.create(userID: user, folder: writeFolder) + let newFolder = try await folderRepository.create(userID: user.id, folder: writeFolder) return newFolder } } diff --git a/Mark-In/Sources/Domain/UseCases/Implements/GenerateLinkUseCaseImpl.swift b/Mark-In/Sources/Domain/UseCases/Implements/GenerateLinkUseCaseImpl.swift index fa6c1e4..d153510 100644 --- a/Mark-In/Sources/Domain/UseCases/Implements/GenerateLinkUseCaseImpl.swift +++ b/Mark-In/Sources/Domain/UseCases/Implements/GenerateLinkUseCaseImpl.swift @@ -9,18 +9,22 @@ import Foundation struct GenerateLinkUseCaseImpl: GenerateLinkUseCase { + private let authUserManager: AuthUserManager private let linkRepository: LinkRepository - init(linkRepository: LinkRepository) { + init( + authUserManager: AuthUserManager, + linkRepository: LinkRepository + ) { + self.authUserManager = authUserManager self.linkRepository = linkRepository } - func execute(writeLink: WriteLink) async throws -> Link { + func execute(writeLink: WriteLink) async throws -> WebLink { - // TODO: #29번 PR 머지 후 AuthManager를 통해 현재 로그인 유저 정보 가져옴 - let user = "testUser" + guard let user = authUserManager.user else { throw AuthError.unauthenticated } - let newLink = try await linkRepository.create(userID: user, link: writeLink) + let newLink = try await linkRepository.create(userID: user.id, link: writeLink) return newLink } } diff --git a/Mark-In/Sources/Domain/UseCases/Interfaces/FetchFolderListUseCase.swift b/Mark-In/Sources/Domain/UseCases/Interfaces/FetchFolderListUseCase.swift index 5104c8d..45783b8 100644 --- a/Mark-In/Sources/Domain/UseCases/Interfaces/FetchFolderListUseCase.swift +++ b/Mark-In/Sources/Domain/UseCases/Interfaces/FetchFolderListUseCase.swift @@ -8,5 +8,5 @@ import Foundation protocol FetchFolderListUseCase { - func execute(userID: String) async throws -> [Folder] + func execute() async throws -> [Folder] } diff --git a/Mark-In/Sources/Domain/UseCases/Interfaces/FetchLinkListUseCase.swift b/Mark-In/Sources/Domain/UseCases/Interfaces/FetchLinkListUseCase.swift index 7ab0fd0..bfe39d7 100644 --- a/Mark-In/Sources/Domain/UseCases/Interfaces/FetchLinkListUseCase.swift +++ b/Mark-In/Sources/Domain/UseCases/Interfaces/FetchLinkListUseCase.swift @@ -8,5 +8,5 @@ import Foundation protocol FetchLinkListUseCase { - func execute(userID: String) async throws -> [Link] + func execute() async throws -> [WebLink] } diff --git a/Mark-In/Sources/Domain/UseCases/Interfaces/GenerateLinkUseCase.swift b/Mark-In/Sources/Domain/UseCases/Interfaces/GenerateLinkUseCase.swift index d457584..7d65f9e 100644 --- a/Mark-In/Sources/Domain/UseCases/Interfaces/GenerateLinkUseCase.swift +++ b/Mark-In/Sources/Domain/UseCases/Interfaces/GenerateLinkUseCase.swift @@ -8,5 +8,5 @@ import Foundation protocol GenerateLinkUseCase { - func execute(writeLink: WriteLink) async throws -> Link + func execute(writeLink: WriteLink) async throws -> WebLink } diff --git a/Mark-In/Sources/Feature/AddFolder/AddFolderReducer.swift b/Mark-In/Sources/Feature/AddFolder/AddFolderReducer.swift index ada7577..2c90e9a 100644 --- a/Mark-In/Sources/Feature/AddFolder/AddFolderReducer.swift +++ b/Mark-In/Sources/Feature/AddFolder/AddFolderReducer.swift @@ -7,6 +7,7 @@ import Foundation +import AppDI import ReducerKit struct AddFolderReducer: Reducer { @@ -22,11 +23,7 @@ struct AddFolderReducer: Reducer { case updateErrorState(Bool) } - private let generateFolderUseCase: GenerateFolderUseCase - - init() { - self.generateFolderUseCase = DIContainer.shared.resolve() - } + @Dependency private var generateFolderUseCase: GenerateFolderUseCase func reduce(into state: inout State, action: Action) -> Effect { switch action { diff --git a/Mark-In/Sources/Feature/AddLink/AddLinkReducer.swift b/Mark-In/Sources/Feature/AddLink/AddLinkReducer.swift index 3389236..06b7e40 100644 --- a/Mark-In/Sources/Feature/AddLink/AddLinkReducer.swift +++ b/Mark-In/Sources/Feature/AddLink/AddLinkReducer.swift @@ -7,26 +7,23 @@ import Foundation +import AppDI import ReducerKit struct AddLinkReducer: Reducer { struct State { - var createdLink: Link? + var createdLink: WebLink? var isSaving: Bool = false var isError: Bool = false } enum Action { case addLinkButtonTapped(link: WriteLink) - case completeSave(Link) + case completeSave(WebLink) case occurError(Bool) } - private let generateLinkUseCase: GenerateLinkUseCase - - init() { - self.generateLinkUseCase = DIContainer.shared.resolve() - } + @Dependency private var generateLinkUseCase: GenerateLinkUseCase func reduce(into state: inout State, action: Action) -> Effect { switch action { diff --git a/Mark-In/Sources/Feature/AddLink/AddLinkView.swift b/Mark-In/Sources/Feature/AddLink/AddLinkView.swift index 8ea8029..e2951fe 100644 --- a/Mark-In/Sources/Feature/AddLink/AddLinkView.swift +++ b/Mark-In/Sources/Feature/AddLink/AddLinkView.swift @@ -22,7 +22,7 @@ struct AddLinkView: View { @State private var currentFolder: Folder private let folders: [Folder] - private let completion: (Link) -> Void + private let completion: (WebLink) -> Void private var isSaving: Bool { store.state.isSaving @@ -30,7 +30,7 @@ struct AddLinkView: View { init( folders: [Folder], - completion: @escaping (Link) -> Void + completion: @escaping (WebLink) -> Void ) { self.folders = folders self._currentFolder = State(initialValue: folders[0]) @@ -92,7 +92,7 @@ struct AddLinkView: View { Button { let link = WriteLink( url: url, - title: title, + title: title.isEmpty ? nil : title, folderID: currentFolder.id ) store.send(.addLinkButtonTapped(link: link)) diff --git a/Mark-In/Sources/Feature/Login/LoginReducer.swift b/Mark-In/Sources/Feature/Login/LoginReducer.swift index e7aed64..b21a2ef 100644 --- a/Mark-In/Sources/Feature/Login/LoginReducer.swift +++ b/Mark-In/Sources/Feature/Login/LoginReducer.swift @@ -12,6 +12,7 @@ import SwiftUI import FirebaseAuth import GoogleSignIn +import AppDI import ReducerKit import Util @@ -29,11 +30,7 @@ struct LoginReducer: Reducer { case empty } - private let signInUseCase: SignInUseCase - - init() { - self.signInUseCase = DIContainer.shared.resolve() - } + @Dependency private var signInUseCase: SignInUseCase func reduce(into state: inout State, action: Action) -> Effect { switch action { diff --git a/Mark-In/Sources/Feature/Main/LinkListView.swift b/Mark-In/Sources/Feature/Main/LinkListView.swift index 72b76d1..b7d4e70 100644 --- a/Mark-In/Sources/Feature/Main/LinkListView.swift +++ b/Mark-In/Sources/Feature/Main/LinkListView.swift @@ -20,7 +20,7 @@ struct LinkListView: View { let store: StoreOf - private var links: [Link] { + private var links: [WebLink] { let totalLinks = store.state.links let tab = store.state.selectedTab ?? .total @@ -76,38 +76,23 @@ struct LinkListView: View { private struct LinkCell: View { - let link: Link + let link: WebLink var body: some View { ZStack(alignment: .bottom) { - // TODO: Link 네이밍 충돌로, 이후 리팩토링 작업 후 적용 예정 -// Link(destination: URL(string: link.url)) { -// AsyncImage( -// url: URL(string: link.thumbnailUrl ?? "") -// ) { image in -// image -// .resizable() -// .aspectRatio(contentMode: .fill) -// } placeholder: { -// Rectangle() -// .fill(.markWhite70) -// } -// .frame(width: ViewConstants.cellWidth, height: ViewConstants.cellHeight) -// } - - AsyncImage( - url: URL(string: link.thumbnailUrl ?? "") - ) { image in - image - .resizable() - .aspectRatio(contentMode: .fill) - } placeholder: { - Rectangle() + Link(destination: URL(string: link.url)!) { + AsyncImage( + url: URL(string: link.thumbnailUrl ?? "") + ) { image in + image + .resizable() + .aspectRatio(contentMode: .fill) + } placeholder: { + Rectangle() + .fill(.markWhite70) + } + .frame(width: ViewConstants.cellWidth, height: ViewConstants.cellHeight) } - .frame( - width: ViewConstants.cellWidth, - height: ViewConstants.cellHeight - ) VStack(alignment: .leading, spacing: 0) { headerTitle diff --git a/Mark-In/Sources/Feature/Main/MainReducer.swift b/Mark-In/Sources/Feature/Main/MainReducer.swift index 526342c..57b4901 100644 --- a/Mark-In/Sources/Feature/Main/MainReducer.swift +++ b/Mark-In/Sources/Feature/Main/MainReducer.swift @@ -7,13 +7,14 @@ import Foundation +import AppDI import ReducerKit struct MainReducer: Reducer { struct State { var isLoading: Bool = true - var links: [Link] = [] + var links: [WebLink] = [] var defaultTabs: [SidebarTab] = [.total, .pin, .nonRead] var folderTabs: [SidebarTab] = [] @@ -24,12 +25,12 @@ struct MainReducer: Reducer { enum Action { case onAppear - case fetchSucceeded([Link], [Folder]) + case fetchSucceeded([WebLink], [Folder]) case changeTab(SidebarTab?) case presentSheet(SheetType?) - case didCreateLink(Link) + case didCreateLink(WebLink) case didCreateFolder(Folder) case occuredError @@ -37,22 +38,16 @@ struct MainReducer: Reducer { case empty } - private let fetchLinkListUseCase: FetchLinkListUseCase - private let fetchFolderListUseCase: FetchFolderListUseCase - - init() { - self.fetchLinkListUseCase = DIContainer.shared.resolve() - self.fetchFolderListUseCase = DIContainer.shared.resolve() - } + @Dependency private var fetchLinkListUseCase: FetchLinkListUseCase + @Dependency private var fetchFolderListUseCase: FetchFolderListUseCase func reduce(into state: inout State, action: Action) -> Effect { switch action { case .onAppear: return .run { do { - // TODO: 실제 로그인 유저 ID 전달 - async let links = await self.fetchLinkListUseCase.execute(userID: "testUser") - async let folders = await self.fetchFolderListUseCase.execute(userID: "testUser") + async let links = await self.fetchLinkListUseCase.execute() + async let folders = await self.fetchFolderListUseCase.execute() return try await .fetchSucceeded(links, folders) } catch { diff --git a/Mark-In/Sources/Feature/MyPage/MyPageReducer.swift b/Mark-In/Sources/Feature/MyPage/MyPageReducer.swift index 9bf6e3b..2698f7f 100644 --- a/Mark-In/Sources/Feature/MyPage/MyPageReducer.swift +++ b/Mark-In/Sources/Feature/MyPage/MyPageReducer.swift @@ -10,6 +10,7 @@ import Foundation import FirebaseAuth import GoogleSignIn +import AppDI import ReducerKit struct MyPageReducer: Reducer { @@ -26,13 +27,8 @@ struct MyPageReducer: Reducer { case empty } - private let signOutUseCase: SignOutUseCase - private let withdrawalUseCase: WithdrawalUseCase - - init() { - self.signOutUseCase = DIContainer.shared.resolve() - self.withdrawalUseCase = DIContainer.shared.resolve() - } + @Dependency private var signOutUseCase: SignOutUseCase + @Dependency private var withdrawalUseCase: WithdrawalUseCase func reduce(into state: inout State, action: Action) -> Effect { switch action {