Building iOS project without “wrappers”
Fastlane become too much popular tool nowadays. Yet, it’s just a wrapper around command line tools. Sometimes you don’t need all that power for simple task, sometimes you want more control. Let’s see how can we automate iOS project testing and delivery using native command line tools.
xcodebuild is a command-line tool that allows you to perform build, query, analyze, test, and archive operations on your Xcode projects and workspaces from the command line.
https://developer.apple.com/library/archive/technotes/tn2339/_index.html
Disclaimer: Commands format as well as parameters may vary in time. The following article is true as for January 2020, macOS 10.15.2 with Xcode 11.3.1 onboard.
AFAIK, the only way to build Xcode project (including iOS project) outside Xcode — use xcodebuild command line tool.
Not sure if Xcode itself use same tools inside, but it’s a different story. It’s pretty powerful command, just type in terminal for help:
xcodebuild -help
Build
First, we try to build our project. Assume we have an iOS project named DemoProject with DemoProject target (with DemoProjectTests target for unit-tests) and DemoProject.xcworkspace. Let’s just build our scheme.
xcodebuild \
-workspace DemoProject.xcworkspace \
-scheme DemoProject \
-destination generic/platform=iOS build
That’s all you need to check if target could be build at all. Now let’s test
Test
xcodebuild \
-workspace DemoProject.xcworkspace \
-scheme DemoProjectTests \
test -destination 'platform=iOS Simulator,OS=13.3,name=Demo iPhone'
I have iPhone simulator named “Demo iPhone” running iOS 13.3 on my machine. You should set yours. Again, that’s all for testing. it either will pass tests or name which are failed. Now it’s time to distribute
Distribute
Distribution is a bit tricky. Yet with xcodebuild
you have all you need. There are three main steps:
- creating archive
- creating *.ipa file from archive
- upload the *.ipa
For archive we should use Release configuration and for now will keep archive in build subfolder inside project.
xcodebuild \
-workspace DemoProject.xcworkspace \
-scheme DemoProject \
-sdk iphoneos \
-configuration Release \
archive -archivePath build/DemoProject.xcarchive
Export stage require one more special file where you describe export options. In this example we will export to App Store (you might want Ad Hoc or something) which also is ok if you plan to test via TestFlight. So we should provide exportOptions.plist file. In this example I also explicitly specify which certificate to use for signature. For now add in project folder.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>generateAppStoreInformation</key>
<false/>
<key>method</key>
<string>app-store</string>
<key>provisioningProfiles</key>
<dict>
<key><***BUNDLE ID FOR TARGET***></key>
<string><***CERTIFICATE NAME***></string>
</dict>
<key>signingCertificate</key>
<string>iPhone Distribution</string>
<key>signingStyle</key>
<string>manual</string>
<key>stripSwiftSymbols</key>
<true/>
<key>teamID</key>
<string><***TEAM ID***></string>
<key>uploadBitcode</key>
<false/>
<key>uploadSymbols</key>
<true/>
</dict>
</plist>
Now we are ready for *.ipa file creation
xcodebuild \
-exportArchive -archivePath build/DemoProject.xcarchive \
-exportOptionsPlist DemoProject/exportOptions.plist \
-exportPath \
build -allowProvisioningUpdates
Final step is to upload file to App Store Connect. For that you shall add app-specific password to account which is set up for distribution.
xcrun altool --upload-app \
-f build/DemoProject.ipa \
-u <EMAIL> \
-p <APP-SPECIFIC PASSWORD>
That’s all. It’s that simple. xcodebuild
has much more to configure. And, of course, everything now can be automated. But you, as a developer, always understand what’s happening with your code. Was it compiled, tested, archived and distributed or failed at which point.