diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 19d4d56..bd38016 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -135,6 +135,30 @@ jobs: name: "Example packages" matrix_string: '${{ needs.construct-example-packages-matrix.outputs.example-packages-matrix }}' + example-xcode-projects: + strategy: + matrix: + destination: + - generic/platform=macOS + - generic/platform=iOS Simulator + - generic/platform=tvOS Simulator + - generic/platform=visionOS Simulator + name: Example Xcode projects + runs-on: macos-26 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + - name: Setup Xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: ">=26.4" + - name: Build + run: ./Scripts/test-xcode-examples.sh + env: + XCODEBUILD_DESTINATION: ${{ matrix.destination }} + benchmarks: name: Benchmarks uses: apple/swift-nio/.github/workflows/benchmarks.yml@main diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 71beb0f..6629729 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -142,6 +142,30 @@ jobs: name: "Example packages" matrix_string: '${{ needs.construct-example-packages-matrix.outputs.example-packages-matrix }}' + example-xcode-projects: + strategy: + matrix: + destination: + - generic/platform=macOS + - generic/platform=iOS Simulator + - generic/platform=tvOS Simulator + - generic/platform=visionOS Simulator + name: Example Xcode projects + runs-on: macos-26 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + - name: Setup Xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: ">=26.4" + - name: Build + run: ./Scripts/test-xcode-examples.sh + env: + XCODEBUILD_DESTINATION: ${{ matrix.destination }} + benchmarks: name: Benchmarks uses: apple/swift-nio/.github/workflows/benchmarks.yml@main diff --git a/Examples/HelloWorldAppExample/.gitignore b/Examples/HelloWorldAppExample/.gitignore new file mode 100644 index 0000000..59acfea --- /dev/null +++ b/Examples/HelloWorldAppExample/.gitignore @@ -0,0 +1,6 @@ +xcuserdata/ +*.ipa +*.dSYM.zip +*.dSYM +build/ +.build/ \ No newline at end of file diff --git a/Examples/HelloWorldAppExample/HelloWorldAppExample.xcodeproj/project.pbxproj b/Examples/HelloWorldAppExample/HelloWorldAppExample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..fa174bc --- /dev/null +++ b/Examples/HelloWorldAppExample/HelloWorldAppExample.xcodeproj/project.pbxproj @@ -0,0 +1,377 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 100; + objects = { + +/* Begin PBXBuildFile section */ + 7E0A346A2F6A190700D884F7 /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 7E0A34692F6A190700D884F7 /* Configuration */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 7EB913542F69F47600A9BD65 /* HelloWorldAppExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorldAppExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 7EB913562F69F47600A9BD65 /* HelloWorldAppExample */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = HelloWorldAppExample; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 7EB913512F69F47600A9BD65 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + files = ( + 7E0A346A2F6A190700D884F7 /* Configuration in Frameworks */, + ); + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 7EB9134B2F69F47600A9BD65 = { + isa = PBXGroup; + children = ( + 7EB913562F69F47600A9BD65 /* HelloWorldAppExample */, + 7EB913552F69F47600A9BD65 /* Products */, + ); + sourceTree = ""; + }; + 7EB913552F69F47600A9BD65 /* Products */ = { + isa = PBXGroup; + children = ( + 7EB913542F69F47600A9BD65 /* HelloWorldAppExample.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7EB913532F69F47600A9BD65 /* HelloWorldAppExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7EB9135F2F69F47800A9BD65 /* Build configuration list for PBXNativeTarget "HelloWorldAppExample" */; + buildPhases = ( + 7EB913502F69F47600A9BD65 /* Sources */, + 7EB913512F69F47600A9BD65 /* Frameworks */, + 7EB913522F69F47600A9BD65 /* Resources */, + ); + buildRules = ( + ); + fileSystemSynchronizedGroups = ( + 7EB913562F69F47600A9BD65 /* HelloWorldAppExample */, + ); + name = HelloWorldAppExample; + packageProductDependencies = ( + 7E0A34692F6A190700D884F7 /* Configuration */, + ); + productName = HelloWorldAppExample; + productReference = 7EB913542F69F47600A9BD65 /* HelloWorldAppExample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7EB9134C2F69F47600A9BD65 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 2630; + LastUpgradeCheck = 2640; + TargetAttributes = { + 7EB913532F69F47600A9BD65 = { + CreatedOnToolsVersion = 26.3; + }; + }; + }; + buildConfigurationList = 7EB9134F2F69F47600A9BD65 /* Build configuration list for PBXProject "HelloWorldAppExample" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7EB9134B2F69F47600A9BD65; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + 7E0A34682F6A190700D884F7 /* XCLocalSwiftPackageReference "../../../swift-configuration" */, + ); + preferredProjectObjectVersion = 100; + productRefGroup = 7EB913552F69F47600A9BD65 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7EB913532F69F47600A9BD65 /* HelloWorldAppExample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7EB913522F69F47600A9BD65 /* Resources */ = { + isa = PBXResourcesBuildPhase; + files = ( + ); + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7EB913502F69F47600A9BD65 /* Sources */ = { + isa = PBXSourcesBuildPhase; + files = ( + ); + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 7EB9135D2F69F47800A9BD65 /* Debug configuration for PBXProject "HelloWorldAppExample" */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 26.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 6.0; + TVOS_DEPLOYMENT_TARGET = 26.0; + XROS_DEPLOYMENT_TARGET = 26.0; + }; + name = Debug; + }; + 7EB9135E2F69F47800A9BD65 /* Release configuration for PBXProject "HelloWorldAppExample" */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 26.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 6.0; + TVOS_DEPLOYMENT_TARGET = 26.0; + XROS_DEPLOYMENT_TARGET = 26.0; + }; + name = Release; + }; + 7EB913602F69F47800A9BD65 /* Debug configuration for PBXNativeTarget "HelloWorldAppExample" */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + ENABLE_APP_SANDBOX = YES; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SELECTED_FILES = readonly; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.HelloWorldAppExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = YES; + SDKROOT = auto; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; + }; + name = Debug; + }; + 7EB913612F69F47800A9BD65 /* Release configuration for PBXNativeTarget "HelloWorldAppExample" */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + ENABLE_APP_SANDBOX = YES; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SELECTED_FILES = readonly; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.HelloWorldAppExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = YES; + SDKROOT = auto; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7EB9134F2F69F47600A9BD65 /* Build configuration list for PBXProject "HelloWorldAppExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7EB9135D2F69F47800A9BD65 /* Debug configuration for PBXProject "HelloWorldAppExample" */, + 7EB9135E2F69F47800A9BD65 /* Release configuration for PBXProject "HelloWorldAppExample" */, + ); + defaultConfigurationName = Release; + }; + 7EB9135F2F69F47800A9BD65 /* Build configuration list for PBXNativeTarget "HelloWorldAppExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7EB913602F69F47800A9BD65 /* Debug configuration for PBXNativeTarget "HelloWorldAppExample" */, + 7EB913612F69F47800A9BD65 /* Release configuration for PBXNativeTarget "HelloWorldAppExample" */, + ); + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 7E0A34682F6A190700D884F7 /* XCLocalSwiftPackageReference "../../../swift-configuration" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = "../../../swift-configuration"; + traits = ( + CommandLineArguments, + default, + ); + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 7E0A34692F6A190700D884F7 /* Configuration */ = { + isa = XCSwiftPackageProductDependency; + productName = Configuration; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 7EB9134C2F69F47600A9BD65 /* Project object */; +} diff --git a/Examples/HelloWorldAppExample/HelloWorldAppExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/HelloWorldAppExample/HelloWorldAppExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Examples/HelloWorldAppExample/HelloWorldAppExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Examples/HelloWorldAppExample/HelloWorldAppExample.xcodeproj/xcshareddata/xcschemes/HelloWorldAppExample.xcscheme b/Examples/HelloWorldAppExample/HelloWorldAppExample.xcodeproj/xcshareddata/xcschemes/HelloWorldAppExample.xcscheme new file mode 100644 index 0000000..1fad7e1 --- /dev/null +++ b/Examples/HelloWorldAppExample/HelloWorldAppExample.xcodeproj/xcshareddata/xcschemes/HelloWorldAppExample.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/HelloWorldAppExample/HelloWorldAppExample/HelloWorldApp.swift b/Examples/HelloWorldAppExample/HelloWorldAppExample/HelloWorldApp.swift new file mode 100644 index 0000000..e49da9c --- /dev/null +++ b/Examples/HelloWorldAppExample/HelloWorldAppExample/HelloWorldApp.swift @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftConfiguration open source project +// +// Copyright (c) 2025 Apple Inc. and the SwiftConfiguration project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftConfiguration project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Configuration +import SwiftUI + +@main +struct HelloWorldAppExampleApp: App { + let greetedName: String + + init() { + let config = ConfigReader(providers: [ + CommandLineArgumentsProvider(), + EnvironmentVariablesProvider(), + ]) + greetedName = config.string(forKey: "greetedName", default: "World") + } + + var body: some Scene { + WindowGroup { + HelloWorldView(greetedName: greetedName) + } + } +} diff --git a/Examples/HelloWorldAppExample/HelloWorldAppExample/HelloWorldView.swift b/Examples/HelloWorldAppExample/HelloWorldAppExample/HelloWorldView.swift new file mode 100644 index 0000000..7a73d25 --- /dev/null +++ b/Examples/HelloWorldAppExample/HelloWorldAppExample/HelloWorldView.swift @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftConfiguration open source project +// +// Copyright (c) 2025 Apple Inc. and the SwiftConfiguration project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftConfiguration project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import SwiftUI + +struct HelloWorldView: View { + let greetedName: String + + var body: some View { + VStack { + Image(systemName: "globe") + .imageScale(.large) + .foregroundStyle(.tint) + Text("Hello, \(greetedName)!") + } + .padding() + } +} + +#Preview { + HelloWorldView(greetedName: "World") +} diff --git a/Examples/HelloWorldAppExample/README.md b/Examples/HelloWorldAppExample/README.md new file mode 100644 index 0000000..15bd941 --- /dev/null +++ b/Examples/HelloWorldAppExample/README.md @@ -0,0 +1,63 @@ +# Hello World App Example + +> **Disclaimer:** This example is designed to demonstrate basic concepts of Swift Configuration and is not production-ready code. It lacks comprehensive error handling, testing, and other features required for production use. + +## Overview + +This example demonstrates the fundamental concepts of Swift Configuration through a simple app that displays a personalized greeting message. The application showcases how to: + +- Set up multiple configuration providers in a hierarchy. +- Use environment variables and command-line arguments as configuration sources. +- Read configuration values with type safety and default fallbacks. + +## What it does + +The application reads a `greetedName` configuration value from multiple sources and displays `"Hello, {name}!"`. The configuration sources are prioritized as follows: + +1. **Command-line configuration** (highest priority) +2. **Environment variables** (lower priority) +3. **Default value** (`"World"` - used when no other source provides the value) + +## Step-by-step instructions + +### Step 1: Build the example + +Open the project in Xcode, and select a target destination. + +### Step 2: Run with default value + +Run the application without any configuration to see the default behavior: + +**Expected output:** +``` +Hello, World! +``` + +### Step 3: Run with environment variable + +Edit the current scheme and enable the `GREETED_NAME` environment variable, then run the application. + +**Expected output:** +``` +Hello, Environment! +``` + +### Step 4: Run with command-line argument + +Edit the current scheme and enable the `--greeted-name` argument, then run the application. + +**Expected output:** +``` +Hello, CommandLine! +``` + +### Step 5: Demonstrate provider priority + +Edit the current scheme and enable both the `--greeted-name` argument and the `GREETED_NAME` environment variable, then run the application. + +**Expected output:** +``` +Hello, CommandLine! +``` + +This demonstrates that command-line arguments take precedence over environment variables in the provider hierarchy. diff --git a/Scripts/test-xcode-examples.sh b/Scripts/test-xcode-examples.sh new file mode 100755 index 0000000..6bddc90 --- /dev/null +++ b/Scripts/test-xcode-examples.sh @@ -0,0 +1,65 @@ +#!/bin/bash +set -euo pipefail + +log() { printf -- "** %s\n" "$*" >&2; } +error() { printf -- "** ERROR: %s\n" "$*" >&2; } +fatal() { error "$@"; exit 1; } + +log "Checking required executables..." +XCODEBUILD_BIN=${XCODEBUILD_BIN:-$(command -v xcodebuild || xcrun -f swift)} || fatal "XCODEBUILD_BIN unset and no xcodebuild on PATH" + +CURRENT_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +REPO_ROOT="$(git -C "${CURRENT_SCRIPT_DIR}" rev-parse --show-toplevel)" +TMP_DIR=$(/usr/bin/mktemp -d -p "${TMPDIR-/tmp}" "$(basename "$0").XXXXXXXXXX") + +PACKAGE_PATH=${PACKAGE_PATH:-${REPO_ROOT}} +EXAMPLES_PACKAGE_PATH="${PACKAGE_PATH}/Examples" +SHARED_EXAMPLE_HARNESS_PACKAGE_PATH="${TMP_DIR}/example-harness" +SHARED_DERIVED_DATA_PATH="${TMP_DIR}/example-derived-data" +SHARED_PACKAGE_CACHE_PATH="${TMP_DIR}/example-cache" +SHARED_CLONED_SOURCES_PATH="${TMP_DIR}/example-cloned-sources" +XCODEBUILD_DESTINATION=${XCODEBUILD_DESTINATION:-"generic/platform=macOS"} +ORIGINAL_LOCAL_DEPENDENCY_PATH=${ORIGINAL_LOCAL_DEPENDENCY_PATH:-"../../../swift-configuration"} + +for EXAMPLE_PACKAGE_PATH in $(find "${EXAMPLES_PACKAGE_PATH}" -maxdepth 2 -name '*.xcodeproj' -type d -print0 | xargs -0 dirname | sort); do + + EXAMPLE_PACKAGE_NAME="$(basename "${EXAMPLE_PACKAGE_PATH}")" + + if [[ "${SINGLE_EXAMPLE_PACKAGE:-${EXAMPLE_PACKAGE_NAME}}" != "${EXAMPLE_PACKAGE_NAME}" ]]; then + log "Skipping example: ${EXAMPLE_PACKAGE_NAME}" + continue + fi + + log "Recreating shared derived data directory: ${SHARED_DERIVED_DATA_PATH}" + rm -rf "${SHARED_DERIVED_DATA_PATH}" + mkdir -v "${SHARED_DERIVED_DATA_PATH}" + + log "Recreating shared example harness directory: ${SHARED_EXAMPLE_HARNESS_PACKAGE_PATH}" + rm -rf "${SHARED_EXAMPLE_HARNESS_PACKAGE_PATH}" + mkdir -v "${SHARED_EXAMPLE_HARNESS_PACKAGE_PATH}" + + log "Copying example contents from ${EXAMPLE_PACKAGE_NAME} to ${SHARED_EXAMPLE_HARNESS_PACKAGE_PATH}" + git archive HEAD "${EXAMPLE_PACKAGE_PATH}" --format tar | tar -C "${SHARED_EXAMPLE_HARNESS_PACKAGE_PATH}" -xvf- --strip-components 2 + + # GNU tar has --touch, but BSD tar does not, so we'll use touch directly. + log "Updating mtime of example contents..." + find "${SHARED_EXAMPLE_HARNESS_PACKAGE_PATH}" -print0 | xargs -0 -n1 touch -m + + # There is no CLI to modify dependencies, revert to sed + log "Re-overriding dependency in ${EXAMPLE_PACKAGE_NAME} to use ${PACKAGE_PATH}" + PBXPROJ_PATH=$(find $SHARED_EXAMPLE_HARNESS_PACKAGE_PATH -name "project.pbxproj" -type f -maxdepth 2) + sed -i '' "s|${ORIGINAL_LOCAL_DEPENDENCY_PATH}|${PACKAGE_PATH}|g" "$PBXPROJ_PATH" + + PROJECT_PATH=$(find $SHARED_EXAMPLE_HARNESS_PACKAGE_PATH -name "*.xcodeproj" -type d -maxdepth 1) + + log "Building example app: ${EXAMPLE_PACKAGE_NAME}" + "${XCODEBUILD_BIN}" build \ + -project "${PROJECT_PATH}" \ + -scheme "${EXAMPLE_PACKAGE_NAME}" \ + -destination "${XCODEBUILD_DESTINATION}" \ + -packageCachePath "${SHARED_PACKAGE_CACHE_PATH}" \ + -clonedSourcePackagesDirPath ${SHARED_CLONED_SOURCES_PATH} \ + -skipPackageUpdates \ + -derivedDataPath "${SHARED_DERIVED_DATA_PATH}" + log "✅ Successfully built the example app ${EXAMPLE_PACKAGE_NAME}." +done