Xcode integration of Swift Package Plugins in Xcode 14

Xcode integration of Swift Package Plugins in Xcode 14

Xcode Extensions to the PackagePlugin API

ยท

3 min read

WWDC 2022 video "Meet Swift Package plugins" explains how to perform actions on Swift packages and Xcode projects with Swift package plugins.

Swift Package plugins can be divided into two types:

  1. Command plugin
  2. Build tool plugin

Both plugin types were introduced in Swift 5.6 and work in Xcode 13.3 when used on Swift packages. So what's new in Xcode 14 (Swift 5.7) ?

Command plugins can be triggered within Xcode

No adoption from plugin authors is required :)

You can trigger the command plugin from the contextual menu

Trigger Plugin from Contextual Menu

You can also trigger the command plugin from the "File Menu"

Trigger Plugin from File Menu

Both options will start the following dialog that lets you choose on which target(s) the command plugin shall be executed

Command Plugin Dialog

Command plugins can be executed on Xcode projects

Adoption from plugin authors is required or otherwise you will receive the following error when triggering a command plugin on an Xcode project: Malformed input JSON: Couldn't find the XcodeProjectPlugin entry point

New in Xcode 14 is a library module called XcodeProjectPlugin which extends SPM's PackagePlugin API.

With the XcodeProjectPlugin API, a Swift package plugin can get a simplified description of the Xcode projectโ€™s structure (XcodePluginContext). The XcodePluginContext input structure is similar to the regular PluginContext structure, except that it provides access to an Xcode project that uses Xcode naming and semantics for the project model (which is somewhat different from that of SwiftPM).

Structure of a command plugin with conditional support for Xcode projects when running in Xcode

import PackagePlugin

@main
struct MyPlugin: CommandPlugin {

    /// This entry point is called when operating on a Swift package.
    func performCommand(context: PluginContext, arguments: [String]) throws {
        print("Command plugin execution for Swift package \(context.package.displayName)")
    }
}

#if canImport(XcodeProjectPlugin)
import XcodeProjectPlugin

extension MyPlugin: XcodeCommandPlugin {

    /// ๐Ÿ‘‡ This entry point is called when operating on an Xcode project.
    func performCommand(context: XcodePluginContext, arguments: [String]) throws {
       print("Command plugin execution for Xcode project \(context.xcodeProject.displayName)")
    }
}
#endif

Build plugins can be executed on Xcode projects

A build tool plugin is able to conform to the new XcodeBuildToolPlugin type. This type was introduced in XcodeProjectPlugin with Xcode 14 Beta 3.

Structure of a build tool plugin with conditional support for Xcode projects when running in Xcode

import PackagePlugin

@main
struct MyPlugin: BuildToolPlugin {

    /// This entry point is called when operating on a Swift package.
    func createBuildCommands(context: PluginContext, target: Target) throws -> [Command]
        return []
    }
}

#if canImport(XcodeProjectPlugin)
import XcodeProjectPlugin

extension MyPlugin: XcodeBuildToolPlugin {

    /// ๐Ÿ‘‡ This entry point is called when operating on an Xcode project.
    func createBuildCommands(context: XcodePluginContext, target: XcodeTarget) throws -> [Command]
        return []
    }
}
#endif

The Xcode target editor lets an App target be configured to use a build tool plugin provided by any of the projectโ€™s package dependencies.

Run Build Tool Plug-ins

Did you find this article valuable?

Support Marco Eidinger by becoming a sponsor. Any amount is appreciated!