diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ace2f22..e43184c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -99,6 +99,51 @@ jobs:
with:
name: BuildConfigDemo.xcresult
path: test_output/BuildConfigDemo.xcresult
+ test_demo_with_package:
+ name: Test package based demo app
+ needs: generate-matrix
+ runs-on: macOS-14
+ strategy:
+ fail-fast: false
+ matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
+ env:
+ DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode_version }}.app/Contents/Developer
+ steps:
+ - uses: actions/checkout@v4
+ - name: Cache SPM build directory
+ uses: actions/cache@v4
+ env:
+ cache-name: swiftpm
+ with:
+ path: |
+ DemoWithPackage/.build
+ DemoWithPackage/Tools/.build
+ key: ${{ runner.os }}-${{ github.job }}-${{ env.cache-name }}-${{ hashFiles('**/Package.swift') }}
+ restore-keys: |
+ ${{ runner.os }}-${{ github.job }}-${{ env.cache-name }}-
+ ${{ runner.os }}-${{ github.job }}-
+ ${{ runner.os }}-
+ - name: Cache DerivedData
+ uses: actions/cache@v4
+ env:
+ cache-name: derived-data
+ with:
+ path: DemoWithPackage/DerivedData
+ key: ${{ runner.os }}-${{ github.job }}-${{ env.cache-name }}-${{ hashFiles('**/Package.swift') }}
+ restore-keys: |
+ ${{ runner.os }}-${{ github.job }}-${{ env.cache-name }}-
+ ${{ runner.os }}-${{ github.job }}-
+ ${{ runner.os }}-
+ - name: Disable SwiftLint Plugin
+ run: sed -i -e 's/.*SwiftLint.*//g' Package.swift
+ - name: test
+ run: ./scripts/test_demo_package.sh
+ - name: upload test results
+ if: ${{ matrix.xcode_version == fromJson(needs.generate-matrix.outputs.matrix).xcode_version[0] && (success() || failure()) }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: BuildConfigDemoPackage.xcresult
+ path: test_output/BuildConfigDemoPackage.xcresult
report:
name: Report
needs: [test, test_demo]
diff --git a/.swiftlint.yml b/.swiftlint.yml
index 5a4553a..6a8f37a 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -1,7 +1,9 @@
included:
- Sources
+ - Plugins
excluded:
- Demo
+ - DemoWithPackage
- Tests
- Package.swift
- Dangerfile.swift
@@ -22,6 +24,7 @@ disabled_rules:
- indentation_width # https://github.com/realm/SwiftLint/issues/3046
- let_var_whitespace # https://github.com/realm/SwiftLint/issues/2980
- missing_docs
+ - multiline_arguments_brackets
- no_extension_access_modifier
- no_grouping_extension
- one_declaration_per_file
diff --git a/DemoWithPackage/.gitignore b/DemoWithPackage/.gitignore
new file mode 100644
index 0000000..b700d21
--- /dev/null
+++ b/DemoWithPackage/.gitignore
@@ -0,0 +1,107 @@
+# Created by https://www.gitignore.io/api/xcode,swift
+
+### Swift ###
+# Xcode
+#
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+## User settings
+xcuserdata/
+
+## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
+*.xcscmblueprint
+*.xccheckout
+
+## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
+build/
+DerivedData/
+*.moved-aside
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+
+## Obj-C/Swift specific
+*.hmap
+
+## App packaging
+*.ipa
+*.dSYM.zip
+*.dSYM
+
+## Playgrounds
+timeline.xctimeline
+playground.xcworkspace
+
+# Swift Package Manager
+# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
+# Packages/
+# Package.pins
+# Package.resolved
+# *.xcodeproj
+# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
+# hence it is not needed unless you have added a package configuration file to your project
+.swiftpm
+
+.build/
+
+# CocoaPods
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
+Pods/
+# Add this line if you want to avoid checking in source code from the Xcode workspace
+*.xcworkspace
+
+# Carthage
+# Add this line if you want to avoid checking in source code from Carthage dependencies.
+# Carthage/Checkouts
+
+Carthage/Build/
+
+# Accio dependency management
+Dependencies/
+.accio/
+
+# fastlane
+# It is recommended to not store the screenshots in the git repo.
+# Instead, use fastlane to re-generate the screenshots whenever they are needed.
+# For more information about the recommended setup visit:
+# https://docs.fastlane.tools/best-practices/source-control/#source-control
+
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots/**/*.png
+fastlane/test_output
+
+# Code Injection
+# After new code Injection tools there's a generated folder /iOSInjectionProject
+# https://github.com/johnno1962/injectionforxcode
+
+iOSInjectionProject/
+
+### Xcode ###
+
+## Xcode 8 and earlier
+
+### Xcode Patch ###
+*.xcodeproj/*
+# !*.xcodeproj/project.pbxproj
+# !*.xcodeproj/xcshareddata/
+# !*.xcworkspace/contents.xcworkspacedata
+# /*.gcno
+# **/xcshareddata/WorkspaceSettings.xcsettings
+
+# End of https://www.gitignore.io/api/xcode,swift
+
+*.lock
+*.generated.swift
+*.plist
+!**/Settings.bundle/Root.plist
+!**/com.mono0926.LicensePlist.plist
+!**/com.mono0926.LicensePlist/*.plist
+.swiftlint.yml
diff --git a/DemoWithPackage/App/Base.lproj/LaunchScreen.storyboard b/DemoWithPackage/App/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..ce83f19
--- /dev/null
+++ b/DemoWithPackage/App/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DemoWithPackage/App/Info.plist b/DemoWithPackage/App/Info.plist
new file mode 100644
index 0000000..51f16b5
--- /dev/null
+++ b/DemoWithPackage/App/Info.plist
@@ -0,0 +1,34 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(MARKETING_VERSION)
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+
+
+
diff --git a/DemoWithPackage/App/main.swift b/DemoWithPackage/App/main.swift
new file mode 100644
index 0000000..52bda87
--- /dev/null
+++ b/DemoWithPackage/App/main.swift
@@ -0,0 +1,11 @@
+//
+// main.swift
+// App
+//
+// Created by 417.72KI on 2023/10/09.
+// Copyright © 2023 417.72KI. All rights reserved.
+//
+
+import BuildConfigSwiftDemo
+
+DemoApp.main()
diff --git a/DemoWithPackage/App/project.yml b/DemoWithPackage/App/project.yml
new file mode 100644
index 0000000..78218cd
--- /dev/null
+++ b/DemoWithPackage/App/project.yml
@@ -0,0 +1,137 @@
+name: BuildConfigSwiftDemoApp
+attributes:
+ ORGANIZATIONNAME: 417.72KI
+configs:
+ Debug: debug
+ Release: release
+settings:
+ base:
+ IPHONEOS_DEPLOYMENT_TARGET: 15.0
+ SWIFT_VERSION: 5.0
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: ${inherited}
+ ALWAYS_SEARCH_USER_PATHS: false
+ CLANG_ENABLE_OBJC_ARC: true
+ CODE_SIGN_STYLE: Manual
+ TARGETED_DEVICE_FAMILY: 1
+ DEVELOPMENT_TEAM: ""
+ LIBRARY_SEARCH_PATHS: $(inherited)
+ CURRENT_PROJECT_VERSION: 1
+ MARKETING_VERSION: 1.0.0
+options:
+ xcodeVersion: 10.2
+ bundleIdPrefix: io.github.417-72KI
+packages:
+ BuildConfigSwiftDemo:
+ path: ../
+targets:
+ Development:
+ type: application
+ platform: iOS
+ sources:
+ - main.swift
+ - Base.lproj
+ info:
+ path: Info.plist
+ properties:
+ CFBundleDevelopmentRegion: $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable: $(EXECUTABLE_NAME)
+ CFBundleIdentifier: $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion: '6.0'
+ CFBundleName: $(PRODUCT_NAME)
+ CFBundlePackageType: APPL
+ CFBundleShortVersionString: $(MARKETING_VERSION)
+ CFBundleVersion: $(CURRENT_PROJECT_VERSION)
+ LSRequiresIPhoneOS: true
+ UILaunchStoryboardName: LaunchScreen
+ UIRequiredDeviceCapabilities:
+ - arm64
+ UISupportedInterfaceOrientations:
+ - UIInterfaceOrientationPortrait
+ settings:
+ base:
+ PRODUCT_NAME: BuildConfigSwiftDemoDev
+ PRODUCT_BUNDLE_IDENTIFIER: io.github.417-72KI.BuildConfigSwiftDemo.dev
+ configs:
+ debug:
+ CODE_SIGN_IDENTITY: iPhone Developer
+ PROVISIONING_PROFILE_SPECIFIER: ""
+ release:
+ CODE_SIGN_IDENTITY: iPhone Distribution
+ PROVISIONING_PROFILE_SPECIFIER: ""
+ EXCLUDED_SOURCE_FILE_NAMES: Mock*
+ dependencies:
+ - package: BuildConfigSwiftDemo
+ Staging:
+ type: application
+ platform: iOS
+ sources:
+ - main.swift
+ - Base.lproj
+ info:
+ path: Info.plist
+ properties:
+ CFBundleDevelopmentRegion: $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable: $(EXECUTABLE_NAME)
+ CFBundleIdentifier: $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion: '6.0'
+ CFBundleName: $(PRODUCT_NAME)
+ CFBundlePackageType: APPL
+ CFBundleShortVersionString: $(MARKETING_VERSION)
+ CFBundleVersion: $(CURRENT_PROJECT_VERSION)
+ LSRequiresIPhoneOS: true
+ UILaunchStoryboardName: LaunchScreen
+ UIRequiredDeviceCapabilities:
+ - arm64
+ UISupportedInterfaceOrientations:
+ - UIInterfaceOrientationPortrait
+ settings:
+ base:
+ PRODUCT_NAME: BuildConfigSwiftDemoStg
+ PRODUCT_BUNDLE_IDENTIFIER: io.github.417-72KI.BuildConfigSwiftDemo.stg
+ configs:
+ debug:
+ CODE_SIGN_IDENTITY: iPhone Developer
+ PROVISIONING_PROFILE_SPECIFIER: ""
+ release:
+ CODE_SIGN_IDENTITY: iPhone Distribution
+ PROVISIONING_PROFILE_SPECIFIER: ""
+ EXCLUDED_SOURCE_FILE_NAMES: Mock*
+ dependencies:
+ - package: BuildConfigSwiftDemo
+ Production:
+ type: application
+ platform: iOS
+ sources:
+ - main.swift
+ - Base.lproj
+ info:
+ path: Info.plist
+ properties:
+ CFBundleDevelopmentRegion: $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable: $(EXECUTABLE_NAME)
+ CFBundleIdentifier: $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion: '6.0'
+ CFBundleName: $(PRODUCT_NAME)
+ CFBundlePackageType: APPL
+ CFBundleShortVersionString: $(MARKETING_VERSION)
+ CFBundleVersion: $(CURRENT_PROJECT_VERSION)
+ LSRequiresIPhoneOS: true
+ UILaunchStoryboardName: LaunchScreen
+ UIRequiredDeviceCapabilities:
+ - arm64
+ UISupportedInterfaceOrientations:
+ - UIInterfaceOrientationPortrait
+ settings:
+ base:
+ PRODUCT_NAME: BuildConfigSwiftDemoProduction
+ PRODUCT_BUNDLE_IDENTIFIER: io.github.417-72KI.BuildConfigSwiftDemo
+ configs:
+ debug:
+ CODE_SIGN_IDENTITY: iPhone Developer
+ PROVISIONING_PROFILE_SPECIFIER: ""
+ release:
+ CODE_SIGN_IDENTITY: iPhone Distribution
+ PROVISIONING_PROFILE_SPECIFIER: ""
+ EXCLUDED_SOURCE_FILE_NAMES: Mock*
+ dependencies:
+ - package: BuildConfigSwiftDemo
diff --git a/DemoWithPackage/BuildConfig/api.yml b/DemoWithPackage/BuildConfig/api.yml
new file mode 100644
index 0000000..fea282a
--- /dev/null
+++ b/DemoWithPackage/BuildConfig/api.yml
@@ -0,0 +1,13 @@
+api:
+ version: 1
+ host: localhost
+ endpoint:
+ login:
+ method: POST
+ path: /login
+ profile:
+ method: GET
+ path: /profile
+ search:
+ method: GET
+ path: /search
diff --git a/DemoWithPackage/BuildConfig/config.yml b/DemoWithPackage/BuildConfig/config.yml
new file mode 100644
index 0000000..0824541
--- /dev/null
+++ b/DemoWithPackage/BuildConfig/config.yml
@@ -0,0 +1,3 @@
+is_debug: true
+environment: debug
+pi: 3.14
diff --git a/DemoWithPackage/BuildConfig/debug/api.yml b/DemoWithPackage/BuildConfig/debug/api.yml
new file mode 100644
index 0000000..f6b147b
--- /dev/null
+++ b/DemoWithPackage/BuildConfig/debug/api.yml
@@ -0,0 +1,2 @@
+api:
+ host: api-dev.example.com
diff --git a/DemoWithPackage/BuildConfig/debug/config.yml b/DemoWithPackage/BuildConfig/debug/config.yml
new file mode 100644
index 0000000..3e0d81f
--- /dev/null
+++ b/DemoWithPackage/BuildConfig/debug/config.yml
@@ -0,0 +1,2 @@
+is_debug: true
+environment: debug
diff --git a/DemoWithPackage/BuildConfig/integration_test/api.yml b/DemoWithPackage/BuildConfig/integration_test/api.yml
new file mode 100644
index 0000000..ff729de
--- /dev/null
+++ b/DemoWithPackage/BuildConfig/integration_test/api.yml
@@ -0,0 +1,2 @@
+api:
+ host: api-staging.example.com
diff --git a/DemoWithPackage/BuildConfig/integration_test/config.yml b/DemoWithPackage/BuildConfig/integration_test/config.yml
new file mode 100644
index 0000000..aedcbb3
--- /dev/null
+++ b/DemoWithPackage/BuildConfig/integration_test/config.yml
@@ -0,0 +1,2 @@
+is_debug: false
+environment: staging
diff --git a/DemoWithPackage/BuildConfig/release/api.yml b/DemoWithPackage/BuildConfig/release/api.yml
new file mode 100644
index 0000000..44a1411
--- /dev/null
+++ b/DemoWithPackage/BuildConfig/release/api.yml
@@ -0,0 +1,2 @@
+api:
+ host: api.example.com
diff --git a/DemoWithPackage/BuildConfig/release/config.yml b/DemoWithPackage/BuildConfig/release/config.yml
new file mode 100644
index 0000000..64e10e7
--- /dev/null
+++ b/DemoWithPackage/BuildConfig/release/config.yml
@@ -0,0 +1,2 @@
+is_debug: false
+environment: production
diff --git a/DemoWithPackage/Package.swift b/DemoWithPackage/Package.swift
new file mode 100644
index 0000000..458fee8
--- /dev/null
+++ b/DemoWithPackage/Package.swift
@@ -0,0 +1,37 @@
+// swift-tools-version: 5.9
+
+import PackageDescription
+import CompilerPluginSupport
+
+let package = Package(
+ name: "BuildConfigSwiftDemo",
+ platforms: [.macOS(.v14), .iOS(.v15)],
+ products: [
+ .library(
+ name: "BuildConfigSwiftDemo",
+ targets: ["BuildConfigSwiftDemo"]
+ ),
+ ],
+ dependencies: [
+ .package(path: "../"),
+ .package(url: "https://github.com/DaveWoodCom/XCGLogger", from: "7.0.1"),
+ .package(url: "https://github.com/417-72KI/StubNetworkKit", from: "0.3.0"),
+ ],
+ targets: [
+ .target(
+ name: "BuildConfigSwiftDemo",
+ dependencies: [
+ "XCGLogger",
+ "StubNetworkKit",
+ ],
+ plugins: [
+ .plugin(name: "BuildConfigSwiftGenerate", package: "BuildConfig.swift"),
+ ]
+ ),
+ .testTarget(
+ name: "BuildConfigSwiftDemoTests",
+ dependencies: ["BuildConfigSwiftDemo"],
+ resources: [.copy("test_config.json")]
+ ),
+ ]
+)
diff --git a/DemoWithPackage/Sources/BuildConfigSwiftDemo/Assets.xcassets/AccentColor.colorset/Contents.json b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/DemoWithPackage/Sources/BuildConfigSwiftDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..9221b9b
--- /dev/null
+++ b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "83.5x83.5"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/DemoWithPackage/Sources/BuildConfigSwiftDemo/Assets.xcassets/Contents.json b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/DemoWithPackage/Sources/BuildConfigSwiftDemo/BuildConfigSwiftDemo.swift b/DemoWithPackage/Sources/BuildConfigSwiftDemo/BuildConfigSwiftDemo.swift
new file mode 100644
index 0000000..08b22b8
--- /dev/null
+++ b/DemoWithPackage/Sources/BuildConfigSwiftDemo/BuildConfigSwiftDemo.swift
@@ -0,0 +1,2 @@
+// The Swift Programming Language
+// https://docs.swift.org/swift-book
diff --git a/DemoWithPackage/Sources/BuildConfigSwiftDemo/Data/BaseEntities.swift b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Data/BaseEntities.swift
new file mode 100644
index 0000000..fbdb9ef
--- /dev/null
+++ b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Data/BaseEntities.swift
@@ -0,0 +1,15 @@
+//
+// BaseEntities.swift
+// BuildConfigSwiftDemo
+//
+// Created by 417.72KI on 2023/10/05.
+// Copyright © 2023 417.72KI. All rights reserved.
+//
+
+import Foundation
+
+protocol Request: Encodable, Hashable {
+}
+
+protocol Response: Decodable, Hashable {
+}
diff --git a/DemoWithPackage/Sources/BuildConfigSwiftDemo/Data/LoginRequest.swift b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Data/LoginRequest.swift
new file mode 100644
index 0000000..c5cb40a
--- /dev/null
+++ b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Data/LoginRequest.swift
@@ -0,0 +1,14 @@
+//
+// LoginRequest.swift
+// BuildConfigSwiftDemo
+//
+// Created by 417.72KI on 2023/10/05.
+// Copyright © 2023 417.72KI. All rights reserved.
+//
+
+import Foundation
+
+struct LoginRequest: Request {
+ var id: String
+ var password: String
+}
diff --git a/DemoWithPackage/Sources/BuildConfigSwiftDemo/Data/LoginResponse.swift b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Data/LoginResponse.swift
new file mode 100644
index 0000000..e675750
--- /dev/null
+++ b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Data/LoginResponse.swift
@@ -0,0 +1,14 @@
+//
+// LoginResponse.swift
+// BuildConfigSwiftDemo
+//
+// Created by 417.72KI on 2023/10/05.
+// Copyright © 2023 417.72KI. All rights reserved.
+//
+
+import Foundation
+
+struct LoginResponse: Response {
+ var accessToken: String
+ var refreshToken: String
+}
diff --git a/DemoWithPackage/Sources/BuildConfigSwiftDemo/Data/SearchResponse.swift b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Data/SearchResponse.swift
new file mode 100644
index 0000000..6a3ccd7
--- /dev/null
+++ b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Data/SearchResponse.swift
@@ -0,0 +1,19 @@
+//
+// SearchResponse.swift
+// BuildConfigSwiftDemo
+//
+// Created by 417.72KI on 2023/10/05.
+// Copyright © 2023 417.72KI. All rights reserved.
+//
+
+import Foundation
+
+struct SearchResponse: Response {
+ var items: [Item]
+}
+
+extension SearchResponse {
+ struct Item: Response {
+ var name: String
+ }
+}
diff --git a/DemoWithPackage/Sources/BuildConfigSwiftDemo/Preview Content/Preview Assets.xcassets/Contents.json b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Preview Content/Preview Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Preview Content/Preview Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/DemoWithPackage/Sources/BuildConfigSwiftDemo/Repository/APIClient.swift b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Repository/APIClient.swift
new file mode 100644
index 0000000..f776370
--- /dev/null
+++ b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Repository/APIClient.swift
@@ -0,0 +1,61 @@
+//
+// APIClient.swift
+// BuildConfigSwiftDemo
+//
+// Created by 417.72KI on 2023/10/05.
+// Copyright © 2023 417.72KI. All rights reserved.
+//
+
+import Foundation
+
+struct APIClient {
+ var config: BuildConfig.Api
+ var session: URLSession
+}
+
+extension APIClient {
+ var host: URL { URL(string: "https://\(config.host)")! }
+
+ var decoder: JSONDecoder {
+ let decoder = JSONDecoder()
+ decoder.keyDecodingStrategy = .convertFromSnakeCase
+ return decoder
+ }
+
+ var encoder: JSONEncoder {
+ let encoder = JSONEncoder()
+ encoder.keyEncodingStrategy = .convertToSnakeCase
+ return encoder
+ }
+}
+
+extension APIClient {
+ func endpoint(_ keyPath: KeyPath) -> E {
+ config.endpoint[keyPath: keyPath]
+ }
+}
+
+extension APIClient {
+ func login(id: String, password: String) async throws -> LoginResponse {
+ let endpoint = endpoint(\.login)
+ let url = host.appendingPathComponent(endpoint.path)
+ var request = URLRequest(url: url)
+ request.httpMethod = endpoint.method
+ request.httpBody = try encoder.encode(LoginRequest(id: id, password: password))
+ let (data, _) = try await session.data(for: request)
+ return try decoder.decode(LoginResponse.self, from: data)
+ }
+
+ @available(iOS 16.0, *)
+ func search(_ text: String) async throws -> SearchResponse {
+ let endpoint = endpoint(\.search)
+ let url = host.appendingPathComponent(endpoint.path)
+ .appending(queryItems: [
+ URLQueryItem(name: "text", value: text)
+ ])
+ var request = URLRequest(url: url)
+ request.httpMethod = endpoint.method
+ let (data, _) = try await session.data(for: request)
+ return try decoder.decode(SearchResponse.self, from: data)
+ }
+}
diff --git a/DemoWithPackage/Sources/BuildConfigSwiftDemo/UI/ContentView.swift b/DemoWithPackage/Sources/BuildConfigSwiftDemo/UI/ContentView.swift
new file mode 100644
index 0000000..46424a7
--- /dev/null
+++ b/DemoWithPackage/Sources/BuildConfigSwiftDemo/UI/ContentView.swift
@@ -0,0 +1,28 @@
+//
+// ContentView.swift
+// BuildConfigSwiftDemo
+//
+// Created by 417.72KI on 2022/04/11.
+// Copyright © 2019 417.72KI. All rights reserved.
+//
+
+import SwiftUI
+
+struct ContentView: View {
+ @Environment(\.buildConfig) var config: BuildConfig
+
+ var body: some View {
+ VStack {
+ Text("isDebug: \(String(config.isDebug))")
+ Text("Environment: \(config.environment)")
+ Text("API version: \(config.api.version, format: .number)")
+ Text("PI: \(config.pi, format: .number)")
+ }
+ }
+}
+
+struct ContentView_Previews: PreviewProvider {
+ static var previews: some View {
+ ContentView()
+ }
+}
diff --git a/DemoWithPackage/Sources/BuildConfigSwiftDemo/UI/DemoApp.swift b/DemoWithPackage/Sources/BuildConfigSwiftDemo/UI/DemoApp.swift
new file mode 100644
index 0000000..75fc9e3
--- /dev/null
+++ b/DemoWithPackage/Sources/BuildConfigSwiftDemo/UI/DemoApp.swift
@@ -0,0 +1,23 @@
+//
+// DemoApp.swift
+// BuildConfigSwiftDemo
+//
+// Created by 417.72KI on 2022/04/11.
+// Copyright © 2019 417.72KI. All rights reserved.
+//
+
+import SwiftUI
+
+public struct DemoApp: App {
+ static var buildConfig: BuildConfig = .default
+
+ public init() {
+ }
+
+ public var body: some Scene {
+ WindowGroup {
+ ContentView()
+ .environment(\.buildConfig, Self.buildConfig)
+ }
+ }
+}
diff --git a/DemoWithPackage/Sources/BuildConfigSwiftDemo/Util/BuildConfig+Environment.swift b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Util/BuildConfig+Environment.swift
new file mode 100644
index 0000000..fa36ac2
--- /dev/null
+++ b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Util/BuildConfig+Environment.swift
@@ -0,0 +1,20 @@
+//
+// BuildConfig+Environment.swift
+// BuildConfigSwiftDemo
+//
+// Created by 417.72KI on 2023/10/09.
+// Copyright © 2023 417.72KI. All rights reserved.
+//
+
+import SwiftUI
+
+struct BuildConfigKey: EnvironmentKey {
+ static let defaultValue = BuildConfig.default
+}
+
+extension EnvironmentValues {
+ var buildConfig: BuildConfig {
+ get { self[BuildConfigKey.self] }
+ set { self[BuildConfigKey.self] = newValue }
+ }
+}
diff --git a/DemoWithPackage/Sources/BuildConfigSwiftDemo/Util/Logger.swift b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Util/Logger.swift
new file mode 100644
index 0000000..bcc658f
--- /dev/null
+++ b/DemoWithPackage/Sources/BuildConfigSwiftDemo/Util/Logger.swift
@@ -0,0 +1,42 @@
+//
+// Logger.swift
+// BuildConfigSwiftDemo
+//
+// Created by 417.72KI on 2019/05/09.
+// Copyright © 2019 417.72KI. All rights reserved.
+//
+
+import Foundation
+import XCGLogger
+
+let log = XCGLogger()
+
+func setupLog(isDebugMode: Bool) {
+ if isDebugMode {
+ log.setup(
+ level: .debug,
+ showLogIdentifier: true,
+ showFunctionName: true,
+ showThreadName: true,
+ showLevel: true,
+ showFileNames: true,
+ showLineNumbers: true,
+ showDate: true,
+ writeToFile: nil,
+ fileLevel: nil
+ )
+ } else {
+ log.setup(
+ level: .info,
+ showLogIdentifier: false,
+ showFunctionName: false,
+ showThreadName: false,
+ showLevel: true,
+ showFileNames: false,
+ showLineNumbers: false,
+ showDate: true,
+ writeToFile: nil,
+ fileLevel: nil
+ )
+ }
+}
diff --git a/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/APIClientTests.swift b/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/APIClientTests.swift
new file mode 100644
index 0000000..f9940bd
--- /dev/null
+++ b/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/APIClientTests.swift
@@ -0,0 +1,64 @@
+//
+// APIClientTests.swift
+// BuildConfigSwiftDemoTests
+//
+// Created by 417.72KI on 2023/10/05.
+// Copyright © 2023 417.72KI. All rights reserved.
+//
+
+import XCTest
+import StubNetworkKit
+
+@testable import BuildConfigSwiftDemo
+
+final class APIClientTests: XCTestCase {
+ var config = BuildConfig.fake.api
+
+ var apiClient: APIClient!
+
+ override func setUpWithError() throws {
+ apiClient = APIClient(config: config,
+ session: defaultStubSession)
+ }
+
+ override func tearDownWithError() throws {
+ clearStubs()
+ }
+
+ func testLogin() async throws {
+ stub {
+ Scheme.is("https")
+ Host.is("localhost")
+ Path.is("/login")
+ Method.isPost()
+ Body.isJson(["id": "john_doe", "password": "password"])
+ }.responseJson(["access_token": "foo", "refresh_token": "bar"])
+
+ let response = try await apiClient.login(id: "john_doe",
+ password: "password")
+ XCTAssertEqual("foo", response.accessToken)
+ XCTAssertEqual("bar", response.refreshToken)
+ }
+
+ @available(iOS 16.0, *)
+ func testSearch() async throws {
+ stub {
+ Scheme.is("https")
+ Host.is("localhost")
+ Path.is("/search")
+ Method.isGet()
+ QueryParams.contains(["text": "寿限無"])
+ }.responseJson([
+ "items": [
+ ["name": "foo"],
+ ["name": "bar"],
+ ["name": "baz"],
+ ]
+ ])
+
+ let response = try await apiClient.search("寿限無")
+ XCTAssertEqual(3, response.items.count)
+ XCTAssertEqual("foo", response.items.first?.name)
+ XCTAssertEqual("baz", response.items.last?.name)
+ }
+}
diff --git a/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/BuildConfigSwiftDemoTests.swift b/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/BuildConfigSwiftDemoTests.swift
new file mode 100644
index 0000000..5e2c5f0
--- /dev/null
+++ b/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/BuildConfigSwiftDemoTests.swift
@@ -0,0 +1,30 @@
+//
+// BuildConfigSwiftDemoTests.swift
+// BuildConfigSwiftDemoTests
+//
+// Created by 417.72KI on 2019/05/09.
+// Copyright © 2019 417.72KI. All rights reserved.
+//
+
+import XCTest
+@testable import BuildConfigSwiftDemo
+
+final class BuildConfigTests: XCTestCase {
+ func testDefault() {
+ let buildConfig = BuildConfig.default
+ XCTAssertEqual(1, buildConfig.api.version)
+ XCTAssertEqual("api-dev.example.com", buildConfig.api.host)
+ XCTAssertEqual("debug", buildConfig.environment)
+ XCTAssertTrue(buildConfig.isDebug)
+ XCTAssertEqual(3.14, buildConfig.pi, accuracy: 0.01)
+ }
+
+ func testLoad() throws {
+ let buildConfig = BuildConfig.fake
+ XCTAssertEqual(100, buildConfig.api.version)
+ XCTAssertEqual("localhost", buildConfig.api.host)
+ XCTAssertEqual("staging", buildConfig.environment)
+ XCTAssertFalse(buildConfig.isDebug)
+ XCTAssertEqual(3.14, buildConfig.pi, accuracy: 0.01)
+ }
+}
diff --git a/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/FakeApp.swift b/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/FakeApp.swift
new file mode 100644
index 0000000..bdd4428
--- /dev/null
+++ b/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/FakeApp.swift
@@ -0,0 +1,25 @@
+//
+// FakeApp.swift
+// BuildConfigSwiftDemoTests
+//
+// Created by 417.72KI on 2023/10/09.
+// Copyright © 2023 417.72KI. All rights reserved.
+//
+
+import SwiftUI
+
+@objc(FakeApp)
+final class FakeApp: NSObject, App {
+ override init() { super.init() }
+
+ var body: some Scene {
+ WindowGroup {
+ Text("This is a fake app.")
+ .foregroundStyle(Color.white)
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
+ .background {
+ Color.black
+ }
+ }
+ }
+}
diff --git a/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/TestHelper.swift b/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/TestHelper.swift
new file mode 100644
index 0000000..86ae87e
--- /dev/null
+++ b/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/TestHelper.swift
@@ -0,0 +1,30 @@
+//
+// TestHelper.swift
+// BuildConfigSwiftDemoTests
+//
+// Created by 417.72KI on 2023/10/05.
+// Copyright © 2023 417.72KI. All rights reserved.
+//
+
+import Foundation
+@testable import BuildConfigSwiftDemo
+
+final class TestHelper {
+ private init() {}
+}
+
+extension TestHelper {
+ static var bundle: Bundle { .module }
+}
+
+extension TestHelper {
+ static func path(forResource name: String, ofType ext: String) -> String? {
+ bundle.path(forResource: name, ofType: ext)
+ }
+}
+
+extension BuildConfig {
+ static var fake: Self {
+ .load(from: TestHelper.path(forResource: "test_config", ofType: "json")!)
+ }
+}
diff --git a/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/test_config.json b/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/test_config.json
new file mode 100644
index 0000000..209b487
--- /dev/null
+++ b/DemoWithPackage/Tests/BuildConfigSwiftDemoTests/test_config.json
@@ -0,0 +1,23 @@
+{
+ "api": {
+ "version": 100,
+ "host": "localhost",
+ "endpoint": {
+ "login": {
+ "path": "/login",
+ "method": "POST"
+ },
+ "profile": {
+ "path": "/profile",
+ "method": "GET"
+ },
+ "search": {
+ "path": "/search",
+ "method": "GET"
+ }
+ }
+ },
+ "environment": "staging",
+ "is_debug": false,
+ "pi": 3.14
+}
diff --git a/Makefile b/Makefile
index 0f93b6d..8748862 100644
--- a/Makefile
+++ b/Makefile
@@ -30,11 +30,14 @@ format:
demo_app_init:
scripts/copy-lint-config.sh && \
- cd Demo && \
- xcrun --sdk macosx swift run -c release --package-path Tools xcodegen
+ xcrun --sdk macosx swift run -c release --package-path Tools xcodegen --spec Demo/project.yml && \
+ xcrun --sdk macosx swift run -c release --package-path Tools xcodegen --spec DemoWithPackage/App/project.yml
demo: demo_app_init
xed Demo/BuildConfigSwiftDemo.xcodeproj
+demo_package: demo_app_init
+ xed DemoWithPackage/App/BuildConfigSwiftDemoApp.xcodeproj
+
demo_test:
./scripts/test_demo.sh
diff --git a/Plugins/BuildConfigSwiftGenerate/BuildConfigSwiftGenerate.swift b/Plugins/BuildConfigSwiftGenerate/BuildConfigSwiftGenerate.swift
index 704e194..7e9de3f 100644
--- a/Plugins/BuildConfigSwiftGenerate/BuildConfigSwiftGenerate.swift
+++ b/Plugins/BuildConfigSwiftGenerate/BuildConfigSwiftGenerate.swift
@@ -7,8 +7,38 @@ struct BuildConfigSwiftGenerate: BuildToolPlugin {
context: PackagePlugin.PluginContext,
target: PackagePlugin.Target
) async throws -> [PackagePlugin.Command] {
- Diagnostics.warning("Command only supported as Xcode build tool plugin")
- return []
+ guard let target = target as? SourceModuleTarget else { return [] }
+
+ guard let buildConfigDirectoryPath = try find(
+ resourceDirectoryName,
+ in: target.directory
+ .removingLastComponent()
+ .removingLastComponent()
+ ) else {
+ throw Error.directoryNotFound(in: target.directory)
+ }
+ let generatedFileContainerPath = context.pluginWorkDirectory
+ .appending(subpath: target.name)
+ .appending(subpath: resourceDirectoryName)
+
+ try FileManager.default.createDirectory(
+ atPath: generatedFileContainerPath.string,
+ withIntermediateDirectories: true
+ )
+
+ let description = "\(target.kind) \(target.name)"
+ return [
+ .buildCommand(
+ displayName: "BuildConfig.swift generate for \(description)",
+ executable: try context.tool(named: "buildconfigswift").path,
+ arguments: [
+ "-o",
+ generatedFileContainerPath.string,
+ buildConfigDirectoryPath.string,
+ ],
+ outputFiles: [generatedFileContainerPath.appending(generatedFileName)]
+ ),
+ ]
}
}
@@ -46,10 +76,10 @@ extension BuildConfigSwiftGenerate: XcodeBuildToolPlugin {
arguments: [
"-o",
generatedFileContainerPath.string,
- buildConfigDirectoryPath.string
+ buildConfigDirectoryPath.string,
],
outputFiles: [generatedFileContainerPath.appending(generatedFileName)]
- )
+ ),
]
}
}
diff --git a/Demo/Tools/.gitignore b/Tools/.gitignore
similarity index 100%
rename from Demo/Tools/.gitignore
rename to Tools/.gitignore
diff --git a/Demo/Tools/Package.swift b/Tools/Package.swift
similarity index 100%
rename from Demo/Tools/Package.swift
rename to Tools/Package.swift
diff --git a/Demo/Tools/README.md b/Tools/README.md
similarity index 100%
rename from Demo/Tools/README.md
rename to Tools/README.md
diff --git a/scripts/copy-lint-config.sh b/scripts/copy-lint-config.sh
index 3050016..e1e1958 100755
--- a/scripts/copy-lint-config.sh
+++ b/scripts/copy-lint-config.sh
@@ -25,3 +25,4 @@ cat "${SRCROOT}/.swiftlint.yml" \
| yq '. | .included = ["BuildConfigSwiftDemo"]
| .excluded = ["Tools", "Package.swift"]' \
> "${SRCROOT}/Demo/.swiftlint.yml"
+cp "${SRCROOT}/Demo/.swiftlint.yml" "${SRCROOT}/DemoWithPackage/.swiftlint.yml"
diff --git a/scripts/test_demo.sh b/scripts/test_demo.sh
index b843e4e..0d3b1a2 100755
--- a/scripts/test_demo.sh
+++ b/scripts/test_demo.sh
@@ -11,7 +11,7 @@ fi
if [[ "$(find Demo -depth 1 -name '*.xcodeproj')" == '' ]]; then
echo '\e[33m`xcodeproj` in `Demo` not found, generating...\e[0m'
$(dirname $0)/copy-lint-config.sh
- xcrun --sdk macosx swift run --package-path Demo/Tools xcodegen --spec Demo/project.yml
+ xcrun --sdk macosx swift run --package-path Tools xcodegen --spec Demo/project.yml
fi
PROJECT_PATH=$(find Demo -depth 1 -name '*.xcodeproj' | head -n 1)
diff --git a/scripts/test_demo_package.sh b/scripts/test_demo_package.sh
new file mode 100755
index 0000000..c231161
--- /dev/null
+++ b/scripts/test_demo_package.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+set -o pipefail
+
+XCRESULT_PATH="$(git rev-parse --show-toplevel)/test_output/BuildConfigDemoPackage.xcresult"
+
+if [[ -e "$XCRESULT_PATH" ]]; then
+ rm -rf "$XCRESULT_PATH"
+fi
+
+$(dirname $0)/copy-lint-config.sh
+
+cd DemoWithPackage
+SCHEME="$(xcrun --sdk macosx xcodebuild -list -json | jq -rc '.workspace.schemes[] | select(. | contains("Demo"))')"
+
+echo "\e[32mScheme: $SCHEME\e[0m"
+
+xcrun --sdk macosx xcodebuild \
+ -skipPackagePluginValidation \
+ -enableCodeCoverage YES \
+ -scheme "${SCHEME}" \
+ -destination 'platform=iOS Simulator,name=iPhone 14 Pro' \
+ -derivedDataPath 'DerivedData' \
+ -clonedSourcePackagesDirPath '.build/SourcePackages' \
+ -resultBundlePath "$XCRESULT_PATH" \
+ clean test | xcpretty