diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..044346b --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +._.DS_Store +**/.DS_Store +**/._.DS_Store + +tyte/.DS_Store +tyte/Components/.DS_Store +xcuserdata/ + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# + +Pods/ diff --git a/APIConstants.swift b/APIConstants.swift new file mode 100644 index 0000000..12c298b --- /dev/null +++ b/APIConstants.swift @@ -0,0 +1,13 @@ +// +// APIConstants.swift +// tyte +// +// Created by Neoself on 10/21/24. +// + +import Foundation + +struct APIConstants { + static let isDevelopment = true + static let baseUrl = isDevelopment ? "http://138.2.124.93:8080/api/v1" : "아직 없음" +} diff --git a/AppState.swift b/AppState.swift new file mode 100644 index 0000000..0adf012 --- /dev/null +++ b/AppState.swift @@ -0,0 +1,11 @@ +import Foundation + +class AppState: ObservableObject { + static let shared = AppState() // 전역적으로 접근 가능한 인스턴스 생성. 이는 싱글톤으로 구현되어있는 APIManager에서 appState를 직접 접근하게 하기 위해서임. + @Published var isLoggedIn: Bool = false // Published 래퍼를 통해 상태 변화를 SwiftUI 뷰에 자동반영 + @Published var isGuestMode: Bool = false + @Published var currentToast: ToastType? + + private init() {} +} + diff --git a/Ddom.xcodeproj/project.pbxproj b/Ddom.xcodeproj/project.pbxproj new file mode 100644 index 0000000..c8aeea3 --- /dev/null +++ b/Ddom.xcodeproj/project.pbxproj @@ -0,0 +1,636 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + E517C1392CCE52570000805C /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = E517C1382CCE52570000805C /* .gitignore */; }; + E58F20032CD209EF007940BB /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E58F1FEE2CD209EF007940BB /* AppState.swift */; }; + E58F20192CD21895007940BB /* APIConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = E58F20182CD21895007940BB /* APIConstants.swift */; }; + E58F201F2CD223EF007940BB /* KakaoSDK in Frameworks */ = {isa = PBXBuildFile; productRef = E58F201E2CD223EF007940BB /* KakaoSDK */; }; + E58F20212CD223EF007940BB /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = E58F20202CD223EF007940BB /* KakaoSDKAuth */; }; + E58F20232CD223EF007940BB /* KakaoSDKCommon in Frameworks */ = {isa = PBXBuildFile; productRef = E58F20222CD223EF007940BB /* KakaoSDKCommon */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + E5C704942CBE4F9B00728DF4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = E5C7047B2CBE4F9A00728DF4 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E5C704822CBE4F9A00728DF4; + remoteInfo = Ddom; + }; + E5C7049E2CBE4F9B00728DF4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = E5C7047B2CBE4F9A00728DF4 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E5C704822CBE4F9A00728DF4; + remoteInfo = Ddom; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + E517C1382CCE52570000805C /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; + E58F1FEE2CD209EF007940BB /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; }; + E58F20182CD21895007940BB /* APIConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIConstants.swift; sourceTree = ""; }; + E5C704832CBE4F9A00728DF4 /* Ddom.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ddom.app; sourceTree = BUILT_PRODUCTS_DIR; }; + E5C704932CBE4F9B00728DF4 /* DdomTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DdomTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + E5C7049D2CBE4F9B00728DF4 /* DdomUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DdomUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + E5C704F32CBE5F7000728DF4 /* Exceptions for "Ddom" folder in "Ddom" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = E5C704822CBE4F9A00728DF4 /* Ddom */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + E5C704852CBE4F9A00728DF4 /* Ddom */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + E5C704F32CBE5F7000728DF4 /* Exceptions for "Ddom" folder in "Ddom" target */, + ); + path = Ddom; + sourceTree = ""; + }; + E5C704962CBE4F9B00728DF4 /* DdomTests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = DdomTests; + sourceTree = ""; + }; + E5C704A02CBE4F9B00728DF4 /* DdomUITests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = DdomUITests; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + E5C704802CBE4F9A00728DF4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E58F201F2CD223EF007940BB /* KakaoSDK in Frameworks */, + E58F20232CD223EF007940BB /* KakaoSDKCommon in Frameworks */, + E58F20212CD223EF007940BB /* KakaoSDKAuth in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E5C704902CBE4F9B00728DF4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E5C7049A2CBE4F9B00728DF4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E5C7047A2CBE4F9A00728DF4 = { + isa = PBXGroup; + children = ( + E58F20182CD21895007940BB /* APIConstants.swift */, + E58F1FEE2CD209EF007940BB /* AppState.swift */, + E517C1382CCE52570000805C /* .gitignore */, + E5C704852CBE4F9A00728DF4 /* Ddom */, + E5C704962CBE4F9B00728DF4 /* DdomTests */, + E5C704A02CBE4F9B00728DF4 /* DdomUITests */, + E5C704842CBE4F9A00728DF4 /* Products */, + ); + sourceTree = ""; + }; + E5C704842CBE4F9A00728DF4 /* Products */ = { + isa = PBXGroup; + children = ( + E5C704832CBE4F9A00728DF4 /* Ddom.app */, + E5C704932CBE4F9B00728DF4 /* DdomTests.xctest */, + E5C7049D2CBE4F9B00728DF4 /* DdomUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + E5C704822CBE4F9A00728DF4 /* Ddom */ = { + isa = PBXNativeTarget; + buildConfigurationList = E5C704A72CBE4F9B00728DF4 /* Build configuration list for PBXNativeTarget "Ddom" */; + buildPhases = ( + E5C7047F2CBE4F9A00728DF4 /* Sources */, + E5C704802CBE4F9A00728DF4 /* Frameworks */, + E5C704812CBE4F9A00728DF4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + E5C704852CBE4F9A00728DF4 /* Ddom */, + ); + name = Ddom; + packageProductDependencies = ( + E58F201E2CD223EF007940BB /* KakaoSDK */, + E58F20202CD223EF007940BB /* KakaoSDKAuth */, + E58F20222CD223EF007940BB /* KakaoSDKCommon */, + ); + productName = Ddom; + productReference = E5C704832CBE4F9A00728DF4 /* Ddom.app */; + productType = "com.apple.product-type.application"; + }; + E5C704922CBE4F9B00728DF4 /* DdomTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = E5C704AA2CBE4F9B00728DF4 /* Build configuration list for PBXNativeTarget "DdomTests" */; + buildPhases = ( + E5C7048F2CBE4F9B00728DF4 /* Sources */, + E5C704902CBE4F9B00728DF4 /* Frameworks */, + E5C704912CBE4F9B00728DF4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + E5C704952CBE4F9B00728DF4 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + E5C704962CBE4F9B00728DF4 /* DdomTests */, + ); + name = DdomTests; + packageProductDependencies = ( + ); + productName = DdomTests; + productReference = E5C704932CBE4F9B00728DF4 /* DdomTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + E5C7049C2CBE4F9B00728DF4 /* DdomUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = E5C704AD2CBE4F9B00728DF4 /* Build configuration list for PBXNativeTarget "DdomUITests" */; + buildPhases = ( + E5C704992CBE4F9B00728DF4 /* Sources */, + E5C7049A2CBE4F9B00728DF4 /* Frameworks */, + E5C7049B2CBE4F9B00728DF4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + E5C7049F2CBE4F9B00728DF4 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + E5C704A02CBE4F9B00728DF4 /* DdomUITests */, + ); + name = DdomUITests; + packageProductDependencies = ( + ); + productName = DdomUITests; + productReference = E5C7049D2CBE4F9B00728DF4 /* DdomUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E5C7047B2CBE4F9A00728DF4 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1600; + LastUpgradeCheck = 1600; + TargetAttributes = { + E5C704822CBE4F9A00728DF4 = { + CreatedOnToolsVersion = 16.0; + }; + E5C704922CBE4F9B00728DF4 = { + CreatedOnToolsVersion = 16.0; + TestTargetID = E5C704822CBE4F9A00728DF4; + }; + E5C7049C2CBE4F9B00728DF4 = { + CreatedOnToolsVersion = 16.0; + TestTargetID = E5C704822CBE4F9A00728DF4; + }; + }; + }; + buildConfigurationList = E5C7047E2CBE4F9A00728DF4 /* Build configuration list for PBXProject "Ddom" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = E5C7047A2CBE4F9A00728DF4; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + E58F201D2CD223EF007940BB /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = E5C704842CBE4F9A00728DF4 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E5C704822CBE4F9A00728DF4 /* Ddom */, + E5C704922CBE4F9B00728DF4 /* DdomTests */, + E5C7049C2CBE4F9B00728DF4 /* DdomUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E5C704812CBE4F9A00728DF4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E517C1392CCE52570000805C /* .gitignore in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E5C704912CBE4F9B00728DF4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E5C7049B2CBE4F9B00728DF4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E5C7047F2CBE4F9A00728DF4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E58F20192CD21895007940BB /* APIConstants.swift in Sources */, + E58F20032CD209EF007940BB /* AppState.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E5C7048F2CBE4F9B00728DF4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E5C704992CBE4F9B00728DF4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + E5C704952CBE4F9B00728DF4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E5C704822CBE4F9A00728DF4 /* Ddom */; + targetProxy = E5C704942CBE4F9B00728DF4 /* PBXContainerItemProxy */; + }; + E5C7049F2CBE4F9B00728DF4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E5C704822CBE4F9A00728DF4 /* Ddom */; + targetProxy = E5C7049E2CBE4F9B00728DF4 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + E5C704A52CBE4F9B00728DF4 /* 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; + 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.0; + 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"; + }; + name = Debug; + }; + E5C704A62CBE4F9B00728DF4 /* 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; + 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.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + E5C704A82CBE4F9B00728DF4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Ddom/Ddom.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Ddom/Preview Content\""; + DEVELOPMENT_TEAM = H856SYKNM8; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Ddom/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + 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.clip.ddom; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + E5C704A92CBE4F9B00728DF4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Ddom/Ddom.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Ddom/Preview Content\""; + DEVELOPMENT_TEAM = H856SYKNM8; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Ddom/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + 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.clip.ddom; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + E5C704AB2CBE4F9B00728DF4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = H856SYKNM8; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = clip.DdomTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Ddom.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Ddom"; + }; + name = Debug; + }; + E5C704AC2CBE4F9B00728DF4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = H856SYKNM8; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = clip.DdomTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Ddom.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Ddom"; + }; + name = Release; + }; + E5C704AE2CBE4F9B00728DF4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = H856SYKNM8; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = clip.DdomUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Ddom; + }; + name = Debug; + }; + E5C704AF2CBE4F9B00728DF4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = H856SYKNM8; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = clip.DdomUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Ddom; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E5C7047E2CBE4F9A00728DF4 /* Build configuration list for PBXProject "Ddom" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E5C704A52CBE4F9B00728DF4 /* Debug */, + E5C704A62CBE4F9B00728DF4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E5C704A72CBE4F9B00728DF4 /* Build configuration list for PBXNativeTarget "Ddom" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E5C704A82CBE4F9B00728DF4 /* Debug */, + E5C704A92CBE4F9B00728DF4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E5C704AA2CBE4F9B00728DF4 /* Build configuration list for PBXNativeTarget "DdomTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E5C704AB2CBE4F9B00728DF4 /* Debug */, + E5C704AC2CBE4F9B00728DF4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E5C704AD2CBE4F9B00728DF4 /* Build configuration list for PBXNativeTarget "DdomUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E5C704AE2CBE4F9B00728DF4 /* Debug */, + E5C704AF2CBE4F9B00728DF4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + E58F201D2CD223EF007940BB /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/kakao/kakao-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.23.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + E58F201E2CD223EF007940BB /* KakaoSDK */ = { + isa = XCSwiftPackageProductDependency; + package = E58F201D2CD223EF007940BB /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDK; + }; + E58F20202CD223EF007940BB /* KakaoSDKAuth */ = { + isa = XCSwiftPackageProductDependency; + package = E58F201D2CD223EF007940BB /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKAuth; + }; + E58F20222CD223EF007940BB /* KakaoSDKCommon */ = { + isa = XCSwiftPackageProductDependency; + package = E58F201D2CD223EF007940BB /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKCommon; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = E5C7047B2CBE4F9A00728DF4 /* Project object */; +} diff --git a/Ddom.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Ddom.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Ddom.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Ddom.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Ddom.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..6981761 --- /dev/null +++ b/Ddom.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,24 @@ +{ + "originHash" : "0a9a0565c23645eacdcd0d177a036429567ab510b257a7f2538ea8bb318bf8d2", + "pins" : [ + { + "identity" : "alamofire", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Alamofire/Alamofire.git", + "state" : { + "revision" : "e16d3481f5ed35f0472cb93350085853d754913f", + "version" : "5.10.1" + } + }, + { + "identity" : "kakao-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kakao/kakao-ios-sdk", + "state" : { + "revision" : "ab4309c1950550add307046ad1e08024c7514603", + "version" : "2.23.0" + } + } + ], + "version" : 3 +} diff --git a/Ddom.xcodeproj/project.xcworkspace/xcuserdata/gimhyeongseog.xcuserdatad/UserInterfaceState.xcuserstate b/Ddom.xcodeproj/project.xcworkspace/xcuserdata/gimhyeongseog.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..8ac406b Binary files /dev/null and b/Ddom.xcodeproj/project.xcworkspace/xcuserdata/gimhyeongseog.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Ddom.xcodeproj/xcuserdata/gimhyeongseog.xcuserdatad/xcschemes/xcschememanagement.plist b/Ddom.xcodeproj/xcuserdata/gimhyeongseog.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..6b3ddec --- /dev/null +++ b/Ddom.xcodeproj/xcuserdata/gimhyeongseog.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + Ddom.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Ddom/Assets.xcassets/AccentColor.colorset/Contents.json b/Ddom/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Ddom/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/AppIcon.appiconset/Contents.json b/Ddom/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..2305880 --- /dev/null +++ b/Ddom/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "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/Ddom/Assets.xcassets/Color/Contents.json b/Ddom/Assets.xcassets/Color/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Gray/Contents.json b/Ddom/Assets.xcassets/Color/Gray/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Gray/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Gray/gray1.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Gray/gray1.colorset/Contents.json new file mode 100644 index 0000000..5391dc8 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Gray/gray1.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.937", + "green" : "0.937", + "red" : "0.941" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.937", + "green" : "0.937", + "red" : "0.941" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Gray/gray10.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Gray/gray10.colorset/Contents.json new file mode 100644 index 0000000..f4a9d52 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Gray/gray10.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.176", + "green" : "0.176", + "red" : "0.176" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.176", + "green" : "0.176", + "red" : "0.176" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Gray/gray2.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Gray/gray2.colorset/Contents.json new file mode 100644 index 0000000..457e953 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Gray/gray2.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.855", + "green" : "0.855", + "red" : "0.855" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.855", + "green" : "0.855", + "red" : "0.855" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Gray/gray3.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Gray/gray3.colorset/Contents.json new file mode 100644 index 0000000..7fa6dcd --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Gray/gray3.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.737", + "green" : "0.737", + "red" : "0.737" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.737", + "green" : "0.737", + "red" : "0.737" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Gray/gray4.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Gray/gray4.colorset/Contents.json new file mode 100644 index 0000000..e8bf15d --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Gray/gray4.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.616", + "green" : "0.616", + "red" : "0.616" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.616", + "green" : "0.616", + "red" : "0.616" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Gray/gray5.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Gray/gray5.colorset/Contents.json new file mode 100644 index 0000000..7e2e99d --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Gray/gray5.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.498", + "green" : "0.498", + "red" : "0.502" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.498", + "green" : "0.498", + "red" : "0.502" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Gray/gray6.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Gray/gray6.colorset/Contents.json new file mode 100644 index 0000000..f0f53c1 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Gray/gray6.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.388", + "green" : "0.388", + "red" : "0.392" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.388", + "green" : "0.388", + "red" : "0.392" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Gray/gray7.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Gray/gray7.colorset/Contents.json new file mode 100644 index 0000000..b8aab62 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Gray/gray7.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.329", + "green" : "0.329", + "red" : "0.333" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.329", + "green" : "0.329", + "red" : "0.333" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Gray/gray8.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Gray/gray8.colorset/Contents.json new file mode 100644 index 0000000..d27aeeb --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Gray/gray8.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.275", + "green" : "0.275", + "red" : "0.278" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.275", + "green" : "0.275", + "red" : "0.278" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Gray/gray9.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Gray/gray9.colorset/Contents.json new file mode 100644 index 0000000..0f4e684 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Gray/gray9.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.220", + "green" : "0.220", + "red" : "0.224" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.220", + "green" : "0.220", + "red" : "0.224" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Primary/Contents.json b/Ddom/Assets.xcassets/Color/Primary/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Primary/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Primary/primary1.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Primary/primary1.colorset/Contents.json new file mode 100644 index 0000000..cc56f19 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Primary/primary1.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.976", + "green" : "0.996", + "red" : "0.988" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.976", + "green" : "0.996", + "red" : "0.988" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Primary/primary10.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Primary/primary10.colorset/Contents.json new file mode 100644 index 0000000..66ed52d --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Primary/primary10.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.345", + "green" : "0.424", + "red" : "0.400" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.345", + "green" : "0.424", + "red" : "0.400" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Primary/primary2.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Primary/primary2.colorset/Contents.json new file mode 100644 index 0000000..4022c0c --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Primary/primary2.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.945", + "green" : "0.984", + "red" : "0.973" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.945", + "green" : "0.984", + "red" : "0.973" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Primary/primary3.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Primary/primary3.colorset/Contents.json new file mode 100644 index 0000000..0426164 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Primary/primary3.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.902", + "green" : "0.976", + "red" : "0.953" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.902", + "green" : "0.976", + "red" : "0.953" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Primary/primary4.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Primary/primary4.colorset/Contents.json new file mode 100644 index 0000000..efd5680 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Primary/primary4.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.855", + "green" : "0.965", + "red" : "0.929" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.855", + "green" : "0.965", + "red" : "0.929" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Primary/primary5.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Primary/primary5.colorset/Contents.json new file mode 100644 index 0000000..83ab954 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Primary/primary5.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.812", + "green" : "0.953", + "red" : "0.906" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.812", + "green" : "0.953", + "red" : "0.906" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Primary/primary6.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Primary/primary6.colorset/Contents.json new file mode 100644 index 0000000..aabf394 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Primary/primary6.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.769", + "green" : "0.941", + "red" : "0.886" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.769", + "green" : "0.941", + "red" : "0.886" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Primary/primary7.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Primary/primary7.colorset/Contents.json new file mode 100644 index 0000000..f21d8e9 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Primary/primary7.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.733", + "green" : "0.871", + "red" : "0.827" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.733", + "green" : "0.871", + "red" : "0.827" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Primary/primary8.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Primary/primary8.colorset/Contents.json new file mode 100644 index 0000000..4cc2b9c --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Primary/primary8.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.604", + "green" : "0.745", + "red" : "0.698" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.604", + "green" : "0.745", + "red" : "0.698" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Primary/primary9.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Primary/primary9.colorset/Contents.json new file mode 100644 index 0000000..3445b0d --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Primary/primary9.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.439", + "green" : "0.537", + "red" : "0.506" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.439", + "green" : "0.537", + "red" : "0.506" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Secondary/Contents.json b/Ddom/Assets.xcassets/Color/Secondary/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Secondary/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Secondary/secondary1.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Secondary/secondary1.colorset/Contents.json new file mode 100644 index 0000000..762daff --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Secondary/secondary1.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.953", + "green" : "0.965", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.953", + "green" : "0.965", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Secondary/secondary10.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Secondary/secondary10.colorset/Contents.json new file mode 100644 index 0000000..1e51643 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Secondary/secondary10.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.235", + "green" : "0.298", + "red" : "0.451" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.235", + "green" : "0.298", + "red" : "0.451" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Secondary/secondary2.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Secondary/secondary2.colorset/Contents.json new file mode 100644 index 0000000..23f633c --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Secondary/secondary2.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.886", + "green" : "0.918", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.886", + "green" : "0.918", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Secondary/secondary3.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Secondary/secondary3.colorset/Contents.json new file mode 100644 index 0000000..7370baa --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Secondary/secondary3.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.796", + "green" : "0.855", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.796", + "green" : "0.855", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Secondary/secondary4.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Secondary/secondary4.colorset/Contents.json new file mode 100644 index 0000000..233f72b --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Secondary/secondary4.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.698", + "green" : "0.788", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.698", + "green" : "0.788", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Secondary/secondary5.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Secondary/secondary5.colorset/Contents.json new file mode 100644 index 0000000..a7e8afc --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Secondary/secondary5.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.608", + "green" : "0.722", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.608", + "green" : "0.722", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Secondary/secondary6.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Secondary/secondary6.colorset/Contents.json new file mode 100644 index 0000000..ca4e1ed --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Secondary/secondary6.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.522", + "green" : "0.663", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.522", + "green" : "0.663", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Secondary/secondary7.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Secondary/secondary7.colorset/Contents.json new file mode 100644 index 0000000..918647a --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Secondary/secondary7.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.443", + "green" : "0.565", + "red" : "0.851" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.443", + "green" : "0.565", + "red" : "0.851" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Secondary/secondary8.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Secondary/secondary8.colorset/Contents.json new file mode 100644 index 0000000..139b144 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Secondary/secondary8.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.369", + "green" : "0.471", + "red" : "0.710" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.369", + "green" : "0.471", + "red" : "0.710" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/Secondary/secondary9.colorset/Contents.json b/Ddom/Assets.xcassets/Color/Secondary/secondary9.colorset/Contents.json new file mode 100644 index 0000000..917a8e7 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/Secondary/secondary9.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.298", + "green" : "0.376", + "red" : "0.569" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.298", + "green" : "0.376", + "red" : "0.569" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/error.colorset/Contents.json b/Ddom/Assets.xcassets/Color/error.colorset/Contents.json new file mode 100644 index 0000000..14abea4 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/error.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.376", + "green" : "0.376", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.376", + "green" : "0.376", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/myBlack.colorset/Contents.json b/Ddom/Assets.xcassets/Color/myBlack.colorset/Contents.json new file mode 100644 index 0000000..784f603 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/myBlack.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.000", + "red" : "0.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.000", + "red" : "0.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/myWhite.colorset/Contents.json b/Ddom/Assets.xcassets/Color/myWhite.colorset/Contents.json new file mode 100644 index 0000000..22c4bb0 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/myWhite.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/success.colorset/Contents.json b/Ddom/Assets.xcassets/Color/success.colorset/Contents.json new file mode 100644 index 0000000..348a5bd --- /dev/null +++ b/Ddom/Assets.xcassets/Color/success.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.267", + "green" : "0.843", + "red" : "0.392" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.267", + "green" : "0.843", + "red" : "0.392" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/surface1.colorset/Contents.json b/Ddom/Assets.xcassets/Color/surface1.colorset/Contents.json new file mode 100644 index 0000000..21c4ea8 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/surface1.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.973", + "green" : "0.973", + "red" : "0.976" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.973", + "green" : "0.973", + "red" : "0.976" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Color/surface2.colorset/Contents.json b/Ddom/Assets.xcassets/Color/surface2.colorset/Contents.json new file mode 100644 index 0000000..d6c4276 --- /dev/null +++ b/Ddom/Assets.xcassets/Color/surface2.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.937", + "green" : "0.937", + "red" : "0.945" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.937", + "green" : "0.937", + "red" : "0.945" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Contents.json b/Ddom/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Ddom/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/Attachment.imageset/Attachment.png b/Ddom/Assets.xcassets/Image/Attachment.imageset/Attachment.png new file mode 100644 index 0000000..a8d3c80 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/Attachment.imageset/Attachment.png differ diff --git a/Ddom/Assets.xcassets/Image/Attachment.imageset/Attachment@2x.png b/Ddom/Assets.xcassets/Image/Attachment.imageset/Attachment@2x.png new file mode 100644 index 0000000..4080373 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/Attachment.imageset/Attachment@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/Attachment.imageset/Attachment@3x.png b/Ddom/Assets.xcassets/Image/Attachment.imageset/Attachment@3x.png new file mode 100644 index 0000000..8d22501 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/Attachment.imageset/Attachment@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/Attachment.imageset/Contents.json b/Ddom/Assets.xcassets/Image/Attachment.imageset/Contents.json new file mode 100644 index 0000000..b3e2c85 --- /dev/null +++ b/Ddom/Assets.xcassets/Image/Attachment.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Attachment.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Attachment@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Attachment@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/Contents.json b/Ddom/Assets.xcassets/Image/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Ddom/Assets.xcassets/Image/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/alarm.imageset/Contents.json b/Ddom/Assets.xcassets/Image/alarm.imageset/Contents.json new file mode 100644 index 0000000..565b000 --- /dev/null +++ b/Ddom/Assets.xcassets/Image/alarm.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "alarm.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "alarm@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "alarm@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/alarm.imageset/alarm.png b/Ddom/Assets.xcassets/Image/alarm.imageset/alarm.png new file mode 100644 index 0000000..796be72 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/alarm.imageset/alarm.png differ diff --git a/Ddom/Assets.xcassets/Image/alarm.imageset/alarm@2x.png b/Ddom/Assets.xcassets/Image/alarm.imageset/alarm@2x.png new file mode 100644 index 0000000..cf43f2c Binary files /dev/null and b/Ddom/Assets.xcassets/Image/alarm.imageset/alarm@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/alarm.imageset/alarm@3x.png b/Ddom/Assets.xcassets/Image/alarm.imageset/alarm@3x.png new file mode 100644 index 0000000..3fa1359 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/alarm.imageset/alarm@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/arrow_down.imageset/Contents.json b/Ddom/Assets.xcassets/Image/arrow_down.imageset/Contents.json new file mode 100644 index 0000000..e395aad --- /dev/null +++ b/Ddom/Assets.xcassets/Image/arrow_down.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "arrow_down.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "arrow_down@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "arrow_down@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/arrow_down.imageset/arrow_down.png b/Ddom/Assets.xcassets/Image/arrow_down.imageset/arrow_down.png new file mode 100644 index 0000000..7c83b0c Binary files /dev/null and b/Ddom/Assets.xcassets/Image/arrow_down.imageset/arrow_down.png differ diff --git a/Ddom/Assets.xcassets/Image/arrow_down.imageset/arrow_down@2x.png b/Ddom/Assets.xcassets/Image/arrow_down.imageset/arrow_down@2x.png new file mode 100644 index 0000000..c6f6e8b Binary files /dev/null and b/Ddom/Assets.xcassets/Image/arrow_down.imageset/arrow_down@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/arrow_down.imageset/arrow_down@3x.png b/Ddom/Assets.xcassets/Image/arrow_down.imageset/arrow_down@3x.png new file mode 100644 index 0000000..065f90a Binary files /dev/null and b/Ddom/Assets.xcassets/Image/arrow_down.imageset/arrow_down@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/arrow_left.imageset/Contents.json b/Ddom/Assets.xcassets/Image/arrow_left.imageset/Contents.json new file mode 100644 index 0000000..a3fd837 --- /dev/null +++ b/Ddom/Assets.xcassets/Image/arrow_left.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "arrow_left.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "arrow_left@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "arrow_left@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/arrow_left.imageset/arrow_left.png b/Ddom/Assets.xcassets/Image/arrow_left.imageset/arrow_left.png new file mode 100644 index 0000000..d04dcc9 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/arrow_left.imageset/arrow_left.png differ diff --git a/Ddom/Assets.xcassets/Image/arrow_left.imageset/arrow_left@2x.png b/Ddom/Assets.xcassets/Image/arrow_left.imageset/arrow_left@2x.png new file mode 100644 index 0000000..2c03c39 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/arrow_left.imageset/arrow_left@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/arrow_left.imageset/arrow_left@3x.png b/Ddom/Assets.xcassets/Image/arrow_left.imageset/arrow_left@3x.png new file mode 100644 index 0000000..164457c Binary files /dev/null and b/Ddom/Assets.xcassets/Image/arrow_left.imageset/arrow_left@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/arrow_right.imageset/Contents.json b/Ddom/Assets.xcassets/Image/arrow_right.imageset/Contents.json new file mode 100644 index 0000000..eaf9a40 --- /dev/null +++ b/Ddom/Assets.xcassets/Image/arrow_right.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "arrow_right.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "arrow_right@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "arrow_right@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/arrow_right.imageset/arrow_right.png b/Ddom/Assets.xcassets/Image/arrow_right.imageset/arrow_right.png new file mode 100644 index 0000000..64d2a9c Binary files /dev/null and b/Ddom/Assets.xcassets/Image/arrow_right.imageset/arrow_right.png differ diff --git a/Ddom/Assets.xcassets/Image/arrow_right.imageset/arrow_right@2x.png b/Ddom/Assets.xcassets/Image/arrow_right.imageset/arrow_right@2x.png new file mode 100644 index 0000000..cbb3783 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/arrow_right.imageset/arrow_right@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/arrow_right.imageset/arrow_right@3x.png b/Ddom/Assets.xcassets/Image/arrow_right.imageset/arrow_right@3x.png new file mode 100644 index 0000000..c8aaf4f Binary files /dev/null and b/Ddom/Assets.xcassets/Image/arrow_right.imageset/arrow_right@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/arrow_up.imageset/Contents.json b/Ddom/Assets.xcassets/Image/arrow_up.imageset/Contents.json new file mode 100644 index 0000000..82d3d49 --- /dev/null +++ b/Ddom/Assets.xcassets/Image/arrow_up.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "arrow_up.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "arrow_up@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "arrow_up@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/arrow_up.imageset/arrow_up.png b/Ddom/Assets.xcassets/Image/arrow_up.imageset/arrow_up.png new file mode 100644 index 0000000..ab7ff82 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/arrow_up.imageset/arrow_up.png differ diff --git a/Ddom/Assets.xcassets/Image/arrow_up.imageset/arrow_up@2x.png b/Ddom/Assets.xcassets/Image/arrow_up.imageset/arrow_up@2x.png new file mode 100644 index 0000000..c28b947 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/arrow_up.imageset/arrow_up@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/arrow_up.imageset/arrow_up@3x.png b/Ddom/Assets.xcassets/Image/arrow_up.imageset/arrow_up@3x.png new file mode 100644 index 0000000..4964268 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/arrow_up.imageset/arrow_up@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/check.imageset/Contents.json b/Ddom/Assets.xcassets/Image/check.imageset/Contents.json new file mode 100644 index 0000000..3f4957b --- /dev/null +++ b/Ddom/Assets.xcassets/Image/check.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "check.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "check@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "check@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/check.imageset/check.png b/Ddom/Assets.xcassets/Image/check.imageset/check.png new file mode 100644 index 0000000..09bad5d Binary files /dev/null and b/Ddom/Assets.xcassets/Image/check.imageset/check.png differ diff --git a/Ddom/Assets.xcassets/Image/check.imageset/check@2x.png b/Ddom/Assets.xcassets/Image/check.imageset/check@2x.png new file mode 100644 index 0000000..08004b0 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/check.imageset/check@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/check.imageset/check@3x.png b/Ddom/Assets.xcassets/Image/check.imageset/check@3x.png new file mode 100644 index 0000000..9bb4de9 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/check.imageset/check@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/clock.imageset/Contents.json b/Ddom/Assets.xcassets/Image/clock.imageset/Contents.json new file mode 100644 index 0000000..f1ae6d1 --- /dev/null +++ b/Ddom/Assets.xcassets/Image/clock.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "clock.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "clock@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "clock@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/clock.imageset/clock.png b/Ddom/Assets.xcassets/Image/clock.imageset/clock.png new file mode 100644 index 0000000..25fdd9e Binary files /dev/null and b/Ddom/Assets.xcassets/Image/clock.imageset/clock.png differ diff --git a/Ddom/Assets.xcassets/Image/clock.imageset/clock@2x.png b/Ddom/Assets.xcassets/Image/clock.imageset/clock@2x.png new file mode 100644 index 0000000..6d6358a Binary files /dev/null and b/Ddom/Assets.xcassets/Image/clock.imageset/clock@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/clock.imageset/clock@3x.png b/Ddom/Assets.xcassets/Image/clock.imageset/clock@3x.png new file mode 100644 index 0000000..43f74f3 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/clock.imageset/clock@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/close.imageset/Contents.json b/Ddom/Assets.xcassets/Image/close.imageset/Contents.json new file mode 100644 index 0000000..940a6d1 --- /dev/null +++ b/Ddom/Assets.xcassets/Image/close.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "close.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "close@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "close@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/close.imageset/close.png b/Ddom/Assets.xcassets/Image/close.imageset/close.png new file mode 100644 index 0000000..58db7b3 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/close.imageset/close.png differ diff --git a/Ddom/Assets.xcassets/Image/close.imageset/close@2x.png b/Ddom/Assets.xcassets/Image/close.imageset/close@2x.png new file mode 100644 index 0000000..7cde3db Binary files /dev/null and b/Ddom/Assets.xcassets/Image/close.imageset/close@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/close.imageset/close@3x.png b/Ddom/Assets.xcassets/Image/close.imageset/close@3x.png new file mode 100644 index 0000000..87f1d8e Binary files /dev/null and b/Ddom/Assets.xcassets/Image/close.imageset/close@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/delete.imageset/Contents.json b/Ddom/Assets.xcassets/Image/delete.imageset/Contents.json new file mode 100644 index 0000000..3930a4c --- /dev/null +++ b/Ddom/Assets.xcassets/Image/delete.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "delete.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "delete@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "delete@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/delete.imageset/delete.png b/Ddom/Assets.xcassets/Image/delete.imageset/delete.png new file mode 100644 index 0000000..39bcb96 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/delete.imageset/delete.png differ diff --git a/Ddom/Assets.xcassets/Image/delete.imageset/delete@2x.png b/Ddom/Assets.xcassets/Image/delete.imageset/delete@2x.png new file mode 100644 index 0000000..97cb569 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/delete.imageset/delete@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/delete.imageset/delete@3x.png b/Ddom/Assets.xcassets/Image/delete.imageset/delete@3x.png new file mode 100644 index 0000000..b48c8b7 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/delete.imageset/delete@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/edit.imageset/Contents.json b/Ddom/Assets.xcassets/Image/edit.imageset/Contents.json new file mode 100644 index 0000000..3719cf6 --- /dev/null +++ b/Ddom/Assets.xcassets/Image/edit.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "edit.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "edit@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "edit@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/edit.imageset/edit.png b/Ddom/Assets.xcassets/Image/edit.imageset/edit.png new file mode 100644 index 0000000..d2b85ad Binary files /dev/null and b/Ddom/Assets.xcassets/Image/edit.imageset/edit.png differ diff --git a/Ddom/Assets.xcassets/Image/edit.imageset/edit@2x.png b/Ddom/Assets.xcassets/Image/edit.imageset/edit@2x.png new file mode 100644 index 0000000..a5b8171 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/edit.imageset/edit@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/edit.imageset/edit@3x.png b/Ddom/Assets.xcassets/Image/edit.imageset/edit@3x.png new file mode 100644 index 0000000..754dd92 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/edit.imageset/edit@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/heart-filled.imageset/Contents.json b/Ddom/Assets.xcassets/Image/heart-filled.imageset/Contents.json new file mode 100644 index 0000000..2343991 --- /dev/null +++ b/Ddom/Assets.xcassets/Image/heart-filled.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "heart-filled.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "heart-filled@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "heart-filled@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/heart-filled.imageset/heart-filled.png b/Ddom/Assets.xcassets/Image/heart-filled.imageset/heart-filled.png new file mode 100644 index 0000000..7540c09 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/heart-filled.imageset/heart-filled.png differ diff --git a/Ddom/Assets.xcassets/Image/heart-filled.imageset/heart-filled@2x.png b/Ddom/Assets.xcassets/Image/heart-filled.imageset/heart-filled@2x.png new file mode 100644 index 0000000..bd02261 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/heart-filled.imageset/heart-filled@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/heart-filled.imageset/heart-filled@3x.png b/Ddom/Assets.xcassets/Image/heart-filled.imageset/heart-filled@3x.png new file mode 100644 index 0000000..feef176 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/heart-filled.imageset/heart-filled@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/heart.imageset/Contents.json b/Ddom/Assets.xcassets/Image/heart.imageset/Contents.json new file mode 100644 index 0000000..0a38ba1 --- /dev/null +++ b/Ddom/Assets.xcassets/Image/heart.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "heart.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "heart@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "heart@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/heart.imageset/heart.png b/Ddom/Assets.xcassets/Image/heart.imageset/heart.png new file mode 100644 index 0000000..176af12 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/heart.imageset/heart.png differ diff --git a/Ddom/Assets.xcassets/Image/heart.imageset/heart@2x.png b/Ddom/Assets.xcassets/Image/heart.imageset/heart@2x.png new file mode 100644 index 0000000..2df497d Binary files /dev/null and b/Ddom/Assets.xcassets/Image/heart.imageset/heart@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/heart.imageset/heart@3x.png b/Ddom/Assets.xcassets/Image/heart.imageset/heart@3x.png new file mode 100644 index 0000000..7fa50a3 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/heart.imageset/heart@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/location-pin.imageset/Contents.json b/Ddom/Assets.xcassets/Image/location-pin.imageset/Contents.json new file mode 100644 index 0000000..a63ed49 --- /dev/null +++ b/Ddom/Assets.xcassets/Image/location-pin.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "location-pin.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "location-pin@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "location-pin@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/location-pin.imageset/location-pin.png b/Ddom/Assets.xcassets/Image/location-pin.imageset/location-pin.png new file mode 100644 index 0000000..3fefcc3 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/location-pin.imageset/location-pin.png differ diff --git a/Ddom/Assets.xcassets/Image/location-pin.imageset/location-pin@2x.png b/Ddom/Assets.xcassets/Image/location-pin.imageset/location-pin@2x.png new file mode 100644 index 0000000..49a8ef5 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/location-pin.imageset/location-pin@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/location-pin.imageset/location-pin@3x.png b/Ddom/Assets.xcassets/Image/location-pin.imageset/location-pin@3x.png new file mode 100644 index 0000000..51882a9 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/location-pin.imageset/location-pin@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/phone.imageset/Contents.json b/Ddom/Assets.xcassets/Image/phone.imageset/Contents.json new file mode 100644 index 0000000..14eb539 --- /dev/null +++ b/Ddom/Assets.xcassets/Image/phone.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "phone.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "phone@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "phone@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/phone.imageset/phone.png b/Ddom/Assets.xcassets/Image/phone.imageset/phone.png new file mode 100644 index 0000000..9f78f9c Binary files /dev/null and b/Ddom/Assets.xcassets/Image/phone.imageset/phone.png differ diff --git a/Ddom/Assets.xcassets/Image/phone.imageset/phone@2x.png b/Ddom/Assets.xcassets/Image/phone.imageset/phone@2x.png new file mode 100644 index 0000000..ee90932 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/phone.imageset/phone@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/phone.imageset/phone@3x.png b/Ddom/Assets.xcassets/Image/phone.imageset/phone@3x.png new file mode 100644 index 0000000..6754c14 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/phone.imageset/phone@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/plus.imageset/Contents.json b/Ddom/Assets.xcassets/Image/plus.imageset/Contents.json new file mode 100644 index 0000000..337868b --- /dev/null +++ b/Ddom/Assets.xcassets/Image/plus.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "plus.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "plus@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "plus@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/plus.imageset/plus.png b/Ddom/Assets.xcassets/Image/plus.imageset/plus.png new file mode 100644 index 0000000..1c4b7b6 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/plus.imageset/plus.png differ diff --git a/Ddom/Assets.xcassets/Image/plus.imageset/plus@2x.png b/Ddom/Assets.xcassets/Image/plus.imageset/plus@2x.png new file mode 100644 index 0000000..550078e Binary files /dev/null and b/Ddom/Assets.xcassets/Image/plus.imageset/plus@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/plus.imageset/plus@3x.png b/Ddom/Assets.xcassets/Image/plus.imageset/plus@3x.png new file mode 100644 index 0000000..16ee8e5 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/plus.imageset/plus@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/qr-code.imageset/Contents.json b/Ddom/Assets.xcassets/Image/qr-code.imageset/Contents.json new file mode 100644 index 0000000..44b95e2 --- /dev/null +++ b/Ddom/Assets.xcassets/Image/qr-code.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "qr-code.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "qr-code@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "qr-code@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/qr-code.imageset/qr-code.png b/Ddom/Assets.xcassets/Image/qr-code.imageset/qr-code.png new file mode 100644 index 0000000..3991d92 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/qr-code.imageset/qr-code.png differ diff --git a/Ddom/Assets.xcassets/Image/qr-code.imageset/qr-code@2x.png b/Ddom/Assets.xcassets/Image/qr-code.imageset/qr-code@2x.png new file mode 100644 index 0000000..3064f6f Binary files /dev/null and b/Ddom/Assets.xcassets/Image/qr-code.imageset/qr-code@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/qr-code.imageset/qr-code@3x.png b/Ddom/Assets.xcassets/Image/qr-code.imageset/qr-code@3x.png new file mode 100644 index 0000000..0cc26ce Binary files /dev/null and b/Ddom/Assets.xcassets/Image/qr-code.imageset/qr-code@3x.png differ diff --git a/Ddom/Assets.xcassets/Image/search.imageset/Contents.json b/Ddom/Assets.xcassets/Image/search.imageset/Contents.json new file mode 100644 index 0000000..b59c79f --- /dev/null +++ b/Ddom/Assets.xcassets/Image/search.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "search.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "search@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "search@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Assets.xcassets/Image/search.imageset/search.png b/Ddom/Assets.xcassets/Image/search.imageset/search.png new file mode 100644 index 0000000..4d31c29 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/search.imageset/search.png differ diff --git a/Ddom/Assets.xcassets/Image/search.imageset/search@2x.png b/Ddom/Assets.xcassets/Image/search.imageset/search@2x.png new file mode 100644 index 0000000..74659e8 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/search.imageset/search@2x.png differ diff --git a/Ddom/Assets.xcassets/Image/search.imageset/search@3x.png b/Ddom/Assets.xcassets/Image/search.imageset/search@3x.png new file mode 100644 index 0000000..ecceed1 Binary files /dev/null and b/Ddom/Assets.xcassets/Image/search.imageset/search@3x.png differ diff --git a/Ddom/Components/BottomSheet/TermsDetailBottomSheet.swift b/Ddom/Components/BottomSheet/TermsDetailBottomSheet.swift new file mode 100644 index 0000000..40e69a2 --- /dev/null +++ b/Ddom/Components/BottomSheet/TermsDetailBottomSheet.swift @@ -0,0 +1,124 @@ +// +// DetailView.swift +// Ddom +// +// Created by Neoself on 11/5/24. +// + +import SwiftUI + +struct TermsDetailBottomSheet: View { + @Environment(\.dismiss) private var dismiss + @ObservedObject var viewModel: CreateAccountViewModel + + var body: some View { + VStack(spacing: 0) { + Capsule() + .fill(.gray2) + .frame(width: 56, height: 8) + .padding(16) + + + ScrollView { + VStack(alignment: .leading, spacing:0) { + Text("또옴과 함께하려면,") + .fontStyle(.title1) + .foregroundStyle(.gray10) + .padding(.bottom,8) + + Text("가입 및 정보 제공에 동의하지 않으면,\n일부 기능이 제한될 수 있어요.") + .fontStyle(.body5) + .foregroundStyle(.gray5) + .padding(.bottom,24) + + Button(action: { + viewModel.toggleAllChecks() + }) { + HStack(spacing: 6){ + Image("check") + .resizable() + .renderingMode(.template) + .foregroundStyle(viewModel.isAllChecked ? .secondary6 :.gray3) + .frame(width: 24, height: 24) + + Text("모두 동의하기") + .fontStyle(.body3) + .foregroundStyle(viewModel.isAllChecked ? .gray10 : .gray3) + } + .frame(maxWidth:.infinity,alignment: .leading) + .padding(.vertical, 12) + .padding(.horizontal, 8) + .background(RoundedRectangle(cornerRadius:8) + .fill(viewModel.isAllChecked ? .gray1 : .surface1) + ) + } + .padding(.bottom, 8) + + VStack(spacing:4){ + checkItem(isChecked:$viewModel.isServiceChecked, + description: "[필수] 서비스 이용약관" + ){ + print("onFirstPress") + } + checkItem(isChecked:$viewModel.isPrivacyChecked, + description: "[필수] 개인정보 수집/이용 동의" + ){ + print("onSecondPress") + } + checkItem(isChecked:$viewModel.isAdvertisementChecked, + description: "[선택] 맞춤형 광고 및 개인정보 제공 동의" + ){ + print("onThirdPress") + } + checkItem(isChecked:$viewModel.isMarketingChecked, + description: "[선택] 마케팅 수신 동의" + ){ + print("onFourthPress") + } + + } + .frame(maxWidth:.infinity,alignment: .leading) + } + } + .padding(.top,16) + + CustomButton(action: {viewModel.handleSubmit()}, + isPrimary: false, + isLoading: false, + text: "동의하고 가입하기", + isDisabled: viewModel.isAgreeButtonDisabled, + isFullWidth: true + ) + } + .padding(.horizontal,16) + .background(.white) + .clipShape(RoundedRectangle(cornerRadius: 20)) + } +} + +@ViewBuilder +private func checkItem(isChecked: Binding, description: String, onPress: @escaping () -> Void) -> some View { + HStack(spacing: 0) { + HStack(spacing:8) { + Image("check") + .renderingMode(.template) + .foregroundStyle(isChecked.wrappedValue ? .secondary6 : .gray3) + + Text(description) + .fontStyle(.body4) + .foregroundStyle(.gray10) + .frame(maxWidth: .infinity, alignment: .leading) + } + .onTapGesture { + isChecked.wrappedValue.toggle() + } + + Spacer() + + Text("보기") + .fontStyle(.caption1) + .foregroundStyle(.gray3) + .onTapGesture(perform: onPress) + } + .padding(8) +} diff --git a/Ddom/Components/Common/CustomButton.swift b/Ddom/Components/Common/CustomButton.swift new file mode 100644 index 0000000..9c5f556 --- /dev/null +++ b/Ddom/Components/Common/CustomButton.swift @@ -0,0 +1,65 @@ +// +// CustomVButton.swift +// tyte +// +// Created by 김 형석 on 9/25/24. +// + +import SwiftUI + +struct CustomButton: View { + let action: () -> Void + let isPrimary: Bool + let isLoading: Bool + let text: String + let isDisabled: Bool + var loadingTint: Color = .gray6 + var font: Font = .body5 + var cornerRadius: CGFloat = 8 + var iconName: String? + var isFullWidth: Bool + + private var enabledBgColor: Color { + isPrimary ? .primary6 : .gray10 + } + private var disabledBgColor: Color { + isPrimary ? .primary4 : .gray3 + } + private var enabledFgColor: Color { + isPrimary ? .gray10 : .white + } + private var disabledFgColor: Color { + isPrimary ? .primary8 : .gray1 + } + + var body: some View { + Button(action: action) { + if isLoading { + ProgressView() + .tint(loadingTint) + .frame(maxHeight:48) + } else { + HStack(spacing:8){ + if(iconName != nil){ + Image(iconName!) + .renderingMode(.template) + .resizable() + .frame(width: 16,height:16) + .foregroundStyle(.white) + } + + Text(text) + .font(font) + + } + .padding(13) + .frame(maxHeight:48) + .frame(maxWidth: isFullWidth ? .infinity : .none) + .background(isDisabled ? disabledBgColor : enabledBgColor) + .foregroundColor(isDisabled ? disabledFgColor : enabledFgColor) + .cornerRadius(cornerRadius) + } + } + .disabled(isDisabled || isLoading) + } +} diff --git a/Ddom/Components/Common/CustomPopupOneBtn.swift b/Ddom/Components/Common/CustomPopupOneBtn.swift new file mode 100644 index 0000000..6084737 --- /dev/null +++ b/Ddom/Components/Common/CustomPopupOneBtn.swift @@ -0,0 +1,64 @@ +import SwiftUI + +struct CustomPopupOneBtn: View { + @Binding var isShowing: Bool + + let title: String + let message: String + let primaryButtonTitle: String + let primaryAction: () -> Void + let isDisabled: Bool + + var body: some View { + ZStack { + Color.black.opacity(0.3) + .edgesIgnoringSafeArea(.all) + + VStack (spacing:0){ + Button(action: { + withAnimation (.fastEaseOut) { isShowing = false } + }) { + Image(systemName: "xmark") + .foregroundColor(.gray) + .padding(8) + } + .frame(maxWidth: .infinity,alignment: .trailing) + + Text(title) + .fontStyle(.heading1) + .foregroundColor(.gray9) + .padding(.top, -16) + .padding(.bottom, 6) + + Text(message) + .fontStyle(.body3) + .foregroundColor(.gray5) + .multilineTextAlignment(.center) + + Spacer().frame(height:20) + + Button(action: { + primaryAction() + withAnimation (.fastEaseOut) { isShowing = false } + }) { + Text(primaryButtonTitle) + .frame(maxWidth: .infinity) + .fontStyle(.title1) + .padding() + .background(isDisabled ? .gray5 : .primary8) + .foregroundStyle(isDisabled ? .gray6 : .gray1) + .cornerRadius(8) + } + .disabled(isDisabled) + + + } + .padding(12) + .background{ + RoundedRectangle(cornerRadius: 16).fill(.gray1) + .shadow(radius: 10) + } + .padding(.horizontal, 40) + } + } +} diff --git a/Ddom/Components/Common/CustomPopupTwoBtn.swift b/Ddom/Components/Common/CustomPopupTwoBtn.swift new file mode 100644 index 0000000..836fe4f --- /dev/null +++ b/Ddom/Components/Common/CustomPopupTwoBtn.swift @@ -0,0 +1,66 @@ +import SwiftUI + +struct CustomPopupTwoBtn: View { + @Binding var isShowing: Bool + + let title: String + let message: String + let primaryButtonTitle: String + let secondaryButtonTitle: String + let primaryAction: () -> Void + let secondaryAction: () -> Void + + var body: some View { + ZStack { + Color.black.opacity(0.3) + .edgesIgnoringSafeArea(.all) + + VStack(spacing: 4) { + Text(title) + .fontStyle(.heading2) + .foregroundColor(.gray9) + .padding(.top,12) + + Text(message) + .fontStyle(.body3) + .foregroundColor(.gray5) + .multilineTextAlignment(.center) + + Spacer().frame(height:12) + + HStack(spacing: 8) { + Button(action: { + secondaryAction() + withAnimation (.fastEaseOut) { isShowing = false } + }) { + Text(secondaryButtonTitle) + .frame(maxWidth: .infinity) + .fontStyle(.body1) + .padding() + .background(.gray2) + .foregroundColor(.gray6) + .cornerRadius(8) + } + + Button(action: { + primaryAction() + withAnimation (.fastEaseOut) { isShowing = false } + }) { + Text(primaryButtonTitle) + .frame(maxWidth: .infinity) + .fontStyle(.body1) + .padding() + .background(.primary9) + .foregroundStyle(.myWhite) + .cornerRadius(8) + } + } + } + .padding() + .background(.myWhite) + .cornerRadius(16) + .shadow(radius: 10) + .padding(.horizontal, 40) + } + } +} diff --git a/Ddom/Components/Common/CustomTextField.swift b/Ddom/Components/Common/CustomTextField.swift new file mode 100644 index 0000000..0525afa --- /dev/null +++ b/Ddom/Components/Common/CustomTextField.swift @@ -0,0 +1,37 @@ +import SwiftUI +// 제너릭 타입 매개변수 선언. +struct CustomTextField: View where T: Hashable { + @Binding var text: T + let placeholder: String + var keyboardType: UIKeyboardType = .default + var onSubmit: (() -> Void)? = nil + var isError: Bool? = false + var textColor: Color = .gray10 + var placeholderColor: Color = .gray3 + var backgroundColor: Color = .surface1 + var borderColor: Color = .gray3 + + var body: some View { + TextField("", + text: Binding( + get: { String(describing: text) }, + set: { if let value = $0 as? T { text = value } } + ), + prompt: Text(placeholder).foregroundColor(placeholderColor) + ) + .fontStyle(.body2) + .foregroundColor(textColor) + .padding(.horizontal,12) + .frame(maxHeight: 48) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(backgroundColor) + .stroke(.error, lineWidth: isError ?? false ? 1 : 0) + ) + .autocapitalization(.none) + .keyboardType(keyboardType) + .onSubmit { + onSubmit?() + } + } +} diff --git a/Ddom/Components/Common/CustomToast.swift b/Ddom/Components/Common/CustomToast.swift new file mode 100644 index 0000000..9bd44bd --- /dev/null +++ b/Ddom/Components/Common/CustomToast.swift @@ -0,0 +1,54 @@ +// +// CustomToast.swift +// ddom +// +// Created by 김 형석 on 9/20/24. +// + +import SwiftUI +import Foundation + +struct CustomToast: View { + let toastData: ToastType + + var body: some View { + HStack (spacing:8){ + Image(systemName: toastData.icon) + .fontStyle(.body3) + .foregroundStyle(.primary10) + + Text(toastData.text) + .font(.body2) + } + .frame(width: 300,alignment: .leading) + .padding() + .background(.gray1) + .foregroundColor(.gray6) + .cornerRadius(8) + .shadow(color: .gray6.opacity(0.2), radius: 16) + } +} + + +enum ToastType { + case googleError + case error(String) + + var text: String{ + switch self { + case .googleError: + return "구글 로그인이 잠시 안되고 있어요. 나중에 다시 시도해주세요." + case .error(let message): + return message + } + } + + var icon: String { + switch self { + case .error: + return "exclamationmark.circle.fill" + default: + return "checkmark.circle.fill" + } + } +} diff --git a/Ddom/Components/Common/NavBar.swift b/Ddom/Components/Common/NavBar.swift new file mode 100644 index 0000000..7bfbfba --- /dev/null +++ b/Ddom/Components/Common/NavBar.swift @@ -0,0 +1,144 @@ +import SwiftUI + +// MARK: - 기본 네비게이션 바 뷰 (로고 + 알림) +struct LogoNavBar: View { + var body: some View { + HStack { + Text("로고") + .font(.system(size: 20, weight: .bold)) + Spacer() + Image("alarm") + .resizable() + .frame(width: 24, height: 24) + } + .padding(.horizontal, 16) + .frame(height: 56) + .background(Color.white) + } +} + +// MARK: - 뒤로가기 네비게이션 바 +struct BackNavBar: View { + @Environment(\.dismiss) private var dismiss + + var body: some View { + HStack { + Button(action: { + dismiss() + }) { + Image("arrow_left") + .resizable() + .frame(width: 24, height: 24) + } + Spacer() + } + .padding(.horizontal, 16) + .padding(.vertical, 10) + .background(.white) + } +} + +// MARK: - 타이틀이 있는 뒤로가기 네비게이션 바 +struct TitleBackNavBar: View { + let title: String + @Environment(\.dismiss) private var dismiss + + var body: some View { + HStack { + Button(action: { + dismiss() + }) { + Image("arrow_left") + .resizable() + .frame(width: 24, height: 24) + } + + Spacer() + + Text(title) + .font(.system(size: 16, weight: .medium)) + + Spacer() + } + .padding(.horizontal, 16) + .frame(height: 56) + .background(Color.white) + } +} + +// MARK: - 드롭다운이 있는 네비게이션 바 +struct DropdownNavBar: View { + @State private var isDropdownOpen = false + @State private var selectedTitle = "이대 신촌" + + var body: some View { + HStack { + Button(action: { + isDropdownOpen.toggle() + }) { + HStack(spacing: 4) { + Text(selectedTitle) + .font(.system(size: 16, weight: .medium)) + Image("arrow_down") + .resizable() + .frame(width: 24, height: 24) + .rotationEffect(isDropdownOpen ? .degrees(180) : .degrees(0)) + } + } + + Spacer() + + Button(action: {}) { + Image("search") + .resizable() + .frame(width: 24, height: 24) + } + } + .padding(.horizontal, 16) + .frame(height: 56) + .background(Color.white) + } +} + +// MARK: - 닫기 버튼이 있는 네비게이션 바 +struct CloseNavBar: View { + let title: String + @Environment(\.dismiss) private var dismiss + + var body: some View { + HStack { + Text(title) + .font(.system(size: 16, weight: .medium)) + + Spacer() + + Button(action: { + dismiss() + }) { + Image("close") + .resizable() + .frame(width: 24, height: 24) + } + } + .padding(.horizontal, 16) + .frame(height: 56) + .background(Color.white) + } +} + +// MARK: - 네비게이션 바 미리보기 +struct NavBarPreview: View { + var body: some View { + VStack(spacing: 20) { + LogoNavBar() + BackNavBar() + TitleBackNavBar(title: "페이지명") + DropdownNavBar() + CloseNavBar(title: "페이지명") + } + } +} + +#Preview { + NavBarPreview() +} diff --git a/Ddom/Components/StoreCard.swift b/Ddom/Components/StoreCard.swift new file mode 100644 index 0000000..25502e3 --- /dev/null +++ b/Ddom/Components/StoreCard.swift @@ -0,0 +1,81 @@ +// +// StoreCArd.swift +// Ddom +// +// Created by Neoself on 11/1/24. +// +import SwiftUI + +struct StoreCard: View { + @ObservedObject var viewModel: StoreListViewModel + let store: Store + let isRegistered: Bool + + var body: some View { + HStack(alignment: .center, spacing: 12) { + ZStack(alignment: .bottomTrailing) { + AsyncImage(url: URL(string: store.storeImgUrl)) { image in + image + .resizable() + .aspectRatio(contentMode: .fill) + + } placeholder: { + RoundedRectangle(cornerRadius: 8) + .fill(.gray2) + } + if isRegistered { + Text("\(store.favoriteUserCount ?? "0")명 단골") + .fontStyle(.caption1) + .foregroundStyle(.myWhite) + .padding(.horizontal,12) + .padding(.vertical,4) + .background( + UnevenRoundedRectangle( + topLeadingRadius: 4, + bottomLeadingRadius: 0, + bottomTrailingRadius: 0, + topTrailingRadius: 0 + ) + .fill(.error) + ) + } + } + .frame(width: 92, height: 92) + .clipShape(RoundedRectangle(cornerRadius: 8)) + + VStack (alignment: .leading, spacing:0) { + Text(store.storeType) + .fontStyle(.body5) + .foregroundStyle(.gray6) + + Text(store.storeName) + .fontStyle(.title2) + .foregroundStyle(.gray10) + .padding(.bottom,8) + + if isRegistered { + Text(store.discountPolicy?.discountDescription ?? "설명 없음") + .fontStyle(.title2) + .foregroundStyle(.primary9) + } + } + + Spacer() + + if isRegistered { + Button(action: {print("pressed")}) { + Image("heart") + .resizable() + .frame(width: 24, height: 24) + .foregroundStyle(.gray10) + .padding(.leading,24) + .padding(.vertical,12) + } + } + } + .padding(.horizontal, 16) + .background(.myWhite) + .opacity(isRegistered ? 1.0 : 0.7) + } +} + diff --git a/Ddom/Ddom.entitlements b/Ddom/Ddom.entitlements new file mode 100644 index 0000000..a812db5 --- /dev/null +++ b/Ddom/Ddom.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.developer.applesignin + + Default + + + diff --git a/Ddom/DdomApp.swift b/Ddom/DdomApp.swift new file mode 100644 index 0000000..86dc369 --- /dev/null +++ b/Ddom/DdomApp.swift @@ -0,0 +1,42 @@ +import SwiftUI +import KakaoSDKCommon +import KakaoSDKAuth + +@main +struct DdomApp: App { + @StateObject private var appState = AppState.shared + + init() { + KakaoSDK.initSDK(appKey: "4200bb2006c1e166c3fbb5783d9c6a89") + } + + var body: some Scene { + WindowGroup { + ContentView() + .environmentObject(appState) + .onOpenURL { url in + if AuthApi.isKakaoTalkLoginUrl(url) { + _ = AuthController.handleOpenUrl(url: url) + } + } + } + } +} + +struct ContentView: View { + @EnvironmentObject var appState: AppState + + var body: some View { + if appState.isLoggedIn || appState.isGuestMode { + MainTabView() + } else { + OnboardingView() + .ignoresSafeArea() + } + } +} + +#Preview { + ContentView() + .environmentObject(AppState.shared) +} diff --git a/Ddom/Extensions/Animation+.swift b/Ddom/Extensions/Animation+.swift new file mode 100644 index 0000000..8803593 --- /dev/null +++ b/Ddom/Extensions/Animation+.swift @@ -0,0 +1,30 @@ +import SwiftUI + +extension Animation { + struct Duration { + static let fast: Double = 0.1 + static let medium: Double = 0.3 + static let slow: Double = 1 + } + + static var fastEaseOut: Animation { + easeOut(duration: Duration.fast) + } + + static var mediumEaseOut: Animation { + easeOut(duration: Duration.medium) + } + + // MARK: - EasnInOut + static var fastEaseInOut: Animation { + easeInOut(duration: Duration.fast) + } + + static var mediumEaseInOut: Animation { + easeInOut(duration: Duration.medium) + } + + static var longEaseInOut: Animation { + easeInOut(duration: Duration.slow) + } +} diff --git a/Ddom/Extensions/Color+.swift b/Ddom/Extensions/Color+.swift new file mode 100644 index 0000000..913ea8f --- /dev/null +++ b/Ddom/Extensions/Color+.swift @@ -0,0 +1,36 @@ +// +// Color+.swift +// Ddom +// +// Created by 김 형석 on 10/15/24. +// + +import Foundation +import SwiftUI + +extension Color { + init(hex: String) { + let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) + var int: UInt64 = 0 + Scanner(string: hex).scanHexInt64(&int) + let a, r, g, b: UInt64 + switch hex.count { + case 3: // RGB (12-bit) + (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) + case 6: // RGB (24-bit) + (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) + case 8: // ARGB (32-bit) + (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) + default: + (a, r, g, b) = (1, 1, 1, 0) + } + + self.init( + .sRGB, + red: Double(r) / 255, + green: Double(g) / 255, + blue: Double(b) / 255, + opacity: Double(a) / 255 + ) + } +} diff --git a/Ddom/Extensions/Font+.swift b/Ddom/Extensions/Font+.swift new file mode 100644 index 0000000..3a09a35 --- /dev/null +++ b/Ddom/Extensions/Font+.swift @@ -0,0 +1,57 @@ +// +// Font+.swift +// Ddom +// +// Created by 김 형석 on 10/15/24. +// +import Foundation +import SwiftUI + +extension Font { + static let heading1 = Font.custom("Pretendard-Bold", size: 28) + static let heading2 = Font.custom("Pretendard-Bold", size: 24) + + static let title1 = Font.custom("Pretendard-Bold", size: 20) + static let title2 = Font.custom("Pretendard-SemiBold", size: 18) + + static let body1 = Font.custom("Pretendard-Bold", size: 18) + static let body2 = Font.custom("Pretendard-Medium", size: 18) + static let body3 = Font.custom("Pretendard-Bold", size: 16) + static let body4 = Font.custom("Pretendard-Medium", size: 16) + static let body5 = Font.custom("Pretendard-Medium", size: 14) + + static let caption1 = Font.custom("Pretendard-Medium", size: 12) + static let caption2 = Font.custom("Pretendard-Medium", size: 12) + + var lineSpacing: CGFloat { + switch self { + case .heading1: + return 42 - 28 + case .heading2: + return 36 - 24 + case .title1: + return 30 - 20 + case .title2: + return 24 - 18 + case .body1, .body2, .body3, .body4: + return 24 - 16 + case .body5: + return 22 - 14 + default: + return 16 - 12 + } + } + + var letterSpacing: CGFloat { + switch self { + case .heading1, .heading2, .title1: + return -1.5 + case .title2, .body1, .body2, .body3: + return -0.25 + case .caption1, .caption2: + return 0 + default: + return 0 + } + } +} diff --git a/Ddom/Extensions/Text+.swift b/Ddom/Extensions/Text+.swift new file mode 100644 index 0000000..964eb94 --- /dev/null +++ b/Ddom/Extensions/Text+.swift @@ -0,0 +1,7 @@ +// +// Text+.swift +// Ddom +// +// Created by Neoself on 11/1/24. +// + diff --git a/Ddom/Extensions/View+.swift b/Ddom/Extensions/View+.swift new file mode 100644 index 0000000..5447af1 --- /dev/null +++ b/Ddom/Extensions/View+.swift @@ -0,0 +1,31 @@ +import SwiftUI + +extension View { + func fontStyle(_ font: Font) -> some View { + self.font(font) + .lineSpacing(CGFloat(font.lineSpacing)) + .kerning(CGFloat(font.letterSpacing)) + } + + func attributedText(_ searchText: String, color: Color) -> some View { + self.overlay( + Text(self.asString()) + .fontStyle(.body4) + .foregroundStyle(color) + .mask( + HStack(spacing: 0) { + ForEach(self.asString().ranges(of: searchText), id: \.self) { range in + Text(String(self.asString()[range])) + } + } + ) + ) + } + + private func asString() -> String { + if let text = (self as? Text)?.asString() { + return text + } + return "" + } +} diff --git a/Ddom/Font/Pretendard-Bold.ttf b/Ddom/Font/Pretendard-Bold.ttf new file mode 100644 index 0000000..fb07fc6 Binary files /dev/null and b/Ddom/Font/Pretendard-Bold.ttf differ diff --git a/Ddom/Font/Pretendard-Medium.ttf b/Ddom/Font/Pretendard-Medium.ttf new file mode 100644 index 0000000..1db67c6 Binary files /dev/null and b/Ddom/Font/Pretendard-Medium.ttf differ diff --git a/Ddom/Font/Pretendard-SemiBold.ttf b/Ddom/Font/Pretendard-SemiBold.ttf new file mode 100644 index 0000000..9f2690f Binary files /dev/null and b/Ddom/Font/Pretendard-SemiBold.ttf differ diff --git a/Ddom/Info.plist b/Ddom/Info.plist new file mode 100644 index 0000000..79c6596 --- /dev/null +++ b/Ddom/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleURLTypes + + + CFBundleURLSchemes + + kakao4200bb2006c1e166c3fbb5783d9c6a89 + + + + LSApplicationQueriesSchemes + + kakaokompassauth + kakaolink + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UIAppFonts + + Pretendard-Bold.ttf + Pretendard-SemiBold.ttf + Pretendard-Medium.ttf + + + diff --git a/Ddom/Models/Store.swift b/Ddom/Models/Store.swift new file mode 100644 index 0000000..c1624f8 --- /dev/null +++ b/Ddom/Models/Store.swift @@ -0,0 +1,22 @@ +struct Store: Codable, Identifiable { + let id: String + let storeName: String + let storeImgUrl: String + let storeType: String + + // MARK: - Registered Store + var favoriteUserCount: String? + var isFavorited: Bool? + let discountPolicy: DiscountPolicy? + + + enum CodingKeys: String, CodingKey { + case id = "storeId" + case storeName,storeImgUrl,storeType,favoriteUserCount,isFavorited,discountPolicy + } +} + +struct DiscountPolicy: Codable { + let discountType: String + let discountDescription: String +} diff --git a/Ddom/Models/User.swift b/Ddom/Models/User.swift new file mode 100644 index 0000000..24f6015 --- /dev/null +++ b/Ddom/Models/User.swift @@ -0,0 +1,23 @@ +// +// User.swift +// tyte +// +// Created by Neoself on 10/23/24. +// +struct User: Codable, Identifiable { + let id: String + let username: String +} + +struct SearchResult: Codable, Identifiable { + let id: String + let username: String + let email: String + let isFriend: Bool + var isPending: Bool + + enum CodingKeys: String, CodingKey { + case id = "_id" // MongoDB의 _id를 id로 매핑 + case username, email, isFriend, isPending + } +} diff --git a/Ddom/Models/Zone.swift b/Ddom/Models/Zone.swift new file mode 100644 index 0000000..6baa155 --- /dev/null +++ b/Ddom/Models/Zone.swift @@ -0,0 +1,12 @@ +// +// User.swift +// tyte +// +// Created by Neoself on 10/23/24. +// + +struct Zone: Codable, Hashable { + let id: String + let name: String + let description: String +} diff --git a/Ddom/Networking/Base/APIEndpoint.swift b/Ddom/Networking/Base/APIEndpoint.swift new file mode 100644 index 0000000..d02c81e --- /dev/null +++ b/Ddom/Networking/Base/APIEndpoint.swift @@ -0,0 +1,35 @@ +// +// APIEndpoint.swift +// tyte +// +// Created by 김 형석 on 9/9/24. +// + +import Foundation + +enum APIEndpoint { + case getStores + case getZones + + case reissueToken + case socialLogin(String) // Provider + case signUp + case checkUsername + + var path: String { + switch self { + case .signUp: + return "/users" + case .reissueToken: + return "/auth/reissue" + case .checkUsername: + return "/users/verify/nickname" + case .socialLogin(let provider): + return "/auth/login/\(provider)" + case .getStores: + return "/stores" + case .getZones: + return "/zones" + } + } +} diff --git a/Ddom/Networking/Base/APIError.swift b/Ddom/Networking/Base/APIError.swift new file mode 100644 index 0000000..96ea20e --- /dev/null +++ b/Ddom/Networking/Base/APIError.swift @@ -0,0 +1,116 @@ +// +// APIError.swift +// Ddom +// +// Created by Neoself on 11/1/24. +// +import Foundation +import Alamofire + +extension APIError { + var requiresLogout: Bool { + switch self { + case .unauthorized: + return true + default: + return false + } + } + + var isNetworkError: Bool { + switch self { + case .networkError, .serverError: + return true + default: + return false + } + } +} + +enum APIError: Error { + case invalidURL + case tokenExpired + case decodingError + case networkError(String) + case serverError(String) + case unauthorized + + case notFound + case unknown + case social + + case invalidTodo + + case emailAlreadyExists + case wrongPassword + case invalidEmail + + case invalidUsername + case invalidPassword + case userNotFound + + init(afError: AFError) { + switch afError { + case .invalidURL(let url): + self = .invalidURL + print("Invalid URL: \(url)") + case .responseSerializationFailed(reason: .decodingFailed(_)): + self = .decodingError + case .responseValidationFailed(reason: .unacceptableStatusCode(let code)): + switch code { + case 401: + self = .unauthorized + case 402: + self = .tokenExpired + case 404: + self = .notFound + //MARK: - Onboarding Error + case 409: + self = .emailAlreadyExists + case 411: + self = .wrongPassword + case 422: + self = .invalidEmail + case 423: + self = .invalidUsername + case 424: + self = .invalidPassword + case 425: + self = .userNotFound + case 431: + self = .invalidTodo + case 500...599: + self = .serverError("Server error: \(code)") + default: + self = .networkError("Network error: \(code)") + } + default: + self = .unknown + } + } + + var localizedDescription: String { + switch self { + case .invalidURL: + return "죄송해요. 주소에 문제가 있어요. 잠시 후 다시 시도해 주세요." + case .decodingError: + return "서버에서 온 정보를 해석하는 데 문제가 있어요. 나중에 다시 시도해 주세요." + case .networkError(let message): + return "네트워크 연결에 문제가 있어요: \(message). 와이파이나 데이터 연결을 확인해 주세요." + case .serverError(let message): + return "서버에 문제가 생겼어요: \(message). 잠시 후에 다시 시도해 주세요." + case .unauthorized: + return "보안을 위해 다시 로그인해 주세요. 불편을 드려 죄송합니다." + case .notFound: + return "찾으시는 정보가 없어요. 주소를 다시 한 번 확인해 주시겠어요?" + case .unknown: + return "알 수 없는 문제가 발생했어요. 앱을 다시 실행해 보시겠어요?" + case .emailAlreadyExists: + return "이미 사용 중인 이메일이에요. 다른 이메일로 시도해 보시는 건 어떨까요?" + case .userNotFound: + return "등록된 사용자를 찾을 수 없어요. 회원가입을 하셨나요?" + default: + return "" + } + } +} diff --git a/Ddom/Networking/Base/APIResponse.swift b/Ddom/Networking/Base/APIResponse.swift new file mode 100644 index 0000000..108c37e --- /dev/null +++ b/Ddom/Networking/Base/APIResponse.swift @@ -0,0 +1,46 @@ +// +// APIResponse.swift +// Ddom +// +// Created by Neoself on 11/1/24. +// +enum APIResponse: Decodable { + case socialLo(SocialLoginResponse) + case needsRegistration(RegisterTokenResponse) + case error(ErrorResponse) +} + +struct StoreResponse: Codable { + let registered: [Store] + let nonRegistered: [Store] +} + +struct ZoneResponse: Codable { + let zone: [Zone] +} + +struct LoginResponse: Codable { + let user: User + let token: String +} + +struct RegisterTokenResponse: Codable { + let registerToken: String +} + +struct SocialLoginResponse: Codable { + let accessToken: String + let refreshToken: String +} +struct SignUpResponse: Codable { + let accessToken: String + let refreshToken: String +} +struct VerifyNicknameResponse : Codable { + let isDuplicated : Bool +} +// 에러 응답 (4XX) +struct ErrorResponse: Codable { + let message: String + let code: String +} diff --git a/Ddom/Networking/Services/AuthService.swift b/Ddom/Networking/Services/AuthService.swift new file mode 100644 index 0000000..3597abc --- /dev/null +++ b/Ddom/Networking/Services/AuthService.swift @@ -0,0 +1,28 @@ +import Foundation +import Combine +import KakaoSDKAuth +import KakaoSDKUser +import Alamofire + +class AuthService:AuthServiceProtocol { + private let networkService: NetworkServiceProtocol + + init( + networkService: NetworkServiceProtocol = NetworkService() + // 구체적인 apiManager 클래스를 직접 주입하는 것이 아닌, 인터페이스만 하위속성으로 명시 후, 초기화 시 주입 + ) { + self.networkService = networkService + } + func socialLogin(idToken: String, provider: String) -> AnyPublisher<(Int, Data), APIError> { + return networkService.requestWithoutAuth(.socialLogin(provider), method: .post, parameters: ["accessToken": idToken] ) + } + + func signUp(_ params:[String:Any]) -> AnyPublisher<(Int, Data), APIError> { + return networkService.requestWithoutAuth(.signUp, method: .post, parameters: params) + } + + func verifyUsername(_ username: String) -> AnyPublisher<(Int, Data), APIError> { + let parameters = ["nickname": username] + return networkService.requestWithoutAuth(.checkUsername, method: .post, parameters: parameters) + } +} diff --git a/Ddom/Networking/Services/NetworkService.swift b/Ddom/Networking/Services/NetworkService.swift new file mode 100644 index 0000000..bf84bfa --- /dev/null +++ b/Ddom/Networking/Services/NetworkService.swift @@ -0,0 +1,91 @@ +// +// NetworkService.swift +// Ddom +// +// Created by Neoself on 10/31/24. +// +import Foundation +import Alamofire +import Combine + +class NetworkService: NetworkServiceProtocol { + func request( + _ endpoint: APIEndpoint, + method: HTTPMethod = .get, + parameters: Parameters? = nil + ) -> AnyPublisher<(Int, Data), APIError> { + return Future { promise in + var headers: HTTPHeaders = [:] + if AppState.shared.isGuestMode { + print("requesting API in guest Mode: returning...") + return + } + + if let token = KeychainManager.shared.getAccessToken() { + headers = ["Authorization": "Bearer \(token)"] + } else if APIConstants.isDevelopment { + headers = ["Authorization": "Bearer dummyToken"] + } else { + promise(.failure(.unauthorized)) + return + } + + AF.request( + APIConstants.baseUrl + endpoint.path, + method: method, + parameters: parameters, + encoding: JSONEncoding.default, + headers: headers + ) + .validate() + .responseData { response in + if let statusCode = response.response?.statusCode, + let data = response.data { + promise(.success((statusCode, data))) + } else if let error = response.error { + promise(.failure(APIError(afError: error))) + } else { + promise(.failure(.unknown)) + } + } + //MARK: Legacy +// .responseDecodable(of: T.self) { response in +// switch response.result { +// case .success(let value): +// promise(.success(value)) +// case .failure(let error): +// promise(.failure(APIError(afError: error))) +// } +// } + + } + .eraseToAnyPublisher() + } + + func requestWithoutAuth( + _ endpoint: APIEndpoint, + method: HTTPMethod = .get, + parameters: Parameters? = nil + ) -> AnyPublisher<(Int, Data), APIError> { + return Future { promise in + AF.request( + APIConstants.baseUrl + endpoint.path, + method: method, + parameters: parameters, + encoding: JSONEncoding.default + ) + .validate() + .responseData { response in + if let statusCode = response.response?.statusCode, + let data = response.data { + promise(.success((statusCode, data))) + } else if let error = response.error { + promise(.failure(APIError(afError: error))) + } else { + promise(.failure(.unknown)) + } + } + } + .eraseToAnyPublisher() + } +} diff --git a/Ddom/Networking/Services/StoreService.swift b/Ddom/Networking/Services/StoreService.swift new file mode 100644 index 0000000..e8015fb --- /dev/null +++ b/Ddom/Networking/Services/StoreService.swift @@ -0,0 +1,22 @@ +import Foundation +import Combine +import KakaoSDKAuth +import KakaoSDKUser + +class StoreService: StoreServiceProtocol { + private let networkService: NetworkServiceProtocol + + init( + networkService: NetworkServiceProtocol = NetworkService() + ) { + self.networkService = networkService + } + + func getZones() -> AnyPublisher<(Int,Data), APIError> { + return networkService.request(.getZones,method: .get, parameters: nil) + } + + func getStores(for locationId:String) -> AnyPublisher<(Int,Data), APIError> { + return networkService.request(.getStores, method: .get, parameters: nil) + } +} diff --git a/Ddom/Preview Content/Preview Assets.xcassets/Contents.json b/Ddom/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Ddom/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ddom/Protocol/ServiceProtocol.swift b/Ddom/Protocol/ServiceProtocol.swift new file mode 100644 index 0000000..e780c47 --- /dev/null +++ b/Ddom/Protocol/ServiceProtocol.swift @@ -0,0 +1,36 @@ +// +// ServiceProtocol.swift +// Ddom +// +// Created by Neoself on 11/1/24. +// + +import Combine +import Alamofire +import Foundation + +protocol NetworkServiceProtocol { + func request( + _ endpoint: APIEndpoint, + method: HTTPMethod, + parameters: Parameters? + ) -> AnyPublisher<(Int,Data), APIError> + + func requestWithoutAuth( + _ endpoint: APIEndpoint, + method: HTTPMethod, + parameters: Parameters? + ) -> AnyPublisher<(Int, Data), APIError> +} + +protocol AuthServiceProtocol { + func socialLogin(idToken: String, provider: String) -> AnyPublisher<(Int, Data), APIError> +// func socialLogin(idToken: String, provider: String) -> AnyPublisher + func signUp(_ params: [String:Any] ) -> AnyPublisher<(Int, Data), APIError> + func verifyUsername(_ username:String) -> AnyPublisher<(Int, Data),APIError> +} + +protocol StoreServiceProtocol{ + func getZones() -> AnyPublisher<(Int, Data), APIError> + func getStores(for locationId:String) -> AnyPublisher<(Int, Data), APIError> +} diff --git a/Ddom/Utilities/KeychainManager.swift b/Ddom/Utilities/KeychainManager.swift new file mode 100644 index 0000000..3c5f869 --- /dev/null +++ b/Ddom/Utilities/KeychainManager.swift @@ -0,0 +1,115 @@ +// +// KeychainManager.swift +// tyte +// +// Created by 김 형석 on 9/16/24. +// + +import Foundation +import Security + +enum KeychainError: Error { + case unknown(OSStatus) + case notFound + case encodingError +} + +protocol KeychainManagerProtocol { + func save(token: String, forKey:String,service:String) throws + func retrieve(forKey: String, service: String) throws -> String + func delete(forKey: String, service: String) throws +} + +class KeychainManager:KeychainManagerProtocol { + static let shared = KeychainManager() // 싱글톤 유지 + private init() {} // 싱글톤 유지 + + func getAccessToken() -> String? { + try? self.retrieve(forKey:"accessToken") + } + func getRefreshToken() -> String? { + try? self.retrieve(forKey:"refreshToken") + } + + func clearToken() { + try? self.delete(forKey: "accessToken") + try? self.delete(forKey: "refreshToken") + } + +// func getUsername() -> String? { +// UserDefaults.standard.string(forKey: "lastLoggedInUsername") +// } + + //MARK: - 핵심 로직 + func save(token: String, forKey key: String, service: String = "com.clip.ddom") throws { + guard let data = token.data(using: .utf8) else { + throw KeychainError.encodingError + } + + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: key, + kSecValueData as String: data, + kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly + ] + + let status = SecItemAdd(query as CFDictionary, nil) + + if status == errSecDuplicateItem { + // Item already exists, let's update it + let updateQuery: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: key + ] + let updateAttributes: [String: Any] = [kSecValueData as String: data] + let updateStatus = SecItemUpdate(updateQuery as CFDictionary, updateAttributes as CFDictionary) + + guard updateStatus == errSecSuccess else { + throw KeychainError.unknown(updateStatus) + } + } else if status != errSecSuccess { + throw KeychainError.unknown(status) + } + } + + func retrieve(forKey key: String, service: String = "com.yourapp.tokens") throws -> String { + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: key, + kSecMatchLimit as String: kSecMatchLimitOne, + kSecReturnData as String: true + ] + + var result: AnyObject? + let status = SecItemCopyMatching(query as CFDictionary, &result) + + guard status != errSecItemNotFound else { + throw KeychainError.notFound + } + guard status == errSecSuccess else { + throw KeychainError.unknown(status) + } + + guard let data = result as? Data, let token = String(data: data, encoding: .utf8) else { + throw KeychainError.unknown(status) + } + + return token + } + + func delete(forKey key: String, service: String = "com.yourapp.tokens") throws { + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: key + ] + + let status = SecItemDelete(query as CFDictionary) + guard status == errSecSuccess || status == errSecItemNotFound else { + throw KeychainError.unknown(status) + } + } +} diff --git a/Ddom/Utilities/Route.swift b/Ddom/Utilities/Route.swift new file mode 100644 index 0000000..367dc40 --- /dev/null +++ b/Ddom/Utilities/Route.swift @@ -0,0 +1,13 @@ +// +// Route.swift +// Ddom +// +// Created by Neoself on 10/27/24. +// + +enum Route: Hashable { + case createAccount + case onboarding + case selectLocation + case searchStore +} diff --git a/Ddom/View/MainTab/HomeView.swift b/Ddom/View/MainTab/HomeView.swift new file mode 100644 index 0000000..f47b4ff --- /dev/null +++ b/Ddom/View/MainTab/HomeView.swift @@ -0,0 +1,24 @@ +// +// HomeView.swift +// Ddom +// +// Created by Neoself on 11/1/24. +// +import SwiftUI + +struct HomeView: View { + @StateObject var viewModel = HomeViewModel() + + var body: some View { + VStack{ + BackNavBar() + Spacer() + + Text("HomeView") + + Spacer() + } + .padding(.horizontal,16) + .toolbar(.hidden) + } +} diff --git a/Ddom/View/MainTab/MainTabView.swift b/Ddom/View/MainTab/MainTabView.swift new file mode 100644 index 0000000..c7d6589 --- /dev/null +++ b/Ddom/View/MainTab/MainTabView.swift @@ -0,0 +1,115 @@ +import SwiftUI + +struct MainTabView: View { + @EnvironmentObject var appState: AppState + + @State private var selectedTab = 1 + @State private var isToastPresented = false + + var body: some View { + ZStack { + VStack(spacing: 0) { + switch(selectedTab) { + case 0: + HomeView() + default: + StoreListView() + } + BottomTab(selectedTab: $selectedTab) + } + .background(.myWhite) + + if isToastPresented, let toast = appState.currentToast { + CustomToast(toastData: toast) + .frame(maxHeight: .infinity, alignment: .top) + .padding(.top, 40) + .zIndex(1) + .transition(.asymmetric( + insertion: .opacity.combined(with: .move(edge: .top)), + removal: .opacity.combined(with: .move(edge: .top)) + )) + .animation(.mediumEaseInOut, value: isToastPresented) + } + } + .onChange(of: appState.currentToast?.text) { _, newValue in + if newValue != nil { + withAnimation { + isToastPresented = true + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 3) { + withAnimation { + isToastPresented = false + } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + appState.currentToast = nil + } + } + } + } + } +} + +struct BottomTab: View { + @Binding var selectedTab: Int + + var body: some View { + let tabBarText = [("gearshape.fill","홈"), ("magnifyingglass","가게 탐색")] + + ZStack { + Rectangle() + .fill(.myWhite) + .shadow(color: .gray5.opacity(0.08), radius: 8) + + HStack(spacing: 0) { + ForEach(0..<2, id: \.self) { index in + TabBarButton( + icon: tabBarText[index].0, + text: tabBarText[index].1, + isSelected: selectedTab == index + ) { + withAnimation(.fastEaseInOut) { + selectedTab = index + } + + } + } + } + .background(.myWhite) + } + .frame(height: 56) + } +} + +struct TabBarButton: View { + let icon: String + let text: String + let isSelected: Bool + let action: () -> Void + + var body: some View { + Button(action: action) { + VStack(spacing: 4) { + Image(systemName:icon) + .renderingMode(.template) + .resizable() + .frame(width:24,height: 24) + .foregroundColor(isSelected ? .primary9 : .gray3) + .fontStyle(.body4) + + Text(text) + .fontStyle(.caption1) + .foregroundColor(isSelected ? .primary9 : .gray5) + } + .frame(maxWidth: .infinity) + .padding(.top, 16) + } + } +} + + + +#Preview { + MainTabView() + .environmentObject(AppState.shared) +} diff --git a/Ddom/View/MainTab/SearchStoreView.swift b/Ddom/View/MainTab/SearchStoreView.swift new file mode 100644 index 0000000..b464b8b --- /dev/null +++ b/Ddom/View/MainTab/SearchStoreView.swift @@ -0,0 +1,156 @@ +// +// SearchStoreView.swift +// Ddom +// +// Created by Neoself on 11/1/24. +// +import SwiftUI + +struct SearchStoreView: View { + @ObservedObject var viewModel : StoreListViewModel + + @Environment(\.dismiss) private var dismiss + @FocusState private var isFocused: Bool + + var body: some View { + VStack(spacing: 0) { + HStack(spacing: 8) { + Button(action: { + dismiss() + }) { + Image("arrow_left") + .resizable() + .frame(width: 24, height: 24) + } + + HStack(spacing: 8) { + Image("search") + .resizable() + .frame(width: 24, height: 24) + .foregroundStyle(.gray10) + + TextField("찾고 싶은 가게를 검색해주세요", text: $viewModel.searchQuery) + .fontStyle(.body4) + .tint(.primary9) + .foregroundStyle(.gray10) + .focused($isFocused) + + if !viewModel.searchQuery.isEmpty { + Button(action: { + viewModel.searchQuery = "" + }) { + Image(systemName: "xmark.circle.fill") + .resizable() + .frame(width: 16, height: 16) + .foregroundStyle(.gray5) + } + } + } + .padding(.horizontal, 12) + .frame(height: 36) + .background(.surface1) + .clipShape(RoundedRectangle(cornerRadius: 8)) + } + .padding(.horizontal, 16) + .padding(.vertical, 10) + + Divider() + .foregroundStyle(.gray2) + + if viewModel.searchQuery.isEmpty { + VStack(alignment: .leading, spacing: 0) { + if !viewModel.recentSearches.isEmpty { + Text("최근 검색") + .fontStyle(.body3) + .foregroundStyle(.gray10) + .padding(.horizontal, 16) + .padding(.vertical, 12) + +// ForEach(viewModel.recentSearches, id: \.self) { search in +// RecentSearchRow(search: search) { +// viewModel.removeRecentSearch(search) +// } +// } + } + } + } else { + ScrollView { + LazyVStack(spacing: 0) { + ForEach(viewModel.filteredStores, id: \.id) { store in + SearchResultRow(store: store, searchQuery: viewModel.searchQuery) + + Divider() + .foregroundStyle(.gray2) + } + } + } + } + + Spacer() + } + .navigationBarHidden(true) + .onAppear { + isFocused = true + } + .toolbar(.hidden) + + } +} + +struct SearchResultRow: View { + let store: Store + let searchQuery: String + + var body: some View { + HStack(spacing: 12) { + AsyncImage(url: URL(string: store.storeImgUrl)) { image in + image + .resizable() + .aspectRatio(contentMode: .fill) + } placeholder: { + Circle() + .fill(.gray2) + } + .frame(width: 40, height: 40) + .clipShape(Circle()) + + Text(store.storeName) + .fontStyle(.body4) + .foregroundStyle(.gray10) + .attributedText(searchQuery, color: .primary9) + + Spacer() + } + .padding(.horizontal, 16) + .padding(.vertical, 12) + .background(.white) + } +} + +struct RecentSearchRow: View { + let search: String + let onDelete: () -> Void + + var body: some View { + HStack { + Text(search) + .fontStyle(.body4) + .foregroundStyle(.gray10) + + Spacer() + + Button(action: onDelete) { + Image(systemName: "xmark") + .resizable() + .frame(width: 16, height: 16) + .foregroundStyle(.gray5) + } + } + .padding(.horizontal, 16) + .padding(.vertical, 12) + .background(.white) + + Divider() + .foregroundStyle(.gray2) + } +} diff --git a/Ddom/View/MainTab/SelectLocationView.swift b/Ddom/View/MainTab/SelectLocationView.swift new file mode 100644 index 0000000..e8ef15b --- /dev/null +++ b/Ddom/View/MainTab/SelectLocationView.swift @@ -0,0 +1,84 @@ +// +// SelectLocationView.swift +// Ddom +// +// Created by Neoself on 11/1/24. +// +import SwiftUI + +struct SelectLocationView: View { + @Environment(\.dismiss) private var dismiss + @ObservedObject var viewModel: StoreListViewModel + + var body: some View { + VStack(spacing: 0) { + BackNavBar() + + VStack{ + Text("또옴 가게 지역 선택") + .fontStyle(.heading2) + .foregroundStyle(.gray10) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 16) + .padding(.vertical, 24) + + VStack{ + ForEach(viewModel.zones, id:\.self) { zone in + locationItem(zone) + } + } + + Spacer() + + CustomButton( + action: { + dismiss() }, + isPrimary: false, + isLoading: false, + text: "선택 완료하기", + isDisabled: false, + isFullWidth: true + ) + .padding(.bottom, 8) + } + .padding(.horizontal, 16) + } + .toolbar(.hidden) + + } + + private func locationItem(_ zone:Zone) -> some View { + Button(action: { + viewModel.selectLocation(zone) + }) { + HStack{ + VStack(alignment: .leading, spacing: 4) { + Text(zone.name.components(separatedBy: ",").joined(separator: "/")) + .fontStyle(.body3) + .foregroundStyle(.gray10) + + Text(viewModel.description[zone.name] ?? "세부설명 없음") + .fontStyle(.body5) + .foregroundStyle(.gray5) + } + + Spacer() + + if viewModel.selectedLocation == zone { + Image("check") + .resizable() + .frame(width: 24, height: 24) + .foregroundStyle(.gray5) } + } + } + .padding(12) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(viewModel.selectedLocation == zone ? .surface1 : .clear) + ) + } +} + +#Preview{ + SelectLocationView(viewModel: StoreListViewModel()) +} diff --git a/Ddom/View/MainTab/StoreListView.swift b/Ddom/View/MainTab/StoreListView.swift new file mode 100644 index 0000000..4e3bd5e --- /dev/null +++ b/Ddom/View/MainTab/StoreListView.swift @@ -0,0 +1,80 @@ +// StoreListView.swift +import SwiftUI + +struct StoreListView: View { + @StateObject private var viewModel = StoreListViewModel() + + var body: some View { + NavigationStack (path:$viewModel.path){ + VStack(spacing: 0) { + storeListHeader(viewModel: viewModel) + + if viewModel.isLoading { + ProgressView() + .frame(maxHeight: .infinity) + } else { + ScrollView { + LazyVStack(spacing: 20) { + ForEach(viewModel.registeredStores, id: \.id) { store in + StoreCard(viewModel:viewModel, store: store, isRegistered:true) + } + + Rectangle() + .fill(.surface1) + .frame(maxWidth: .infinity,maxHeight: 4) + + ForEach(viewModel.nonRegisteredStores, id: \.id) { store in + StoreCard(viewModel:viewModel,store: store, isRegistered:false) + } + } + .padding(.vertical, 20) + } + } + } + .navigationDestination(for: Route.self) { route in + switch route { + case .selectLocation: + SelectLocationView(viewModel:viewModel) + case .searchStore: + SearchStoreView(viewModel:viewModel) + default: + EmptyView() + } + } + } + } +} + +private func storeListHeader(viewModel:StoreListViewModel)-> some View { + HStack { + Button(action: { + viewModel.path.append(Route.selectLocation) + }) { + HStack(spacing: 4) { + Text( + viewModel.selectedLocation?.name.components(separatedBy: ",").joined(separator: " ") ?? "지역 선택") + .fontStyle(.title1) + .foregroundStyle(.gray10) + + Image("arrow_down") + .resizable() + .frame(width: 24, height: 24) + .foregroundStyle(.gray10) + } + } + + Spacer() + + Button(action: { + viewModel.path.append(Route.searchStore) + + }) { + Image("search") + .resizable() + .frame(width: 24, height: 24) + } + } + .padding(.horizontal, 16) + .padding(.vertical,10) + .background(.myWhite) +} diff --git a/Ddom/View/Onboarding/CreateAccountView.swift b/Ddom/View/Onboarding/CreateAccountView.swift new file mode 100644 index 0000000..a201842 --- /dev/null +++ b/Ddom/View/Onboarding/CreateAccountView.swift @@ -0,0 +1,165 @@ +// +// CreateAccountView.swift +// Ddom +// +// Created by 김 형석 on 10/15/24. +// + +import SwiftUI + +struct CreateAccountView: View { + @StateObject var viewModel = CreateAccountViewModel() + @FocusState private var focusedField: Field? + + enum Field: Hashable { + case username + case phone + } + + var body: some View { + ZStack{ + Color.clear + .contentShape(Rectangle()) + .onTapGesture { + focusedField = nil + } + + VStack{ + BackNavBar() + VStack(alignment: .leading){ + VStack(alignment: .leading){ + HStack(spacing: 0){ + Text("회원가입") + .fontStyle(.heading2) + .foregroundStyle(.secondary6) + Text("을 위해") + .fontStyle(.heading2) + .foregroundStyle(.gray10) + } + + Text("간단한 정보를 알려주세요") + .fontStyle(.heading2) + .foregroundStyle(.gray10) + } + .padding(.vertical,24) + + VStack(alignment: .leading){ + HStack(spacing:2){ + Text("닉네임") + .fontStyle(.body3) + .foregroundStyle(.gray10) + + Text("*") + .fontStyle(.body3) + .foregroundStyle(Color(hex:"ff6f6f")) + + Spacer() + } + + HStack(spacing:10){ + CustomTextField( + text: $viewModel.username, + placeholder: "닉네임을 입력해주세요", + keyboardType: .default, + onSubmit: {print("username") }, + isError: !viewModel.errorText.isEmpty + ) + .focused($focusedField, equals: .username) + + CustomButton( + action:{ viewModel.handleDuplicateCheckButton() }, + isPrimary: false, + isLoading: viewModel.isLoading, + text: "중복확인", + isDisabled: viewModel.username.isEmpty || viewModel.isUsernameValid, + isFullWidth: false + ) + } + + statusText(viewModel: viewModel) + } + .padding(.bottom,32) + + HStack(spacing:2){ + Text("휴대폰 번호") + .fontStyle(.body3) + .foregroundStyle(.gray10) + Text("*") + .fontStyle(.body3) + .foregroundStyle(Color(hex:"ff6f6f")) + + Spacer() + } + + CustomTextField( + text: $viewModel.phone, + placeholder: "000-0000-0000", + keyboardType: .numberPad, + onSubmit: {print("number") } + ) + .frame(maxWidth: .infinity) + .focused($focusedField, equals: .phone) + + Spacer() + + CustomButton( + action: { + viewModel.isDetailViewPresent = true + }, + isPrimary: false, + isLoading: false, + text: "입력하기", + isDisabled: viewModel.isSignUpButtonDisabled, + isFullWidth: true) + + } + .padding(.horizontal,16) + } + } + .sheet(isPresented: $viewModel.isDetailViewPresent) { + TermsDetailBottomSheet(viewModel:viewModel) + .presentationDetents([.height(540)]) + .presentationDragIndicator(.hidden) + .presentationBackground(.clear) + } + .onAppear{ + focusedField = Field.username + } + .toolbar(.hidden) + } +} + +@ViewBuilder +private func statusText(viewModel:CreateAccountViewModel) -> some View { + if viewModel.errorText.isEmpty { + if viewModel.isUsernameValid { + HStack(spacing:4){ + Image("check") + .resizable() + .renderingMode(.template) + .frame(width:16,height:16) + .foregroundStyle(.success) + + Text("중복확인이 완료 되었습니다") + .fontStyle(.caption1) + .foregroundStyle(.success) + } + .padding(.leading,12) + } else { + Text("최소 2자~8자 (영문,국문 가능)") + .fontStyle(.caption1) + .foregroundStyle(.gray5) + .padding(.leading,12) + } + } else { + Text(viewModel.errorText) + .fontStyle(.caption1) + .foregroundStyle(.error) + .padding(.leading,12) + } +} + +#Preview { + CreateAccountView() +} + diff --git a/Ddom/View/Onboarding/OnboardingView.swift b/Ddom/View/Onboarding/OnboardingView.swift new file mode 100644 index 0000000..77d0031 --- /dev/null +++ b/Ddom/View/Onboarding/OnboardingView.swift @@ -0,0 +1,126 @@ +// OnboardingView.swift +// Ddom +// Created by 김 형석 on 10/15/24. + +import SwiftUI +import UIKit +import AuthenticationServices + +struct OnboardingView: View { + @StateObject var viewModel = OnboardingViewModel() + + var body: some View { + NavigationStack(path: $viewModel.navigationPath) { + VStack(spacing: 0) { + Spacer().frame(height:56) + VStack(alignment: .leading, spacing: 2) { + Text("또 오고싶은 가게라면?") + .fontStyle(.heading1) + .foregroundStyle(.gray10) + .frame(maxWidth: .infinity,alignment: .leading) + + Text("또옴 가게를 등록하고 혜택을 받아보세요!") + .fontStyle(.body2) + .foregroundStyle(.gray5) + } + .padding(.horizontal, 16) + .padding(.bottom, 20) + + TabView { + ForEach(0..<3) { index in + SwipeableContentView(index: index) + } + } + .tint(.gray7) + .foregroundStyle(.gray7) + .tabViewStyle(PageTabViewStyle(indexDisplayMode: .automatic)) + .padding(.horizontal, 16) + + Spacer() + + VStack(spacing: 8) { + Button(action: { + viewModel.performKakaoLogin() + }) { + HStack { + Image(systemName: "message.fill") + Text("Kakao로 로그인하기") + } + .fontStyle(.body5) + .foregroundStyle(.gray10) + .padding() + .frame(maxWidth: .infinity) + .background(Color(hex:"FAE300")) + .cornerRadius(8) + } + + SignInWithAppleButton( + onRequest: { request in + request.requestedScopes = [.fullName,.email] + }, + onCompletion: { result in + switch result { + case .success(let authResults): + viewModel.performAppleLogin(authResults) + case .failure(let error): + print(error.localizedDescription) + } + } + ) + .frame(height: 50) + .cornerRadius(10) +// .overlay( +// RoundedRectangle(cornerRadius: 10) +// .stroke(.gray60, lineWidth: 1) +// ) + + Button(action: { + viewModel.moveToMainTabView() + }) { + Text("로그인 없이 사용하기") + .fontStyle(.caption) + .foregroundStyle(.gray4) + .padding(.top,8) + .padding(.bottom,16) + } + } + .padding(.horizontal, 20) + .padding(.bottom, 8) + } + .edgesIgnoringSafeArea(.bottom) + .onAppear{setupAppearance()} + .navigationDestination(for: Route.self) { route in + switch route { + case .createAccount: + CreateAccountView() + default: + CreateAccountView() + } + } + } + } +} + +func setupAppearance() { + UIPageControl.appearance().currentPageIndicatorTintColor = .gray2 + UIPageControl.appearance().pageIndicatorTintColor = .gray1 +} + +struct SwipeableContentView: View { + let index: Int + + var body: some View { + VStack { + Text("스와이프 가능한 콘텐츠 \(index + 1)") + .fontStyle(.heading1) + .foregroundColor(.gray2) + } + .frame(maxWidth: .infinity, maxHeight: 389) + .background(.surface1) + .cornerRadius(16) + } +} + +#Preview { + OnboardingView() +} diff --git a/Ddom/ViewModel/CreateAccountViewModel.swift b/Ddom/ViewModel/CreateAccountViewModel.swift new file mode 100644 index 0000000..237cc08 --- /dev/null +++ b/Ddom/ViewModel/CreateAccountViewModel.swift @@ -0,0 +1,192 @@ +// +// CreateAccountViewModel.swift +// Ddom +// +// Created by Neoself on 10/30/24. +// + +import Foundation +import SwiftUI +import Combine + +class CreateAccountViewModel: ObservableObject { + private let appState: AppState = AppState.shared + + @Published var username: String = "" { didSet{ + if username != oldValue { + withAnimation (.mediumEaseInOut){ + isUsernameValid = false + errorText = "" + } + } + }} + + @Published var phone: String = "" { + didSet { + let numbers = phone.filter { $0.isNumber } // 숫자만 추출 + // 최대 11자리로 제한 + if numbers.count > 11 { + phone = oldValue + return + } + + // 형식에 맞게 하이픈 추가 + let formattedNumber = formatPhoneNumber(numbers) + + // 현재 입력값이 이미 포맷된 값과 다르다면 업데이트 + if phone != formattedNumber { + phone = formattedNumber + } + } + } + + @Published var errorText: String = "" + @Published var isUsernameValid: Bool = false + @Published var isDetailViewPresent: Bool = false + @Published var isLoading:Bool = false + + @Published var isServiceChecked:Bool = false + @Published var isPrivacyChecked:Bool = false + @Published var isAdvertisementChecked:Bool = false + @Published var isMarketingChecked:Bool = false + + var isAllChecked:Bool { + isServiceChecked && + isPrivacyChecked && + isAdvertisementChecked && + isMarketingChecked + } + + func toggleAllChecks() { + let newValue = !isAllChecked + withAnimation(.fastEaseOut) { + isServiceChecked = newValue + isPrivacyChecked = newValue + isAdvertisementChecked = newValue + isMarketingChecked = newValue + } + } + + var isAgreeButtonDisabled:Bool { + !(isServiceChecked && isPrivacyChecked) || + isLoading + } + + var isSignUpButtonDisabled:Bool { + username.isEmpty || + phone.isEmpty || + !isUsernameValid || + isLoading + } + + private let authService: AuthService + + init(authService: AuthService = AuthService()) { + self.authService = authService + } + + private var cancellables = Set() + + private func formatPhoneNumber(_ numbers: String) -> String { + var result = "" + let length = numbers.count + + for (index, char) in numbers.enumerated() { + if index == 3 || (index == 7 && length > 7) { + result += "-" + } + result.append(char) + } + return result + } + + func handleSubmit(){ + isDetailViewPresent = false + signUp() + } + + func handleDuplicateCheckButton(){ + if username.count<2 { + withAnimation(.mediumEaseInOut){ + errorText = "2자 이상 입력해주세요" + return + } + } else if username.count>8 { + withAnimation(.mediumEaseInOut){ + errorText="최대 8자까지 가능해요" + return + } + } + verifyUsername() + } + + func verifyUsername() { + isLoading = true + authService.verifyUsername(username) + .receive(on: DispatchQueue.main) + .sink { [weak self] completion in + self?.isLoading = false + if case .failure(let error) = completion { + print(error.localizedDescription) + } + } receiveValue: { [weak self] (statusCode, data) in + guard let self = self else {return} + if statusCode == 200 { + if let res = try? JSONDecoder().decode(VerifyNicknameResponse.self, from: data) { + if res.isDuplicated { + errorText="닉네임 중복입니다" + } else { + isUsernameValid = true + } + } + } + } + .store(in: &cancellables) + } + + func signUp() { + isLoading = true + let params = [ + "registerToken": UserDefaults.standard.string(forKey: "registerToken") ?? "dummyRegisterToken", + "servicePermission":isServiceChecked, + "privatePermission":isPrivacyChecked, + "advertisingPermission":isAdvertisementChecked, + "marketingPermission":isMarketingChecked, + "nickname": username, + "phoneNumber": phone + ] as [String : Any] + + authService.signUp(params) + .receive(on: DispatchQueue.main) + .sink { [weak self] completion in + self?.isLoading = false + if case .failure(let error) = completion { + print(error.localizedDescription) + } + } receiveValue: { [weak self] (statusCode, data) in + guard let self = self else {return} + print(statusCode) + if statusCode == 200 { + if let res = try? JSONDecoder().decode(SignUpResponse.self, from: data) { + handleSuccessfulLogin( + accessToken: res.accessToken, + refreshToken: res.refreshToken + ) + } + } else { + print("Unexpected status code: \(statusCode)") + } + } + .store(in: &cancellables) + } + + func handleSuccessfulLogin(accessToken: String,refreshToken:String) { + do { + try KeychainManager.shared.save(token: accessToken, forKey: "accessToken") + try KeychainManager.shared.save(token: refreshToken, forKey: "refreshToken") + appState.isLoggedIn = true + } catch { + print(error.localizedDescription) + } + } +} diff --git a/Ddom/ViewModel/HomeViewModel.swift b/Ddom/ViewModel/HomeViewModel.swift new file mode 100644 index 0000000..dafadbd --- /dev/null +++ b/Ddom/ViewModel/HomeViewModel.swift @@ -0,0 +1,48 @@ +// +// HomeViewModel.swift +// Ddom +// +// Created by Neoself on 11/1/24. +// +import Foundation +import SwiftUI +import Combine + +class HomeViewModel: ObservableObject { + private let appState: AppState = AppState.shared + + @Published var errorText: String = "" + @Published var isUsernameValid: Bool = false + @Published var isLoading:Bool = false + + private let storeService: StoreServiceProtocol + + init(storeService: StoreServiceProtocol = StoreService()) { + self.storeService = storeService + } + + private var cancellables = Set() + + func getLocations() { + + } + + + func signUp() { + appState.isLoggedIn = true + // authService.signUp(username: username, phone: phone) + // .receive(on: DispatchQueue.main) + // .sink { [weak self] completion in + // self?.isLoading = false + // if case .failure(let error) = completion { + // + // print(error.localizedDescription) + // } + // } receiveValue: { [weak self] signUpResponse in + // self?.handleSuccessfulLogin(loginResponse: signUpResponse) + // } + // .store(in: &cancellables) + } + +} + diff --git a/Ddom/ViewModel/OnboardingViewModel.swift b/Ddom/ViewModel/OnboardingViewModel.swift new file mode 100644 index 0000000..46132e5 --- /dev/null +++ b/Ddom/ViewModel/OnboardingViewModel.swift @@ -0,0 +1,112 @@ +import Foundation +import SwiftUI +import Combine +import AuthenticationServices + +import KakaoSDKCommon +import KakaoSDKAuth +import KakaoSDKUser + +class OnboardingViewModel: ObservableObject { + private let appState: AppState = AppState.shared + + @Published var navigationPath = NavigationPath() + @Published var isSocialLoading: Bool = false + private let authService: AuthServiceProtocol + + init(authService: AuthServiceProtocol = AuthService()) { + self.authService = authService + } + + private var cancellables = Set() + //MARK: - 소셜로그인 관련 메서드 + func performKakaoLogin() { + isSocialLoading = true + if UserApi.isKakaoTalkLoginAvailable() { + UserApi.shared.loginWithKakaoTalk { [weak self] (oauthToken, error) in + if let error = error { + print(error.localizedDescription) + self?.isSocialLoading = false + return + } + + if let token = oauthToken?.accessToken { + self?.authenticateWithServer(for:"KAKAO", with:token) + } + } + } else { + UserApi.shared.loginWithKakaoAccount { [weak self] (oauthToken, error) in + if let error = error { + print(error.localizedDescription) + self?.isSocialLoading = false + return + } + if let token = oauthToken?.accessToken { + self?.authenticateWithServer(for:"KAKAO", with:token) + } + } + } + } + + // TODO: Apple 처음 로그인 시, 이메일 값 UserDefaults로 저장 구현후, 서버에 같이 인자로 전달 + func performAppleLogin(_ result: ASAuthorization) { + isSocialLoading = true + + guard let appleIDCredential = result.credential as? ASAuthorizationAppleIDCredential else { + print("Error: Unexpected credential type") + isSocialLoading = false + return + } + + guard let identityTokenData = appleIDCredential.identityToken, + let identityToken = String(data: identityTokenData, encoding: .utf8) else { + print("Error: Unable to fetch identity token or authorization code") + isSocialLoading = false + return + } + + authenticateWithServer(for:"APPLE", with:identityToken) + } + + private func authenticateWithServer(for provider:String, with idToken: String) { + authService.socialLogin(idToken: idToken, provider: provider) + .receive(on: DispatchQueue.main) + .sink { [weak self] completion in + self?.isSocialLoading = false + print(completion) + if case .failure(let error) = completion { + print("Error occurred: \(error)") + } + } receiveValue: { [weak self] (statusCode, data) in + guard let self = self else {return} + switch statusCode { + case 200: + if let res = try? JSONDecoder().decode(SocialLoginResponse.self, from: data) { + handleSuccessfulLogin( + accessToken: res.accessToken, + refreshToken: res.refreshToken + ) + } + + case 201: + if let res = try? JSONDecoder().decode(RegisterTokenResponse.self, from: data) { + UserDefaults.standard.set(res.registerToken, forKey: "registerToken") + navigationPath.append(Route.createAccount) + } + default: + print("Unexpected status code: \(statusCode)") + } + } + .store(in: &cancellables) + } + + private func handleSuccessfulLogin(accessToken: String,refreshToken:String) { + do { + try KeychainManager.shared.save(token: accessToken, forKey: "accessToken") + try KeychainManager.shared.save(token: refreshToken, forKey: "refreshToken") + appState.isLoggedIn = true + } catch { + print(error.localizedDescription) + } + } +} diff --git a/Ddom/ViewModel/StoreListViewModel.swift b/Ddom/ViewModel/StoreListViewModel.swift new file mode 100644 index 0000000..84f132c --- /dev/null +++ b/Ddom/ViewModel/StoreListViewModel.swift @@ -0,0 +1,107 @@ +// +// StoreListViewModel.swift +// Ddom +// +// Created by Neoself on 11/1/24. +// + +import Foundation +import Combine +import SwiftUI + +class StoreListViewModel: ObservableObject { + @Published var path = NavigationPath() + + @Published var searchQuery = "" { didSet{ + if searchQuery.isEmpty { + filteredStores = [] + } else { + filteredStores = registeredStores.filter { + $0.storeName.localizedCaseInsensitiveContains(searchQuery) + } + } + }} + + @Published var registeredStores: [Store] = [] + @Published var nonRegisteredStores: [Store] = [] + @Published var selectedLocation: Zone? = nil + @Published var zones: [Zone] = [] + @Published var filteredStores: [Store] = [] + + @Published var recentSearches: [String] = [] + + let description = [ + "이대신촌":"신촌동, 봉원동, 창천동, 대현동 등", + "건대성수":"성수동, 화양동 등" + ] + + @Published var isLoading: Bool = false + + + private let storeService: StoreServiceProtocol + private var cancellables = Set() + + init(storeService: StoreServiceProtocol = StoreService()) { + self.storeService = storeService + fetchInitialData() + } + + func selectLocation(_ zone: Zone) { + print("selectLocation") + selectedLocation = zone + getStores(selectedLocation:zone) + } + + private func fetchInitialData() { + isLoading = true + print("fetchInitialData") + storeService.getZones() + .receive(on: DispatchQueue.main) + .sink { [weak self] completion in + self?.isLoading = false + if case .failure(let error) = completion { + print("Error fetching data: \(error)") + } + } receiveValue: { [weak self] (statusCode,data) in + guard let self = self else { return } + + if statusCode == 200 { + if let res = try? JSONDecoder().decode(ZoneResponse.self, from: data) { + zones = res.zone + print("asdf\(res)") + guard let _selectedLocation = res.zone.first else {return} + selectedLocation = _selectedLocation + getStores(selectedLocation: _selectedLocation) + print("asdf\(res)") + } + print("heelo") + } else { + print("Unexpected status code in getZones: \(statusCode)") + } + } + .store(in: &cancellables) + } + + private func getStores(selectedLocation:Zone) { + isLoading = true + storeService.getStores(for: selectedLocation.id) + .receive(on: DispatchQueue.main) + .sink { [weak self] completion in + self?.isLoading = false + if case .failure(let error) = completion { + print("Error fetching data: \(error)") + } + } receiveValue: { [weak self] (statusCode,data) in + guard let self = self else { return } + if statusCode == 200 { + if let res = try? JSONDecoder().decode(StoreResponse.self, from: data) { + registeredStores = res.registered + nonRegisteredStores = res.nonRegistered + } + } else { + print("Unexpected status code in getStores: \(statusCode)") + } + } + .store(in: &cancellables) + } +} diff --git a/DdomTests/DdomTests.swift b/DdomTests/DdomTests.swift new file mode 100644 index 0000000..0d93986 --- /dev/null +++ b/DdomTests/DdomTests.swift @@ -0,0 +1,17 @@ +// +// DdomTests.swift +// DdomTests +// +// Created by 김 형석 on 10/15/24. +// + +import Testing +@testable import Ddom + +struct DdomTests { + + @Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. + } + +} diff --git a/DdomUITests/DdomUITests.swift b/DdomUITests/DdomUITests.swift new file mode 100644 index 0000000..09e2175 --- /dev/null +++ b/DdomUITests/DdomUITests.swift @@ -0,0 +1,43 @@ +// +// DdomUITests.swift +// DdomUITests +// +// Created by 김 형석 on 10/15/24. +// + +import XCTest + +final class DdomUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + @MainActor + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + @MainActor + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/DdomUITests/DdomUITestsLaunchTests.swift b/DdomUITests/DdomUITestsLaunchTests.swift new file mode 100644 index 0000000..350d87a --- /dev/null +++ b/DdomUITests/DdomUITestsLaunchTests.swift @@ -0,0 +1,33 @@ +// +// DdomUITestsLaunchTests.swift +// DdomUITests +// +// Created by 김 형석 on 10/15/24. +// + +import XCTest + +final class DdomUITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + @MainActor + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +}