From 5ba3e8c05039559f9e658baaa5a0193d151c1f63 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Feb 2026 17:43:14 +0000 Subject: [PATCH 1/7] Fix SPM plugins failing when package path contains spaces URL.path() defaults to percentEncoded: true, which converts spaces to %20. This causes FileManager.fileExists, chmod, and CLI argument passing to fail when any path component contains spaces. Changed all .path() calls in plugin code to .path(percentEncoded: false). https://claude.ai/code/session_019ZDtwgBVq34rEkrVfQG94u --- .../InstallCLIPluginCommand.swift | 20 +++++++------- .../SafeDIGenerateDependencyTree.swift | 26 +++++++++---------- .../SafeDIGenerateDependencyTree.swift | 26 +++++++++---------- Plugins/Shared.swift | 4 +-- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Plugins/InstallSafeDITool/InstallCLIPluginCommand.swift b/Plugins/InstallSafeDITool/InstallCLIPluginCommand.swift index 140f6222..af4ff67c 100644 --- a/Plugins/InstallSafeDITool/InstallCLIPluginCommand.swift +++ b/Plugins/InstallSafeDITool/InstallCLIPluginCommand.swift @@ -67,19 +67,19 @@ struct InstallSafeDITool: CommandPlugin { let (downloadedURL, _) = try await URLSession.shared.download( for: URLRequest(url: githubDownloadURL) ) - let downloadedFileAttributes = try FileManager.default.attributesOfItem(atPath: downloadedURL.path()) + let downloadedFileAttributes = try FileManager.default.attributesOfItem(atPath: downloadedURL.path(percentEncoded: false)) guard let currentPermissions = downloadedFileAttributes[.posixPermissions] as? NSNumber, // Add executable attributes to the downloaded file. - chmod(downloadedURL.path(), mode_t(currentPermissions.uint32Value) | S_IXUSR | S_IXGRP | S_IXOTH) == 0 + chmod(downloadedURL.path(percentEncoded: false), mode_t(currentPermissions.uint32Value) | S_IXUSR | S_IXGRP | S_IXOTH) == 0 else { - Diagnostics.error("Failed to make downloaded file \(downloadedURL.path()) executable") + Diagnostics.error("Failed to make downloaded file \(downloadedURL.path(percentEncoded: false)) executable") exit(1) } try FileManager.default.createDirectory( at: expectedToolFolder, withIntermediateDirectories: true ) - if FileManager.default.fileExists(atPath: expectedToolLocation.path()) { + if FileManager.default.fileExists(atPath: expectedToolLocation.path(percentEncoded: false)) { try FileManager.default.removeItem(at: expectedToolLocation) } try FileManager.default.moveItem( @@ -87,7 +87,7 @@ struct InstallSafeDITool: CommandPlugin { to: expectedToolLocation ) let gitIgnoreLocation = context.safediFolder.appending(component: ".gitignore") - if !FileManager.default.fileExists(atPath: gitIgnoreLocation.path()) { + if !FileManager.default.fileExists(atPath: gitIgnoreLocation.path(percentEncoded: false)) { try """ */\(expectedToolLocation.lastPathComponent) """.write( @@ -143,19 +143,19 @@ struct InstallSafeDITool: CommandPlugin { let (downloadedURL, _) = try await URLSession.shared.download( for: URLRequest(url: githubDownloadURL) ) - let downloadedFileAttributes = try FileManager.default.attributesOfItem(atPath: downloadedURL.path()) + let downloadedFileAttributes = try FileManager.default.attributesOfItem(atPath: downloadedURL.path(percentEncoded: false)) guard let currentPermissions = downloadedFileAttributes[.posixPermissions] as? NSNumber, // Add executable attributes to the downloaded file. - chmod(downloadedURL.path(), mode_t(currentPermissions.uint32Value) | S_IXUSR | S_IXGRP | S_IXOTH) == 0 + chmod(downloadedURL.path(percentEncoded: false), mode_t(currentPermissions.uint32Value) | S_IXUSR | S_IXGRP | S_IXOTH) == 0 else { - Diagnostics.error("Failed to make downloaded file \(downloadedURL.path()) executable") + Diagnostics.error("Failed to make downloaded file \(downloadedURL.path(percentEncoded: false)) executable") exit(1) } try FileManager.default.createDirectory( at: expectedToolFolder, withIntermediateDirectories: true ) - if FileManager.default.fileExists(atPath: expectedToolLocation.path()) { + if FileManager.default.fileExists(atPath: expectedToolLocation.path(percentEncoded: false)) { try FileManager.default.removeItem(at: expectedToolLocation) } try FileManager.default.moveItem( @@ -163,7 +163,7 @@ struct InstallSafeDITool: CommandPlugin { to: expectedToolLocation ) let gitIgnoreLocation = safediFolder.appending(component: ".gitignore") - if !FileManager.default.fileExists(atPath: gitIgnoreLocation.path()) { + if !FileManager.default.fileExists(atPath: gitIgnoreLocation.path(percentEncoded: false)) { try """ */\(expectedToolLocation.lastPathComponent) """.write( diff --git a/Plugins/SafeDIGenerator/SafeDIGenerateDependencyTree.swift b/Plugins/SafeDIGenerator/SafeDIGenerateDependencyTree.swift index 2f60abc6..c002e39a 100644 --- a/Plugins/SafeDIGenerator/SafeDIGenerateDependencyTree.swift +++ b/Plugins/SafeDIGenerator/SafeDIGenerateDependencyTree.swift @@ -56,28 +56,28 @@ struct SafeDIGenerateDependencyTree: BuildToolPlugin { ) let includeCSV = context.safediFolder.appending(components: "configuration", "include.csv") - let includeArguments: [String] = if FileManager.default.fileExists(atPath: includeCSV.path()) { + let includeArguments: [String] = if FileManager.default.fileExists(atPath: includeCSV.path(percentEncoded: false)) { [ "--include-file-path", - includeCSV.path(), + includeCSV.path(percentEncoded: false), ] } else { [] } let additionalImportedModulesCSV = context.safediFolder.appending(components: "configuration", "additionalImportedModules.csv") - let additionalImportedModulesArguments: [String] = if FileManager.default.fileExists(atPath: additionalImportedModulesCSV.path()) { + let additionalImportedModulesArguments: [String] = if FileManager.default.fileExists(atPath: additionalImportedModulesCSV.path(percentEncoded: false)) { [ "--additional-imported-modules-file-path", - additionalImportedModulesCSV.path(), + additionalImportedModulesCSV.path(percentEncoded: false), ] } else { [] } let arguments = [ - inputSourcesFile.path(), + inputSourcesFile.path(percentEncoded: false), "--dependency-tree-output", - outputSwiftFile.path(), + outputSwiftFile.path(percentEncoded: false), ] + includeArguments + additionalImportedModulesArguments let downloadedToolLocation = context.downloadedToolLocation @@ -87,7 +87,7 @@ struct SafeDIGenerateDependencyTree: BuildToolPlugin { Using a debug SafeDITool binary, which is 15x slower than the release version. To install the release SafeDITool binary for version \(safeDIVersion), run: - \tswift package --package-path \(context.package.directoryURL.path()) --allow-network-connections all --allow-writing-to-package-directory safedi-release-install + \tswift package --package-path \(context.package.directoryURL.path(percentEncoded: false)) --allow-network-connections all --allow-writing-to-package-directory safedi-release-install """) } @@ -173,28 +173,28 @@ extension Target { ) let includeCSV = context.safediFolder.appending(components: "configuration", "include.csv") - let includeArguments: [String] = if FileManager.default.fileExists(atPath: includeCSV.path()) { + let includeArguments: [String] = if FileManager.default.fileExists(atPath: includeCSV.path(percentEncoded: false)) { [ "--include-file-path", - includeCSV.path(), + includeCSV.path(percentEncoded: false), ] } else { [] } let additionalImportedModulesCSV = context.safediFolder.appending(components: "configuration", "additionalImportedModules.csv") - let additionalImportedModulesArguments: [String] = if FileManager.default.fileExists(atPath: additionalImportedModulesCSV.path()) { + let additionalImportedModulesArguments: [String] = if FileManager.default.fileExists(atPath: additionalImportedModulesCSV.path(percentEncoded: false)) { [ "--additional-imported-modules-file-path", - additionalImportedModulesCSV.path(), + additionalImportedModulesCSV.path(percentEncoded: false), ] } else { [] } let arguments = [ - inputSourcesFile.path(), + inputSourcesFile.path(percentEncoded: false), "--dependency-tree-output", - outputSwiftFile.path(), + outputSwiftFile.path(percentEncoded: false), ] + includeArguments + additionalImportedModulesArguments let downloadedToolLocation = context.downloadedToolLocation diff --git a/Plugins/SafeDIPrebuiltGenerator/SafeDIGenerateDependencyTree.swift b/Plugins/SafeDIPrebuiltGenerator/SafeDIGenerateDependencyTree.swift index d2bfb66a..b4462f0e 100644 --- a/Plugins/SafeDIPrebuiltGenerator/SafeDIGenerateDependencyTree.swift +++ b/Plugins/SafeDIPrebuiltGenerator/SafeDIGenerateDependencyTree.swift @@ -56,28 +56,28 @@ struct SafeDIGenerateDependencyTree: BuildToolPlugin { ) let includeCSV = context.safediFolder.appending(components: "configuration", "include.csv") - let includeArguments: [String] = if FileManager.default.fileExists(atPath: includeCSV.path()) { + let includeArguments: [String] = if FileManager.default.fileExists(atPath: includeCSV.path(percentEncoded: false)) { [ "--include-file-path", - includeCSV.path(), + includeCSV.path(percentEncoded: false), ] } else { [] } let additionalImportedModulesCSV = context.safediFolder.appending(components: "configuration", "additionalImportedModules.csv") - let additionalImportedModulesArguments: [String] = if FileManager.default.fileExists(atPath: additionalImportedModulesCSV.path()) { + let additionalImportedModulesArguments: [String] = if FileManager.default.fileExists(atPath: additionalImportedModulesCSV.path(percentEncoded: false)) { [ "--additional-imported-modules-file-path", - additionalImportedModulesCSV.path(), + additionalImportedModulesCSV.path(percentEncoded: false), ] } else { [] } let arguments = [ - inputSourcesFile.path(), + inputSourcesFile.path(percentEncoded: false), "--dependency-tree-output", - outputSwiftFile.path(), + outputSwiftFile.path(percentEncoded: false), ] + includeArguments + additionalImportedModulesArguments let downloadedToolLocation = context.downloadedToolLocation @@ -97,7 +97,7 @@ struct SafeDIGenerateDependencyTree: BuildToolPlugin { """ } } - throw NoReleaseBinaryFoundError(safeDIVersion: safeDIVersion, packagePath: context.package.directoryURL.path()) + throw NoReleaseBinaryFoundError(safeDIVersion: safeDIVersion, packagePath: context.package.directoryURL.path(percentEncoded: false)) } else { struct NoReleaseBinaryAvailableError: Error, CustomStringConvertible { var description: String { @@ -183,28 +183,28 @@ extension Target { ) let includeCSV = context.safediFolder.appending(components: "configuration", "include.csv") - let includeArguments: [String] = if FileManager.default.fileExists(atPath: includeCSV.path()) { + let includeArguments: [String] = if FileManager.default.fileExists(atPath: includeCSV.path(percentEncoded: false)) { [ "--include-file-path", - includeCSV.path(), + includeCSV.path(percentEncoded: false), ] } else { [] } let additionalImportedModulesCSV = context.safediFolder.appending(components: "configuration", "additionalImportedModules.csv") - let additionalImportedModulesArguments: [String] = if FileManager.default.fileExists(atPath: additionalImportedModulesCSV.path()) { + let additionalImportedModulesArguments: [String] = if FileManager.default.fileExists(atPath: additionalImportedModulesCSV.path(percentEncoded: false)) { [ "--additional-imported-modules-file-path", - additionalImportedModulesCSV.path(), + additionalImportedModulesCSV.path(percentEncoded: false), ] } else { [] } let arguments = [ - inputSourcesFile.path(), + inputSourcesFile.path(percentEncoded: false), "--dependency-tree-output", - outputSwiftFile.path(), + outputSwiftFile.path(percentEncoded: false), ] + includeArguments + additionalImportedModulesArguments let downloadedToolLocation = context.downloadedToolLocation diff --git a/Plugins/Shared.swift b/Plugins/Shared.swift index 7be35897..6eaef007 100644 --- a/Plugins/Shared.swift +++ b/Plugins/Shared.swift @@ -59,7 +59,7 @@ import PackagePlugin } var downloadedToolLocation: URL? { - guard FileManager.default.fileExists(atPath: expectedToolLocation.path()) else { return nil } + guard FileManager.default.fileExists(atPath: expectedToolLocation.path(percentEncoded: false)) else { return nil } return expectedToolLocation } } @@ -111,7 +111,7 @@ extension PackagePlugin.PluginContext { var downloadedToolLocation: URL? { guard let expectedToolLocation, - FileManager.default.fileExists(atPath: expectedToolLocation.path()) + FileManager.default.fileExists(atPath: expectedToolLocation.path(percentEncoded: false)) else { return nil } return expectedToolLocation } From 5994a35be0ebf0195378176c0c89b46c21f7e758 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Feb 2026 17:52:22 +0000 Subject: [PATCH 2/7] Rename ExamplePackageIntegration to have spaces in the path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This serves as a CI regression test for the .path(percentEncoded:) fix — if paths with spaces break again, this CI job will fail. https://claude.ai/code/session_019ZDtwgBVq34rEkrVfQG94u --- .github/workflows/ci.yml | 2 +- .../Package.resolved | 0 .../Package.swift | 0 .../Sources/ChildAModule/ChildA.swift | 0 .../Sources/ChildBModule/ChildB.swift | 0 .../Sources/ChildCModule/ChildC.swift | 0 .../Sources/GrandchildrenModule/Grandchild C/GrandchildC.swift | 0 .../Sources/GrandchildrenModule/GrandchildA.swift | 0 .../Sources/GrandchildrenModule/GrandchildB.swift | 0 .../Sources/RootModule/Root.swift | 0 .../Sources/SharedModule/SharedThing.swift | 0 .../Sources/SharedModule/UserDefaults.swift | 0 12 files changed, 1 insertion(+), 1 deletion(-) rename Examples/{ExamplePackageIntegration => Example Package Integration}/Package.resolved (100%) rename Examples/{ExamplePackageIntegration => Example Package Integration}/Package.swift (100%) rename Examples/{ExamplePackageIntegration => Example Package Integration}/Sources/ChildAModule/ChildA.swift (100%) rename Examples/{ExamplePackageIntegration => Example Package Integration}/Sources/ChildBModule/ChildB.swift (100%) rename Examples/{ExamplePackageIntegration => Example Package Integration}/Sources/ChildCModule/ChildC.swift (100%) rename Examples/{ExamplePackageIntegration => Example Package Integration}/Sources/GrandchildrenModule/Grandchild C/GrandchildC.swift (100%) rename Examples/{ExamplePackageIntegration => Example Package Integration}/Sources/GrandchildrenModule/GrandchildA.swift (100%) rename Examples/{ExamplePackageIntegration => Example Package Integration}/Sources/GrandchildrenModule/GrandchildB.swift (100%) rename Examples/{ExamplePackageIntegration => Example Package Integration}/Sources/RootModule/Root.swift (100%) rename Examples/{ExamplePackageIntegration => Example Package Integration}/Sources/SharedModule/SharedThing.swift (100%) rename Examples/{ExamplePackageIntegration => Example Package Integration}/Sources/SharedModule/UserDefaults.swift (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c583a534..6e56e1eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,7 @@ jobs: - name: Select Xcode Version run: sudo xcode-select --switch /Applications/Xcode_16.app/Contents/Developer - name: Build Package Integration - run: xcrun swift build --package-path Examples/ExamplePackageIntegration + run: xcrun swift build --package-path "Examples/Example Package Integration" spm-prebuilt-package-integration: name: Build Prebuilt Package Integration on Xcode 16 diff --git a/Examples/ExamplePackageIntegration/Package.resolved b/Examples/Example Package Integration/Package.resolved similarity index 100% rename from Examples/ExamplePackageIntegration/Package.resolved rename to Examples/Example Package Integration/Package.resolved diff --git a/Examples/ExamplePackageIntegration/Package.swift b/Examples/Example Package Integration/Package.swift similarity index 100% rename from Examples/ExamplePackageIntegration/Package.swift rename to Examples/Example Package Integration/Package.swift diff --git a/Examples/ExamplePackageIntegration/Sources/ChildAModule/ChildA.swift b/Examples/Example Package Integration/Sources/ChildAModule/ChildA.swift similarity index 100% rename from Examples/ExamplePackageIntegration/Sources/ChildAModule/ChildA.swift rename to Examples/Example Package Integration/Sources/ChildAModule/ChildA.swift diff --git a/Examples/ExamplePackageIntegration/Sources/ChildBModule/ChildB.swift b/Examples/Example Package Integration/Sources/ChildBModule/ChildB.swift similarity index 100% rename from Examples/ExamplePackageIntegration/Sources/ChildBModule/ChildB.swift rename to Examples/Example Package Integration/Sources/ChildBModule/ChildB.swift diff --git a/Examples/ExamplePackageIntegration/Sources/ChildCModule/ChildC.swift b/Examples/Example Package Integration/Sources/ChildCModule/ChildC.swift similarity index 100% rename from Examples/ExamplePackageIntegration/Sources/ChildCModule/ChildC.swift rename to Examples/Example Package Integration/Sources/ChildCModule/ChildC.swift diff --git a/Examples/ExamplePackageIntegration/Sources/GrandchildrenModule/Grandchild C/GrandchildC.swift b/Examples/Example Package Integration/Sources/GrandchildrenModule/Grandchild C/GrandchildC.swift similarity index 100% rename from Examples/ExamplePackageIntegration/Sources/GrandchildrenModule/Grandchild C/GrandchildC.swift rename to Examples/Example Package Integration/Sources/GrandchildrenModule/Grandchild C/GrandchildC.swift diff --git a/Examples/ExamplePackageIntegration/Sources/GrandchildrenModule/GrandchildA.swift b/Examples/Example Package Integration/Sources/GrandchildrenModule/GrandchildA.swift similarity index 100% rename from Examples/ExamplePackageIntegration/Sources/GrandchildrenModule/GrandchildA.swift rename to Examples/Example Package Integration/Sources/GrandchildrenModule/GrandchildA.swift diff --git a/Examples/ExamplePackageIntegration/Sources/GrandchildrenModule/GrandchildB.swift b/Examples/Example Package Integration/Sources/GrandchildrenModule/GrandchildB.swift similarity index 100% rename from Examples/ExamplePackageIntegration/Sources/GrandchildrenModule/GrandchildB.swift rename to Examples/Example Package Integration/Sources/GrandchildrenModule/GrandchildB.swift diff --git a/Examples/ExamplePackageIntegration/Sources/RootModule/Root.swift b/Examples/Example Package Integration/Sources/RootModule/Root.swift similarity index 100% rename from Examples/ExamplePackageIntegration/Sources/RootModule/Root.swift rename to Examples/Example Package Integration/Sources/RootModule/Root.swift diff --git a/Examples/ExamplePackageIntegration/Sources/SharedModule/SharedThing.swift b/Examples/Example Package Integration/Sources/SharedModule/SharedThing.swift similarity index 100% rename from Examples/ExamplePackageIntegration/Sources/SharedModule/SharedThing.swift rename to Examples/Example Package Integration/Sources/SharedModule/SharedThing.swift diff --git a/Examples/ExamplePackageIntegration/Sources/SharedModule/UserDefaults.swift b/Examples/Example Package Integration/Sources/SharedModule/UserDefaults.swift similarity index 100% rename from Examples/ExamplePackageIntegration/Sources/SharedModule/UserDefaults.swift rename to Examples/Example Package Integration/Sources/SharedModule/UserDefaults.swift From 97d5736e09215ac05711fc6b91288927dd9f55ac Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Feb 2026 17:56:57 +0000 Subject: [PATCH 3/7] Bump version to 1.5.1 https://claude.ai/code/session_019ZDtwgBVq34rEkrVfQG94u --- Plugins/Shared.swift | 2 +- SafeDI.podspec | 2 +- Sources/SafeDITool/SafeDITool.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Plugins/Shared.swift b/Plugins/Shared.swift index 6eaef007..a224dac9 100644 --- a/Plugins/Shared.swift +++ b/Plugins/Shared.swift @@ -29,7 +29,7 @@ import PackagePlugin // As of Xcode 15.0, Xcode command plugins have no way to read the package manifest, therefore we must hardcode the version number. // It is okay for this number to be behind the most current release if the inputs and outputs to SafeDITool have not changed. // Unlike SPM plugins, Xcode plugins can not determine the current version number, so we must hardcode it. - "1.5.0" + "1.5.1" } var safeDIOrigin: URL { diff --git a/SafeDI.podspec b/SafeDI.podspec index aa39bbc6..16e59b56 100644 --- a/SafeDI.podspec +++ b/SafeDI.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'SafeDI' - s.version = '1.5.0' + s.version = '1.5.1' s.summary = 'Compile-time-safe dependency injection' s.homepage = 'https://github.com/dfed/SafeDI' s.license = 'MIT' diff --git a/Sources/SafeDITool/SafeDITool.swift b/Sources/SafeDITool/SafeDITool.swift index 06dc5430..2be304aa 100644 --- a/Sources/SafeDITool/SafeDITool.swift +++ b/Sources/SafeDITool/SafeDITool.swift @@ -50,7 +50,7 @@ struct SafeDITool: AsyncParsableCommand, Sendable { // MARK: Internal static var currentVersion: String { - "1.5.0" + "1.5.1" } func run() async throws { From 8e0055f65de22a8236ce20444d21749e556e1db2 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Feb 2026 17:59:03 +0000 Subject: [PATCH 4/7] Fix broken links to renamed Example Package Integration directory https://claude.ai/code/session_019ZDtwgBVq34rEkrVfQG94u --- Documentation/Manual.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/Manual.md b/Documentation/Manual.md index 05f8a667..d2036b9f 100644 --- a/Documentation/Manual.md +++ b/Documentation/Manual.md @@ -511,7 +511,7 @@ UIKit applications’ natural root is the `UIApplicationDelegate`-conforming app ## Example applications -We’ve tied everything together with an example multi-user notes application backed by SwiftUI. You can compile and run this code in [an example single-module Xcode project](../Examples/ExampleProjectIntegration). This same multi-user notes app also exists in [an example multi-module Xcode project](../Examples/ExampleMultiProjectIntegration), and also in [an example Xcode project using CocoaPods](../Examples/ExampleCocoaPodsIntegration). We have also created [an example multi-module `Package.swift` that integrates with SafeDI](../Examples/ExamplePackageIntegration). +We’ve tied everything together with an example multi-user notes application backed by SwiftUI. You can compile and run this code in [an example single-module Xcode project](../Examples/ExampleProjectIntegration). This same multi-user notes app also exists in [an example multi-module Xcode project](../Examples/ExampleMultiProjectIntegration), and also in [an example Xcode project using CocoaPods](../Examples/ExampleCocoaPodsIntegration). We have also created [an example multi-module `Package.swift` that integrates with SafeDI](../Examples/Example%20Package%20Integration). ## Under the hood diff --git a/README.md b/README.md index 20a60716..d430cc93 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ If your first-party code is entirely contained in a Swift Package with one or mo ] ``` -You can see this integration in practice in the [ExamplePackageIntegration](Examples/ExamplePackageIntegration) package. +You can see this integration in practice in the [Example Package Integration](Examples/Example%20Package%20Integration) package. Unlike the `SafeDIGenerator` Xcode project plugin, the `SafeDIGenerator` Swift package plugin finds source files in dependent modules without additional configuration steps. If you find that SafeDI’s generated dependency tree is missing required imports, you may create a `.safedi/configuration/additionalImportedModules.csv` with a comma-separated list of module names to import. The `.safedi/` folder must be placed in the same folder as your `Package.swift` file. From 624aae420fd548c7af95b41eab11c0dfef142f3c Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Feb 2026 18:02:04 +0000 Subject: [PATCH 5/7] Use angle brackets for markdown links with spaces in path The link checker resolves %20 literally against the filesystem. Angle bracket syntax () lets markdown handle the spaces correctly while keeping the actual path on disk. https://claude.ai/code/session_019ZDtwgBVq34rEkrVfQG94u --- Documentation/Manual.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/Manual.md b/Documentation/Manual.md index d2036b9f..8074d0b9 100644 --- a/Documentation/Manual.md +++ b/Documentation/Manual.md @@ -511,7 +511,7 @@ UIKit applications’ natural root is the `UIApplicationDelegate`-conforming app ## Example applications -We’ve tied everything together with an example multi-user notes application backed by SwiftUI. You can compile and run this code in [an example single-module Xcode project](../Examples/ExampleProjectIntegration). This same multi-user notes app also exists in [an example multi-module Xcode project](../Examples/ExampleMultiProjectIntegration), and also in [an example Xcode project using CocoaPods](../Examples/ExampleCocoaPodsIntegration). We have also created [an example multi-module `Package.swift` that integrates with SafeDI](../Examples/Example%20Package%20Integration). +We’ve tied everything together with an example multi-user notes application backed by SwiftUI. You can compile and run this code in [an example single-module Xcode project](../Examples/ExampleProjectIntegration). This same multi-user notes app also exists in [an example multi-module Xcode project](../Examples/ExampleMultiProjectIntegration), and also in [an example Xcode project using CocoaPods](../Examples/ExampleCocoaPodsIntegration). We have also created [an example multi-module `Package.swift` that integrates with SafeDI](<../Examples/Example Package Integration>). ## Under the hood diff --git a/README.md b/README.md index d430cc93..dc319f01 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ If your first-party code is entirely contained in a Swift Package with one or mo ] ``` -You can see this integration in practice in the [Example Package Integration](Examples/Example%20Package%20Integration) package. +You can see this integration in practice in the [Example Package Integration]() package. Unlike the `SafeDIGenerator` Xcode project plugin, the `SafeDIGenerator` Swift package plugin finds source files in dependent modules without additional configuration steps. If you find that SafeDI’s generated dependency tree is missing required imports, you may create a `.safedi/configuration/additionalImportedModules.csv` with a comma-separated list of module names to import. The `.safedi/` folder must be placed in the same folder as your `Package.swift` file. From 7228feae2c0315e9819b4da08e24fbed6ec3188b Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Feb 2026 18:04:14 +0000 Subject: [PATCH 6/7] Use plain spaces in markdown links to Example Package Integration https://claude.ai/code/session_019ZDtwgBVq34rEkrVfQG94u --- Documentation/Manual.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/Manual.md b/Documentation/Manual.md index 8074d0b9..0284cec1 100644 --- a/Documentation/Manual.md +++ b/Documentation/Manual.md @@ -511,7 +511,7 @@ UIKit applications’ natural root is the `UIApplicationDelegate`-conforming app ## Example applications -We’ve tied everything together with an example multi-user notes application backed by SwiftUI. You can compile and run this code in [an example single-module Xcode project](../Examples/ExampleProjectIntegration). This same multi-user notes app also exists in [an example multi-module Xcode project](../Examples/ExampleMultiProjectIntegration), and also in [an example Xcode project using CocoaPods](../Examples/ExampleCocoaPodsIntegration). We have also created [an example multi-module `Package.swift` that integrates with SafeDI](<../Examples/Example Package Integration>). +We’ve tied everything together with an example multi-user notes application backed by SwiftUI. You can compile and run this code in [an example single-module Xcode project](../Examples/ExampleProjectIntegration). This same multi-user notes app also exists in [an example multi-module Xcode project](../Examples/ExampleMultiProjectIntegration), and also in [an example Xcode project using CocoaPods](../Examples/ExampleCocoaPodsIntegration). We have also created [an example multi-module `Package.swift` that integrates with SafeDI](../Examples/Example Package Integration). ## Under the hood diff --git a/README.md b/README.md index dc319f01..66607138 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ If your first-party code is entirely contained in a Swift Package with one or mo ] ``` -You can see this integration in practice in the [Example Package Integration]() package. +You can see this integration in practice in the [Example Package Integration](Examples/Example Package Integration) package. Unlike the `SafeDIGenerator` Xcode project plugin, the `SafeDIGenerator` Swift package plugin finds source files in dependent modules without additional configuration steps. If you find that SafeDI’s generated dependency tree is missing required imports, you may create a `.safedi/configuration/additionalImportedModules.csv` with a comma-separated list of module names to import. The `.safedi/` folder must be placed in the same folder as your `Package.swift` file. From 94efa66e6161a4599db92cf1d383420cbdf25096 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Feb 2026 18:41:14 +0000 Subject: [PATCH 7/7] Quote package path in diagnostic shell commands Without quotes, users who copy the suggested command get a broken invocation when their package path contains spaces. https://claude.ai/code/session_019ZDtwgBVq34rEkrVfQG94u --- Plugins/SafeDIGenerator/SafeDIGenerateDependencyTree.swift | 2 +- .../SafeDIPrebuiltGenerator/SafeDIGenerateDependencyTree.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/SafeDIGenerator/SafeDIGenerateDependencyTree.swift b/Plugins/SafeDIGenerator/SafeDIGenerateDependencyTree.swift index c002e39a..d8d17030 100644 --- a/Plugins/SafeDIGenerator/SafeDIGenerateDependencyTree.swift +++ b/Plugins/SafeDIGenerator/SafeDIGenerateDependencyTree.swift @@ -87,7 +87,7 @@ struct SafeDIGenerateDependencyTree: BuildToolPlugin { Using a debug SafeDITool binary, which is 15x slower than the release version. To install the release SafeDITool binary for version \(safeDIVersion), run: - \tswift package --package-path \(context.package.directoryURL.path(percentEncoded: false)) --allow-network-connections all --allow-writing-to-package-directory safedi-release-install + \tswift package --package-path "\(context.package.directoryURL.path(percentEncoded: false))" --allow-network-connections all --allow-writing-to-package-directory safedi-release-install """) } diff --git a/Plugins/SafeDIPrebuiltGenerator/SafeDIGenerateDependencyTree.swift b/Plugins/SafeDIPrebuiltGenerator/SafeDIGenerateDependencyTree.swift index b4462f0e..ee80833a 100644 --- a/Plugins/SafeDIPrebuiltGenerator/SafeDIGenerateDependencyTree.swift +++ b/Plugins/SafeDIPrebuiltGenerator/SafeDIGenerateDependencyTree.swift @@ -93,7 +93,7 @@ struct SafeDIGenerateDependencyTree: BuildToolPlugin { var description: String { """ Install the release SafeDITool binary for version \(safeDIVersion): - \tswift package --package-path \(packagePath) --allow-network-connections all --allow-writing-to-package-directory safedi-release-install + \tswift package --package-path "\(packagePath)" --allow-network-connections all --allow-writing-to-package-directory safedi-release-install """ } }