Use SwiftPM to build iOS target

So far I haven't run into any issues. I've even managed to successfully compile Delta Client (a Minecraft client written in Swift) and run it on iOS with this method (i did have to add -Xcc -isysroot -Xcc /path/to/iossdk -Xcc --target=arm64-apple-ios15.4 to the swift build flags because of dependencies containing c code). Delta Client relies on numerous packages and all of them compile without issues. The main dependencies are swift-log, swift-argument-parser, swift-parsing, swift-nio, ZippyJSON, ZipFoundation, IDZSwiftCommonCrypto and FirebladeECS.

However, I have not tried any executable targets with binary dependencies or plugins, if you have an example iOS project somewhere that uses either of those features let me know. If it's an xcodeproj, that's fine because Swift Bundler can convert Xcodeprojs to Swift Bundler projects automatically (Swift Bundler projects are swift packages with a configuration file containing app bundle creation related configuration).

1 Like

Ok, I've found an unavoidable issue with this approach now. Dependencies that have a platform condition are always evaluated using the platform being compiled on (I checked the SwiftPM source code and as far as a I can see there is no option to change this).

For Swift Bundler I will be switching to integrating with the swift-package-manager library so that I can have more control and ensure that everything correctly targets iOS (like Xcode would).

This is a significant bug that plagues other platforms such as Android and WASI. Can you point me to where you found the cause so I can fix it.

1 Like

Now that I look a bit closer, I can only see it being an issue for Darwin platforms (the part of the code that I'm looking at should correctly identify that Android is being built for). The bug is in BuildParameters.swift. If the destination triple is Darwin, that code assumes that it's macOS. But that assumption only affects Package manifest evaluation as far as I can see, because elsewhere the full triple is used.

It seems to be scattered all over the place, there's another place in BuildPlan line 106 where it assumes Darwin means macOS. There are probably more places too.

I just changed that line to return .iOS instead of return .macOS to check if it was the cause, and it successfully built my package containing conditional dependencies for iOS. Of course this change now means that it won't build for macOS, but if the computed property is fixed to return the correct platform (instead of just macOS) it should fix my problem.

2 Likes

For what it's worth, here's a little shell script with a slightly more generic solution. I put this in my path as ios-swift:

#!/bin/bash

DEPLOYMENT=16.0
if [[ $1 == "--deployment" ]]
then
    DEPLOYMENT=$2
    shift
    shift
fi

COMMAND=$1
SDK="$(xcode-select -p)/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk"
TARGET="arm64-apple-ios$DEPLOYMENT"
shift

echo "Building for iOS $DEPLOYMENT"
swift "$COMMAND" --arch arm64 -Xswiftc -sdk -Xswiftc "$SDK" -Xswiftc -target -Xswiftc "$TARGET" --scratch-path "./.build/$TARGET" "$@"

It pulls the SDK path from xcode-select, and you can build for a different deployment target with (eg) iOS-swft --deployment 15.0 build.

3 Likes

The default deployment in that script is hard-coded, which isn't ideal. With a bit of work it could be extracted from somewhere like - $(xcode-select -p)/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/SDKSettings.json

2 Likes

Thanks @samdeane, this was a life-saver

1 Like

For anyone wondering: This can be simplified a bit with the new --sdk and --triple flags in SPM, so passing explicit swiftc flags should no longer be needed (off top of my head, haven't checked whether this works in the script):

swift "$COMMAND" --arch arm64 --sdk "$SDK" --triple "$TARGET" --scratch-path "./.build/$TARGET" "$@"

I have used this successfully for cross-compiling a Swift package as part of a Rust crate: nuit/nuit-bridge-swiftui/build.rs at bd7a6496c23597ef54b381576558165badd5f009 · fwcd/nuit · GitHub

1 Like