diff --git a/final/GitHubSearch-final.xcodeproj/project.pbxproj b/final/GitHubSearch-final.xcodeproj/project.pbxproj new file mode 100644 index 0000000..7e62b0d --- /dev/null +++ b/final/GitHubSearch-final.xcodeproj/project.pbxproj @@ -0,0 +1,548 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + EC4BC239293463F4001B2F91 /* APIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC4BC238293463F4001B2F91 /* APIError.swift */; }; + ECC8142829334ADF0038A09F /* GitHubSearchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECC8142729334ADF0038A09F /* GitHubSearchApp.swift */; }; + ECC8142C29334AE00038A09F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ECC8142B29334AE00038A09F /* Assets.xcassets */; }; + ECC8142F29334AE00038A09F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ECC8142E29334AE00038A09F /* Preview Assets.xcassets */; }; + ECC8143929334AE00038A09F /* RepositoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECC8143829334AE00038A09F /* RepositoryTests.swift */; }; + ECC8145429334C590038A09F /* ComposableArchitecture in Frameworks */ = {isa = PBXBuildFile; productRef = ECC8145329334C590038A09F /* ComposableArchitecture */; }; + ECC8145629334C590038A09F /* Dependencies in Frameworks */ = {isa = PBXBuildFile; productRef = ECC8145529334C590038A09F /* Dependencies */; }; + ECC8145C29334CB70038A09F /* RepoSearchClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECC8145829334CB70038A09F /* RepoSearchClient.swift */; }; + ECC8145D29334CB70038A09F /* RepoSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECC8145929334CB70038A09F /* RepoSearch.swift */; }; + ECC8145E29334CB70038A09F /* RepositoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECC8145A29334CB70038A09F /* RepositoryModel.swift */; }; + ECC8145F29334CB70038A09F /* RepoSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECC8145B29334CB70038A09F /* RepoSearchView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + ECC8143529334AE00038A09F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = ECC8141C29334ADF0038A09F /* Project object */; + proxyType = 1; + remoteGlobalIDString = ECC8142329334ADF0038A09F; + remoteInfo = GitHubSearch; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + EC4BC238293463F4001B2F91 /* APIError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIError.swift; sourceTree = ""; }; + ECC8142429334ADF0038A09F /* GitHubSearch-final.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "GitHubSearch-final.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + ECC8142729334ADF0038A09F /* GitHubSearchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitHubSearchApp.swift; sourceTree = ""; }; + ECC8142B29334AE00038A09F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + ECC8142E29334AE00038A09F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + ECC8143429334AE00038A09F /* GitHubSearch-finalTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "GitHubSearch-finalTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + ECC8143829334AE00038A09F /* RepositoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepositoryTests.swift; sourceTree = ""; }; + ECC8145829334CB70038A09F /* RepoSearchClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepoSearchClient.swift; sourceTree = ""; }; + ECC8145929334CB70038A09F /* RepoSearch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepoSearch.swift; sourceTree = ""; }; + ECC8145A29334CB70038A09F /* RepositoryModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepositoryModel.swift; sourceTree = ""; }; + ECC8145B29334CB70038A09F /* RepoSearchView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepoSearchView.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + ECC8142129334ADF0038A09F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ECC8145429334C590038A09F /* ComposableArchitecture in Frameworks */, + ECC8145629334C590038A09F /* Dependencies in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + ECC8143129334AE00038A09F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + ECC8141B29334ADF0038A09F = { + isa = PBXGroup; + children = ( + ECC8142629334ADF0038A09F /* GitHubSearch */, + ECC8143729334AE00038A09F /* GitHubSearchTests */, + ECC8142529334ADF0038A09F /* Products */, + ); + sourceTree = ""; + }; + ECC8142529334ADF0038A09F /* Products */ = { + isa = PBXGroup; + children = ( + ECC8142429334ADF0038A09F /* GitHubSearch-final.app */, + ECC8143429334AE00038A09F /* GitHubSearch-finalTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + ECC8142629334ADF0038A09F /* GitHubSearch */ = { + isa = PBXGroup; + children = ( + ECC8142729334ADF0038A09F /* GitHubSearchApp.swift */, + EC4BC238293463F4001B2F91 /* APIError.swift */, + ECC8145729334C6B0038A09F /* Repository */, + ECC8145129334AFC0038A09F /* Resources */, + ); + path = GitHubSearch; + sourceTree = ""; + }; + ECC8142D29334AE00038A09F /* Preview Content */ = { + isa = PBXGroup; + children = ( + ECC8142E29334AE00038A09F /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + ECC8143729334AE00038A09F /* GitHubSearchTests */ = { + isa = PBXGroup; + children = ( + ECC8143829334AE00038A09F /* RepositoryTests.swift */, + ); + path = GitHubSearchTests; + sourceTree = ""; + }; + ECC8145129334AFC0038A09F /* Resources */ = { + isa = PBXGroup; + children = ( + ECC8142D29334AE00038A09F /* Preview Content */, + ECC8142B29334AE00038A09F /* Assets.xcassets */, + ); + path = Resources; + sourceTree = ""; + }; + ECC8145729334C6B0038A09F /* Repository */ = { + isa = PBXGroup; + children = ( + ECC8145B29334CB70038A09F /* RepoSearchView.swift */, + ECC8145929334CB70038A09F /* RepoSearch.swift */, + ECC8145829334CB70038A09F /* RepoSearchClient.swift */, + ECC8145A29334CB70038A09F /* RepositoryModel.swift */, + ); + path = Repository; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + ECC8142329334ADF0038A09F /* GitHubSearch-final */ = { + isa = PBXNativeTarget; + buildConfigurationList = ECC8144829334AE00038A09F /* Build configuration list for PBXNativeTarget "GitHubSearch-final" */; + buildPhases = ( + ECC8142029334ADF0038A09F /* Sources */, + ECC8142129334ADF0038A09F /* Frameworks */, + ECC8142229334ADF0038A09F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "GitHubSearch-final"; + packageProductDependencies = ( + ECC8145329334C590038A09F /* ComposableArchitecture */, + ECC8145529334C590038A09F /* Dependencies */, + ); + productName = GitHubSearch; + productReference = ECC8142429334ADF0038A09F /* GitHubSearch-final.app */; + productType = "com.apple.product-type.application"; + }; + ECC8143329334AE00038A09F /* GitHubSearch-finalTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = ECC8144B29334AE00038A09F /* Build configuration list for PBXNativeTarget "GitHubSearch-finalTests" */; + buildPhases = ( + ECC8143029334AE00038A09F /* Sources */, + ECC8143129334AE00038A09F /* Frameworks */, + ECC8143229334AE00038A09F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ECC8143629334AE00038A09F /* PBXTargetDependency */, + ); + name = "GitHubSearch-finalTests"; + productName = GitHubSearchTests; + productReference = ECC8143429334AE00038A09F /* GitHubSearch-finalTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + ECC8141C29334ADF0038A09F /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1410; + LastUpgradeCheck = 1410; + TargetAttributes = { + ECC8142329334ADF0038A09F = { + CreatedOnToolsVersion = 14.1; + }; + ECC8143329334AE00038A09F = { + CreatedOnToolsVersion = 14.1; + TestTargetID = ECC8142329334ADF0038A09F; + }; + }; + }; + buildConfigurationList = ECC8141F29334ADF0038A09F /* Build configuration list for PBXProject "GitHubSearch-final" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = ECC8141B29334ADF0038A09F; + packageReferences = ( + ECC8145229334C590038A09F /* XCRemoteSwiftPackageReference "swift-composable-architecture" */, + ); + productRefGroup = ECC8142529334ADF0038A09F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + ECC8142329334ADF0038A09F /* GitHubSearch-final */, + ECC8143329334AE00038A09F /* GitHubSearch-finalTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + ECC8142229334ADF0038A09F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ECC8142F29334AE00038A09F /* Preview Assets.xcassets in Resources */, + ECC8142C29334AE00038A09F /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + ECC8143229334AE00038A09F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + ECC8142029334ADF0038A09F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ECC8145C29334CB70038A09F /* RepoSearchClient.swift in Sources */, + ECC8145E29334CB70038A09F /* RepositoryModel.swift in Sources */, + ECC8142829334ADF0038A09F /* GitHubSearchApp.swift in Sources */, + ECC8145F29334CB70038A09F /* RepoSearchView.swift in Sources */, + ECC8145D29334CB70038A09F /* RepoSearch.swift in Sources */, + EC4BC239293463F4001B2F91 /* APIError.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + ECC8143029334AE00038A09F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ECC8143929334AE00038A09F /* RepositoryTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + ECC8143629334AE00038A09F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = ECC8142329334ADF0038A09F /* GitHubSearch-final */; + targetProxy = ECC8143529334AE00038A09F /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + ECC8144629334AE00038A09F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + ECC8144729334AE00038A09F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + ECC8144929334AE00038A09F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"GitHubSearch/Resources/Preview Content\""; + DEVELOPMENT_TEAM = 8B27K52UU7; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.dy-kim.GitHubSearch"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + ECC8144A29334AE00038A09F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"GitHubSearch/Resources/Preview Content\""; + DEVELOPMENT_TEAM = 8B27K52UU7; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.dy-kim.GitHubSearch"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; + ECC8144C29334AE00038A09F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 8B27K52UU7; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.dy-kim.GitHubSearchTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GitHubSearch-final.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/GitHubSearch-final"; + }; + name = Debug; + }; + ECC8144D29334AE00038A09F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 8B27K52UU7; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.dy-kim.GitHubSearchTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GitHubSearch-final.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/GitHubSearch-final"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + ECC8141F29334ADF0038A09F /* Build configuration list for PBXProject "GitHubSearch-final" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + ECC8144629334AE00038A09F /* Debug */, + ECC8144729334AE00038A09F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + ECC8144829334AE00038A09F /* Build configuration list for PBXNativeTarget "GitHubSearch-final" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + ECC8144929334AE00038A09F /* Debug */, + ECC8144A29334AE00038A09F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + ECC8144B29334AE00038A09F /* Build configuration list for PBXNativeTarget "GitHubSearch-finalTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + ECC8144C29334AE00038A09F /* Debug */, + ECC8144D29334AE00038A09F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + ECC8145229334C590038A09F /* XCRemoteSwiftPackageReference "swift-composable-architecture" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/pointfreeco/swift-composable-architecture"; + requirement = { + kind = exactVersion; + version = 0.46.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + ECC8145329334C590038A09F /* ComposableArchitecture */ = { + isa = XCSwiftPackageProductDependency; + package = ECC8145229334C590038A09F /* XCRemoteSwiftPackageReference "swift-composable-architecture" */; + productName = ComposableArchitecture; + }; + ECC8145529334C590038A09F /* Dependencies */ = { + isa = XCSwiftPackageProductDependency; + package = ECC8145229334C590038A09F /* XCRemoteSwiftPackageReference "swift-composable-architecture" */; + productName = Dependencies; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = ECC8141C29334ADF0038A09F /* Project object */; +} diff --git a/final/GitHubSearch-final.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/final/GitHubSearch-final.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/final/GitHubSearch-final.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/final/GitHubSearch-final.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/final/GitHubSearch-final.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/final/GitHubSearch-final.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/final/GitHubSearch-final.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/final/GitHubSearch-final.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..cfd8f05 --- /dev/null +++ b/final/GitHubSearch-final.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,77 @@ +{ + "pins" : [ + { + "identity" : "combine-schedulers", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/combine-schedulers", + "state" : { + "revision" : "882ac01eb7ef9e36d4467eb4b1151e74fcef85ab", + "version" : "0.9.1" + } + }, + { + "identity" : "swift-case-paths", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-case-paths", + "state" : { + "revision" : "bb436421f57269fbcfe7360735985321585a86e5", + "version" : "0.10.1" + } + }, + { + "identity" : "swift-clocks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-clocks", + "state" : { + "revision" : "20b25ca0dd88ebfb9111ec937814ddc5a8880172", + "version" : "0.2.0" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections", + "state" : { + "revision" : "f504716c27d2e5d4144fa4794b12129301d17729", + "version" : "1.0.3" + } + }, + { + "identity" : "swift-composable-architecture", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-composable-architecture", + "state" : { + "revision" : "52dca7e5edd6ec9e0b529380843a8cb13f57d7d7", + "version" : "0.46.0" + } + }, + { + "identity" : "swift-custom-dump", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-custom-dump", + "state" : { + "revision" : "ead7d30cc224c3642c150b546f4f1080d1c411a8", + "version" : "0.6.1" + } + }, + { + "identity" : "swift-identified-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-identified-collections", + "state" : { + "revision" : "a08887de589e3829d488e0b4b707b2ca804b1060", + "version" : "0.5.0" + } + }, + { + "identity" : "xctest-dynamic-overlay", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", + "state" : { + "revision" : "5a5457a744239896e9b0b03a8e1a5069c3e7b91f", + "version" : "0.6.0" + } + } + ], + "version" : 2 +} diff --git a/final/GitHubSearch/APIError.swift b/final/GitHubSearch/APIError.swift new file mode 100644 index 0000000..bb2c262 --- /dev/null +++ b/final/GitHubSearch/APIError.swift @@ -0,0 +1,5 @@ +import Foundation + +enum APIError: Error { + case invalidURL +} diff --git a/final/GitHubSearch/GitHubSearchApp.swift b/final/GitHubSearch/GitHubSearchApp.swift new file mode 100644 index 0000000..e395525 --- /dev/null +++ b/final/GitHubSearch/GitHubSearchApp.swift @@ -0,0 +1,17 @@ +import SwiftUI + +import ComposableArchitecture + +@main +struct GitHubSearchApp: App { + var body: some Scene { + WindowGroup { + RepoSearchView( + store: Store( + initialState: RepoSearch.State(), + reducer: RepoSearch()._printChanges() + ) + ) + } + } +} diff --git a/final/GitHubSearch/Repository/RepoSearch.swift b/final/GitHubSearch/Repository/RepoSearch.swift new file mode 100644 index 0000000..69dd91a --- /dev/null +++ b/final/GitHubSearch/Repository/RepoSearch.swift @@ -0,0 +1,34 @@ +import ComposableArchitecture + +struct RepoSearch: ReducerProtocol { + struct State: Equatable { + var keyword = "" + var searchResults = [String]() + } + + enum Action: Equatable { + case keywordChanged(String) + case search + } + + func reduce(into state: inout State, action: Action) -> EffectTask { + switch action { + case let .keywordChanged(keyword): + state.keyword = keyword + return .none + + case .search: + state.searchResults = self.sampleRepoLists.filter { + $0.contains(state.keyword) + } + return .none + } + } + + private let sampleRepoLists = [ + "Swift", + "SwiftyJSON", + "SwiftGuide", + "SwiftterSwift", + ] +} diff --git a/final/GitHubSearch/Repository/RepoSearchClient.swift b/final/GitHubSearch/Repository/RepoSearchClient.swift new file mode 100644 index 0000000..51f4f59 --- /dev/null +++ b/final/GitHubSearch/Repository/RepoSearchClient.swift @@ -0,0 +1,52 @@ +import Foundation + +import ComposableArchitecture +import XCTestDynamicOverlay + +struct RepoSearchClient { + var search: @Sendable (String) async throws -> RepositoryModel +} + +extension DependencyValues { + var repoSearchClient: RepoSearchClient { + get { self[RepoSearchClient.self] } + set { self[RepoSearchClient.self] = newValue } + } +} + +// MARK: - Live API implementation + +extension RepoSearchClient: DependencyKey { + static let liveValue = RepoSearchClient( + search: { keyword in + guard let url = URL(string: "https://api.github.com/search/repositories?q=\(keyword)") else { + throw APIError.invalidURL + } + let (data, _) = try await URLSession.shared.data(from: url) + return try JSONDecoder().decode(RepositoryModel.self, from: data) + } + ) +} + +extension RepoSearchClient: TestDependencyKey { + static let previewValue = Self( + search: { _ in .mock } + ) + + static let testValue = Self( + search: unimplemented("\(Self.self).search") + ) +} + +// Workshop 진행 중에 임시로 활용할 코드 +// +//func sampleSearchRequest(keyword: String, send: Send) async throws { +// guard let url = URL(string: "https://api.github.com/search/repositories?q=\(keyword)") else { +// await send(RepoSearch.Action.dataLoaded(.failure(APIError.invalidUrlError))) +// return +// } +// let (data, _) = try await URLSession.shared.data(from: url) +// let result = await TaskResult { try JSONDecoder().decode(RepositoryModel.self, from: data) } +// +// await send(RepoSearch.Action.dataLoaded(result)) +//} diff --git a/final/GitHubSearch/Repository/RepoSearchView.swift b/final/GitHubSearch/Repository/RepoSearchView.swift new file mode 100644 index 0000000..8a8f5f5 --- /dev/null +++ b/final/GitHubSearch/Repository/RepoSearchView.swift @@ -0,0 +1,50 @@ +import SwiftUI + +import ComposableArchitecture + +struct RepoSearchView: View { + let store: StoreOf + + var body: some View { + WithViewStore(self.store) { viewStore in + NavigationView { + VStack { + HStack { + TextField( + "Search repo", + text: Binding( + get: { viewStore.keyword }, + set: { viewStore.send(.keywordChanged($0)) } + ) + ) + .textFieldStyle(.roundedBorder) + + Button("Search") { + viewStore.send(.search) + } + .buttonStyle(.borderedProminent) + } + .padding() + + List { + ForEach(viewStore.searchResults, id: \.self) { + Text($0) + } + } + } + .navigationTitle("Github Search") + } + } + } +} + +struct RepoSearchView_Previews: PreviewProvider { + static var previews: some View { + RepoSearchView( + store: Store( + initialState: RepoSearch.State(), + reducer: RepoSearch() + ) + ) + } +} diff --git a/final/GitHubSearch/Repository/RepositoryModel.swift b/final/GitHubSearch/Repository/RepositoryModel.swift new file mode 100644 index 0000000..b96c77d --- /dev/null +++ b/final/GitHubSearch/Repository/RepositoryModel.swift @@ -0,0 +1,23 @@ +struct RepositoryModel: Decodable, Equatable, Sendable { + var items: [Result] + + struct Result: Decodable, Equatable, Sendable { + var name: String + + enum CodingKeys: String, CodingKey { + case name = "full_name" + } + } +} + +// MARK: - Mock data + +extension RepositoryModel { + static let mock = Self( + items: [ + RepositoryModel.Result(name: "Swift"), + RepositoryModel.Result(name: "SwiftLint"), + RepositoryModel.Result(name: "SwiftAlgorithm") + ] + ) +} diff --git a/final/GitHubSearch/Resources/Assets.xcassets/AccentColor.colorset/Contents.json b/final/GitHubSearch/Resources/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/final/GitHubSearch/Resources/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/final/GitHubSearch/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/final/GitHubSearch/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/final/GitHubSearch/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/final/GitHubSearch/Resources/Assets.xcassets/Contents.json b/final/GitHubSearch/Resources/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/final/GitHubSearch/Resources/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/final/GitHubSearch/Resources/Preview Content/Preview Assets.xcassets/Contents.json b/final/GitHubSearch/Resources/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/final/GitHubSearch/Resources/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/final/GitHubSearchTests/RepositoryTests.swift b/final/GitHubSearchTests/RepositoryTests.swift new file mode 100644 index 0000000..02d862e --- /dev/null +++ b/final/GitHubSearchTests/RepositoryTests.swift @@ -0,0 +1,28 @@ +import XCTest + +import ComposableArchitecture + +@testable import GitHubSearch_final + +@MainActor +final class RepositoryTests: XCTestCase { + func test_user_get_repoSearchResults_when_search() async { + let store = TestStore( + initialState: RepoSearch.State(), + reducer: RepoSearch() + ) + + await store.send(.keywordChanged("Swift")) { + $0.keyword = "Swift" + } + + await store.send(.search) { + $0.searchResults = [ + "Swift", + "SwiftyJSON", + "SwiftGuide", + "SwiftterSwift", + ] + } + } +} diff --git a/starter/GitHubSearch/Repository/RepositoryModel.swift b/starter/GitHubSearch/Repository/RepositoryModel.swift index a2a57a2..61f3eff 100644 --- a/starter/GitHubSearch/Repository/RepositoryModel.swift +++ b/starter/GitHubSearch/Repository/RepositoryModel.swift @@ -1,13 +1,11 @@ struct RepositoryModel: Decodable, Equatable, Sendable { var items: [Result] - struct Result: Decodable, Equatable, Identifiable, Sendable { + struct Result: Decodable, Equatable, Sendable { var name: String - var id: Int enum CodingKeys: String, CodingKey { case name = "full_name" - case id } } } @@ -17,9 +15,9 @@ struct RepositoryModel: Decodable, Equatable, Sendable { extension RepositoryModel { static let mock = Self( items: [ - RepositoryModel.Result(name: "Swift", id: 1), - RepositoryModel.Result(name: "SwiftLint", id: 2), - RepositoryModel.Result(name: "SwiftAlgorithm", id: 3) + RepositoryModel.Result(name: "Swift"), + RepositoryModel.Result(name: "SwiftLint"), + RepositoryModel.Result(name: "SwiftAlgorithm") ] ) }