Binary targets and fashionable Swift packages
Swift 5.6 launched numerous new options for the Swift Package deal Supervisor infrastructure. We had been already capable of outline binary targets, and use xcframeworks as binary goal dependencies for our apps. They work nice if you’re concentrating on Apple platforms, however sadly the xcframework format shouldn’t be appropriate with Linux distributions, to not point out the Home windows working system.
That is the place artifact bundles might help. If you’re creating apps for a number of platforms now you can create an artifact bundle, place all of the appropriate variants into this new construction and SPM can select the fitting one primarily based in your structure. ?
Earlier than we really dive in to our major subject, I will shortly present you methods to create an xcframework and ship it as a binary goal through SPM.
XCFrameworks and SPM
Earlier than the introduction of the brand new format we needed to fiddle with FAT binaries to help a number of platforms. I’ve a deep dive article about frameworks and instruments that you should utilize to assemble a FAT binary, however I not advocate it since XCFrameworks are right here to remain. ?
In an effort to construct an XCFramework, it’s important to use Xcode and a course of could be very easy. You simply have to pick the Framework kind below the iOS tab if you create a brand new mission. Be happy to call it, add your Swift supply code and that is it.
You possibly can construct this mission utilizing the command line for a number of platforms through the next script.
# construct for iOS gadgets
xcodebuild archive
-scheme MySDK
-sdk iphoneos
-archivePath "construct/ios_devices.xcarchive"
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
SKIP_INSTALL=NO
# construct for iOS simulators
xcodebuild archive
-scheme MySDK
-sdk iphonesimulator
-archivePath "construct/ios_simulators.xcarchive"
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
SKIP_INSTALL=NO
# construct for macOS gadgets
xcodebuild archive
-sdk macosx MACOSX_DEPLOYMENT_TARGET=11.0
-arch x86_64 -arch arm64
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
-scheme "MySDK"
-archivePath "construct/macos_devices.xcarchive" SKIP_INSTALL=NO
# mix the slices and create the xcframework file
xcodebuild -create-xcframework
-framework construct/ios_devices.xcarchive/Merchandise/Library/Frameworks/MySDK.framework
-framework construct/ios_simulators.xcarchive/Merchandise/Library/Frameworks/MySDK.framework
-framework construct/macos_devices.xcarchive/Merchandise/Library/Frameworks/MySDK.framework
-output MySDK.xcframework
You possibly can even construct variations for Catalyst and different working techniques, if you perform a little search you may simply work out the required parameters and configuration. Lengthy story quick, it’s totally straightforward to create an xcframework output together with all form of platform slices for particular gadgets. ?
Now if you wish to use this XCFramework, you may merely drag and drop it to your Xcode mission and it ought to work with out additional points (if it accommodates the required slices). Alternatively you should utilize Swift bundle supervisor and create a binary goal an hook up your exterior framework bundle through SPM. That is how a quite simple configuration file seems like.
import PackageDescription
let bundle = Package deal(
identify: "MySDK",
merchandise: [
.library(name: "MySDK", targets: ["MySDK"]),
],
dependencies: [
],
targets: [
.binaryTarget(name: "MySDK", path: "./MySDK.xcframework")
]
)
In your mission you should utilize the library product as an ordinary dependency, and the underlying binary goal will care for importing the required header information and linking the precise library. The one downside with this method is that it’s macOS (or to be much more exact Apple OS solely).
Say hey to artifact bundles for Swift PM
All proper, so XCFrameworks cannot be used below Linux, however individuals like to jot down command line scripts in Swift and use them for server aspect tasks. In some instances these scripts (or plugins), wish to name exterior scripts that aren’t put in on the system by default. That is the place artifact bundles might help, as a result of it makes attainable to ship a number of variations of the identical executable binary file. ?
Artifact bundles usually are not a substitute for xcframeworks, however extra like an addition, or enchancment because the proposal title signifies this, for the Swift bundle supervisor plugin structure. They permit us to ship precompiled binary information for a number of platforms, this fashion plugin authors do not need to compile these instruments from supply and the plugin execution time will be closely decreased.
There’s a nice weblog put up about wrapping the SwiftLint executable in an artifact bundle, so I do not actually wish to get into the main points this time, as a result of it is fairly easy. The proposal itself helps lots to know the essential setup, additionally the older binary dependencies proposal accommodates some associated information good job Swift staff. ?
I would like to present an honorable point out to Karim Alweheshy, who’s actively working with the brand new Swift bundle supervisor plugin infrastructure, he has an incredible repository on GitHub that demos artifact bundles so please have a look when you’ve got time. ?
Anyway, I will present you methods to wrap an executable into an artifact bundle. Presently there is no approach to wrap libraries into artifact bundles, that is going to be added in a while.
# create a easy hey world executable mission
mkdir MyApp
cd $_
swift bundle init --type=executable
# construct the mission utilizing launch config
swift construct -c launch
# copy the binary
cp $(swift construct --show-bin-path -c launch)/MyApp ./myapp
# init a brand new instance mission
mkdir MyPluginExample
cd $_
swift bundle init
mkdir myapp.artifactbundle
cd $_
mkdir myapp-1.0.0-macos
cd $_
mkdir bin
Now the file construction is prepared, we should always create a brand new information.json file below the artifactbundle listing with the next contents. This may describe your bundle with the out there binary variants, you may check out the proposals for the out there triplets variations.
{
"schemaVersion": "1.0",
"artifacts": {
"myapp": {
"model": "1.0.0",
"kind": "executable",
"variants": [
{
"path": "myapp-1.0.0-macos/bin/myapp",
"supportedTriples": ["x86_64-apple-macosx", "arm64-apple-macosx"]
}
]
}
}
}
Copy the myapp binary below the myapp-1.0.0-macos/bin/myapp
location, and eventually we’ll make a quite simple command plugin to take advangate of this newly added device.
import PackagePlugin
import Basis
@major
struct MyDistCommandPlugin: CommandPlugin {
func performCommand(context: PluginContext, arguments: [String]) throws {
let myAppTool = strive context.device(named: "myapp")
let myAppToolURL = URL(fileURLWithPath: myAppTool.path.string)
let course of = strive Course of.run(myAppToolURL, arguments: [])
course of.waitUntilExit()
}
}
Watch out with the paths and file names, I used lowercase letters for every part on this instance, I like to recommend to comply with this sample if you create your artifact bundle binaries.
swift bundle plugin --list
# ‘hey’ (plugin ‘HelloCommand’ in bundle ‘MyPluginExample’)
swift bundle hey
# Hey, world!
That is it, now we have got a working artifact bundle with a customized made executable out there for macOS. We are able to use this artifact bundle as a dependency for a plugin and run the device by utilizing the plugin APIs. I would actually love to have the ability to cross compile Swift libraries and executable information in a while, this might make the event / deployment workflow a bit simpler. Anyway, artifact bundles are a pleasant little addition, I actually like the best way you may ship binaries for a number of platforms and I hope that we’re going to have the ability to share libraries as properly in a similar way. ?