diff --git a/week1/Memorize/Memorize/ContentView.swift b/week1/Memorize/Memorize/ContentView.swift index aa12427..2efd8b5 100644 --- a/week1/Memorize/Memorize/ContentView.swift +++ b/week1/Memorize/Memorize/ContentView.swift @@ -8,6 +8,7 @@ import SwiftUI struct ContentView: View { + @State var countOfCards = 8 @State var contents = Emoji.vehicles.emojiList @@ -29,6 +30,7 @@ struct ContentView: View { } struct ContentView_Previews: PreviewProvider { + static var previews: some View { ContentView() } diff --git a/week1/Memorize/Memorize/Data/EmojiData.swift b/week1/Memorize/Memorize/Data/EmojiData.swift index f5b782c..a08ebb8 100644 --- a/week1/Memorize/Memorize/Data/EmojiData.swift +++ b/week1/Memorize/Memorize/Data/EmojiData.swift @@ -24,13 +24,6 @@ enum Emoji { } var shuffle: [String] { - switch self { - case .vehicles: - return Emoji.vehicles.emojiList.shuffled() - case .faces: - return Emoji.faces.emojiList.shuffled() - case .sports: - return Emoji.sports.emojiList.shuffled() - } + return self.emojiList.shuffled() } } diff --git a/week1/Memorize/Memorize/SubViews/ButtonTabbar.swift b/week1/Memorize/Memorize/SubViews/ButtonTabbar.swift index 1d4edcf..41cfe8d 100644 --- a/week1/Memorize/Memorize/SubViews/ButtonTabbar.swift +++ b/week1/Memorize/Memorize/SubViews/ButtonTabbar.swift @@ -8,6 +8,7 @@ import SwiftUI struct ButtonTabbar: View { + @Binding var countOfCards: Int @Binding var content: [String] @State var previousCount = 0 @@ -89,6 +90,7 @@ struct ButtonTabbar: View { } struct ButtonTabbar_Previews: PreviewProvider { + static var previews: some View { ButtonTabbar(countOfCards: .constant(8), content: .constant([""])) .previewLayout(.fixed(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height * 0.1)) diff --git a/week1/Memorize/Memorize/SubViews/CardView.swift b/week1/Memorize/Memorize/SubViews/CardView.swift index 4c306d0..1f6477a 100644 --- a/week1/Memorize/Memorize/SubViews/CardView.swift +++ b/week1/Memorize/Memorize/SubViews/CardView.swift @@ -8,6 +8,7 @@ import SwiftUI struct CardView: View { + @State var isFaceUp = true var content = "" @@ -30,6 +31,7 @@ struct CardView: View { } struct CardView_Previews: PreviewProvider { + static var previews: some View { CardView() } diff --git a/week2/Memorize/Memorize.xcodeproj/project.pbxproj b/week2/Memorize/Memorize.xcodeproj/project.pbxproj new file mode 100644 index 0000000..290c9f0 --- /dev/null +++ b/week2/Memorize/Memorize.xcodeproj/project.pbxproj @@ -0,0 +1,408 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + 6F89DC0C28A1274A00C73BBE /* EmojiTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F89DC0B28A1274A00C73BBE /* EmojiTheme.swift */; }; + 6F8BD4F6289FA933003F9AF8 /* ButtonTabbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F8BD4F5289FA933003F9AF8 /* ButtonTabbar.swift */; }; + 6FC460F72892A6D1003DA739 /* MemorizeApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FC460F62892A6D1003DA739 /* MemorizeApp.swift */; }; + 6FC460F92892A6D1003DA739 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FC460F82892A6D1003DA739 /* ContentView.swift */; }; + 6FC460FB2892A6D4003DA739 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FC460FA2892A6D4003DA739 /* Assets.xcassets */; }; + 6FC460FE2892A6D4003DA739 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FC460FD2892A6D4003DA739 /* Preview Assets.xcassets */; }; + 6FC461072892BC27003DA739 /* CardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FC461062892BC27003DA739 /* CardView.swift */; }; + 6FE0DA9F289CE2A3000A93C5 /* MemoryGame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FE0DA9E289CE2A3000A93C5 /* MemoryGame.swift */; }; + 6FE0DAA1289CECCE000A93C5 /* EmojiMemoryGame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FE0DAA0289CECCE000A93C5 /* EmojiMemoryGame.swift */; }; + 6FFE685F28A6C68900F3FB66 /* WellDoneView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFE685E28A6C68900F3FB66 /* WellDoneView.swift */; }; + 6FFE686128A6C81E00F3FB66 /* TopInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFE686028A6C81E00F3FB66 /* TopInfoView.swift */; }; + 6FFE686328A6C89200F3FB66 /* CardSetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFE686228A6C89200F3FB66 /* CardSetView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 6F89DC0B28A1274A00C73BBE /* EmojiTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiTheme.swift; sourceTree = ""; }; + 6F8BD4F5289FA933003F9AF8 /* ButtonTabbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonTabbar.swift; sourceTree = ""; }; + 6FC460F32892A6D1003DA739 /* Memorize.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Memorize.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 6FC460F62892A6D1003DA739 /* MemorizeApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemorizeApp.swift; sourceTree = ""; }; + 6FC460F82892A6D1003DA739 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 6FC460FA2892A6D4003DA739 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 6FC460FD2892A6D4003DA739 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 6FC461062892BC27003DA739 /* CardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardView.swift; sourceTree = ""; }; + 6FE0DA9E289CE2A3000A93C5 /* MemoryGame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryGame.swift; sourceTree = ""; }; + 6FE0DAA0289CECCE000A93C5 /* EmojiMemoryGame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMemoryGame.swift; sourceTree = ""; }; + 6FFE685E28A6C68900F3FB66 /* WellDoneView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WellDoneView.swift; sourceTree = ""; }; + 6FFE686028A6C81E00F3FB66 /* TopInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopInfoView.swift; sourceTree = ""; }; + 6FFE686228A6C89200F3FB66 /* CardSetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardSetView.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6FC460F02892A6D1003DA739 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6F347EB02893DAA9005329DE /* SubViews */ = { + isa = PBXGroup; + children = ( + 6FFE686028A6C81E00F3FB66 /* TopInfoView.swift */, + 6FC461062892BC27003DA739 /* CardView.swift */, + 6FFE685E28A6C68900F3FB66 /* WellDoneView.swift */, + 6F8BD4F5289FA933003F9AF8 /* ButtonTabbar.swift */, + 6FFE686228A6C89200F3FB66 /* CardSetView.swift */, + ); + path = SubViews; + sourceTree = ""; + }; + 6FC460EA2892A6D1003DA739 = { + isa = PBXGroup; + children = ( + 6FC460F52892A6D1003DA739 /* Memorize */, + 6FC460F42892A6D1003DA739 /* Products */, + ); + sourceTree = ""; + }; + 6FC460F42892A6D1003DA739 /* Products */ = { + isa = PBXGroup; + children = ( + 6FC460F32892A6D1003DA739 /* Memorize.app */, + ); + name = Products; + sourceTree = ""; + }; + 6FC460F52892A6D1003DA739 /* Memorize */ = { + isa = PBXGroup; + children = ( + 6FC460F62892A6D1003DA739 /* MemorizeApp.swift */, + 6FE0DA9B289CE233000A93C5 /* Model */, + 6FE0DA9C289CE239000A93C5 /* View */, + 6FE0DA9D289CE23D000A93C5 /* ViewModel */, + 6FC460FA2892A6D4003DA739 /* Assets.xcassets */, + 6FC460FC2892A6D4003DA739 /* Preview Content */, + ); + path = Memorize; + sourceTree = ""; + }; + 6FC460FC2892A6D4003DA739 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 6FC460FD2892A6D4003DA739 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 6FE0DA9B289CE233000A93C5 /* Model */ = { + isa = PBXGroup; + children = ( + 6FE0DA9E289CE2A3000A93C5 /* MemoryGame.swift */, + 6F89DC0B28A1274A00C73BBE /* EmojiTheme.swift */, + ); + path = Model; + sourceTree = ""; + }; + 6FE0DA9C289CE239000A93C5 /* View */ = { + isa = PBXGroup; + children = ( + 6FC460F82892A6D1003DA739 /* ContentView.swift */, + 6F347EB02893DAA9005329DE /* SubViews */, + ); + path = View; + sourceTree = ""; + }; + 6FE0DA9D289CE23D000A93C5 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 6FE0DAA0289CECCE000A93C5 /* EmojiMemoryGame.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6FC460F22892A6D1003DA739 /* Memorize */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6FC461012892A6D4003DA739 /* Build configuration list for PBXNativeTarget "Memorize" */; + buildPhases = ( + 6FC460EF2892A6D1003DA739 /* Sources */, + 6FC460F02892A6D1003DA739 /* Frameworks */, + 6FC460F12892A6D1003DA739 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Memorize; + productName = Memorize; + productReference = 6FC460F32892A6D1003DA739 /* Memorize.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6FC460EB2892A6D1003DA739 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1340; + LastUpgradeCheck = 1340; + TargetAttributes = { + 6FC460F22892A6D1003DA739 = { + CreatedOnToolsVersion = 13.4.1; + }; + }; + }; + buildConfigurationList = 6FC460EE2892A6D1003DA739 /* Build configuration list for PBXProject "Memorize" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6FC460EA2892A6D1003DA739; + productRefGroup = 6FC460F42892A6D1003DA739 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6FC460F22892A6D1003DA739 /* Memorize */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6FC460F12892A6D1003DA739 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6FC460FE2892A6D4003DA739 /* Preview Assets.xcassets in Resources */, + 6FC460FB2892A6D4003DA739 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6FC460EF2892A6D1003DA739 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6F8BD4F6289FA933003F9AF8 /* ButtonTabbar.swift in Sources */, + 6F89DC0C28A1274A00C73BBE /* EmojiTheme.swift in Sources */, + 6FFE685F28A6C68900F3FB66 /* WellDoneView.swift in Sources */, + 6FC460F92892A6D1003DA739 /* ContentView.swift in Sources */, + 6FC461072892BC27003DA739 /* CardView.swift in Sources */, + 6FFE686328A6C89200F3FB66 /* CardSetView.swift in Sources */, + 6FFE686128A6C81E00F3FB66 /* TopInfoView.swift in Sources */, + 6FE0DA9F289CE2A3000A93C5 /* MemoryGame.swift in Sources */, + 6FC460F72892A6D1003DA739 /* MemorizeApp.swift in Sources */, + 6FE0DAA1289CECCE000A93C5 /* EmojiMemoryGame.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 6FC460FF2892A6D4003DA739 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + 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; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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 = 15.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 6FC461002892A6D4003DA739 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + 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; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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 = 15.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 6FC461022892A6D4003DA739 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Memorize/Preview Content\""; + DEVELOPMENT_TEAM = 3FAK98S72C; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + 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 = jum.Memorize; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 6FC461032892A6D4003DA739 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Memorize/Preview Content\""; + DEVELOPMENT_TEAM = 3FAK98S72C; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + 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 = jum.Memorize; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6FC460EE2892A6D1003DA739 /* Build configuration list for PBXProject "Memorize" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6FC460FF2892A6D4003DA739 /* Debug */, + 6FC461002892A6D4003DA739 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6FC461012892A6D4003DA739 /* Build configuration list for PBXNativeTarget "Memorize" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6FC461022892A6D4003DA739 /* Debug */, + 6FC461032892A6D4003DA739 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6FC460EB2892A6D1003DA739 /* Project object */; +} diff --git a/week2/Memorize/Memorize.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/week2/Memorize/Memorize.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/week2/Memorize/Memorize.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/week2/Memorize/Memorize.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/week2/Memorize/Memorize.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/week2/Memorize/Memorize.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/week2/Memorize/Memorize/Assets.xcassets/AccentColor.colorset/Contents.json b/week2/Memorize/Memorize/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/week2/Memorize/Memorize/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week2/Memorize/Memorize/Assets.xcassets/AppIcon.appiconset/Contents.json b/week2/Memorize/Memorize/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/week2/Memorize/Memorize/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week2/Memorize/Memorize/Assets.xcassets/Contents.json b/week2/Memorize/Memorize/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/week2/Memorize/Memorize/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week2/Memorize/Memorize/MemorizeApp.swift b/week2/Memorize/Memorize/MemorizeApp.swift new file mode 100644 index 0000000..d2a6c60 --- /dev/null +++ b/week2/Memorize/Memorize/MemorizeApp.swift @@ -0,0 +1,20 @@ +// +// MemorizeApp.swift +// Memorize +// +// Created by 임주민 on 2022/07/28. +// + +import SwiftUI + +@main +struct MemorizeApp: App { + + let memoryGameManager = EmojiMemoryGame() + + var body: some Scene { + WindowGroup { + ContentView(memoryGameManager: memoryGameManager) + } + } +} diff --git a/week2/Memorize/Memorize/Model/EmojiTheme.swift b/week2/Memorize/Memorize/Model/EmojiTheme.swift new file mode 100644 index 0000000..3b4e618 --- /dev/null +++ b/week2/Memorize/Memorize/Model/EmojiTheme.swift @@ -0,0 +1,77 @@ +// +// EmojiTheme.swift +// Memorize +// +// Created by 임주민 on 2022/08/08. +// + +import Foundation + +enum EmojiTheme: String, CaseIterable { + case vehicles + case faces + case sports + case hearts + case flags + case foods + + var content: [String] { + switch self { + case .vehicles: + return ["🚗", "🚕", "🚙", "🚌", "🚜", "🚚", "🚛", "🛻", "🚎", "🚐", "🚒", "🚑", "🚓", "🏎", "🚞"] + case .faces: + return ["😓", "🫡", "🫥", "😡", "😈", "💀", "👹" ,"💩", "👻", "🤖", "🎃"] + case .sports: + return ["⚽️", "⚾️", "🏈", "🏀", "🏓"] + case .hearts: + return ["❤️", "🧡", "💛", "💔", "❤️‍🔥", "❤️‍🩹", "💚", "💙", "💝", "💖", "💜", "🤍", "🖤"] + case .flags: + return ["🏴󠁧󠁢󠁳󠁣󠁴󠁿", "🇧🇬", "🇮🇸", "🇰🇷", "🇭🇰", "🇵🇷", "🇩🇰", "🇦🇪", "🇯🇵", "🇲🇱", "🇺🇸", "🇬🇧", "🇨🇳" ,"🇵🇭", "🇨🇦", "🇹🇷"] + case .foods: + return ["🍟", "🍮", "🍡", "🍭", "🥐", "🥝", "🍬", "🍅", "🍋", "🍇", "🍓", "🌽"] + } + } + + enum themColor { + case pink + case yellow + case orange + case green + case black + case blue + } + + var color: themColor { + switch self { + case .vehicles: + return .pink + case .faces: + return .yellow + case .sports: + return .orange + case .hearts: + return .green + case .flags: + return .black + case .foods: + return .blue + } + } + + var numberOfCardPairsToShow: Int { + switch self { + case .vehicles: + return 5 + case .faces: + return 6 + case .sports: + return 6 + case .hearts: + return 4 + case .flags: + return 6 + case .foods: + return 5 + } + } +} diff --git a/week2/Memorize/Memorize/Model/MemoryGame.swift b/week2/Memorize/Memorize/Model/MemoryGame.swift new file mode 100644 index 0000000..a09313f --- /dev/null +++ b/week2/Memorize/Memorize/Model/MemoryGame.swift @@ -0,0 +1,71 @@ +// +// MemoryGame.swift +// Memorize +// +// Created by 임주민 on 2022/08/05. +// + +import Foundation + +struct MemoryGame where CardContent: Equatable { + + private(set) var cards: [Card] + private(set) var point = 0 + private var numberOfMatchedCard = 0 + private var previousChosenCardIndex: Int? { + get { cards.indices.filter({cards[$0].isFaceUp}).oneAndOnly } + set { cards.indices.forEach{cards[$0].isFaceUp = ($0 == newValue)} } + } + var isGameDone: Bool { + numberOfMatchedCard == cards.count + } + + init(numberOfCardPairs: Int, createContent: (Int) -> CardContent) { + cards = [Card]() + for pairIndex in 0...Card + + init(_ card: MemoryGame.Card) { + self.card = card + } + + var body: some View { + ZStack{ + let shape = RoundedRectangle(cornerRadius: 15) + if card.isFaceUp { + shape.strokeBorder(lineWidth: 5) + Text(card.content).font(.system(size: 40)) + } else if card.isMatched { + shape.fill().opacity(0) + } else { + shape.fill() + } + } + .aspectRatio(2 / 3, contentMode: .fit) + } +} + +struct CardView_Previews: PreviewProvider { + + static var previews: some View { + let card = MemoryGame.Card(isFaceUp: true, isMatched: false, content: "🫡", id: 0) + CardView(card) + } +} diff --git a/week2/Memorize/Memorize/View/SubViews/TopInfoView.swift b/week2/Memorize/Memorize/View/SubViews/TopInfoView.swift new file mode 100644 index 0000000..3c191aa --- /dev/null +++ b/week2/Memorize/Memorize/View/SubViews/TopInfoView.swift @@ -0,0 +1,28 @@ +// +// TopInfoView.swift +// Memorize +// +// Created by 임주민 on 2022/08/13. +// + +import SwiftUI + +struct TopInfoView: View { + + let title: String + let point: Int + + var body: some View { + VStack { + Text(title) + Text("point : \(point)").font(.body) + } + } +} + +struct TopInfoView_Previews: PreviewProvider { + + static var previews: some View { + TopInfoView(title: "Title", point: 0) + } +} diff --git a/week2/Memorize/Memorize/View/SubViews/WellDoneView.swift b/week2/Memorize/Memorize/View/SubViews/WellDoneView.swift new file mode 100644 index 0000000..9c24e75 --- /dev/null +++ b/week2/Memorize/Memorize/View/SubViews/WellDoneView.swift @@ -0,0 +1,27 @@ +// +// WellDoneView.swift +// Memorize +// +// Created by 임주민 on 2022/08/13. +// + +import SwiftUI + +struct WellDoneView: View { + + let point: Int + + var body: some View { + VStack { + Text("Well Done!") + Text("You got \(point) Points!!") + } + } +} + +struct WellDoneView_Previews: PreviewProvider { + + static var previews: some View { + WellDoneView(point: 0) + } +} diff --git a/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift new file mode 100644 index 0000000..283ceb1 --- /dev/null +++ b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift @@ -0,0 +1,67 @@ +// +// EmojiMemoryGame.swift +// Memorize +// +// Created by 임주민 on 2022/08/05. +// + +import Foundation +import SwiftUI + +class EmojiMemoryGame: ObservableObject { + + private static func create() -> MemoryGame { + let numberOfCardPairs = theme.numberOfCardPairsToShow > theme.content.count + ? theme.content.count + : theme.numberOfCardPairsToShow + return MemoryGame(numberOfCardPairs: numberOfCardPairs) { pairIndex in theme.content[pairIndex] } + } + + @Published private var memoryGameModel = create() + static var theme = EmojiTheme.allCases.randomElement() ?? .vehicles + + var cards: [MemoryGame.Card] { + memoryGameModel.cards + } + + var isGameDone: Bool { + memoryGameModel.isGameDone + } + + var point: Int { + memoryGameModel.point + } + + var title: String { + let firstUppercasedTitle = EmojiMemoryGame.theme.rawValue.prefix(1).uppercased() + EmojiMemoryGame.theme.rawValue.dropFirst() + return firstUppercasedTitle + } + + var themecolor: Color { + switch EmojiMemoryGame.theme.color { + case .pink: + return .pink + case .yellow: + return .yellow + case .orange: + return .orange + case .green: + return .green + case .black: + return .black + case .blue: + return .blue + } + } + + // MARK: - Intentions + + func choose(_ card: MemoryGame.Card) { + memoryGameModel.choose(card) + } + + func newGame() { + EmojiMemoryGame.theme = EmojiTheme.allCases.randomElement() ?? .vehicles + memoryGameModel = EmojiMemoryGame.create() + } +} diff --git a/week4/Calculator/Calculator.xcodeproj/project.pbxproj b/week4/Calculator/Calculator.xcodeproj/project.pbxproj new file mode 100644 index 0000000..026e5f3 --- /dev/null +++ b/week4/Calculator/Calculator.xcodeproj/project.pbxproj @@ -0,0 +1,412 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + 6F1A921828B13A71009FBF28 /* CalculatorApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A921728B13A71009FBF28 /* CalculatorApp.swift */; }; + 6F1A921A28B13A71009FBF28 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A921928B13A71009FBF28 /* ContentView.swift */; }; + 6F1A921C28B13A72009FBF28 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6F1A921B28B13A72009FBF28 /* Assets.xcassets */; }; + 6F1A921F28B13A72009FBF28 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6F1A921E28B13A72009FBF28 /* Preview Assets.xcassets */; }; + 6F1A922928B13AE8009FBF28 /* CalculatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922828B13AE8009FBF28 /* CalculatorModel.swift */; }; + 6F1A922B28B13B04009FBF28 /* CaculatorManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922A28B13B04009FBF28 /* CaculatorManager.swift */; }; + 6F1A922D28B2159B009FBF28 /* ResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922C28B2159B009FBF28 /* ResultView.swift */; }; + 6F2AB33028B8D9970054B4BB /* NumberPadGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB32F28B8D9970054B4BB /* NumberPadGrid.swift */; }; + 6F2AB33228B8D9A60054B4BB /* NumberPad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB33128B8D9A60054B4BB /* NumberPad.swift */; }; + 6F2AB33528B8DC930054B4BB /* Ex+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB33428B8DC930054B4BB /* Ex+Color.swift */; }; + 6F2AB33728B90EB10054B4BB /* Ex+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB33628B90EB10054B4BB /* Ex+String.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 6F1A921428B13A71009FBF28 /* Calculator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Calculator.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 6F1A921728B13A71009FBF28 /* CalculatorApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorApp.swift; sourceTree = ""; }; + 6F1A921928B13A71009FBF28 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 6F1A921B28B13A72009FBF28 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 6F1A921E28B13A72009FBF28 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 6F1A922828B13AE8009FBF28 /* CalculatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorModel.swift; sourceTree = ""; }; + 6F1A922A28B13B04009FBF28 /* CaculatorManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaculatorManager.swift; sourceTree = ""; }; + 6F1A922C28B2159B009FBF28 /* ResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultView.swift; sourceTree = ""; }; + 6F2AB32F28B8D9970054B4BB /* NumberPadGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberPadGrid.swift; sourceTree = ""; }; + 6F2AB33128B8D9A60054B4BB /* NumberPad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberPad.swift; sourceTree = ""; }; + 6F2AB33428B8DC930054B4BB /* Ex+Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Ex+Color.swift"; sourceTree = ""; }; + 6F2AB33628B90EB10054B4BB /* Ex+String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Ex+String.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6F1A921128B13A71009FBF28 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6F1A920B28B13A71009FBF28 = { + isa = PBXGroup; + children = ( + 6F1A921628B13A71009FBF28 /* Calculator */, + 6F1A921528B13A71009FBF28 /* Products */, + ); + sourceTree = ""; + }; + 6F1A921528B13A71009FBF28 /* Products */ = { + isa = PBXGroup; + children = ( + 6F1A921428B13A71009FBF28 /* Calculator.app */, + ); + name = Products; + sourceTree = ""; + }; + 6F1A921628B13A71009FBF28 /* Calculator */ = { + isa = PBXGroup; + children = ( + 6F1A921728B13A71009FBF28 /* CalculatorApp.swift */, + 6F2AB33328B8DC800054B4BB /* Extensions */, + 6F1A922528B13A93009FBF28 /* Model */, + 6F1A922628B13A9B009FBF28 /* ViewModel */, + 6F1A922728B13AA3009FBF28 /* View */, + 6F1A921B28B13A72009FBF28 /* Assets.xcassets */, + 6F1A921D28B13A72009FBF28 /* Preview Content */, + ); + path = Calculator; + sourceTree = ""; + }; + 6F1A921D28B13A72009FBF28 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 6F1A921E28B13A72009FBF28 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 6F1A922528B13A93009FBF28 /* Model */ = { + isa = PBXGroup; + children = ( + 6F1A922828B13AE8009FBF28 /* CalculatorModel.swift */, + ); + path = Model; + sourceTree = ""; + }; + 6F1A922628B13A9B009FBF28 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 6F1A922A28B13B04009FBF28 /* CaculatorManager.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + 6F1A922728B13AA3009FBF28 /* View */ = { + isa = PBXGroup; + children = ( + 6F1A921928B13A71009FBF28 /* ContentView.swift */, + 6F1A923928B22476009FBF28 /* SubViews */, + ); + path = View; + sourceTree = ""; + }; + 6F1A923928B22476009FBF28 /* SubViews */ = { + isa = PBXGroup; + children = ( + 6F1A922C28B2159B009FBF28 /* ResultView.swift */, + 6F2AB32F28B8D9970054B4BB /* NumberPadGrid.swift */, + 6F2AB33128B8D9A60054B4BB /* NumberPad.swift */, + ); + path = SubViews; + sourceTree = ""; + }; + 6F2AB33328B8DC800054B4BB /* Extensions */ = { + isa = PBXGroup; + children = ( + 6F2AB33428B8DC930054B4BB /* Ex+Color.swift */, + 6F2AB33628B90EB10054B4BB /* Ex+String.swift */, + ); + path = Extensions; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6F1A921328B13A71009FBF28 /* Calculator */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6F1A922228B13A72009FBF28 /* Build configuration list for PBXNativeTarget "Calculator" */; + buildPhases = ( + 6F1A921028B13A71009FBF28 /* Sources */, + 6F1A921128B13A71009FBF28 /* Frameworks */, + 6F1A921228B13A71009FBF28 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Calculator; + productName = Calculator; + productReference = 6F1A921428B13A71009FBF28 /* Calculator.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6F1A920C28B13A71009FBF28 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1340; + LastUpgradeCheck = 1340; + TargetAttributes = { + 6F1A921328B13A71009FBF28 = { + CreatedOnToolsVersion = 13.4.1; + }; + }; + }; + buildConfigurationList = 6F1A920F28B13A71009FBF28 /* Build configuration list for PBXProject "Calculator" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6F1A920B28B13A71009FBF28; + productRefGroup = 6F1A921528B13A71009FBF28 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6F1A921328B13A71009FBF28 /* Calculator */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6F1A921228B13A71009FBF28 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6F1A921F28B13A72009FBF28 /* Preview Assets.xcassets in Resources */, + 6F1A921C28B13A72009FBF28 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6F1A921028B13A71009FBF28 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6F2AB33528B8DC930054B4BB /* Ex+Color.swift in Sources */, + 6F1A922928B13AE8009FBF28 /* CalculatorModel.swift in Sources */, + 6F1A921A28B13A71009FBF28 /* ContentView.swift in Sources */, + 6F1A922D28B2159B009FBF28 /* ResultView.swift in Sources */, + 6F2AB33728B90EB10054B4BB /* Ex+String.swift in Sources */, + 6F1A922B28B13B04009FBF28 /* CaculatorManager.swift in Sources */, + 6F1A921828B13A71009FBF28 /* CalculatorApp.swift in Sources */, + 6F2AB33228B8D9A60054B4BB /* NumberPad.swift in Sources */, + 6F2AB33028B8D9970054B4BB /* NumberPadGrid.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 6F1A922028B13A72009FBF28 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + 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; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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 = 15.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 6F1A922128B13A72009FBF28 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + 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; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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 = 15.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 6F1A922328B13A72009FBF28 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Calculator/Preview Content\""; + DEVELOPMENT_TEAM = 3FAK98S72C; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + 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 = jum.Calculator; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 6F1A922428B13A72009FBF28 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Calculator/Preview Content\""; + DEVELOPMENT_TEAM = 3FAK98S72C; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + 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 = jum.Calculator; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6F1A920F28B13A71009FBF28 /* Build configuration list for PBXProject "Calculator" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6F1A922028B13A72009FBF28 /* Debug */, + 6F1A922128B13A72009FBF28 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6F1A922228B13A72009FBF28 /* Build configuration list for PBXNativeTarget "Calculator" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6F1A922328B13A72009FBF28 /* Debug */, + 6F1A922428B13A72009FBF28 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6F1A920C28B13A71009FBF28 /* Project object */; +} diff --git a/week4/Calculator/Calculator.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/week4/Calculator/Calculator.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/week4/Calculator/Calculator.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/week4/Calculator/Calculator.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/week4/Calculator/Calculator.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/week4/Calculator/Calculator.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/week4/Calculator/Calculator/Assets.xcassets/AccentColor.colorset/Contents.json b/week4/Calculator/Calculator/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/week4/Calculator/Calculator/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week4/Calculator/Calculator/Assets.xcassets/AppIcon.appiconset/Contents.json b/week4/Calculator/Calculator/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/week4/Calculator/Calculator/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week4/Calculator/Calculator/Assets.xcassets/Contents.json b/week4/Calculator/Calculator/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/week4/Calculator/Calculator/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week4/Calculator/Calculator/CalculatorApp.swift b/week4/Calculator/Calculator/CalculatorApp.swift new file mode 100644 index 0000000..5bb77cd --- /dev/null +++ b/week4/Calculator/Calculator/CalculatorApp.swift @@ -0,0 +1,20 @@ +// +// CalculatorApp.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import SwiftUI + +@main +struct CalculatorApp: App { + + let caculatorManager = CalculatorManager() + + var body: some Scene { + WindowGroup { + ContentView(calculatorManager: caculatorManager) + } + } +} diff --git a/week4/Calculator/Calculator/Extensions/Ex+Color.swift b/week4/Calculator/Calculator/Extensions/Ex+Color.swift new file mode 100644 index 0000000..2838f78 --- /dev/null +++ b/week4/Calculator/Calculator/Extensions/Ex+Color.swift @@ -0,0 +1,22 @@ +// +// Ex+Color.swift +// Calculator +// +// Created by 임주민 on 2022/08/26. +// + +import SwiftUI + +extension Color { + + init(hex: String) { + let scanner = Scanner(string: hex) + _ = scanner.scanString("#") + var rgb: UInt64 = 0 + scanner.scanHexInt64(&rgb) + let r = Double((rgb >> 16) & 0xFF) / 255.0 + let g = Double((rgb >> 8) & 0xFF) / 255.0 + let b = Double((rgb >> 0) & 0xFF) / 255.0 + self.init(red: r, green: g, blue: b) + } +} diff --git a/week4/Calculator/Calculator/Extensions/Ex+String.swift b/week4/Calculator/Calculator/Extensions/Ex+String.swift new file mode 100644 index 0000000..69c9861 --- /dev/null +++ b/week4/Calculator/Calculator/Extensions/Ex+String.swift @@ -0,0 +1,17 @@ +// +// Ex+String.swift +// Calculator +// +// Created by 임주민 on 2022/08/26. +// + +import Foundation + +extension String { + + mutating func removeWithDecimal() { + let removedDecimal = (Decimal(string: self) ?? 0) / 10 + let removedInteger = NSDecimalNumber(decimal: removedDecimal).intValue + self = String(removedInteger) + } +} diff --git a/week4/Calculator/Calculator/Model/CalculatorModel.swift b/week4/Calculator/Calculator/Model/CalculatorModel.swift new file mode 100644 index 0000000..eb0e7c3 --- /dev/null +++ b/week4/Calculator/Calculator/Model/CalculatorModel.swift @@ -0,0 +1,218 @@ +// +// Calculator.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import Foundation + +struct Calculator { + + var state: InputState = .newPreviousNumber + var operation: ArithmeticOperation? { + willSet { + if nextNumber != nil { calculate() } + } + } + var showingText: String + private(set) var previousNumber: Decimal? { + didSet { + guard let previousNumber = previousNumber else { return } + showingText = "\(previousNumber)" + } + } + private(set) var nextNumber: Decimal? { + didSet { + guard let nextNumber = nextNumber else { return } + showingText = "\(nextNumber)" + } + } + private(set) var result: Decimal { + didSet { + showingText = "\(result)" + previousNumber = result + nextNumber = nil + state = .finishInput + } + } + + enum InputState { + case newPreviousNumber + case newNextNumber + case ongoingPreviousNumber + case ongoingNextNumber + case finishInput + } + + mutating func clickNumber(_ number: Decimal) { + switch state { + case .newPreviousNumber, .finishInput: + previousNumber = number + state = .ongoingPreviousNumber + case .newNextNumber: + nextNumber = number + state = .ongoingNextNumber + case .ongoingPreviousNumber: + previousNumber = joinNumbers(with: number) + case .ongoingNextNumber: + nextNumber = joinNumbers(with: number) + } + } + + mutating func calculate() { + guard let priorNumber = previousNumber, + let laterNumber = nextNumber, + let operation = operation else { return } + switch operation { + case .multiply: + result = priorNumber * laterNumber + case .divide: + result = priorNumber / laterNumber + case .minus: + result = priorNumber - laterNumber + case .plus: + result = priorNumber + laterNumber + default: + print("error") + } + } + + mutating func joinNumbers(with nextElement: Decimal) -> Decimal { + let joinedString = showingText + "\(nextElement)" + guard let joinedNumber = Decimal(string: joinedString) else { return 0 } + return joinedNumber + } + + mutating func changeSign() { + if let priorNumber = previousNumber { + if let afterNumber = nextNumber { + nextNumber = -afterNumber + } else { + previousNumber = -priorNumber + } + } + } + + mutating func percent() { + if let priorNumber = previousNumber { + if let afterNumber = nextNumber { + nextNumber = afterNumber / 100 + } else { + previousNumber = priorNumber / 100 + } + } + } + + mutating func addPoint() { + if !showingText.contains(".") { + showingText = showingText + "." + if showingText.compare("0") == .orderedSame { + state = .ongoingPreviousNumber + } + } + } +} + +extension Calculator { + + enum ArithmeticOperation: Identifiable, Equatable { + case equal + case plus(Bool) + case minus(Bool) + case multiply(Bool) + case divide(Bool) + case percent + case sign + case clear(clearType) + case number(Decimal) + case point + + var id: String { return content } + + var content: String { + switch self { + case .equal: + return "=" + case .plus: + return "+" + case .minus: + return "-" + case .multiply: + return "*" + case .divide: + return "/" + case .percent: + return "%" + case .sign: + return "±" + case .clear(.displayResult): + return "C" + case .clear(.allData): + return "AC" + case .number(let value): + return "\(value)" + case .point: + return "." + } + } + + var isActive: Bool { + switch self { + case .plus(let isActive), .multiply(let isActive), .minus(let isActive), .divide(let isActive): + return isActive + default: + return false + } + } + + var backgroundColor: String { + switch self { + case .clear, .percent, .sign: + return "#A0A0A0" + case .number, .point: + return "#323232" + case .plus, .divide, .multiply, .minus, .equal: + return isActive ? "#FFFFFF" : "#E99D39" + } + } + + var textColor: String { + switch self { + case .clear, .percent, .sign: + return "#000000" + case .plus, .divide, .multiply, .minus, .equal, .number, .point: + return isActive ? "#E99D39" : "#FFFFFF" + } + } + + var buttonSize: Int { + switch self { + case .number(0): + return 2 + default: + return 1 + } + } + + mutating func activeButton(active: Bool) { + switch self { + case .divide: + self = .divide(active) + case .multiply: + self = .multiply(active) + case .minus: + self = .minus(active) + case .plus: + self = .plus(active) + default: + break + } + } + } + + enum clearType { + case allData + case displayResult + } +} diff --git a/week4/Calculator/Calculator/Preview Content/Preview Assets.xcassets/Contents.json b/week4/Calculator/Calculator/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/week4/Calculator/Calculator/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week4/Calculator/Calculator/View/ContentView.swift b/week4/Calculator/Calculator/View/ContentView.swift new file mode 100644 index 0000000..e55c5e3 --- /dev/null +++ b/week4/Calculator/Calculator/View/ContentView.swift @@ -0,0 +1,34 @@ +// +// ContentView.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import SwiftUI + +struct ContentView: View { + + @ObservedObject var calculatorManager: CalculatorManager + + var body: some View { + ZStack{ + Color.black.ignoresSafeArea() + VStack { + Spacer() + ResultView(showingText: calculatorManager.showingText) + .gesture( + DragGesture() + .onEnded { _ in calculatorManager.slideToRemove() } + ) + NumberPadGrid(items: calculatorManager.buttonList , columnsCount: 4) { item in + Button { + calculatorManager.click(item) + } label: { + NumberPad(buttonData: item) + } + } + } + } + } +} diff --git a/week4/Calculator/Calculator/View/SubViews/NumberPad.swift b/week4/Calculator/Calculator/View/SubViews/NumberPad.swift new file mode 100644 index 0000000..29444a8 --- /dev/null +++ b/week4/Calculator/Calculator/View/SubViews/NumberPad.swift @@ -0,0 +1,41 @@ +// +// NumberPad.swift +// Calculator +// +// Created by changgyo seo on 2022/08/26. +// + +import SwiftUI + +struct NumberPad: View { + + var buttonData: Calculator.ArithmeticOperation + + var body: some View { + GeometryReader { geometry in + ZStack { + RoundedRectangle(cornerRadius: cornerRadius(in: geometry.size)) + .foregroundColor(Color(hex: buttonData.backgroundColor)) + Text(buttonData.content) + .foregroundColor(Color(hex: buttonData.textColor)) + .font(font(in: geometry.size)) + } + } + } + + private func cornerRadius(in size: CGSize) -> CGFloat { + size.width * DrawConstans.buttonCornerRadius + } + + private func font(in size: CGSize) -> Font { + Font.system(size: size.height * DrawConstans.fontSizeScale) + } +} + +extension NumberPad { + + private enum DrawConstans { + static let buttonCornerRadius = 0.5 + static let fontSizeScale = 0.4 + } +} diff --git a/week4/Calculator/Calculator/View/SubViews/NumberPadGrid.swift b/week4/Calculator/Calculator/View/SubViews/NumberPadGrid.swift new file mode 100644 index 0000000..1bb1db6 --- /dev/null +++ b/week4/Calculator/Calculator/View/SubViews/NumberPadGrid.swift @@ -0,0 +1,89 @@ +// +// NumberPadGrid.swift +// Calculator +// +// Created by changgyo seo on 2022/08/26. +// + +import SwiftUI + +struct NumberPadGrid: View { + + let buttonDataArray: [ButtonDataArray] + let columnsCount: Int + let content: (Calculator.ArithmeticOperation) -> buttonListView + + var body: some View { + VStack(spacing: 0) { + ForEach(buttonDataArray) { items in + HStack(spacing: 0) { + ForEach(items.array) { item in + let width = widthForColumnsCount(in: UIScreen.main.bounds.size, + buttonWidth: item.buttonSize) + let height = width / CGFloat(item.buttonSize) + content(item) + .padding(5) + .frame(width: width, height: height, alignment: .center) + } + } + } + } + } + + init(items: [Calculator.ArithmeticOperation], columnsCount: Int, @ViewBuilder content: @escaping (Calculator.ArithmeticOperation) -> buttonListView) { + self.buttonDataArray = ButtonDataArray.makeTwoDemsionArrayForColumnsCount(items: items, columnsCount: columnsCount) + self.columnsCount = columnsCount + self.content = content + } + + private func widthForColumnsCount(in size: CGSize, buttonWidth width: Int) -> CGFloat { + let sizeForOne = size.width / CGFloat(columnsCount) + return sizeForOne * CGFloat(width) + } +} + +extension NumberPadGrid { + + struct ButtonDataArray: Identifiable { + + var id: Int + var array: [Calculator.ArithmeticOperation] + var totalWidth: Int + + init() { + array = [Calculator.ArithmeticOperation]() + id = 1 + totalWidth = 0 + } + + mutating func append(_ newItem: Calculator.ArithmeticOperation) { + array.append(newItem) + } + + mutating func changeId() { + id += 1 + totalWidth = 0 + array.removeAll() + } + + static func makeTwoDemsionArrayForColumnsCount(items: [Calculator.ArithmeticOperation], columnsCount: Int) -> [ButtonDataArray] { + var arrayWithColumnsCount = ButtonDataArray() + var TwoDemesionArray = [ButtonDataArray]() + var rowWidth = 0 + items.forEach { item in + if rowWidth + item.buttonSize <= columnsCount { + arrayWithColumnsCount.append(item) + rowWidth += item.buttonSize + } else { + arrayWithColumnsCount.totalWidth = rowWidth + TwoDemesionArray.append(arrayWithColumnsCount) + arrayWithColumnsCount.changeId() + arrayWithColumnsCount.append(item) + rowWidth = item.buttonSize + } + } + TwoDemesionArray.append(arrayWithColumnsCount) + return TwoDemesionArray + } + } +} diff --git a/week4/Calculator/Calculator/View/SubViews/ResultView.swift b/week4/Calculator/Calculator/View/SubViews/ResultView.swift new file mode 100644 index 0000000..6c52efe --- /dev/null +++ b/week4/Calculator/Calculator/View/SubViews/ResultView.swift @@ -0,0 +1,35 @@ +// +// ResultView.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import SwiftUI + +struct ResultView: View { + + let showingText: String + + var body: some View { + HStack { + Spacer() + Text(showingText) + .lineLimit(1) + .foregroundColor(.white) + .font(.system(size: DrawConstans.fontSize)) + .minimumScaleFactor(DrawConstans.fontMinimumSacleFactor) + .padding(.trailing, DrawConstans.textPadding) + } + .contentShape(Rectangle()) + } +} + +extension ResultView { + + private enum DrawConstans { + static let fontSize = 80.0 + static let fontMinimumSacleFactor = 0.5 + static let textPadding = 20.0 + } +} diff --git a/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift new file mode 100644 index 0000000..0181495 --- /dev/null +++ b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift @@ -0,0 +1,114 @@ +// +// CaculatorManager.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import SwiftUI + +class CalculatorManager: ObservableObject { + + @Published private var calculateModel = create() + @Published var buttonList: [Calculator.ArithmeticOperation] = + [ + .clear(.allData), .sign, .percent, .divide(false), + .number(7), .number(8), .number(9), .multiply(false), + .number(4), .number(5), .number(6), .minus(false), + .number(1), .number(2), .number(3), .plus(false), + .number(0), .point, .equal + ] + + var showingText: String { + get { + if calculateModel.showingText != "0" { + guard let clearIndex = buttonList.firstIndex(of: .clear(.allData)) else { return calculateModel.showingText } + buttonList[clearIndex] = .clear(.displayResult) + } else { + guard let clearIndex = buttonList.firstIndex(of: .clear(.displayResult)) else { return calculateModel.showingText } + buttonList[clearIndex] = .clear(.allData) + } + return calculateModel.showingText + } + set { calculateModel.showingText = newValue } + } + + private static func create() -> Calculator { + return Calculator(showingText: "0", result: 0) + } + + // MARK: - Intentions + + func click(_ clickType: Calculator.ArithmeticOperation) { + switch clickType { + case .number(let decimal): + if calculateModel.operation == nil, + calculateModel.state != .ongoingPreviousNumber { + calculateModel.state = .newPreviousNumber + } + for index in buttonList.indices { + if buttonList[index].isActive { + switch buttonList[index] { + case .plus, .minus, .divide, .multiply: + buttonList[index].activeButton(active: false) + default: + print("error") + } + } + } + clickNumber(decimal) + case .equal: + calculate() + case .clear(.allData): + clear() + case .clear(.displayResult): + showingText = "0" + case .sign: + changeSign() + case .percent: + percent() + case .point: + addPoint() + default: + for index in buttonList.indices { + buttonList[index] == clickType + ? buttonList[index].activeButton(active: true) + : buttonList[index].activeButton(active: false) + } + clickOperation(clickType) + } + } + + func clickNumber(_ number: Decimal) { + calculateModel.clickNumber(number) + } + + func clickOperation(_ operation: Calculator.ArithmeticOperation?) { + calculateModel.operation = operation + calculateModel.state = .newNextNumber + } + + func calculate() { + calculateModel.calculate() + } + + func clear() { + calculateModel = CalculatorManager.create() + } + + func changeSign() { + calculateModel.changeSign() + } + + func slideToRemove() { + showingText.removeWithDecimal() + } + + func percent() { + calculateModel.percent() + } + + func addPoint() { + calculateModel.addPoint() + } +} diff --git a/week5/ApiTest/ApiTest.xcodeproj/project.pbxproj b/week5/ApiTest/ApiTest.xcodeproj/project.pbxproj new file mode 100644 index 0000000..170005a --- /dev/null +++ b/week5/ApiTest/ApiTest.xcodeproj/project.pbxproj @@ -0,0 +1,412 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + 6FB0595328C073A5004C8C20 /* ApiTestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0595228C073A5004C8C20 /* ApiTestApp.swift */; }; + 6FB0595728C073A6004C8C20 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FB0595628C073A6004C8C20 /* Assets.xcassets */; }; + 6FB0595A28C073A6004C8C20 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FB0595928C073A6004C8C20 /* Preview Assets.xcassets */; }; + 6FB0596128C073F0004C8C20 /* SearchList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596028C073F0004C8C20 /* SearchList.swift */; }; + 6FB0596328C0760C004C8C20 /* BookFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596228C0760C004C8C20 /* BookFinder.swift */; }; + 6FB0596528C07622004C8C20 /* Book.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596428C07622004C8C20 /* Book.swift */; }; + 6FB0596A28C08B98004C8C20 /* APIConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596928C08B98004C8C20 /* APIConstants.swift */; }; + 6FB0596E28C0C084004C8C20 /* BookCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596D28C0C084004C8C20 /* BookCell.swift */; }; + 6FB0597128C0D353004C8C20 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0597028C0D353004C8C20 /* String+Extension.swift */; }; + 6FB0597328C1E6CF004C8C20 /* Blog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0597228C1E6CF004C8C20 /* Blog.swift */; }; + 6FB0597928C1FC2F004C8C20 /* BlogCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0597828C1FC2F004C8C20 /* BlogCell.swift */; }; + 6FB0597B28C20940004C8C20 /* BlogWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0597A28C20940004C8C20 /* BlogWebView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 6FB0594F28C073A5004C8C20 /* ApiTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ApiTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 6FB0595228C073A5004C8C20 /* ApiTestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiTestApp.swift; sourceTree = ""; }; + 6FB0595628C073A6004C8C20 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 6FB0595928C073A6004C8C20 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 6FB0596028C073F0004C8C20 /* SearchList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchList.swift; sourceTree = ""; }; + 6FB0596228C0760C004C8C20 /* BookFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookFinder.swift; sourceTree = ""; }; + 6FB0596428C07622004C8C20 /* Book.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Book.swift; sourceTree = ""; }; + 6FB0596928C08B98004C8C20 /* APIConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIConstants.swift; sourceTree = ""; }; + 6FB0596D28C0C084004C8C20 /* BookCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookCell.swift; sourceTree = ""; }; + 6FB0597028C0D353004C8C20 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; + 6FB0597228C1E6CF004C8C20 /* Blog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Blog.swift; sourceTree = ""; }; + 6FB0597828C1FC2F004C8C20 /* BlogCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlogCell.swift; sourceTree = ""; }; + 6FB0597A28C20940004C8C20 /* BlogWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlogWebView.swift; sourceTree = ""; }; + 6FB0597C28C209BF004C8C20 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6FB0594C28C073A5004C8C20 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6FB0594628C073A5004C8C20 = { + isa = PBXGroup; + children = ( + 6FB0595128C073A5004C8C20 /* ApiTest */, + 6FB0595028C073A5004C8C20 /* Products */, + ); + sourceTree = ""; + }; + 6FB0595028C073A5004C8C20 /* Products */ = { + isa = PBXGroup; + children = ( + 6FB0594F28C073A5004C8C20 /* ApiTest.app */, + ); + name = Products; + sourceTree = ""; + }; + 6FB0595128C073A5004C8C20 /* ApiTest */ = { + isa = PBXGroup; + children = ( + 6FB0597C28C209BF004C8C20 /* Info.plist */, + 6FB0595228C073A5004C8C20 /* ApiTestApp.swift */, + 6FB0596928C08B98004C8C20 /* APIConstants.swift */, + 6FB0596F28C0D325004C8C20 /* Extensions */, + 6FB0596628C07628004C8C20 /* Model */, + 6FB0596728C0762B004C8C20 /* ViewModel */, + 6FB0596828C07638004C8C20 /* View */, + 6FB0595628C073A6004C8C20 /* Assets.xcassets */, + 6FB0595828C073A6004C8C20 /* Preview Content */, + ); + path = ApiTest; + sourceTree = ""; + }; + 6FB0595828C073A6004C8C20 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 6FB0595928C073A6004C8C20 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 6FB0596628C07628004C8C20 /* Model */ = { + isa = PBXGroup; + children = ( + 6FB0596428C07622004C8C20 /* Book.swift */, + 6FB0597228C1E6CF004C8C20 /* Blog.swift */, + ); + path = Model; + sourceTree = ""; + }; + 6FB0596728C0762B004C8C20 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 6FB0596228C0760C004C8C20 /* BookFinder.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + 6FB0596828C07638004C8C20 /* View */ = { + isa = PBXGroup; + children = ( + 6FB0596028C073F0004C8C20 /* SearchList.swift */, + 6FB0596D28C0C084004C8C20 /* BookCell.swift */, + 6FB0597828C1FC2F004C8C20 /* BlogCell.swift */, + 6FB0597A28C20940004C8C20 /* BlogWebView.swift */, + ); + path = View; + sourceTree = ""; + }; + 6FB0596F28C0D325004C8C20 /* Extensions */ = { + isa = PBXGroup; + children = ( + 6FB0597028C0D353004C8C20 /* String+Extension.swift */, + ); + path = Extensions; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6FB0594E28C073A5004C8C20 /* ApiTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6FB0595D28C073A6004C8C20 /* Build configuration list for PBXNativeTarget "ApiTest" */; + buildPhases = ( + 6FB0594B28C073A5004C8C20 /* Sources */, + 6FB0594C28C073A5004C8C20 /* Frameworks */, + 6FB0594D28C073A5004C8C20 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ApiTest; + productName = ApiTest; + productReference = 6FB0594F28C073A5004C8C20 /* ApiTest.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6FB0594728C073A5004C8C20 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1340; + LastUpgradeCheck = 1340; + TargetAttributes = { + 6FB0594E28C073A5004C8C20 = { + CreatedOnToolsVersion = 13.4.1; + }; + }; + }; + buildConfigurationList = 6FB0594A28C073A5004C8C20 /* Build configuration list for PBXProject "ApiTest" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6FB0594628C073A5004C8C20; + productRefGroup = 6FB0595028C073A5004C8C20 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6FB0594E28C073A5004C8C20 /* ApiTest */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6FB0594D28C073A5004C8C20 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6FB0595A28C073A6004C8C20 /* Preview Assets.xcassets in Resources */, + 6FB0595728C073A6004C8C20 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6FB0594B28C073A5004C8C20 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6FB0597928C1FC2F004C8C20 /* BlogCell.swift in Sources */, + 6FB0596E28C0C084004C8C20 /* BookCell.swift in Sources */, + 6FB0597128C0D353004C8C20 /* String+Extension.swift in Sources */, + 6FB0597B28C20940004C8C20 /* BlogWebView.swift in Sources */, + 6FB0596A28C08B98004C8C20 /* APIConstants.swift in Sources */, + 6FB0596528C07622004C8C20 /* Book.swift in Sources */, + 6FB0596128C073F0004C8C20 /* SearchList.swift in Sources */, + 6FB0597328C1E6CF004C8C20 /* Blog.swift in Sources */, + 6FB0596328C0760C004C8C20 /* BookFinder.swift in Sources */, + 6FB0595328C073A5004C8C20 /* ApiTestApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 6FB0595B28C073A6004C8C20 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + 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; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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 = 15.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 6FB0595C28C073A6004C8C20 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + 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; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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 = 15.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 6FB0595E28C073A6004C8C20 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"ApiTest/Preview Content\""; + DEVELOPMENT_TEAM = 3FAK98S72C; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ApiTest/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 = jum.ApiTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 6FB0595F28C073A6004C8C20 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"ApiTest/Preview Content\""; + DEVELOPMENT_TEAM = 3FAK98S72C; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ApiTest/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 = jum.ApiTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6FB0594A28C073A5004C8C20 /* Build configuration list for PBXProject "ApiTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6FB0595B28C073A6004C8C20 /* Debug */, + 6FB0595C28C073A6004C8C20 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6FB0595D28C073A6004C8C20 /* Build configuration list for PBXNativeTarget "ApiTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6FB0595E28C073A6004C8C20 /* Debug */, + 6FB0595F28C073A6004C8C20 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6FB0594728C073A5004C8C20 /* Project object */; +} diff --git a/week5/ApiTest/ApiTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/week5/ApiTest/ApiTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/week5/ApiTest/ApiTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/week5/ApiTest/ApiTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/week5/ApiTest/ApiTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/week5/ApiTest/ApiTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/week5/ApiTest/ApiTest/APIConstants.swift b/week5/ApiTest/ApiTest/APIConstants.swift new file mode 100644 index 0000000..bb60ad3 --- /dev/null +++ b/week5/ApiTest/ApiTest/APIConstants.swift @@ -0,0 +1,20 @@ +// +// APIConstants.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import Foundation + +enum APIConstants: String { + + case getBookInfo = "/v1/search/book.json" + case getBookBlog = "/v1/search/blog.json" + + static let clientID = "_hygi4FZdNOUpQsd7K__" + static let clientSecret = "CFBpqq7uVg" + static let scheme = "https" + static let host = "openapi.naver.com" + static let successRange = 200..<300 +} diff --git a/week5/ApiTest/ApiTest/ApiTestApp.swift b/week5/ApiTest/ApiTest/ApiTestApp.swift new file mode 100644 index 0000000..4be1d76 --- /dev/null +++ b/week5/ApiTest/ApiTest/ApiTestApp.swift @@ -0,0 +1,19 @@ +// +// ApiTestApp.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import SwiftUI + +@main +struct ApiTestApp: App { + + var body: some Scene { + WindowGroup { + let viewModel = BookFinder() + SearchList(bookFinder: viewModel) + } + } +} diff --git a/week5/ApiTest/ApiTest/Assets.xcassets/AccentColor.colorset/Contents.json b/week5/ApiTest/ApiTest/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/week5/ApiTest/ApiTest/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/ApiTest/ApiTest/Assets.xcassets/AppIcon.appiconset/Contents.json b/week5/ApiTest/ApiTest/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..5a3257a --- /dev/null +++ b/week5/ApiTest/ApiTest/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,93 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/ApiTest/ApiTest/Assets.xcassets/Contents.json b/week5/ApiTest/ApiTest/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/week5/ApiTest/ApiTest/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/ApiTest/ApiTest/Extensions/String+Extension.swift b/week5/ApiTest/ApiTest/Extensions/String+Extension.swift new file mode 100644 index 0000000..9e5f04d --- /dev/null +++ b/week5/ApiTest/ApiTest/Extensions/String+Extension.swift @@ -0,0 +1,33 @@ +// +// String+Extension.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import Foundation + +extension String { + + var insertComma: String { + let numberFormatter = NumberFormatter() + numberFormatter.numberStyle = .decimal + guard let doubleValue = Double(self), + let insertedCommaValue = numberFormatter.string(from: NSNumber(value: doubleValue)) + else { return self } + return insertedCommaValue + } +} + +extension AttributedString { + + /// HTML형식의 문자열을 기본 문자열로 변환합니다. + /// - Parameter htmlString: HTML 형식의 문자열. + init?(htmlString: String) { + let option: [NSAttributedString.DocumentReadingOptionKey: NSAttributedString.DocumentType] = [.documentType: .html] + guard let htmlData = htmlString.data(using: .utf16), + let nsStr = try? NSAttributedString(data: htmlData, options: option, documentAttributes: nil) + else { return nil } + self.init(nsStr.string) + } +} diff --git a/week5/ApiTest/ApiTest/Info.plist b/week5/ApiTest/ApiTest/Info.plist new file mode 100644 index 0000000..6a6654d --- /dev/null +++ b/week5/ApiTest/ApiTest/Info.plist @@ -0,0 +1,11 @@ + + + + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + + diff --git a/week5/ApiTest/ApiTest/Model/Blog.swift b/week5/ApiTest/ApiTest/Model/Blog.swift new file mode 100644 index 0000000..e4716dc --- /dev/null +++ b/week5/ApiTest/ApiTest/Model/Blog.swift @@ -0,0 +1,41 @@ +// +// BlogSearch.swift +// ApiTest +// +// Created by 임주민 on 2022/09/02. +// + +import Foundation + +struct BookBlogSearch { + + var searchKeyword = "" + var blogs: [Blog] = [] +} + +struct ResponseBlog: Codable { + + let lastBuildDate: String + let total, start, display: Int + let items: [Blog] +} + +struct Blog: Codable, Identifiable { + + let title: String + let link: String + let description, bloggername: String + let bloggerlink: String + let postdate: String + let id: Int? + + init(_ blog: Blog, id: Int) { + title = blog.title + link = blog.link + description = blog.description + bloggername = blog.bloggername + bloggerlink = blog.bloggerlink + postdate = blog.postdate + self.id = id + } +} diff --git a/week5/ApiTest/ApiTest/Model/Book.swift b/week5/ApiTest/ApiTest/Model/Book.swift new file mode 100644 index 0000000..1120579 --- /dev/null +++ b/week5/ApiTest/ApiTest/Model/Book.swift @@ -0,0 +1,44 @@ +// +// Book.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import Foundation + +struct BookSearch { + + var searchKeyword = "" + var books: [Book] = [] +} + +struct ResponseBook: Codable { + + let lastBuildDate: String + let total, start, display: Int + let items: [Book] +} + +struct Book: Codable, Identifiable { + + let title: String + let link: String + let image: String + let author, discount, publisher, pubdate: String + let isbn, description: String + let id: Int? + + init(_ book: Book, id: Int) { + title = book.title + link = book.link + image = book.image + author = book.author + discount = book.discount + publisher = book.publisher + pubdate = book.pubdate + isbn = book.isbn + description = book.description + self.id = id + } +} diff --git a/week5/ApiTest/ApiTest/Preview Content/Preview Assets.xcassets/Contents.json b/week5/ApiTest/ApiTest/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/week5/ApiTest/ApiTest/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/ApiTest/ApiTest/View/BlogCell.swift b/week5/ApiTest/ApiTest/View/BlogCell.swift new file mode 100644 index 0000000..3978418 --- /dev/null +++ b/week5/ApiTest/ApiTest/View/BlogCell.swift @@ -0,0 +1,29 @@ +// +// BlogCell.swift +// ApiTest +// +// Created by 임주민 on 2022/09/02. +// + +import SwiftUI + +struct BlogCell: View { + + let blog: Blog + + var body: some View { + VStack(alignment: .leading) { + Text(AttributedString(htmlString: blog.title) ?? "") + .font(.body) + .fontWeight(.bold) + .lineLimit(2) + .fixedSize(horizontal: false, vertical: true) + Text(blog.bloggername) + Text(blog.postdate).foregroundColor(.indigo) + Text(AttributedString(htmlString: blog.description) ?? "") + .foregroundColor(.gray) + .lineLimit(3) + .fixedSize(horizontal: false, vertical: true) + }.font(.footnote) + } +} diff --git a/week5/ApiTest/ApiTest/View/BlogWebView.swift b/week5/ApiTest/ApiTest/View/BlogWebView.swift new file mode 100644 index 0000000..47637f1 --- /dev/null +++ b/week5/ApiTest/ApiTest/View/BlogWebView.swift @@ -0,0 +1,25 @@ +// +// BlogWebView.swift +// ApiTest +// +// Created by 임주민 on 2022/09/02. +// + +import SwiftUI +import WebKit + +struct BlogWebView: UIViewRepresentable { + + var urlToLoad: String + + func makeUIView(context: Context) -> WKWebView { + guard let url = URL(string: self.urlToLoad) + else { return WKWebView() } + let webView = WKWebView() + webView.load(URLRequest(url: url)) + return webView + } + + func updateUIView(_ uiView: WKWebView, context: UIViewRepresentableContext) { + } +} diff --git a/week5/ApiTest/ApiTest/View/BookCell.swift b/week5/ApiTest/ApiTest/View/BookCell.swift new file mode 100644 index 0000000..8f0b05a --- /dev/null +++ b/week5/ApiTest/ApiTest/View/BookCell.swift @@ -0,0 +1,54 @@ +// +// BookCell.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import SwiftUI + +struct BookCell: View { + + let book: Book + + var body: some View { + VStack(alignment: .leading, spacing: 5) { + AsyncImage(url: URL(string: book.image)) { phase in + switch phase { + case .success(let image): + image + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 80, height: 120, alignment: .center) + case .failure: + Image(systemName: "questionmark.square") + .resizable() + .aspectRatio(contentMode: .fit) + .padding() + .frame(width: 80, height: 120, alignment: .center) + case .empty: + ProgressView() + .frame(width: 80, height: 120, alignment: .center) + @unknown default: + EmptyView() + .frame(width: 80, height: 120, alignment: .center) + } + } + Text(book.title) + .fontWeight(.bold) + .foregroundColor(.indigo) + .lineLimit(2) + .fixedSize(horizontal: false, vertical: true) + Text(book.author + "(지은이)") + Text(book.publisher + " | " + book.pubdate) + Text(book.discount == "0" + ? "가격정보 없음" + : book.discount.insertComma + "원") + .foregroundColor(.pink) + .fontWeight(.bold) + Spacer() + } + .font(.footnote) + .frame(width: 100, height: 230) + } +} diff --git a/week5/ApiTest/ApiTest/View/SearchList.swift b/week5/ApiTest/ApiTest/View/SearchList.swift new file mode 100644 index 0000000..6c2b6c2 --- /dev/null +++ b/week5/ApiTest/ApiTest/View/SearchList.swift @@ -0,0 +1,50 @@ +// +// BookList.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import SwiftUI + +struct SearchList: View { + + @ObservedObject var bookFinder: BookFinder + + var body: some View { + NavigationView { + List { + HStack { + TextField(text: $bookFinder.bookModel.searchKeyword, label: { Text("ex. 해리포터") }) + Button { + bookFinder.fetchBookList() + bookFinder.fetchBookBlog() + } label: { + Text("검색") + }.buttonStyle(.bordered) + } + Section { + ScrollView(.horizontal) { + LazyHStack { + ForEach(bookFinder.bookModel.books) { book in + BookCell(book: book) + Spacer() + } + } + } + } + Section { + VStack(spacing: 8) { + ForEach(bookFinder.blogModel.blogs) { blog in + BlogCell(blog: blog) + NavigationLink(destination: BlogWebView(urlToLoad: blog.link)) { + Text("글 보러가기").font(.footnote) + } + Spacer() + } + } + } + } + } + } +} diff --git a/week5/ApiTest/ApiTest/ViewModel/BookFinder.swift b/week5/ApiTest/ApiTest/ViewModel/BookFinder.swift new file mode 100644 index 0000000..ebbcdff --- /dev/null +++ b/week5/ApiTest/ApiTest/ViewModel/BookFinder.swift @@ -0,0 +1,78 @@ +// +// BookFinder.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import SwiftUI + +class BookFinder: ObservableObject { + + @Published var bookModel = BookSearch() + @Published var blogModel = BookBlogSearch() + + private func makeURLComponents(path: String, query: String) -> URLComponents { + var urlComponents = URLComponents() + urlComponents.scheme = APIConstants.scheme + urlComponents.host = APIConstants.host + urlComponents.path = path + urlComponents.queryItems = [URLQueryItem(name: "query", value: query)] + + return urlComponents + } + + private func makeURLGETRequest(url: URL) -> URLRequest { + var urlRequest = URLRequest(url: url) + urlRequest.httpMethod = "GET" + urlRequest.addValue(APIConstants.clientID, forHTTPHeaderField: "X-Naver-Client-Id") + urlRequest.addValue(APIConstants.clientSecret, forHTTPHeaderField: "X-Naver-Client-Secret") + + return urlRequest + } + + func fetchBookList() { + guard let componentsURL = makeURLComponents(path: APIConstants.getBookInfo.rawValue, + query: bookModel.searchKeyword).url + else { return } + let requestURL = makeURLGETRequest(url: componentsURL) + let task = URLSession.shared.dataTask(with: requestURL) { data, response, error in + guard error == nil, + let httpURLResponse = response as? HTTPURLResponse, + APIConstants.successRange.contains(httpURLResponse.statusCode) + else { return } + guard let data = data, + let parsedData = try? JSONDecoder().decode(ResponseBook.self, from: data) + else { return } + DispatchQueue.main.async { [weak self] in + self?.bookModel.books = parsedData.items.indices.map { + Book(parsedData.items[$0], id: parsedData.start + $0) + } + } + } + task.resume() + } + + func fetchBookBlog() { + guard let componentsURL = makeURLComponents(path: APIConstants.getBookBlog.rawValue, + query: bookModel.searchKeyword + "책").url + else { return } + let requestURL = makeURLGETRequest(url: componentsURL) + let task = URLSession.shared.dataTask(with: requestURL) { data, response, error in + guard error == nil, + let httpURLResponse = response as? HTTPURLResponse, + APIConstants.successRange.contains(httpURLResponse.statusCode) + else { return } + guard let data = data, + let parsedData = try? JSONDecoder().decode(ResponseBlog.self, from: data) + else { return } + DispatchQueue.main.async { [weak self] in + self?.blogModel.blogs = parsedData.items + self?.blogModel.blogs = parsedData.items.indices.map { + Blog(parsedData.items[$0], id: parsedData.start + $0) + } + } + } + task.resume() + } +}