diff --git a/koin.xcodeproj/project.pbxproj b/koin.xcodeproj/project.pbxproj index 29bf32683..cf61dc458 100644 --- a/koin.xcodeproj/project.pbxproj +++ b/koin.xcodeproj/project.pbxproj @@ -730,6 +730,12 @@ 83E96DB82BBC6FBF00164914 /* BusSearchDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E96DB72BBC6FBF00164914 /* BusSearchDto.swift */; }; 83EBB87A2D0FDFD700346018 /* KoinPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83EBB8792D0FDFD700346018 /* KoinPickerView.swift */; }; 83EBB87E2D0FE1BE00346018 /* FetchKoinPickerDateUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83EBB87D2D0FE1BE00346018 /* FetchKoinPickerDateUseCase.swift */; }; + AA20000000000000000001 /* PublisherTestSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA10000000000000000001 /* PublisherTestSupport.swift */; }; + AA20000000000000000002 /* DiningFixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA10000000000000000002 /* DiningFixtures.swift */; }; + AA20000000000000000003 /* SpyDiningRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA10000000000000000003 /* SpyDiningRepository.swift */; }; + AA20000000000000000004 /* DateProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA10000000000000000004 /* DateProviderTests.swift */; }; + AA20000000000000000005 /* FetchDiningListUseCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA10000000000000000005 /* FetchDiningListUseCaseTests.swift */; }; + AA20000000000000000006 /* ShareMenuListUseCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA10000000000000000006 /* ShareMenuListUseCaseTests.swift */; }; B438AEE42E756AB600E889C4 /* OrderImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B438AEE32E756AB600E889C4 /* OrderImage.swift */; }; B46B8CC22E76CB7300A8E797 /* ShopSummaryDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = B46B8CC12E76CB7300A8E797 /* ShopSummaryDto.swift */; }; B46B8CC42E76CC6300A8E797 /* FetchOrderShopSummaryFromShopUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = B46B8CC32E76CC6300A8E797 /* FetchOrderShopSummaryFromShopUseCase.swift */; }; @@ -975,8 +981,6 @@ D2FFA9872D63343600EF8E56 /* ReportLostItemUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FFA9862D63343600EF8E56 /* ReportLostItemUseCase.swift */; }; D2FFA98B2D63654A00EF8E56 /* FetchLostItemsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FFA98A2D63654A00EF8E56 /* FetchLostItemsRequest.swift */; }; D80AD4E42E6C6BC30061334B /* waveLogo.json in Resources */ = {isa = PBXBuildFile; fileRef = D80AD4E32E6C6BC30061334B /* waveLogo.json */; }; - D80AD4E52E6C6BC30061334B /* waveLogo.json in Resources */ = {isa = PBXBuildFile; fileRef = D80AD4E32E6C6BC30061334B /* waveLogo.json */; }; - D80AD4E62E6C6BC30061334B /* waveLogo.json in Resources */ = {isa = PBXBuildFile; fileRef = D80AD4E32E6C6BC30061334B /* waveLogo.json */; }; D837FB122E254BD8002BFB9F /* CustomSessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D837FB112E254BD8002BFB9F /* CustomSessionManager.swift */; }; D85939752DC12C9D00CE4CB2 /* CheckDuplicatedIdUsecase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85939742DC12C9D00CE4CB2 /* CheckDuplicatedIdUsecase.swift */; }; D85939772DC12D0900CE4CB2 /* CheckDuplicatedIdRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85939762DC12D0900CE4CB2 /* CheckDuplicatedIdRequest.swift */; }; @@ -997,8 +1001,6 @@ D8B4E0042DBFAD1A001FBC89 /* CheckVerificationCodeRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B4E0032DBFAD1A001FBC89 /* CheckVerificationCodeRequest.swift */; }; D8F5C54A2E6EF76800FB6708 /* OrderFloatingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F5C5492E6EF76800FB6708 /* OrderFloatingButton.swift */; }; D8F5C54C2E6F012500FB6708 /* LottieAnimationManageable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F5C54B2E6F012500FB6708 /* LottieAnimationManageable.swift */; }; - D8F5C5542E6F144300FB6708 /* floatingLogo.json in Resources */ = {isa = PBXBuildFile; fileRef = D8F5C5532E6F144300FB6708 /* floatingLogo.json */; }; - D8F5C5552E6F144300FB6708 /* floatingLogo.json in Resources */ = {isa = PBXBuildFile; fileRef = D8F5C5532E6F144300FB6708 /* floatingLogo.json */; }; D8F5C5562E6F144300FB6708 /* floatingLogo.json in Resources */ = {isa = PBXBuildFile; fileRef = D8F5C5532E6F144300FB6708 /* floatingLogo.json */; }; F1A000010000000000000001 /* ZoomingCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1A000010000000000000002 /* ZoomingCollectionView.swift */; }; F1A000010000000000000003 /* ZoomingCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1A000010000000000000004 /* ZoomingCollectionViewCell.swift */; }; @@ -1012,14 +1014,7 @@ remoteGlobalIDString = 839D71152BEB1570001BC7F7; remoteInfo = NotificationService; }; - A001E2C52845091F00D6C310 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = A001E2AC2845091D00D6C310 /* Project object */; - proxyType = 1; - remoteGlobalIDString = A001E2B32845091D00D6C310; - remoteInfo = koin; - }; - A001E2CF2845091F00D6C310 /* PBXContainerItemProxy */ = { + EC692289302700A400EE26ED /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = A001E2AC2845091D00D6C310 /* Project object */; proxyType = 1; @@ -1749,10 +1744,12 @@ 83EBB8792D0FDFD700346018 /* KoinPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KoinPickerView.swift; sourceTree = ""; }; 83EBB87D2D0FE1BE00346018 /* FetchKoinPickerDateUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchKoinPickerDateUseCase.swift; sourceTree = ""; }; A001E2B42845091D00D6C310 /* koin.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = koin.app; sourceTree = BUILT_PRODUCTS_DIR; }; - A001E2C42845091F00D6C310 /* koinTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = koinTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - A001E2CE2845091F00D6C310 /* koinUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = koinUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - A001E2D22845091F00D6C310 /* koinUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = koinUITests.swift; sourceTree = ""; }; - A001E2D42845091F00D6C310 /* koinUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = koinUITestsLaunchTests.swift; sourceTree = ""; }; + AA10000000000000000001 /* PublisherTestSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublisherTestSupport.swift; sourceTree = ""; }; + AA10000000000000000002 /* DiningFixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiningFixtures.swift; sourceTree = ""; }; + AA10000000000000000003 /* SpyDiningRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpyDiningRepository.swift; sourceTree = ""; }; + AA10000000000000000004 /* DateProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateProviderTests.swift; sourceTree = ""; }; + AA10000000000000000005 /* FetchDiningListUseCaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchDiningListUseCaseTests.swift; sourceTree = ""; }; + AA10000000000000000006 /* ShareMenuListUseCaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareMenuListUseCaseTests.swift; sourceTree = ""; }; B438AEE32E756AB600E889C4 /* OrderImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderImage.swift; sourceTree = ""; }; B46B8CC12E76CB7300A8E797 /* ShopSummaryDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShopSummaryDto.swift; sourceTree = ""; }; B46B8CC32E76CC6300A8E797 /* FetchOrderShopSummaryFromShopUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchOrderShopSummaryFromShopUseCase.swift; sourceTree = ""; }; @@ -2023,6 +2020,7 @@ D8F5C5492E6EF76800FB6708 /* OrderFloatingButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderFloatingButton.swift; sourceTree = ""; }; D8F5C54B2E6F012500FB6708 /* LottieAnimationManageable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LottieAnimationManageable.swift; sourceTree = ""; }; D8F5C5532E6F144300FB6708 /* floatingLogo.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = floatingLogo.json; sourceTree = ""; }; + EC692285302700A400EE26ED /* koinUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = koinUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F1A000010000000000000002 /* ZoomingCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZoomingCollectionView.swift; sourceTree = ""; }; F1A000010000000000000004 /* ZoomingCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZoomingCollectionViewCell.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -2058,14 +2056,7 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - A001E2C12845091F00D6C310 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A001E2CB2845091F00D6C310 /* Frameworks */ = { + EC692282302700A400EE26ED /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( @@ -4545,9 +4536,9 @@ D27DCD682BA5BADC0081FD36 /* Info.plist */, D2D380E02BA6AC50009AD5A9 /* koin.entitlements */, D27DD0202BA608310081FD36 /* Koin */, - A001E2D12845091F00D6C310 /* koinUITests */, 839D71172BEB1570001BC7F7 /* NotificationService */, 7C7F56B52FFB9F4A00847151 /* Common */, + EC69228F302700E800EE26ED /* koinUnitTests */, A001E2B52845091D00D6C310 /* Products */, ); sourceTree = ""; @@ -4556,20 +4547,37 @@ isa = PBXGroup; children = ( A001E2B42845091D00D6C310 /* koin.app */, - A001E2C42845091F00D6C310 /* koinTests.xctest */, - A001E2CE2845091F00D6C310 /* koinUITests.xctest */, 839D71162BEB1570001BC7F7 /* NotificationService.appex */, + EC692285302700A400EE26ED /* koinUnitTests.xctest */, ); name = Products; sourceTree = ""; }; - A001E2D12845091F00D6C310 /* koinUITests */ = { + AA30000000000000000001 /* Support */ = { isa = PBXGroup; children = ( - A001E2D22845091F00D6C310 /* koinUITests.swift */, - A001E2D42845091F00D6C310 /* koinUITestsLaunchTests.swift */, + AA10000000000000000001 /* PublisherTestSupport.swift */, + AA10000000000000000002 /* DiningFixtures.swift */, ); - path = koinUITests; + path = Support; + sourceTree = ""; + }; + AA30000000000000000002 /* Doubles */ = { + isa = PBXGroup; + children = ( + AA10000000000000000003 /* SpyDiningRepository.swift */, + ); + path = Doubles; + sourceTree = ""; + }; + AA30000000000000000003 /* Dining */ = { + isa = PBXGroup; + children = ( + AA10000000000000000004 /* DateProviderTests.swift */, + AA10000000000000000005 /* FetchDiningListUseCaseTests.swift */, + AA10000000000000000006 /* ShareMenuListUseCaseTests.swift */, + ); + path = Dining; sourceTree = ""; }; B46B8D242E770A3100A8E797 /* Fonts */ = { @@ -5480,6 +5488,16 @@ path = Dining; sourceTree = ""; }; + EC69228F302700E800EE26ED /* koinUnitTests */ = { + isa = PBXGroup; + children = ( + AA30000000000000000001 /* Support */, + AA30000000000000000002 /* Doubles */, + AA30000000000000000003 /* Dining */, + ); + path = koinUnitTests; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -5520,41 +5538,25 @@ productReference = A001E2B42845091D00D6C310 /* koin.app */; productType = "com.apple.product-type.application"; }; - A001E2C32845091F00D6C310 /* koinTests */ = { + EC692284302700A400EE26ED /* koinUnitTests */ = { isa = PBXNativeTarget; - buildConfigurationList = A001E2DB2845091F00D6C310 /* Build configuration list for PBXNativeTarget "koinTests" */; + buildConfigurationList = EC69228D302700A400EE26ED /* Build configuration list for PBXNativeTarget "koinUnitTests" */; buildPhases = ( - A001E2C02845091F00D6C310 /* Sources */, - A001E2C12845091F00D6C310 /* Frameworks */, - A001E2C22845091F00D6C310 /* Resources */, + EC692281302700A400EE26ED /* Sources */, + EC692282302700A400EE26ED /* Frameworks */, + EC692283302700A400EE26ED /* Resources */, ); buildRules = ( ); dependencies = ( - A001E2C62845091F00D6C310 /* PBXTargetDependency */, + EC69228A302700A400EE26ED /* PBXTargetDependency */, ); - name = koinTests; - productName = koinTests; - productReference = A001E2C42845091F00D6C310 /* koinTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - A001E2CD2845091F00D6C310 /* koinUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = A001E2DE2845091F00D6C310 /* Build configuration list for PBXNativeTarget "koinUITests" */; - buildPhases = ( - A001E2CA2845091F00D6C310 /* Sources */, - A001E2CB2845091F00D6C310 /* Frameworks */, - A001E2CC2845091F00D6C310 /* Resources */, - ); - buildRules = ( + name = koinUnitTests; + packageProductDependencies = ( ); - dependencies = ( - A001E2D02845091F00D6C310 /* PBXTargetDependency */, - ); - name = koinUITests; - productName = koinUITests; - productReference = A001E2CE2845091F00D6C310 /* koinUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; + productName = koinUnitTests; + productReference = EC692285302700A400EE26ED /* koinUnitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ @@ -5563,7 +5565,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1530; + LastSwiftUpdateCheck = 2640; LastUpgradeCheck = 1530; TargetAttributes = { 839D71152BEB1570001BC7F7 = { @@ -5572,12 +5574,8 @@ A001E2B32845091D00D6C310 = { CreatedOnToolsVersion = 13.3.1; }; - A001E2C32845091F00D6C310 = { - CreatedOnToolsVersion = 13.3.1; - TestTargetID = A001E2B32845091D00D6C310; - }; - A001E2CD2845091F00D6C310 = { - CreatedOnToolsVersion = 13.3.1; + EC692284302700A400EE26ED = { + CreatedOnToolsVersion = 26.4.1; TestTargetID = A001E2B32845091D00D6C310; }; }; @@ -5611,9 +5609,8 @@ projectRoot = ""; targets = ( A001E2B32845091D00D6C310 /* koin */, - A001E2C32845091F00D6C310 /* koinTests */, - A001E2CD2845091F00D6C310 /* koinUITests */, 839D71152BEB1570001BC7F7 /* NotificationService */, + EC692284302700A400EE26ED /* koinUnitTests */, ); }; /* End PBXProject section */ @@ -5648,21 +5645,10 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - A001E2C22845091F00D6C310 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D80AD4E52E6C6BC30061334B /* waveLogo.json in Resources */, - D8F5C5552E6F144300FB6708 /* floatingLogo.json in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A001E2CC2845091F00D6C310 /* Resources */ = { + EC692283302700A400EE26ED /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - D80AD4E62E6C6BC30061334B /* waveLogo.json in Resources */, - D8F5C5542E6F144300FB6708 /* floatingLogo.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -6664,17 +6650,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - A001E2C02845091F00D6C310 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A001E2CA2845091F00D6C310 /* Sources */ = { + EC692281302700A400EE26ED /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + AA20000000000000000001 /* PublisherTestSupport.swift in Sources */, + AA20000000000000000002 /* DiningFixtures.swift in Sources */, + AA20000000000000000003 /* SpyDiningRepository.swift in Sources */, + AA20000000000000000004 /* DateProviderTests.swift in Sources */, + AA20000000000000000005 /* FetchDiningListUseCaseTests.swift in Sources */, + AA20000000000000000006 /* ShareMenuListUseCaseTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -6686,15 +6671,10 @@ target = 839D71152BEB1570001BC7F7 /* NotificationService */; targetProxy = 839D711B2BEB1570001BC7F7 /* PBXContainerItemProxy */; }; - A001E2C62845091F00D6C310 /* PBXTargetDependency */ = { + EC69228A302700A400EE26ED /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = A001E2B32845091D00D6C310 /* koin */; - targetProxy = A001E2C52845091F00D6C310 /* PBXContainerItemProxy */; - }; - A001E2D02845091F00D6C310 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = A001E2B32845091D00D6C310 /* koin */; - targetProxy = A001E2CF2845091F00D6C310 /* PBXContainerItemProxy */; + targetProxy = EC692289302700A400EE26ED /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -6954,79 +6934,56 @@ }; name = Release; }; - A001E2DC2845091F00D6C310 /* Debug */ = { + EC69228B302700A400EE26ED /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = K626UYGR25; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_CFBundleDisplayName = "코인(stage)"; - IPHONEOS_DEPLOYMENT_TARGET = 15.4; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.bcsdlab.koinTests; + PRODUCT_BUNDLE_IDENTIFIER = com.bcsdlab.koinUnitTests; PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/koin.app/koin"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/koin.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/koin"; }; name = Debug; }; - A001E2DD2845091F00D6C310 /* Release */ = { + EC69228C302700A400EE26ED /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = K626UYGR25; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_CFBundleDisplayName = "코인(stage)"; - IPHONEOS_DEPLOYMENT_TARGET = 15.4; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.bcsdlab.koinTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/koin.app/koin"; - }; - name = Release; - }; - A001E2DF2845091F00D6C310 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = K626UYGR25; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_CFBundleDisplayName = "코인(stage)"; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.bcsdlab.koinUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = koin; - }; - name = Debug; - }; - A001E2E02845091F00D6C310 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = K626UYGR25; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_CFBundleDisplayName = "코인(stage)"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.bcsdlab.koinUITests; + PRODUCT_BUNDLE_IDENTIFIER = com.bcsdlab.koinUnitTests; PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = koin; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/koin.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/koin"; }; name = Release; }; @@ -7060,20 +7017,11 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - A001E2DB2845091F00D6C310 /* Build configuration list for PBXNativeTarget "koinTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - A001E2DC2845091F00D6C310 /* Debug */, - A001E2DD2845091F00D6C310 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - A001E2DE2845091F00D6C310 /* Build configuration list for PBXNativeTarget "koinUITests" */ = { + EC69228D302700A400EE26ED /* Build configuration list for PBXNativeTarget "koinUnitTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - A001E2DF2845091F00D6C310 /* Debug */, - A001E2E02845091F00D6C310 /* Release */, + EC69228B302700A400EE26ED /* Debug */, + EC69228C302700A400EE26ED /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/koin.xcodeproj/xcshareddata/xcschemes/NotificationService.xcscheme b/koin.xcodeproj/xcshareddata/xcschemes/NotificationService.xcscheme index 25a323533..023295073 100644 --- a/koin.xcodeproj/xcshareddata/xcschemes/NotificationService.xcscheme +++ b/koin.xcodeproj/xcshareddata/xcschemes/NotificationService.xcscheme @@ -44,6 +44,19 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES" shouldAutocreateTestPlan = "YES"> + + + + + + + + + + + + + skipped = "NO" + parallelizable = "YES"> - - - - diff --git a/koinUITests/koinUITests.swift b/koinUITests/koinUITests.swift deleted file mode 100644 index 36c80c9c3..000000000 --- a/koinUITests/koinUITests.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// koinUITests.swift -// koinUITests -// -// Created by 정태훈 on 2022/05/30. -// - -import XCTest - -class koinUITests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // UI tests must launch the application that they test. - let app = XCUIApplication() - app.launch() - - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testLaunchPerformance() throws { - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { - // This measures how long it takes to launch your application. - measure(metrics: [XCTApplicationLaunchMetric()]) { - XCUIApplication().launch() - } - } - } -} diff --git a/koinUITests/koinUITestsLaunchTests.swift b/koinUITests/koinUITestsLaunchTests.swift deleted file mode 100644 index b99bf8c12..000000000 --- a/koinUITests/koinUITestsLaunchTests.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// koinUITestsLaunchTests.swift -// koinUITests -// -// Created by 정태훈 on 2022/05/30. -// - -import XCTest - -class koinUITestsLaunchTests: XCTestCase { - - override class var runsForEachTargetApplicationUIConfiguration: Bool { - true - } - - override func setUpWithError() throws { - continueAfterFailure = false - } - - func testLaunch() throws { - let app = XCUIApplication() - app.launch() - - // Insert steps here to perform after app launch but before taking a screenshot, - // such as logging into a test account or navigating somewhere in the app - - let attachment = XCTAttachment(screenshot: app.screenshot()) - attachment.name = "Launch Screen" - attachment.lifetime = .keepAlways - add(attachment) - } -} diff --git a/koinUnitTests/Dining/DateProviderTests.swift b/koinUnitTests/Dining/DateProviderTests.swift new file mode 100644 index 000000000..6c8a3989a --- /dev/null +++ b/koinUnitTests/Dining/DateProviderTests.swift @@ -0,0 +1,80 @@ +// +// DateProviderTests.swift +// koinUnitTests +// +// Created by 이은지 on 8/8/26. +// + +import Foundation +import Testing +@testable import koin + +@Suite("DateProvider - 시간대 판단 로직 경계값") +struct DateProviderTests { + + private let sut = DefaultDateProvider() + private let calendar = Calendar.current + + private func date(hour: Int, minute: Int) throws -> Date { + try #require(DiningFixture.date(hour: hour, minute: minute, calendar: calendar)) + } + + @Test("09:00 이전이면 breakfast를 반환한다") + func 아홉시_이전이면_breakfast를_반환한다() throws { + let input = try date(hour: 8, minute: 59) + + let result = sut.execute(date: input) + + #expect(result.diningType == .breakfast) + #expect(calendar.isDate(result.date, inSameDayAs: input)) + } + + @Test("09:00이면 lunch를 반환한다") + func 아홉시면_lunch를_반환한다() throws { + let input = try date(hour: 9, minute: 0) + + let result = sut.execute(date: input) + + #expect(result.diningType == .lunch) + #expect(calendar.isDate(result.date, inSameDayAs: input)) + } + + @Test("13:30이면 lunch를 반환한다") + func 열세시_삼십분이면_lunch를_반환한다() throws { + let input = try date(hour: 13, minute: 30) + + let result = sut.execute(date: input) + + #expect(result.diningType == .lunch) + } + + @Test("13:31이면 dinner를 반환한다") + func 열세시_삼십일분이면_dinner를_반환한다() throws { + let input = try date(hour: 13, minute: 31) + + let result = sut.execute(date: input) + + #expect(result.diningType == .dinner) + #expect(calendar.isDate(result.date, inSameDayAs: input)) + } + + @Test("18:30이면 dinner를 반환한다") + func 열여덟시_삼십분이면_dinner를_반환한다() throws { + let input = try date(hour: 18, minute: 30) + + let result = sut.execute(date: input) + + #expect(result.diningType == .dinner) + } + + @Test("18:30 이후면 다음 날 breakfast를 반환한다", arguments: [(18, 31), (23, 59)]) + func 열여덟시_삼십분_이후면_다음_날_breakfast를_반환한다(hour: Int, minute: Int) throws { + let input = try date(hour: hour, minute: minute) + let expectedNextDay = try #require(calendar.date(byAdding: .day, value: 1, to: input)) + + let result = sut.execute(date: input) + + #expect(result.diningType == .breakfast) + #expect(calendar.isDate(result.date, inSameDayAs: expectedNextDay)) + } +} diff --git a/koinUnitTests/Dining/FetchDiningListUseCaseTests.swift b/koinUnitTests/Dining/FetchDiningListUseCaseTests.swift new file mode 100644 index 000000000..68dc75446 --- /dev/null +++ b/koinUnitTests/Dining/FetchDiningListUseCaseTests.swift @@ -0,0 +1,88 @@ +// +// FetchDiningListUseCaseTests.swift +// koinUnitTests +// +// Created by 이은지 on 8/8/26. +// + +import Foundation +import Testing +@testable import koin + +@Suite("FetchDiningListUseCase - segmentControl에 따른 데이터 필터링") +struct FetchDiningListUseCaseTests { + + private func makeSUT( + stubbedDiningList: [DiningDto] + ) -> (sut: DefaultFetchDiningListUseCase, spy: SpyDiningRepository) { + let spy = SpyDiningRepository() + spy.stubbedDiningList = stubbedDiningList + return (DefaultFetchDiningListUseCase(diningRepository: spy), spy) + } + + private func diningInfo( + type: DiningType, + date: Date = Date() + ) -> CurrentDiningTime { + CurrentDiningTime(date: date, diningType: type) + } + + @Test("요청한 시간대의 식단만 반환한다") + func 요청한_시간대의_식단만_반환한다() async throws { + let (sut, _) = makeSUT(stubbedDiningList: [ + DiningFixture.dto(id: 1, type: .breakfast, place: .cornerA), + DiningFixture.dto(id: 2, type: .lunch, place: .cornerB), + DiningFixture.dto(id: 3, type: .dinner, place: .cornerC), + DiningFixture.dto(id: 4, type: .lunch, place: .special) + ]) + + let result = try await sut.execute(diningInfo: diningInfo(type: .lunch)).firstValue() + + #expect(result.count == 2) + #expect(result.allSatisfy { $0.type == .lunch }) + #expect(result.map(\.id).sorted() == [2, 4]) + } + + @Test( + "미운영 메뉴는 제외한다", + arguments: [DiningType.breakfast, .lunch, .dinner] + ) + func 미운영_메뉴는_제외한다(requestedType: DiningType) async throws { + let (sut, _) = makeSUT(stubbedDiningList: [ + DiningFixture.dto(id: 1, type: requestedType, place: .cornerA, menu: ["미운영"]), + DiningFixture.dto(id: 2, type: requestedType, place: .cornerB, menu: ["김치찌개", "밥"]) + ]) + + let result = try await sut.execute(diningInfo: diningInfo(type: requestedType)).firstValue() + + #expect(result.count == 1) + #expect(result.first?.id == 2) + #expect(!result.contains { $0.menu.first == "미운영" }) + } + + @Test("장소 우선순위대로 정렬한다") + func 장소_우선순위대로_정렬한다() async throws { + let (sut, _) = makeSUT(stubbedDiningList: [ + DiningFixture.dto(id: 1, type: .lunch, place: .secondCampus), + DiningFixture.dto(id: 2, type: .lunch, place: .special), + DiningFixture.dto(id: 3, type: .lunch, place: .cornerC), + DiningFixture.dto(id: 4, type: .lunch, place: .cornerA), + DiningFixture.dto(id: 5, type: .lunch, place: .cornerB) + ]) + + let result = try await sut.execute(diningInfo: diningInfo(type: .lunch)).firstValue() + + #expect(result.map(\.place) == [.cornerA, .cornerB, .cornerC, .special, .secondCampus]) + } + + @Test("요청 날짜를 yyMMdd 형식으로 전달한다") + func 요청_날짜를_yyMMdd_형식으로_전달한다() async throws { + let (sut, spy) = makeSUT(stubbedDiningList: []) + let requestedDate = try #require(DiningFixture.date(year: 2026, month: 8, day: 3)) + + _ = try await sut.execute(diningInfo: diningInfo(type: .lunch, date: requestedDate)).firstValue() + + #expect(spy.fetchDiningListCallCount == 1) + #expect(spy.receivedFetchRequests.first?.date == "260803") + } +} diff --git a/koinUnitTests/Dining/ShareMenuListUseCaseTests.swift b/koinUnitTests/Dining/ShareMenuListUseCaseTests.swift new file mode 100644 index 000000000..528b5c062 --- /dev/null +++ b/koinUnitTests/Dining/ShareMenuListUseCaseTests.swift @@ -0,0 +1,82 @@ +// +// ShareMenuListUseCaseTests.swift +// koinUnitTests +// +// Created by 이은지 on 8/8/26. +// + +import Foundation +import Testing +@testable import koin + +@Suite("ShareMenuListUseCase - 카카오톡 식단 공유하기") +struct ShareMenuListUseCaseTests { + + @Test("DiningItem을 ShareDiningMenu로 변환하면 메뉴와 이미지를 유지한다") + func DiningItem을_ShareDiningMenu로_변환하면_메뉴와_이미지를_유지한다() { + let item = DiningFixture.item( + type: .lunch, + place: .cornerA, + menu: ["김치찌개", "밥"], + imageUrl: "url1" + ) + + let shareModel = item.toShareDiningItem() + + #expect(shareModel.menuList == ["김치찌개", "밥"]) + #expect(shareModel.imageUrl == "url1") + #expect(shareModel.type == .lunch) + #expect(shareModel.place == .cornerA) + } + + @Test("날짜를 yyMMdd 형식으로 변환한다") + func 날짜를_yyMMdd_형식으로_변환한다() { + let item = DiningFixture.item(date: "2026-08-03") + + let shareModel = item.toShareDiningItem() + + #expect(shareModel.date == "260803") + } + + @Test( + "날짜 변환에 실패하면 원본 문자열을 사용한다", + arguments: ["", "날짜없음", "2026-13-45"] + ) + func 날짜_변환에_실패하면_원본_문자열을_사용한다(invalidDate: String) { + let item = DiningFixture.item(date: invalidDate) + + let shareModel = item.toShareDiningItem() + + #expect(shareModel.date == invalidDate) + } + + @Test("구분자가 달라도 파싱에 성공하면 yyMMdd로 변환된다") + func 구분자가_달라도_파싱에_성공하면_yyMMdd로_변환된다() { + let item = DiningFixture.item(date: "2026/08/03") + + let shareModel = item.toShareDiningItem() + + #expect(shareModel.date == "260803") + } + + @Test("공유 모델을 레포지토리에 전달한다") + func 공유_모델을_레포지토리에_전달한다() throws { + let spy = SpyDiningRepository() + let sut = DefaultShareMenuListUseCase(diningRepository: spy) + let shareModel = DiningFixture.item( + date: "2026-08-03", + place: .cornerB, + menu: ["돈까스"], + imageUrl: "url2" + ).toShareDiningItem() + + sut.execute(shareModel: shareModel) + + #expect(spy.receivedShareModels.count == 1) + let received = try #require(spy.receivedShareModels.first) + #expect(received.menuList == shareModel.menuList) + #expect(received.imageUrl == shareModel.imageUrl) + #expect(received.date == shareModel.date) + #expect(received.place == shareModel.place) + } +} diff --git a/koinUnitTests/Doubles/SpyDiningRepository.swift b/koinUnitTests/Doubles/SpyDiningRepository.swift new file mode 100644 index 000000000..fff72f6d0 --- /dev/null +++ b/koinUnitTests/Doubles/SpyDiningRepository.swift @@ -0,0 +1,37 @@ +// +// SpyDiningRepository.swift +// koinUnitTests +// +// Created by 이은지 on 8/8/26. +// + +import Combine +import Foundation +@testable import koin + +/// 호출 여부와 전달된 인자를 기록만 하고, 검증은 테스트 코드에 맡기는 테스트 더블. +final class SpyDiningRepository: DiningRepository { + + /// `fetchDiningList`가 돌려줄 응답. 테스트에서 시나리오별로 갈아끼운다. + var stubbedDiningList: [DiningDto] = [] + + private(set) var fetchDiningListCallCount = 0 + private(set) var receivedFetchRequests: [FetchDiningListRequest] = [] + private(set) var receivedShareModels: [ShareDiningMenu] = [] + + func fetchDiningList(requestModel: FetchDiningListRequest) -> AnyPublisher<[DiningDto], ErrorResponse> { + fetchDiningListCallCount += 1 + receivedFetchRequests.append(requestModel) + return Just(stubbedDiningList) + .setFailureType(to: ErrorResponse.self) + .eraseToAnyPublisher() + } + + func fetchCoopShopList() -> AnyPublisher { + Empty().eraseToAnyPublisher() + } + + func shareMenuList(shareModel: ShareDiningMenu) { + receivedShareModels.append(shareModel) + } +} diff --git a/koinUnitTests/Support/DiningFixtures.swift b/koinUnitTests/Support/DiningFixtures.swift new file mode 100644 index 000000000..0ceb71d94 --- /dev/null +++ b/koinUnitTests/Support/DiningFixtures.swift @@ -0,0 +1,83 @@ +// +// DiningFixture.swift +// koinUnitTests +// +// Created by 이은지 on 8/8/26. +// + +import Foundation +@testable import koin + +enum DiningFixture { + + static func dto( + id: Int = 1, + date: String = "2026-08-03", + type: DiningType = .lunch, + place: DiningPlace = .cornerA, + menu: [String]? = ["김치찌개", "밥"], + imageURL: String? = nil + ) -> DiningDto { + DiningDto( + id: id, + date: date, + type: type, + place: place, + priceCard: nil, + priceCash: nil, + kcal: nil, + menu: menu, + createdAt: "", + updatedAt: "", + soldoutAt: nil, + changedAt: nil, + imageURL: imageURL, + likes: 0, + isLiked: false + ) + } + + static func item( + id: Int = 1, + date: String = "2026-08-03", + type: DiningType = .lunch, + place: DiningPlace = .cornerA, + menu: [String] = ["김치찌개", "밥"], + imageUrl: String? = nil + ) -> DiningItem { + DiningItem( + id: id, + type: type, + place: place, + priceCard: nil, + priceCash: nil, + kcal: 0, + menu: menu, + soldoutAt: nil, + changedAt: nil, + imageUrl: imageUrl, + likes: 0, + isLiked: false, + date: date + ) + } + + /// 프로덕션 코드(`DefaultDateProvider`, `Date.formatDateToYYMMDD()`)가 `Calendar.current`와 + /// 기본 `DateFormatter`를 쓰기 때문에, 테스트도 같은 기준으로 Date를 만든다. + static func date( + year: Int = 2026, + month: Int = 8, + day: Int = 3, + hour: Int = 0, + minute: Int = 0, + calendar: Calendar = .current + ) -> Date? { + var components = DateComponents() + components.year = year + components.month = month + components.day = day + components.hour = hour + components.minute = minute + return calendar.date(from: components) + } +} diff --git a/koinUnitTests/Support/PublisherTestSupport.swift b/koinUnitTests/Support/PublisherTestSupport.swift new file mode 100644 index 000000000..8dceeb16e --- /dev/null +++ b/koinUnitTests/Support/PublisherTestSupport.swift @@ -0,0 +1,26 @@ +// +// PublisherTestError.swift +// koinUnitTests +// +// Created by 이은지 on 8/8/26. +// + +import Combine +import Foundation + +enum PublisherTestError: Error { + case finishedWithoutValue +} + +extension Publisher { + /// Combine 파이프라인이 방출하는 첫 번째 값을 async로 받아온다. + /// + /// 프로덕션 코드가 `AnyPublisher`를 반환하기 때문에, Swift Testing의 `async` 테스트에서 + /// `await`로 결과를 기다리기 위해 `Publisher.values`(AsyncSequence)를 경유한다. + func firstValue() async throws -> Output { + for try await value in values { + return value + } + throw PublisherTestError.finishedWithoutValue + } +}