index all rss twitter github linkedin email

Álvaro Ramírez

16 June 2021 xcodebuild's SPM support (Xcode 11)

Had been a while since I looked into generating Xcode projects from a Swift package. On my latest use of the generate-xcodeproj subcommand, I was greeted by a nice warning surprise…

swift package generate-xcodeproj
warning: Xcode can open and build Swift Packages directly. 'generate-xcodeproj' is no longer needed and will be deprecated soon.
generated: ./FooBar.xcodeproj

Xcode can handle Swift packages directly. Similarly, xcodebuild can handle them too. This isn't new. It's likely been available since Xcode 11. I just totally missed it.

Note: I've yet to dig into Xcode 13 beta, as Swift packages may already support the build/test features I was after in xcodebuild (like build/test on Catalyst).

In any case, on to xcodebuild… but let's first create a brand new Swift package.

Creating a Swift package library

mkdir FooBar && cd FooBar
swift package init --type library
Creating library package: FooBar
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/FooBar/FooBar.swift
Creating Tests/
Creating Tests/FooBarTests/
Creating Tests/FooBarTests/FooBarTests.swift

List package schemes

We can use xcodebuild to list the available schemes.

xcodebuild -list
Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -list

User defaults from command line:
    IDEPackageSupportUseBuiltinSCM = YES

Resolve Package Graph

Resolved source packages:
  FooBar: /tmp/FooBar

Information about workspace "FooBar":
    Schemes:
        FooBar

Show supported platform, architecture, etc

Similarly, we can list destinations supported for the schemes.

xcodebuild -showdestinations -scheme FooBar
Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -showdestinations -scheme FooBar

User defaults from command line:
    IDEPackageSupportUseBuiltinSCM = YES

Resolve Package Graph

Resolved source packages:
  FooBar: /tmp/FooBar



	Available destinations for the "FooBar" scheme:
		{ platform:macOS, arch:x86_64, id:... }
		{ platform:macOS, arch:x86_64, variant:Mac Catalyst, id:... }
            ...
		{ platform:iOS Simulator, id:..., OS:14.5, name:iPhone 12 Pro }

	Ineligible destinations for the "FooBar" scheme:
            ...

macOS builds

Let's build for macOS, though let's first import UIKit into FooBar.swift. This ensures we get an expected failure when building for macOS.

import UIKit

struct FooBar {
  var text = "Hello, World!"
}

Now let's attempt to build it…

xcodebuild build -quiet -scheme FooBar -destination 'platform=macOS'
--- xcodebuild: WARNING: Using the first of multiple matching destinations:
{ platform:macOS, arch:x86_64, id:3D097357-EB7D-565D-9058-CE7C3135927B }
{ platform:macOS, arch:x86_64, variant:Mac Catalyst, id:3D097357-EB7D-565D-9058-CE7C3135927B }
/tmp/FooBar/Sources/FooBar/FooBar.swift:1:8: error: no such module 'UIKit'
import UIKit
       ^
note: Using new build system
note: Building targets in parallel
note: Planning build
note: Analyzing workspace
note: Using build description from disk
note: Build preparation complete
** BUILD FAILED **

The failure expected as UIKit isn't available on your typical macOS builds.

macOS Catalyst builds

We do, however, have Catalyst available, so we can use its variant to build for macOS with UIKit support, and.. voilà!

xcodebuild build -quiet -scheme FooBar -destination 'platform=macOS,variant=Mac Catalyst' && echo \\o/
\o/