From 45d9c282dcfa1c2e55287327933a3004e1148d2a Mon Sep 17 00:00:00 2001 From: sihyungyou Date: Mon, 2 Nov 2020 18:54:40 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat=20:=E2=9A=A1=EF=B8=8F=20=EB=84=A4?= =?UTF-8?q?=ED=8A=B8=EC=9B=8C=ED=81=AC=20=EB=AA=A8=EB=8D=B8=20=EC=84=A4?= =?UTF-8?q?=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 네트워크 프레임워크 생성 - 네트워크 모델 설계 --- .../IssueTracker.xcodeproj/project.pbxproj | 311 ++++++++++++++++++ .../xcschemes/IssueTracker.xcscheme | 10 + .../NetworkFramework/DataLoader.swift | 77 +++++ iOS/IssueTracker/NetworkFramework/Info.plist | 22 ++ .../NetworkFramework/NetworkFramework.h | 19 ++ .../NetworkFrameworkTests/Info.plist | 22 ++ .../NetworkFrameworkTests.swift | 34 ++ 7 files changed, 495 insertions(+) create mode 100644 iOS/IssueTracker/NetworkFramework/DataLoader.swift create mode 100644 iOS/IssueTracker/NetworkFramework/Info.plist create mode 100644 iOS/IssueTracker/NetworkFramework/NetworkFramework.h create mode 100644 iOS/IssueTracker/NetworkFrameworkTests/Info.plist create mode 100644 iOS/IssueTracker/NetworkFrameworkTests/NetworkFrameworkTests.swift diff --git a/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj b/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj index f1cfba3..6544d26 100644 --- a/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj +++ b/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj @@ -16,6 +16,13 @@ 950EA0612547FFF9005BE133 /* HeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 950EA05F2547FFF9005BE133 /* HeaderView.xib */; }; 950EB00D254A8DE2004857C2 /* MilestoneListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950EB00C254A8DE2004857C2 /* MilestoneListViewModel.swift */; }; 950EB00F254A94D0004857C2 /* UILabel+FontSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950EB00E254A94D0004857C2 /* UILabel+FontSize.swift */; }; + 9521FCB9254FE7FE00FC0220 /* NetworkFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9521FCB0254FE7FD00FC0220 /* NetworkFramework.framework */; }; + 9521FCC0254FE7FE00FC0220 /* NetworkFrameworkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9521FCBF254FE7FE00FC0220 /* NetworkFrameworkTests.swift */; }; + 9521FCC2254FE7FE00FC0220 /* NetworkFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 9521FCB2254FE7FD00FC0220 /* NetworkFramework.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9521FCC5254FE7FE00FC0220 /* NetworkFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9521FCB0254FE7FD00FC0220 /* NetworkFramework.framework */; }; + 9521FCC6254FE7FE00FC0220 /* NetworkFramework.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9521FCB0254FE7FD00FC0220 /* NetworkFramework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9521FCCF254FE84D00FC0220 /* DataLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9521FCCE254FE84D00FC0220 /* DataLoader.swift */; }; + 9521FCD1255007DC00FC0220 /* DataLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9521FCCE254FE84D00FC0220 /* DataLoader.swift */; }; 952FF3D5254805EE0042A96F /* Header+CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 952FF3D4254805EE0042A96F /* Header+CollectionView.swift */; }; 95C2F8952547EBE0006B3BC6 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C2F8942547EBE0006B3BC6 /* Label.swift */; }; 95C2F8982547EDC7006B3BC6 /* ColorConvertTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C2F8972547EDC7006B3BC6 /* ColorConvertTests.swift */; }; @@ -82,6 +89,27 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 9521FCBA254FE7FE00FC0220 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 95E9F5702546EC06001FBA0D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9521FCAF254FE7FD00FC0220; + remoteInfo = NetworkFramework; + }; + 9521FCBC254FE7FE00FC0220 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 95E9F5702546EC06001FBA0D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 95E9F5772546EC06001FBA0D; + remoteInfo = IssueTracker; + }; + 9521FCC3254FE7FE00FC0220 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 95E9F5702546EC06001FBA0D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9521FCAF254FE7FD00FC0220; + remoteInfo = NetworkFramework; + }; 95E9F58F2546EC0C001FBA0D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 95E9F5702546EC06001FBA0D /* Project object */; @@ -91,6 +119,20 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 9521FCCA254FE7FE00FC0220 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 9521FCC6254FE7FE00FC0220 /* NetworkFramework.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 0109B64F210AD3E237D228DA /* Pods_IssueTrackerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_IssueTrackerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1F538126B46A1B17CF926A6A /* Pods-IssueTracker.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-IssueTracker.debug.xcconfig"; path = "Target Support Files/Pods-IssueTracker/Pods-IssueTracker.debug.xcconfig"; sourceTree = ""; }; @@ -103,6 +145,13 @@ 950EA05F2547FFF9005BE133 /* HeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HeaderView.xib; sourceTree = ""; }; 950EB00C254A8DE2004857C2 /* MilestoneListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneListViewModel.swift; sourceTree = ""; }; 950EB00E254A94D0004857C2 /* UILabel+FontSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+FontSize.swift"; sourceTree = ""; }; + 9521FCB0254FE7FD00FC0220 /* NetworkFramework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NetworkFramework.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9521FCB2254FE7FD00FC0220 /* NetworkFramework.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NetworkFramework.h; sourceTree = ""; }; + 9521FCB3254FE7FD00FC0220 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9521FCB8254FE7FE00FC0220 /* NetworkFrameworkTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NetworkFrameworkTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 9521FCBF254FE7FE00FC0220 /* NetworkFrameworkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkFrameworkTests.swift; sourceTree = ""; }; + 9521FCC1254FE7FE00FC0220 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9521FCCE254FE84D00FC0220 /* DataLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataLoader.swift; sourceTree = ""; }; 952FF3D4254805EE0042A96F /* Header+CollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Header+CollectionView.swift"; sourceTree = ""; }; 95C2F8942547EBE0006B3BC6 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; 95C2F8972547EDC7006B3BC6 /* ColorConvertTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorConvertTests.swift; sourceTree = ""; }; @@ -159,10 +208,26 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 9521FCAD254FE7FD00FC0220 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9521FCB5254FE7FE00FC0220 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9521FCB9254FE7FE00FC0220 /* NetworkFramework.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 95E9F5752546EC06001FBA0D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 9521FCC5254FE7FE00FC0220 /* NetworkFramework.framework in Frameworks */, 60D4FF13E042363CE8805B41 /* Pods_IssueTracker.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -197,6 +262,25 @@ path = View; sourceTree = ""; }; + 9521FCB1254FE7FD00FC0220 /* NetworkFramework */ = { + isa = PBXGroup; + children = ( + 9521FCB2254FE7FD00FC0220 /* NetworkFramework.h */, + 9521FCB3254FE7FD00FC0220 /* Info.plist */, + 9521FCCE254FE84D00FC0220 /* DataLoader.swift */, + ); + path = NetworkFramework; + sourceTree = ""; + }; + 9521FCBE254FE7FE00FC0220 /* NetworkFrameworkTests */ = { + isa = PBXGroup; + children = ( + 9521FCBF254FE7FE00FC0220 /* NetworkFrameworkTests.swift */, + 9521FCC1254FE7FE00FC0220 /* Info.plist */, + ); + path = NetworkFrameworkTests; + sourceTree = ""; + }; 952FF3D1254804120042A96F /* 06.Extensions */ = { isa = PBXGroup; children = ( @@ -277,6 +361,8 @@ 9613C12D2547B265005EF13C /* .swiftlint.yml */, 95E9F57A2546EC06001FBA0D /* IssueTracker */, 95E9F5912546EC0C001FBA0D /* IssueTrackerTests */, + 9521FCB1254FE7FD00FC0220 /* NetworkFramework */, + 9521FCBE254FE7FE00FC0220 /* NetworkFrameworkTests */, 95E9F5792546EC06001FBA0D /* Products */, 9E2F3F721E0479B9F088C18C /* Pods */, 55C5FD67C43DD39CDB72E799 /* Frameworks */, @@ -288,6 +374,8 @@ children = ( 95E9F5782546EC06001FBA0D /* IssueTracker.app */, 95E9F58E2546EC0C001FBA0D /* IssueTrackerTests.xctest */, + 9521FCB0254FE7FD00FC0220 /* NetworkFramework.framework */, + 9521FCB8254FE7FE00FC0220 /* NetworkFrameworkTests.xctest */, ); name = Products; sourceTree = ""; @@ -499,7 +587,55 @@ }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + 9521FCAB254FE7FD00FC0220 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 9521FCC2254FE7FE00FC0220 /* NetworkFramework.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ + 9521FCAF254FE7FD00FC0220 /* NetworkFramework */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9521FCC7254FE7FE00FC0220 /* Build configuration list for PBXNativeTarget "NetworkFramework" */; + buildPhases = ( + 9521FCAB254FE7FD00FC0220 /* Headers */, + 9521FCAC254FE7FD00FC0220 /* Sources */, + 9521FCAD254FE7FD00FC0220 /* Frameworks */, + 9521FCAE254FE7FD00FC0220 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = NetworkFramework; + productName = NetworkFramework; + productReference = 9521FCB0254FE7FD00FC0220 /* NetworkFramework.framework */; + productType = "com.apple.product-type.framework"; + }; + 9521FCB7254FE7FE00FC0220 /* NetworkFrameworkTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9521FCCB254FE7FE00FC0220 /* Build configuration list for PBXNativeTarget "NetworkFrameworkTests" */; + buildPhases = ( + 9521FCB4254FE7FE00FC0220 /* Sources */, + 9521FCB5254FE7FE00FC0220 /* Frameworks */, + 9521FCB6254FE7FE00FC0220 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 9521FCBB254FE7FE00FC0220 /* PBXTargetDependency */, + 9521FCBD254FE7FE00FC0220 /* PBXTargetDependency */, + ); + name = NetworkFrameworkTests; + productName = NetworkFrameworkTests; + productReference = 9521FCB8254FE7FE00FC0220 /* NetworkFrameworkTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 95E9F5772546EC06001FBA0D /* IssueTracker */ = { isa = PBXNativeTarget; buildConfigurationList = 95E9F5972546EC0C001FBA0D /* Build configuration list for PBXNativeTarget "IssueTracker" */; @@ -509,10 +645,12 @@ 95E9F5752546EC06001FBA0D /* Frameworks */, 95E9F5762546EC06001FBA0D /* Resources */, 9613C12C2547B1F5005EF13C /* ShellScript */, + 9521FCCA254FE7FE00FC0220 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( + 9521FCC4254FE7FE00FC0220 /* PBXTargetDependency */, ); name = IssueTracker; productName = IssueTracker; @@ -548,6 +686,14 @@ LastUpgradeCheck = 1160; ORGANIZATIONNAME = "IssueTracker-15"; TargetAttributes = { + 9521FCAF254FE7FD00FC0220 = { + CreatedOnToolsVersion = 11.6; + LastSwiftMigration = 1160; + }; + 9521FCB7254FE7FE00FC0220 = { + CreatedOnToolsVersion = 11.6; + TestTargetID = 95E9F5772546EC06001FBA0D; + }; 95E9F5772546EC06001FBA0D = { CreatedOnToolsVersion = 11.6; }; @@ -572,11 +718,27 @@ targets = ( 95E9F5772546EC06001FBA0D /* IssueTracker */, 95E9F58D2546EC0C001FBA0D /* IssueTrackerTests */, + 9521FCAF254FE7FD00FC0220 /* NetworkFramework */, + 9521FCB7254FE7FE00FC0220 /* NetworkFrameworkTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 9521FCAE254FE7FD00FC0220 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9521FCB6254FE7FE00FC0220 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 95E9F5762546EC06001FBA0D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -671,6 +833,23 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 9521FCAC254FE7FD00FC0220 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9521FCCF254FE84D00FC0220 /* DataLoader.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9521FCB4254FE7FE00FC0220 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9521FCD1255007DC00FC0220 /* DataLoader.swift in Sources */, + 9521FCC0254FE7FE00FC0220 /* NetworkFrameworkTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 95E9F5742546EC06001FBA0D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -745,6 +924,21 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 9521FCBB254FE7FE00FC0220 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 9521FCAF254FE7FD00FC0220 /* NetworkFramework */; + targetProxy = 9521FCBA254FE7FE00FC0220 /* PBXContainerItemProxy */; + }; + 9521FCBD254FE7FE00FC0220 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 95E9F5772546EC06001FBA0D /* IssueTracker */; + targetProxy = 9521FCBC254FE7FE00FC0220 /* PBXContainerItemProxy */; + }; + 9521FCC4254FE7FE00FC0220 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 9521FCAF254FE7FD00FC0220 /* NetworkFramework */; + targetProxy = 9521FCC3254FE7FE00FC0220 /* PBXContainerItemProxy */; + }; 95E9F5902546EC0C001FBA0D /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 95E9F5772546EC06001FBA0D /* IssueTracker */; @@ -772,6 +966,103 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 9521FCC8254FE7FE00FC0220 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = B3PWYBKFUK; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = NetworkFramework/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "IssueTracker-15.NetworkFramework"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 9521FCC9254FE7FE00FC0220 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = B3PWYBKFUK; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = NetworkFramework/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "IssueTracker-15.NetworkFramework"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 9521FCCC254FE7FE00FC0220 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = B3PWYBKFUK; + INFOPLIST_FILE = NetworkFrameworkTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "IssueTracker-15.NetworkFrameworkTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/IssueTracker.app/IssueTracker"; + }; + name = Debug; + }; + 9521FCCD254FE7FE00FC0220 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = B3PWYBKFUK; + INFOPLIST_FILE = NetworkFrameworkTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "IssueTracker-15.NetworkFrameworkTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/IssueTracker.app/IssueTracker"; + }; + name = Release; + }; 95E9F5952546EC0C001FBA0D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -890,6 +1181,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 1F538126B46A1B17CF926A6A /* Pods-IssueTracker.debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = B3PWYBKFUK; @@ -909,6 +1201,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 2E821A460A4A5DD482366AE3 /* Pods-IssueTracker.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = B3PWYBKFUK; @@ -973,6 +1266,24 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 9521FCC7254FE7FE00FC0220 /* Build configuration list for PBXNativeTarget "NetworkFramework" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9521FCC8254FE7FE00FC0220 /* Debug */, + 9521FCC9254FE7FE00FC0220 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9521FCCB254FE7FE00FC0220 /* Build configuration list for PBXNativeTarget "NetworkFrameworkTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9521FCCC254FE7FE00FC0220 /* Debug */, + 9521FCCD254FE7FE00FC0220 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 95E9F5732546EC06001FBA0D /* Build configuration list for PBXProject "IssueTracker" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/iOS/IssueTracker/IssueTracker.xcodeproj/xcshareddata/xcschemes/IssueTracker.xcscheme b/iOS/IssueTracker/IssueTracker.xcodeproj/xcshareddata/xcschemes/IssueTracker.xcscheme index ee26a37..809934f 100644 --- a/iOS/IssueTracker/IssueTracker.xcodeproj/xcshareddata/xcschemes/IssueTracker.xcscheme +++ b/iOS/IssueTracker/IssueTracker.xcodeproj/xcshareddata/xcschemes/IssueTracker.xcscheme @@ -49,6 +49,16 @@ ReferencedContainer = "container:IssueTracker.xcodeproj"> + + + + { + private let session: URLSession + + init(session: URLSession) { + self.session = session + } + + public func perform(with urlRequest: URLRequest, completionHandler: @escaping (_ response: T?) -> Void) { + session.dataTask(with: urlRequest) { (data, response, error) in + guard error == nil else { return } + if let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 { + do { + completionHandler(try JSONDecoder().decode(T.self, from: data)) + } catch { + print(error.localizedDescription) + } + } + }.resume() + } +} + +public struct HTTPHeaders { + public var headers = [String: String]() + public init() { } +} + +public class Request { + public init() { } + + public func asURLRequest(url: URL, method: HTTPMethod, header: HTTPHeaders) -> URLRequest { + var urlRequest = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 10.0) + urlRequest.setHTTPHeader(header: header) + urlRequest.httpMethod = method.rawValue + + return urlRequest + } +} + +extension URLRequest { + mutating func setHTTPHeader(header: HTTPHeaders) { + header.headers.forEach { header in + self.setValue(header.value, forHTTPHeaderField: header.key) + } + } +} + +protocol URLConvertible { + func asURL() throws -> URL +} + +extension String: URLConvertible { + public func asURL() throws -> URL { + guard let url = URL(string: self) else { throw NetworkError.invalidURL(self) } + return url + } +} + +enum NetworkError: Error { + case invalidURL(String) +} + +public enum HTTPMethod: String { + case get = "GET" + case post = "POST" + case patch = "PATCH" + case delete = "DELETE" +} diff --git a/iOS/IssueTracker/NetworkFramework/Info.plist b/iOS/IssueTracker/NetworkFramework/Info.plist new file mode 100644 index 0000000..9bcb244 --- /dev/null +++ b/iOS/IssueTracker/NetworkFramework/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/iOS/IssueTracker/NetworkFramework/NetworkFramework.h b/iOS/IssueTracker/NetworkFramework/NetworkFramework.h new file mode 100644 index 0000000..dfdd300 --- /dev/null +++ b/iOS/IssueTracker/NetworkFramework/NetworkFramework.h @@ -0,0 +1,19 @@ +// +// NetworkFramework.h +// NetworkFramework +// +// Created by sihyung you on 2020/11/02. +// Copyright © 2020 IssueTracker-15. All rights reserved. +// + +#import + +//! Project version number for NetworkFramework. +FOUNDATION_EXPORT double NetworkFrameworkVersionNumber; + +//! Project version string for NetworkFramework. +FOUNDATION_EXPORT const unsigned char NetworkFrameworkVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/iOS/IssueTracker/NetworkFrameworkTests/Info.plist b/iOS/IssueTracker/NetworkFrameworkTests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/iOS/IssueTracker/NetworkFrameworkTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/iOS/IssueTracker/NetworkFrameworkTests/NetworkFrameworkTests.swift b/iOS/IssueTracker/NetworkFrameworkTests/NetworkFrameworkTests.swift new file mode 100644 index 0000000..fa78657 --- /dev/null +++ b/iOS/IssueTracker/NetworkFrameworkTests/NetworkFrameworkTests.swift @@ -0,0 +1,34 @@ +// +// NetworkFrameworkTests.swift +// NetworkFrameworkTests +// +// Created by sihyung you on 2020/11/02. +// Copyright © 2020 IssueTracker-15. All rights reserved. +// + +import XCTest +@testable import NetworkFramework + +class NetworkFrameworkTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} From 576557f0842851a5e29f8db2fbcb059a3119f6ae Mon Sep 17 00:00:00 2001 From: SHINVVOO Date: Mon, 2 Nov 2020 20:46:54 +0900 Subject: [PATCH 2/5] =?UTF-8?q?refactor:=20:rainbow:=20label,milestone=20m?= =?UTF-8?q?odel=20api=20model=EA=B3=BC=20=EB=A7=A4=EC=B9=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../02.LabelScene/Model/Label.swift | 44 ++++++++++++++-- .../ViewModel/LabelListViewModel.swift | 28 +++++------ .../03.MilestoneScene/Model/Milestone.swift | 50 ++++++++++++++++++- 3 files changed, 102 insertions(+), 20 deletions(-) diff --git a/iOS/IssueTracker/IssueTracker/02.LabelScene/Model/Label.swift b/iOS/IssueTracker/IssueTracker/02.LabelScene/Model/Label.swift index 7505c39..37f3d28 100644 --- a/iOS/IssueTracker/IssueTracker/02.LabelScene/Model/Label.swift +++ b/iOS/IssueTracker/IssueTracker/02.LabelScene/Model/Label.swift @@ -8,8 +8,44 @@ import Foundation -struct Label { - var title: String - var description: String - var hexColor: String +struct Label: Codable { + let id: Int + let title: String + let description: String + let hexColor: String + + init(id: Int, title: String, description: String, hexColor: String) { + self.id = id + self.title = title + self.description = description + self.hexColor = hexColor + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: DeCodingKeys.self) + id = try container.decode(Int.self, forKey: .id) + title = try container.decode(String.self, forKey: .title) + description = try container.decode(String.self, forKey: .description) + hexColor = try container.decode(String.self, forKey: .hexColor) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: EnCodingKeys.self) + try container.encode(title, forKey: .title) + try container.encode(description, forKey: .description) + try container.encode(hexColor, forKey: .hexColor) + } + + enum EnCodingKeys: String, CodingKey { + case title = "name" + case description + case hexColor = "color" + } + + enum DeCodingKeys: String, CodingKey { + case id + case title = "name" + case description + case hexColor = "color" + } } diff --git a/iOS/IssueTracker/IssueTracker/02.LabelScene/ViewModel/LabelListViewModel.swift b/iOS/IssueTracker/IssueTracker/02.LabelScene/ViewModel/LabelListViewModel.swift index 623bac3..f2fc40e 100644 --- a/iOS/IssueTracker/IssueTracker/02.LabelScene/ViewModel/LabelListViewModel.swift +++ b/iOS/IssueTracker/IssueTracker/02.LabelScene/ViewModel/LabelListViewModel.swift @@ -23,31 +23,31 @@ class LabelListViewModel: LabelListViewModelProtocol { private var labels = [Label]() func editLabel(at indexPath: IndexPath, title: String, desc: String, hexColor: String) { - labels[indexPath.row] = Label(title: title, description: desc, hexColor: hexColor) + labels[indexPath.row] = Label(id: 0, title: title, description: desc, hexColor: hexColor) didFetch?() } func addNewLabel(title: String, desc: String, hexColor: String) { - let newLabel: Label = Label(title: title, description: desc, hexColor: hexColor) + let newLabel: Label = Label(id: 0, title: title, description: desc, hexColor: hexColor) labels.insert(newLabel, at: 0) didFetch?() } func needFetchItems() { - labels = [Label(title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), - Label(title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), - Label(title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), - Label(title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), - Label(title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), - Label(title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), - Label(title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), - Label(title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), - Label(title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), - Label(title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), - Label(title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), - Label(title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879")] + labels = [Label(id: 0, title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), + Label(id: 0, title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), + Label(id: 0, title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), + Label(id: 0, title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), + Label(id: 0, title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), + Label(id: 0, title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), + Label(id: 0, title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), + Label(id: 0, title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), + Label(id: 0, title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), + Label(id: 0, title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), + Label(id: 0, title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), + Label(id: 0, title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879")] didFetch?() } diff --git a/iOS/IssueTracker/IssueTracker/03.MilestoneScene/Model/Milestone.swift b/iOS/IssueTracker/IssueTracker/03.MilestoneScene/Model/Milestone.swift index f3d9111..42d03ea 100644 --- a/iOS/IssueTracker/IssueTracker/03.MilestoneScene/Model/Milestone.swift +++ b/iOS/IssueTracker/IssueTracker/03.MilestoneScene/Model/Milestone.swift @@ -8,10 +8,56 @@ import Foundation -struct Milestone { +struct Milestone: Codable { let id: Int let title: String let description: String + let openIssuesLength: Int + let closeIssueLength: Int let dueDate: String - // TODO:- Milestone에 포함되어 있는 Issue들에 대한 정보 (미정) + + init(id: Int, title: String, description: String, dueDate: String, openIssuesLength: Int, closeIssueLength: Int) { + self.id = id + self.title = title + self.description = description + self.dueDate = dueDate + self.openIssuesLength = openIssuesLength + self.closeIssueLength = closeIssueLength + } + + init(id: Int, title: String,description: String, dueDate: String) { + self.init(id: id, title: title, description: description, dueDate: dueDate, openIssuesLength: 10, closeIssueLength: 10) + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: DeCodingKeys.self) + id = try container.decode(Int.self, forKey: .id) + title = try container.decode(String.self, forKey: .title) + description = try container.decode(String.self, forKey: .description) + openIssuesLength = try container.decode(Int.self, forKey: .openIssueLength) + closeIssueLength = try container.decode(Int.self, forKey: .closeIssueLength) + dueDate = try container.decode(String.self, forKey: .dueDate) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: EnCodingKeys.self) + try container.encode(title, forKey: .title) + try container.encode(description, forKey: .description) + try container.encode(dueDate, forKey: .dueDate) + } + + enum EnCodingKeys: CodingKey { + case title + case dueDate + case description + } + + enum DeCodingKeys: String, CodingKey { + case id + case title + case description + case openIssueLength = "OpenIssuesLength" + case closeIssueLength = "CloseIssuesLength" + case dueDate + } } From a8eb324da6a553f3ae247dde3cd5d188481893f6 Mon Sep 17 00:00:00 2001 From: SHINVVOO Date: Mon, 2 Nov 2020 20:50:16 +0900 Subject: [PATCH 3/5] refactor: :rainbow: JSONDecoder+decode Extension --- .../IssueTracker.xcodeproj/project.pbxproj | 12 ++++++++---- .../{Int+Clamp.swift => CGFloat+Clamp.swift} | 0 .../06.Extensions/JSONDecoder+Decode.swift | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) rename iOS/IssueTracker/IssueTracker/06.Extensions/{Int+Clamp.swift => CGFloat+Clamp.swift} (100%) create mode 100644 iOS/IssueTracker/IssueTracker/06.Extensions/JSONDecoder+Decode.swift diff --git a/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj b/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj index 6544d26..408c8ca 100644 --- a/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj +++ b/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj @@ -53,6 +53,7 @@ 963971F22549BC5F005BB4FA /* LabelListViewModelMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963971F12549BC5F005BB4FA /* LabelListViewModelMock.swift */; }; 963971F52549D5AF005BB4FA /* LabelCellViewTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963971F42549D5AF005BB4FA /* LabelCellViewTest.swift */; }; 963971F92549D9B6005BB4FA /* LabelSubmitFormViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963971F82549D9B6005BB4FA /* LabelSubmitFormViewTests.swift */; }; + 965F09712550299A00B3A248 /* JSONDecoder+Decode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965F09702550299A00B3A248 /* JSONDecoder+Decode.swift */; }; 966ADAE3254D8B0800A480DD /* SubmitFormView+LoadFromNib.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966ADAE2254D8B0800A480DD /* SubmitFormView+LoadFromNib.swift */; }; 966ADAE5254D9B7C00A480DD /* MilestoneSubmitFieldsView+LoadFromNib.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966ADAE4254D9B7C00A480DD /* MilestoneSubmitFieldsView+LoadFromNib.swift */; }; 966ADAE8254D9BD400A480DD /* LabelSubmitFieldsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966ADAE7254D9BD400A480DD /* LabelSubmitFieldsView.swift */; }; @@ -82,7 +83,7 @@ 96C8357525490F1A0086A6E6 /* BadgeLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C8357425490F1A0086A6E6 /* BadgeLabelView.swift */; }; 96F95BFA254E6C4A00EC00C6 /* IssueListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96F95BF9254E6C4A00EC00C6 /* IssueListViewController.swift */; }; 96F95BFC254E6E4C00EC00C6 /* Issue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96F95BFB254E6E4C00EC00C6 /* Issue.swift */; }; - 96F95C02254E891E00EC00C6 /* Int+Clamp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96F95C01254E891E00EC00C6 /* Int+Clamp.swift */; }; + 96F95C02254E891E00EC00C6 /* CGFloat+Clamp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96F95C01254E891E00EC00C6 /* CGFloat+Clamp.swift */; }; 96F95C07254EA70B00EC00C6 /* IssueCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96F95C05254EA70B00EC00C6 /* IssueCellView.swift */; }; 96F95C08254EA70B00EC00C6 /* IssueCellView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 96F95C06254EA70B00EC00C6 /* IssueCellView.xib */; }; D2C7CE68FC3C8579A64346BE /* Pods_IssueTrackerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0109B64F210AD3E237D228DA /* Pods_IssueTrackerTests.framework */; }; @@ -175,6 +176,7 @@ 963971F12549BC5F005BB4FA /* LabelListViewModelMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelListViewModelMock.swift; sourceTree = ""; }; 963971F42549D5AF005BB4FA /* LabelCellViewTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelCellViewTest.swift; sourceTree = ""; }; 963971F82549D9B6005BB4FA /* LabelSubmitFormViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelSubmitFormViewTests.swift; sourceTree = ""; }; + 965F09702550299A00B3A248 /* JSONDecoder+Decode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+Decode.swift"; sourceTree = ""; }; 966ADAE2254D8B0800A480DD /* SubmitFormView+LoadFromNib.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SubmitFormView+LoadFromNib.swift"; sourceTree = ""; }; 966ADAE4254D9B7C00A480DD /* MilestoneSubmitFieldsView+LoadFromNib.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MilestoneSubmitFieldsView+LoadFromNib.swift"; sourceTree = ""; }; 966ADAE7254D9BD400A480DD /* LabelSubmitFieldsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelSubmitFieldsView.swift; sourceTree = ""; }; @@ -199,7 +201,7 @@ 96C8357425490F1A0086A6E6 /* BadgeLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeLabelView.swift; sourceTree = ""; }; 96F95BF9254E6C4A00EC00C6 /* IssueListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueListViewController.swift; sourceTree = ""; }; 96F95BFB254E6E4C00EC00C6 /* Issue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Issue.swift; sourceTree = ""; }; - 96F95C01254E891E00EC00C6 /* Int+Clamp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Clamp.swift"; sourceTree = ""; }; + 96F95C01254E891E00EC00C6 /* CGFloat+Clamp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGFloat+Clamp.swift"; sourceTree = ""; }; 96F95C05254EA70B00EC00C6 /* IssueCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueCellView.swift; sourceTree = ""; }; 96F95C06254EA70B00EC00C6 /* IssueCellView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IssueCellView.xib; sourceTree = ""; }; B9280E9634EEE3C0BADC1EBF /* Pods-IssueTrackerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-IssueTrackerTests.release.xcconfig"; path = "Target Support Files/Pods-IssueTrackerTests/Pods-IssueTrackerTests.release.xcconfig"; sourceTree = ""; }; @@ -293,7 +295,8 @@ 9505446E254AA6E0007767C6 /* UIView+ShowAlert.swift */, 95054472254AAC0D007767C6 /* CollectionView+.swift */, 96A58F54254AA5BC00248F1E /* String+Date.swift */, - 96F95C01254E891E00EC00C6 /* Int+Clamp.swift */, + 96F95C01254E891E00EC00C6 /* CGFloat+Clamp.swift */, + 965F09702550299A00B3A248 /* JSONDecoder+Decode.swift */, ); path = 06.Extensions; sourceTree = ""; @@ -873,8 +876,9 @@ 95E9F57C2546EC07001FBA0D /* AppDelegate.swift in Sources */, 96A58F55254AA5BC00248F1E /* String+Date.swift in Sources */, 950EA05C2547FFA0005BE133 /* LabelCellView.swift in Sources */, - 96F95C02254E891E00EC00C6 /* Int+Clamp.swift in Sources */, + 96F95C02254E891E00EC00C6 /* CGFloat+Clamp.swift in Sources */, 950EB00F254A94D0004857C2 /* UILabel+FontSize.swift in Sources */, + 965F09712550299A00B3A248 /* JSONDecoder+Decode.swift in Sources */, 9679DFB6254D767400550BE4 /* SubmitFormView.swift in Sources */, 95C2F8952547EBE0006B3BC6 /* Label.swift in Sources */, 96C8357525490F1A0086A6E6 /* BadgeLabelView.swift in Sources */, diff --git a/iOS/IssueTracker/IssueTracker/06.Extensions/Int+Clamp.swift b/iOS/IssueTracker/IssueTracker/06.Extensions/CGFloat+Clamp.swift similarity index 100% rename from iOS/IssueTracker/IssueTracker/06.Extensions/Int+Clamp.swift rename to iOS/IssueTracker/IssueTracker/06.Extensions/CGFloat+Clamp.swift diff --git a/iOS/IssueTracker/IssueTracker/06.Extensions/JSONDecoder+Decode.swift b/iOS/IssueTracker/IssueTracker/06.Extensions/JSONDecoder+Decode.swift new file mode 100644 index 0000000..e7c9b5b --- /dev/null +++ b/iOS/IssueTracker/IssueTracker/06.Extensions/JSONDecoder+Decode.swift @@ -0,0 +1,17 @@ +// +// JSONDecoder+Decode.swift +// IssueTracker +// +// Created by 김신우 on 2020/11/02. +// Copyright © 2020 IssueTracker-15. All rights reserved. +// + +import Foundation + +extension JSONDecoder { + + static func decode(_ type: T.Type, from data: Data) -> T? { + return try? JSONDecoder().decode(type, from: data) + } + +} From 9f673060a047c8760b9931771a031e672ee8c3de Mon Sep 17 00:00:00 2001 From: sihyungyou Date: Mon, 2 Nov 2020 22:32:55 +0900 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=F0=9F=8C=88=20=EB=84=A4=ED=8A=B8?= =?UTF-8?q?=EC=9B=8C=ED=81=AC=20=EB=AA=A8=EB=8D=B8=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20EndPoint=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - EndPoint를 정의함으로써 네트워크 프레임워크와 비즈니스 로직 코드 분리 - 레이블, 마일스톤에 대한 각각의 EndPoint 정의 --- .../IssueTracker.xcodeproj/project.pbxproj | 16 ++++ .../IssueTracker/07.Supports/Info.plist | 5 ++ .../08.Endpoints/LabelEndPoint.swift | 80 +++++++++++++++++++ .../08.Endpoints/MilestoneEndPoint.swift | 80 +++++++++++++++++++ .../IssueTracker/SceneDelegate.swift | 26 ++++++ .../NetworkFramework/DataLoader.swift | 28 ++++++- 6 files changed, 231 insertions(+), 4 deletions(-) create mode 100644 iOS/IssueTracker/IssueTracker/08.Endpoints/LabelEndPoint.swift create mode 100644 iOS/IssueTracker/IssueTracker/08.Endpoints/MilestoneEndPoint.swift diff --git a/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj b/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj index 408c8ca..d2a2873 100644 --- a/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj +++ b/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj @@ -24,6 +24,8 @@ 9521FCCF254FE84D00FC0220 /* DataLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9521FCCE254FE84D00FC0220 /* DataLoader.swift */; }; 9521FCD1255007DC00FC0220 /* DataLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9521FCCE254FE84D00FC0220 /* DataLoader.swift */; }; 952FF3D5254805EE0042A96F /* Header+CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 952FF3D4254805EE0042A96F /* Header+CollectionView.swift */; }; + 956B5D0525503D20001CBAA9 /* MilestoneEndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956B5D0425503D20001CBAA9 /* MilestoneEndPoint.swift */; }; + 956B5D0725503E8A001CBAA9 /* LabelEndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956B5D0625503E8A001CBAA9 /* LabelEndPoint.swift */; }; 95C2F8952547EBE0006B3BC6 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C2F8942547EBE0006B3BC6 /* Label.swift */; }; 95C2F8982547EDC7006B3BC6 /* ColorConvertTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C2F8972547EDC7006B3BC6 /* ColorConvertTests.swift */; }; 95C2F89C2547EF47006B3BC6 /* String+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C2F89B2547EF47006B3BC6 /* String+Color.swift */; }; @@ -154,6 +156,8 @@ 9521FCC1254FE7FE00FC0220 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9521FCCE254FE84D00FC0220 /* DataLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataLoader.swift; sourceTree = ""; }; 952FF3D4254805EE0042A96F /* Header+CollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Header+CollectionView.swift"; sourceTree = ""; }; + 956B5D0425503D20001CBAA9 /* MilestoneEndPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneEndPoint.swift; sourceTree = ""; }; + 956B5D0625503E8A001CBAA9 /* LabelEndPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelEndPoint.swift; sourceTree = ""; }; 95C2F8942547EBE0006B3BC6 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; 95C2F8972547EDC7006B3BC6 /* ColorConvertTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorConvertTests.swift; sourceTree = ""; }; 95C2F89B2547EF47006B3BC6 /* String+Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Color.swift"; sourceTree = ""; }; @@ -340,6 +344,15 @@ path = 07.Supports; sourceTree = ""; }; + 956B5D0325503D06001CBAA9 /* 08.Endpoints */ = { + isa = PBXGroup; + children = ( + 956B5D0425503D20001CBAA9 /* MilestoneEndPoint.swift */, + 956B5D0625503E8A001CBAA9 /* LabelEndPoint.swift */, + ); + path = 08.Endpoints; + sourceTree = ""; + }; 95C2F8932547EBD1006B3BC6 /* Model */ = { isa = PBXGroup; children = ( @@ -396,6 +409,7 @@ 952FF3DA254814650042A96F /* 07.Supports */, 952FF3D1254804120042A96F /* 06.Extensions */, 95E9F5812546EC07001FBA0D /* Main.storyboard */, + 956B5D0325503D06001CBAA9 /* 08.Endpoints */, ); path = IssueTracker; sourceTree = ""; @@ -882,11 +896,13 @@ 9679DFB6254D767400550BE4 /* SubmitFormView.swift in Sources */, 95C2F8952547EBE0006B3BC6 /* Label.swift in Sources */, 96C8357525490F1A0086A6E6 /* BadgeLabelView.swift in Sources */, + 956B5D0525503D20001CBAA9 /* MilestoneEndPoint.swift in Sources */, 966ADAE3254D8B0800A480DD /* SubmitFormView+LoadFromNib.swift in Sources */, 9679DFBF254D7A0E00550BE4 /* MilestoneSubmitFieldsView.swift in Sources */, 95C36581254A6F80008ACCB5 /* MilestoneListViewController.swift in Sources */, 96F95BFC254E6E4C00EC00C6 /* Issue.swift in Sources */, 9670B37F2549E9E10026B0C9 /* MainTabBarController.swift in Sources */, + 956B5D0725503E8A001CBAA9 /* LabelEndPoint.swift in Sources */, 95E9F57E2546EC07001FBA0D /* SceneDelegate.swift in Sources */, 9679DFBD254D78D000550BE4 /* SubmitFieldProtocol.swift in Sources */, 952FF3D5254805EE0042A96F /* Header+CollectionView.swift in Sources */, diff --git a/iOS/IssueTracker/IssueTracker/07.Supports/Info.plist b/iOS/IssueTracker/IssueTracker/07.Supports/Info.plist index 2a3483c..bc6586a 100644 --- a/iOS/IssueTracker/IssueTracker/07.Supports/Info.plist +++ b/iOS/IssueTracker/IssueTracker/07.Supports/Info.plist @@ -2,6 +2,11 @@ + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable diff --git a/iOS/IssueTracker/IssueTracker/08.Endpoints/LabelEndPoint.swift b/iOS/IssueTracker/IssueTracker/08.Endpoints/LabelEndPoint.swift new file mode 100644 index 0000000..7e38413 --- /dev/null +++ b/iOS/IssueTracker/IssueTracker/08.Endpoints/LabelEndPoint.swift @@ -0,0 +1,80 @@ +// +// LabelEndPoint.swift +// IssueTracker +// +// Created by sihyung you on 2020/11/02. +// Copyright © 2020 IssueTracker-15. All rights reserved. +// + +import Foundation +import NetworkFramework + +struct LabelEndPoint: EndPoint { + var requestType: RequestType + var parameter: String = "" + var httpBody: Data? + + init(requestType: RequestType, parameter: String, httpBody: Data? = nil) { + self.requestType = requestType + self.parameter = parameter + self.httpBody = httpBody + } + + init(requestType: RequestType, httpBody: Data? = nil) { + self.requestType = requestType + self.httpBody = httpBody + } + + enum RequestType { + case fetch + case create + case edit + case delete + case test + } + + var scheme: String { + switch self { + default: + #warning("실제 서버는 http") + return "https" + } + } + + var baseURL: String { + switch self { + default: + return "h3rb9c0ugl.execute-api.ap-northeast-2.amazonaws.com" + } + } + + var path: String { + switch requestType { + case .fetch: + return "/api/label" + case .create: + return "/api/label" + case .edit: + return "/api/label/" + parameter + case .delete: + return "/api/label/" + parameter + case .test: + return "/develop/baminchan/" + parameter + } + } + + var method: HTTPMethod { + switch requestType { + case .fetch: + return .get + case .create: + return .post + case .edit: + return .patch + case .delete: + return .delete + case .test: + return .get + } + } +} diff --git a/iOS/IssueTracker/IssueTracker/08.Endpoints/MilestoneEndPoint.swift b/iOS/IssueTracker/IssueTracker/08.Endpoints/MilestoneEndPoint.swift new file mode 100644 index 0000000..f7b12e7 --- /dev/null +++ b/iOS/IssueTracker/IssueTracker/08.Endpoints/MilestoneEndPoint.swift @@ -0,0 +1,80 @@ +// +// MilestoneEndPoint.swift +// IssueTracker +// +// Created by sihyung you on 2020/11/02. +// Copyright © 2020 IssueTracker-15. All rights reserved. +// + +import Foundation +import NetworkFramework + +struct MilestoneEndPoint: EndPoint { + var requestType: RequestType + var parameter: String = "" + var httpBody: Data? + + init(requestType: RequestType, parameter: String, httpBody: Data? = nil) { + self.requestType = requestType + self.parameter = parameter + self.httpBody = httpBody + } + + init(requestType: RequestType, httpBody: Data? = nil) { + self.requestType = requestType + self.httpBody = httpBody + } + + enum RequestType { + case fetch + case create + case edit + case delete + case test + } + + var scheme: String { + switch self { + default: + #warning("실제 서버는 http") + return "https" + } + } + + var baseURL: String { + switch self { + default: + return "h3rb9c0ugl.execute-api.ap-northeast-2.amazonaws.com" + } + } + + var path: String { + switch requestType { + case .fetch: + return "/api/milestones" + case .create: + return "/api/milestone" + case .edit: + return "/api/milestone/" + parameter + case .delete: + return "/api/milestone/" + parameter + case .test: + return "/develop/baminchan/" + parameter + } + } + + var method: HTTPMethod { + switch requestType { + case .fetch: + return .get + case .create: + return .post + case .edit: + return .patch + case .delete: + return .delete + case .test: + return .get + } + } +} diff --git a/iOS/IssueTracker/IssueTracker/SceneDelegate.swift b/iOS/IssueTracker/IssueTracker/SceneDelegate.swift index b1a6bcf..3ca2982 100644 --- a/iOS/IssueTracker/IssueTracker/SceneDelegate.swift +++ b/iOS/IssueTracker/IssueTracker/SceneDelegate.swift @@ -7,6 +7,24 @@ // import UIKit +import NetworkFramework + +struct FoodListResponse: Codable { + var statusCode: Int + var body: [StoreItem] = [StoreItem]() +} + +struct StoreItem: Codable { + var detail_hash: String + var image: String + var alt: String + var delivery_type: [String] + var title: String + var description: String + var n_price: String? + var s_price: String + var badge: [String]? +} class SceneDelegate: UIResponder, UIWindowSceneDelegate { @@ -18,6 +36,14 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { else { return } // TODO:- Network, Cache, AuthService 초기화 및 로그인 여부 검증 -> AppDelegate로?? mainTabVC.setupSubViewControllers() + + let milestoneEndPoint = MilestoneEndPoint(requestType: .test, parameter: "main") + let session = URLSession.init(configuration: .default, delegate: nil, delegateQueue: nil) + let dataLoader = DataLoader(session: session) + dataLoader.reqeust(endpoint: milestoneEndPoint) { (response) in + print(response!) + } + } func sceneDidDisconnect(_ scene: UIScene) { diff --git a/iOS/IssueTracker/NetworkFramework/DataLoader.swift b/iOS/IssueTracker/NetworkFramework/DataLoader.swift index 8f31c4c..7e3fd20 100644 --- a/iOS/IssueTracker/NetworkFramework/DataLoader.swift +++ b/iOS/IssueTracker/NetworkFramework/DataLoader.swift @@ -11,18 +11,29 @@ import Foundation public class DataLoader { private let session: URLSession - init(session: URLSession) { + public init(session: URLSession) { self.session = session } - public func perform(with urlRequest: URLRequest, completionHandler: @escaping (_ response: T?) -> Void) { + public func reqeust(endpoint: EndPoint, completion: @escaping (_ response: T?) -> Void) { + var components = URLComponents() + components.scheme = endpoint.scheme + components.host = endpoint.baseURL + components.path = endpoint.path + + guard let url = components.url else { return } + + var urlRequest = URLRequest(url: url) + urlRequest.httpMethod = endpoint.method.rawValue + urlRequest.httpBody = endpoint.httpBody + session.dataTask(with: urlRequest) { (data, response, error) in guard error == nil else { return } if let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 { do { - completionHandler(try JSONDecoder().decode(T.self, from: data)) + completion(try JSONDecoder().decode(T.self, from: data)) } catch { - print(error.localizedDescription) + print(NetworkError.decodingError("\(T.Type.self)")) } } }.resume() @@ -67,6 +78,7 @@ extension String: URLConvertible { enum NetworkError: Error { case invalidURL(String) + case decodingError(String) } public enum HTTPMethod: String { @@ -75,3 +87,11 @@ public enum HTTPMethod: String { case patch = "PATCH" case delete = "DELETE" } + +public protocol EndPoint { + var scheme: String { get } + var baseURL: String { get } + var path: String { get } + var method: HTTPMethod { get } + var httpBody: Data? { get } +} From d4bf5fe068115d28dec68ceb4bf23a263ba35046 Mon Sep 17 00:00:00 2001 From: sihyungyou Date: Tue, 3 Nov 2020 16:41:00 +0900 Subject: [PATCH 5/5] =?UTF-8?q?feat=20:=E2=9A=A1=EF=B8=8F=20=EB=84=A4?= =?UTF-8?q?=ED=8A=B8=EC=9B=8C=ED=81=AC=20API=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IssueTracker.xcodeproj/project.pbxproj | 26 +++---------- .../ViewModel/LabelListViewModel.swift | 37 +++++++++++-------- .../Model => 04.Models}/Issue.swift | 0 .../Model => 04.Models}/Label.swift | 8 ++-- .../Model => 04.Models}/Milestone.swift | 14 +++---- .../08.Endpoints/LabelEndPoint.swift | 29 +++++++++++---- .../08.Endpoints/MilestoneEndPoint.swift | 31 +++++++++++----- .../IssueTracker/SceneDelegate.swift | 25 ------------- .../NetworkFramework/DataLoader.swift | 29 +++++++++++++-- 9 files changed, 106 insertions(+), 93 deletions(-) rename iOS/IssueTracker/IssueTracker/{01.IssueScene/Model => 04.Models}/Issue.swift (100%) rename iOS/IssueTracker/IssueTracker/{02.LabelScene/Model => 04.Models}/Label.swift (89%) rename iOS/IssueTracker/IssueTracker/{03.MilestoneScene/Model => 04.Models}/Milestone.swift (79%) diff --git a/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj b/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj index d2a2873..e5ac14d 100644 --- a/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj +++ b/iOS/IssueTracker/IssueTracker.xcodeproj/project.pbxproj @@ -310,7 +310,6 @@ children = ( 96C734D1254943BF00DEC9A4 /* ViewModel */, 950EA0592547FF74005BE133 /* View */, - 95C2F8932547EBD1006B3BC6 /* Model */, ); path = 02.LabelScene; sourceTree = ""; @@ -353,12 +352,14 @@ path = 08.Endpoints; sourceTree = ""; }; - 95C2F8932547EBD1006B3BC6 /* Model */ = { + 957DBF9A25513FA80051123C /* 04.Models */ = { isa = PBXGroup; children = ( + 96F95BFB254E6E4C00EC00C6 /* Issue.swift */, 95C2F8942547EBE0006B3BC6 /* Label.swift */, + 966DD038254A5B1600A44FBA /* Milestone.swift */, ); - path = Model; + path = 04.Models; sourceTree = ""; }; 95C36589254A7415008ACCB5 /* MilestoneCell */ = { @@ -405,6 +406,7 @@ 96F95BF5254E691500EC00C6 /* 01.IssueScene */, 952FF3D7254813F50042A96F /* 02.LabelScene */, 966DD034254A5ADF00A44FBA /* 03.MilestoneScene */, + 957DBF9A25513FA80051123C /* 04.Models */, 96C8357325490EFF0086A6E6 /* 05.CustomUI */, 952FF3DA254814650042A96F /* 07.Supports */, 952FF3D1254804120042A96F /* 06.Extensions */, @@ -458,7 +460,6 @@ 966DD034254A5ADF00A44FBA /* 03.MilestoneScene */ = { isa = PBXGroup; children = ( - 966DD037254A5B0100A44FBA /* Model */, 966DD035254A5AF300A44FBA /* ViewModel */, 966DD036254A5AFA00A44FBA /* View */, ); @@ -484,14 +485,6 @@ name = View; sourceTree = ""; }; - 966DD037254A5B0100A44FBA /* Model */ = { - isa = PBXGroup; - children = ( - 966DD038254A5B1600A44FBA /* Milestone.swift */, - ); - path = Model; - sourceTree = ""; - }; 9670B37D2549E9B80026B0C9 /* 00.MainTabBarScene */ = { isa = PBXGroup; children = ( @@ -551,7 +544,6 @@ 96F95BF5254E691500EC00C6 /* 01.IssueScene */ = { isa = PBXGroup; children = ( - 96F95BF8254E693500EC00C6 /* Model */, 96F95BF7254E692F00EC00C6 /* View */, 96F95BF6254E692800EC00C6 /* ViewModel */, ); @@ -574,14 +566,6 @@ path = View; sourceTree = ""; }; - 96F95BF8254E693500EC00C6 /* Model */ = { - isa = PBXGroup; - children = ( - 96F95BFB254E6E4C00EC00C6 /* Issue.swift */, - ); - path = Model; - sourceTree = ""; - }; 96F95C03254EA6D000EC00C6 /* IssueCellView */ = { isa = PBXGroup; children = ( diff --git a/iOS/IssueTracker/IssueTracker/02.LabelScene/ViewModel/LabelListViewModel.swift b/iOS/IssueTracker/IssueTracker/02.LabelScene/ViewModel/LabelListViewModel.swift index f2fc40e..1c7ef93 100644 --- a/iOS/IssueTracker/IssueTracker/02.LabelScene/ViewModel/LabelListViewModel.swift +++ b/iOS/IssueTracker/IssueTracker/02.LabelScene/ViewModel/LabelListViewModel.swift @@ -7,6 +7,7 @@ // import Foundation +import NetworkFramework protocol LabelListViewModelProtocol { var didFetch: (() -> Void)? { get set } @@ -23,32 +24,38 @@ class LabelListViewModel: LabelListViewModelProtocol { private var labels = [Label]() func editLabel(at indexPath: IndexPath, title: String, desc: String, hexColor: String) { - labels[indexPath.row] = Label(id: 0, title: title, description: desc, hexColor: hexColor) + labels[indexPath.row] = Label(title: title, description: desc, hexColor: hexColor) didFetch?() } func addNewLabel(title: String, desc: String, hexColor: String) { - let newLabel: Label = Label(id: 0, title: title, description: desc, hexColor: hexColor) + let newLabel: Label = Label(title: title, description: desc, hexColor: hexColor) labels.insert(newLabel, at: 0) didFetch?() } func needFetchItems() { - labels = [Label(id: 0, title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), - Label(id: 0, title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), - Label(id: 0, title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), - Label(id: 0, title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), - Label(id: 0, title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), - Label(id: 0, title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), - Label(id: 0, title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), - Label(id: 0, title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), - Label(id: 0, title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), - Label(id: 0, title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879"), - Label(id: 0, title: "feature", description: "기능에 대한 레이블입니다.", hexColor: "#FF5D5D"), - Label(id: 0, title: "bug", description: "수정할 버그에 대한 레이블입니다.", hexColor: "#96F879")] - didFetch?() + // 네트워크 통신으로 fetch + let labelFetchEndPoint = LabelEndPoint(requestType: .fetch) + let session = URLSession.init(configuration: .default, delegate: nil, delegateQueue: nil) + let dataLoader = DataLoader<[Label]>(session: session) + dataLoader.reqeust(endpoint: labelFetchEndPoint) { [weak self] (response) in + switch response { + case .success(let data): + guard let data = data else { return } + self?.labels = data + DispatchQueue.main.async { + self?.didFetch?() + } + case .failure(let error): + switch error { + case .decodingError(let message), .invalidURL(let message), .responseError(let message): + print(message) + } + } + } } func cellForItemAt(path: IndexPath) -> LabelItemViewModel { diff --git a/iOS/IssueTracker/IssueTracker/01.IssueScene/Model/Issue.swift b/iOS/IssueTracker/IssueTracker/04.Models/Issue.swift similarity index 100% rename from iOS/IssueTracker/IssueTracker/01.IssueScene/Model/Issue.swift rename to iOS/IssueTracker/IssueTracker/04.Models/Issue.swift diff --git a/iOS/IssueTracker/IssueTracker/02.LabelScene/Model/Label.swift b/iOS/IssueTracker/IssueTracker/04.Models/Label.swift similarity index 89% rename from iOS/IssueTracker/IssueTracker/02.LabelScene/Model/Label.swift rename to iOS/IssueTracker/IssueTracker/04.Models/Label.swift index 37f3d28..f69346b 100644 --- a/iOS/IssueTracker/IssueTracker/02.LabelScene/Model/Label.swift +++ b/iOS/IssueTracker/IssueTracker/04.Models/Label.swift @@ -14,8 +14,8 @@ struct Label: Codable { let description: String let hexColor: String - init(id: Int, title: String, description: String, hexColor: String) { - self.id = id + init(title: String, description: String, hexColor: String) { + self.id = -1 self.title = title self.description = description self.hexColor = hexColor @@ -37,14 +37,14 @@ struct Label: Codable { } enum EnCodingKeys: String, CodingKey { - case title = "name" + case title case description case hexColor = "color" } enum DeCodingKeys: String, CodingKey { case id - case title = "name" + case title case description case hexColor = "color" } diff --git a/iOS/IssueTracker/IssueTracker/03.MilestoneScene/Model/Milestone.swift b/iOS/IssueTracker/IssueTracker/04.Models/Milestone.swift similarity index 79% rename from iOS/IssueTracker/IssueTracker/03.MilestoneScene/Model/Milestone.swift rename to iOS/IssueTracker/IssueTracker/04.Models/Milestone.swift index 42d03ea..e72ad98 100644 --- a/iOS/IssueTracker/IssueTracker/03.MilestoneScene/Model/Milestone.swift +++ b/iOS/IssueTracker/IssueTracker/04.Models/Milestone.swift @@ -12,11 +12,11 @@ struct Milestone: Codable { let id: Int let title: String let description: String - let openIssuesLength: Int - let closeIssueLength: Int + let openIssuesLength: String + let closeIssueLength: String let dueDate: String - init(id: Int, title: String, description: String, dueDate: String, openIssuesLength: Int, closeIssueLength: Int) { + init(id: Int, title: String, description: String, dueDate: String, openIssuesLength: String, closeIssueLength: String) { self.id = id self.title = title self.description = description @@ -25,8 +25,8 @@ struct Milestone: Codable { self.closeIssueLength = closeIssueLength } - init(id: Int, title: String,description: String, dueDate: String) { - self.init(id: id, title: title, description: description, dueDate: dueDate, openIssuesLength: 10, closeIssueLength: 10) + init(id: Int, title: String, description: String, dueDate: String) { + self.init(id: id, title: title, description: description, dueDate: dueDate, openIssuesLength: "0", closeIssueLength: "0") } init(from decoder: Decoder) throws { @@ -34,8 +34,8 @@ struct Milestone: Codable { id = try container.decode(Int.self, forKey: .id) title = try container.decode(String.self, forKey: .title) description = try container.decode(String.self, forKey: .description) - openIssuesLength = try container.decode(Int.self, forKey: .openIssueLength) - closeIssueLength = try container.decode(Int.self, forKey: .closeIssueLength) + openIssuesLength = try container.decode(String.self, forKey: .openIssueLength) + closeIssueLength = try container.decode(String.self, forKey: .closeIssueLength) dueDate = try container.decode(String.self, forKey: .dueDate) } diff --git a/iOS/IssueTracker/IssueTracker/08.Endpoints/LabelEndPoint.swift b/iOS/IssueTracker/IssueTracker/08.Endpoints/LabelEndPoint.swift index 7e38413..8d60dbf 100644 --- a/iOS/IssueTracker/IssueTracker/08.Endpoints/LabelEndPoint.swift +++ b/iOS/IssueTracker/IssueTracker/08.Endpoints/LabelEndPoint.swift @@ -30,21 +30,26 @@ struct LabelEndPoint: EndPoint { case create case edit case delete - case test } var scheme: String { switch self { default: - #warning("실제 서버는 http") - return "https" + return "http" } } var baseURL: String { switch self { default: - return "h3rb9c0ugl.execute-api.ap-northeast-2.amazonaws.com" + return "118.67.134.194" + } + } + + var port: Int { + switch self { + default: + return 3000 } } @@ -58,8 +63,6 @@ struct LabelEndPoint: EndPoint { return "/api/label/" + parameter case .delete: return "/api/label/" + parameter - case .test: - return "/develop/baminchan/" + parameter } } @@ -73,8 +76,18 @@ struct LabelEndPoint: EndPoint { return .patch case .delete: return .delete - case .test: - return .get + } + } + + var statusCode: Int { + switch requestType { + case .fetch, .edit: + return 200 + case .create: + return 201 + case .delete: + return 204 + } } } diff --git a/iOS/IssueTracker/IssueTracker/08.Endpoints/MilestoneEndPoint.swift b/iOS/IssueTracker/IssueTracker/08.Endpoints/MilestoneEndPoint.swift index f7b12e7..844e85c 100644 --- a/iOS/IssueTracker/IssueTracker/08.Endpoints/MilestoneEndPoint.swift +++ b/iOS/IssueTracker/IssueTracker/08.Endpoints/MilestoneEndPoint.swift @@ -30,36 +30,40 @@ struct MilestoneEndPoint: EndPoint { case create case edit case delete - case test } var scheme: String { switch self { default: - #warning("실제 서버는 http") - return "https" + return "http" } } var baseURL: String { switch self { default: - return "h3rb9c0ugl.execute-api.ap-northeast-2.amazonaws.com" + return "118.67.134.194" + } + } + + var port: Int { + switch self { + default: + return 3000 } } var path: String { switch requestType { case .fetch: - return "/api/milestones" + return "/api/milestone" case .create: return "/api/milestone" case .edit: return "/api/milestone/" + parameter case .delete: return "/api/milestone/" + parameter - case .test: - return "/develop/baminchan/" + parameter + } } @@ -73,8 +77,17 @@ struct MilestoneEndPoint: EndPoint { return .patch case .delete: return .delete - case .test: - return .get + } + } + + var statusCode: Int { + switch requestType { + case .fetch, .edit: + return 200 + case .create: + return 201 + case .delete: + return 204 } } } diff --git a/iOS/IssueTracker/IssueTracker/SceneDelegate.swift b/iOS/IssueTracker/IssueTracker/SceneDelegate.swift index 3ca2982..cccc3ae 100644 --- a/iOS/IssueTracker/IssueTracker/SceneDelegate.swift +++ b/iOS/IssueTracker/IssueTracker/SceneDelegate.swift @@ -9,23 +9,6 @@ import UIKit import NetworkFramework -struct FoodListResponse: Codable { - var statusCode: Int - var body: [StoreItem] = [StoreItem]() -} - -struct StoreItem: Codable { - var detail_hash: String - var image: String - var alt: String - var delivery_type: [String] - var title: String - var description: String - var n_price: String? - var s_price: String - var badge: [String]? -} - class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? @@ -36,14 +19,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { else { return } // TODO:- Network, Cache, AuthService 초기화 및 로그인 여부 검증 -> AppDelegate로?? mainTabVC.setupSubViewControllers() - - let milestoneEndPoint = MilestoneEndPoint(requestType: .test, parameter: "main") - let session = URLSession.init(configuration: .default, delegate: nil, delegateQueue: nil) - let dataLoader = DataLoader(session: session) - dataLoader.reqeust(endpoint: milestoneEndPoint) { (response) in - print(response!) - } - } func sceneDidDisconnect(_ scene: UIScene) { diff --git a/iOS/IssueTracker/NetworkFramework/DataLoader.swift b/iOS/IssueTracker/NetworkFramework/DataLoader.swift index 7e3fd20..b8caadb 100644 --- a/iOS/IssueTracker/NetworkFramework/DataLoader.swift +++ b/iOS/IssueTracker/NetworkFramework/DataLoader.swift @@ -9,16 +9,19 @@ import Foundation public class DataLoader { + public typealias RequestResult = Result + private let session: URLSession public init(session: URLSession) { self.session = session } - public func reqeust(endpoint: EndPoint, completion: @escaping (_ response: T?) -> Void) { + public func reqeust(endpoint: EndPoint, completion: @escaping (RequestResult) -> Void) { var components = URLComponents() components.scheme = endpoint.scheme components.host = endpoint.baseURL + components.port = endpoint.port components.path = endpoint.path guard let url = components.url else { return } @@ -26,12 +29,27 @@ public class DataLoader { var urlRequest = URLRequest(url: url) urlRequest.httpMethod = endpoint.method.rawValue urlRequest.httpBody = endpoint.httpBody + urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") session.dataTask(with: urlRequest) { (data, response, error) in + guard error == nil else { return } - if let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 { + guard let response = response as? HTTPURLResponse, response.statusCode == endpoint.statusCode else { + completion(.failure(.responseError("요청에 실패했습니다."))) + return + } + + if let data = data { do { - completion(try JSONDecoder().decode(T.self, from: data)) + // let dataString = String(data: data, encoding: .utf8) + // print("data string : \(dataString!)") + // print("response status code : \(response.statusCode)") + + if endpoint.method == .delete { + completion(.success(nil)) + } else { + completion(.success(try JSONDecoder().decode(T.self, from: data))) + } } catch { print(NetworkError.decodingError("\(T.Type.self)")) } @@ -76,9 +94,10 @@ extension String: URLConvertible { } } -enum NetworkError: Error { +public enum NetworkError: Error { case invalidURL(String) case decodingError(String) + case responseError(String) } public enum HTTPMethod: String { @@ -94,4 +113,6 @@ public protocol EndPoint { var path: String { get } var method: HTTPMethod { get } var httpBody: Data? { get } + var port: Int { get } + var statusCode: Int { get } }