Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Documentation/Manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 Package Integration).

## Under the hood

Expand Down
20 changes: 10 additions & 10 deletions Plugins/InstallSafeDITool/InstallCLIPluginCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,27 +67,27 @@ 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(
at: downloadedURL,
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(
Expand Down Expand Up @@ -143,27 +143,27 @@ 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(
at: downloadedURL,
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(
Expand Down
26 changes: 13 additions & 13 deletions Plugins/SafeDIGenerator/SafeDIGenerateDependencyTree.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
""")
}

Expand Down Expand Up @@ -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
Expand Down
28 changes: 14 additions & 14 deletions Plugins/SafeDIPrebuiltGenerator/SafeDIGenerateDependencyTree.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -93,11 +93,11 @@ 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
"""
}
}
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 {
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions Plugins/Shared.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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.

Expand Down
2 changes: 1 addition & 1 deletion SafeDI.podspec
Original file line number Diff line number Diff line change
@@ -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'
Expand Down
2 changes: 1 addition & 1 deletion Sources/SafeDITool/SafeDITool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ struct SafeDITool: AsyncParsableCommand, Sendable {
// MARK: Internal

static var currentVersion: String {
"1.5.0"
"1.5.1"
}

func run() async throws {
Expand Down
Loading