Skip to content

Latest commit

 

History

History
898 lines (644 loc) · 24.6 KB

File metadata and controls

898 lines (644 loc) · 24.6 KB

iOS

Uses xcodebuild:

https://developer.apple.com/documentation/xcode

Artifacts

IPA - iOS App Store Package

MyApp.ipa

Packaged file format used for installing iOS applications on iPhones, iPads etc.

Dependencies - CocoaPods

https://cocoapods.org/

https://guides.cocoapods.org/

CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects.

It's written in Ruby and can be installed using the system ruby.

Install cocoapods:

sudo gem install cocoapods

Install project's pod dependencies:

pod install

This uses a Podfile build file that looks like this:

platform :ios, '8.0'
use_frameworks!

target 'MyApp' do
  pod 'AFNetworking', '~> 2.6'
  pod 'ORStackView', '~> 3.0'
  pod 'SwiftyJSON', '~> 2.3'
end

Creating a Pod:

pod spec create "$APP"

Edit the podspec file:

"$EDITOR" "$APP".podspec

Lint the podspec file:

pod spec lint "$APP".podspec

Reinstall dependencies:

pod deintegrate &&
pod install

Metadata

Metadata is found in:

<AppName>.xcodeproj/

and

<AppName>.xcworkspace

You can open XCode like this:

open "$APP".xcodeproj
open "$APP".xcworkspace

Clean

xcodebuild clean
xcodebuild -workspace "$APP".xcworkspace \
           -scheme SIT \
           -configuration SIT \
           clean

Install the Platform SDK first, otherwise you'll get an error like this:

Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -workspace MyApp.xcworkspace -scheme SIT -configuration SIT clean

User defaults from command line:
    IDEPackageSupportUseBuiltinSCM = YES

2025-01-30 14:18:57.750 xcodebuild[47478:154973817] Writing error result bundle to /var/folders/30/kxjrq3fj5tqdhsvbj3p9m2fh0000gq/T/ResultBundle_2025-30-01_14-18-0057.xcresult
xcodebuild: error: Found no destinations for the scheme 'SIT' and action clean.

Running this:

xcodebuild -workspace "$APP".xcworkspace \
           -scheme SIT \
           -configuration SIT \
           clean

Output should look something like this:

Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -workspace MyApp.xcworkspace -scheme SIT -configuration SIT clean

User defaults from command line:
    IDEPackageSupportUseBuiltinSCM = YES

--- xcodebuild: WARNING: Using the first of multiple matching destinations:
{ platform:iOS, id:dvtdevice-DVTiPhonePlaceholder-iphoneos:placeholder, name:Any iOS Device }
{ platform:iOS Simulator, id:dvtdevice-DVTiOSDeviceSimulatorPlaceholder-iphonesimulator:placeholder, name:Any iOS Simulator Device }
{ platform:iOS Simulator, id:FAF9573D-6EFF-4F38-A5D6-E0613D8AD15F, OS:17.5, name:iPad (10th generation) }
{ platform:iOS Simulator, id:FAF9573D-6EFF-4F38-A5D6-E0613D8AD15F, OS:17.5, name:iPad (10th generation) }
{ platform:iOS Simulator, id:D9DEB37D-DB2A-4C70-B12F-758FB2DDEF28, OS:17.5, name:iPad Air 11-inch (M2) }
{ platform:iOS Simulator, id:D9DEB37D-DB2A-4C70-B12F-758FB2DDEF28, OS:17.5, name:iPad Air 11-inch (M2) }
{ platform:iOS Simulator, id:3B3701D6-8908-4C5B-AE9D-28065AAB3361, OS:17.5, name:iPad Air 13-inch (M2) }
{ platform:iOS Simulator, id:3B3701D6-8908-4C5B-AE9D-28065AAB3361, OS:17.5, name:iPad Air 13-inch (M2) }
{ platform:iOS Simulator, id:4AA9C940-8B08-497A-8873-91913B1D8A26, OS:17.5, name:iPad Pro 11-inch (M4) }
{ platform:iOS Simulator, id:4AA9C940-8B08-497A-8873-91913B1D8A26, OS:17.5, name:iPad Pro 11-inch (M4) }
{ platform:iOS Simulator, id:59DA5271-F055-44DA-9266-C545D4308195, OS:17.5, name:iPad Pro 13-inch (M4) }
{ platform:iOS Simulator, id:59DA5271-F055-44DA-9266-C545D4308195, OS:17.5, name:iPad Pro 13-inch (M4) }
{ platform:iOS Simulator, id:5507F622-1A42-4914-AF9A-AA644851A559, OS:17.5, name:iPad mini (6th generation) }
{ platform:iOS Simulator, id:5507F622-1A42-4914-AF9A-AA644851A559, OS:17.5, name:iPad mini (6th generation) }
{ platform:iOS Simulator, id:8359DDF7-572A-4AEA-99B4-689EF151872A, OS:17.5, name:iPhone 15 }
{ platform:iOS Simulator, id:8359DDF7-572A-4AEA-99B4-689EF151872A, OS:17.5, name:iPhone 15 }
{ platform:iOS Simulator, id:10BEF2E3-FEBD-49AD-8A6F-E4F4C559EBF4, OS:17.5, name:iPhone 15 Plus }
{ platform:iOS Simulator, id:10BEF2E3-FEBD-49AD-8A6F-E4F4C559EBF4, OS:17.5, name:iPhone 15 Plus }
{ platform:iOS Simulator, id:2E91DE33-0CAD-4189-BE35-C00ABBA69E19, OS:17.5, name:iPhone 15 Pro }
{ platform:iOS Simulator, id:2E91DE33-0CAD-4189-BE35-C00ABBA69E19, OS:17.5, name:iPhone 15 Pro }
{ platform:iOS Simulator, id:41104926-C610-474A-9846-5C840E36CE82, OS:17.5, name:iPhone 15 Pro Max }
{ platform:iOS Simulator, id:41104926-C610-474A-9846-5C840E36CE82, OS:17.5, name:iPhone 15 Pro Max }
{ platform:iOS Simulator, id:C4EE5DBE-6048-4BCC-BDEA-8EE7E8C400C6, OS:17.5, name:iPhone SE (3rd generation) }
{ platform:iOS Simulator, id:C4EE5DBE-6048-4BCC-BDEA-8EE7E8C400C6, OS:17.5, name:iPhone SE (3rd generation) }

** CLEAN SUCCEEDED **

Build Destinations and Simulators

xcodebuild \
    -workspace "$APP".xcworkspace \
    -scheme SIT \
    -configuration SIT \
    -showdestinations

If you see this output, you have no available destinations:

Command line invocation:
/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -workspace MyApp.xcworkspace -scheme SIT -configuration SIT -showdestinations

User defaults from command line:
IDEPackageSupportUseBuiltinSCM = YES

        Ineligible destinations for the "SIT" scheme:
                { platform:iOS, id:dvtdevice-DVTiPhonePlaceholder-iphoneos:placeholder, name:Any iOS Device, error:iOS 17.5 is not installed. To use with Xcode, first download and install the platform }
xcodebuild -workspace MyApp.xcworkspace   -scheme SIT   -configuration SIT   -showdestinations
Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -workspace MyApp.xcworkspace -scheme SIT -configuration SIT -showdestinations

User defaults from command line:
    IDEPackageSupportUseBuiltinSCM = YES



        Available destinations for the "SIT" scheme:
                { platform:iOS, id:dvtdevice-DVTiPhonePlaceholder-iphoneos:placeholder, name:Any iOS Device }
                { platform:iOS Simulator, id:dvtdevice-DVTiOSDeviceSimulatorPlaceholder-iphonesimulator:placeholder, name:Any iOS Simulator Device }
                { platform:iOS Simulator, id:FAF9573D-6EFF-4F38-A5D6-E0613D8AD15F, OS:17.5, name:iPad (10th generation) }
                { platform:iOS Simulator, id:D9DEB37D-DB2A-4C70-B12F-758FB2DDEF28, OS:17.5, name:iPad Air 11-inch (M2) }
                { platform:iOS Simulator, id:3B3701D6-8908-4C5B-AE9D-28065AAB3361, OS:17.5, name:iPad Air 13-inch (M2) }
                { platform:iOS Simulator, id:4AA9C940-8B08-497A-8873-91913B1D8A26, OS:17.5, name:iPad Pro 11-inch (M4) }
                { platform:iOS Simulator, id:59DA5271-F055-44DA-9266-C545D4308195, OS:17.5, name:iPad Pro 13-inch (M4) }
                { platform:iOS Simulator, id:5507F622-1A42-4914-AF9A-AA644851A559, OS:17.5, name:iPad mini (6th generation) }
                { platform:iOS Simulator, id:8359DDF7-572A-4AEA-99B4-689EF151872A, OS:17.5, name:iPhone 15 }
                { platform:iOS Simulator, id:10BEF2E3-FEBD-49AD-8A6F-E4F4C559EBF4, OS:17.5, name:iPhone 15 Plus }
                { platform:iOS Simulator, id:2E91DE33-0CAD-4189-BE35-C00ABBA69E19, OS:17.5, name:iPhone 15 Pro }
                { platform:iOS Simulator, id:41104926-C610-474A-9846-5C840E36CE82, OS:17.5, name:iPhone 15 Pro Max }
                { platform:iOS Simulator, id:C4EE5DBE-6048-4BCC-BDEA-8EE7E8C400C6, OS:17.5, name:iPhone SE (3rd generation) }

See available simulators:

xcrun simctl list

See available runtimes:

xcrun simctl list runtimes
== Runtimes ==
iOS 17.5 (17.5 - 21F79) - com.apple.CoreSimulator.SimRuntime.iOS-17-5
tvOS 17.5 (17.5 - 21L569) - com.apple.CoreSimulator.SimRuntime.tvOS-17-5
watchOS 10.5 (10.5 - 21T575) - com.apple.CoreSimulator.SimRuntime.watchOS-10-5
visionOS 1.2 (1.2 - 21O5565d) - com.apple.CoreSimulator.SimRuntime.xrOS-1-2

Boot one if necessary:

xcrun simctl boot "iPhone 14"
xcrun simctl erase all

Delete derived data:

rm -rf ~/Library/Developer/Xcode/DerivedData

or

xcodebuild -workspace "$APP".xcworkspace \
  -scheme SIT \
  -configuration SIT \
  -destination 'platform=iOS,name=iPhone 14,OS=16.4' \
  clean

Output should look like:

xcodebuild -workspace MyApp.xcworkspace   -scheme SIT   -configuration SIT   -showdestinations
Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -workspace MyApp.xcworkspace -scheme SIT -configuration SIT -showdestinations

User defaults from command line:
    IDEPackageSupportUseBuiltinSCM = YES



        Available destinations for the "SIT" scheme:
                { platform:iOS, id:dvtdevice-DVTiPhonePlaceholder-iphoneos:placeholder, name:Any iOS Device }
                { platform:iOS Simulator, id:dvtdevice-DVTiOSDeviceSimulatorPlaceholder-iphonesimulator:placeholder, name:Any iOS Simulator Device }
                { platform:iOS Simulator, id:FAF9573D-6EFF-4F38-A5D6-E0613D8AD15F, OS:17.5, name:iPad (10th generation) }
                { platform:iOS Simulator, id:D9DEB37D-DB2A-4C70-B12F-758FB2DDEF28, OS:17.5, name:iPad Air 11-inch (M2) }
                { platform:iOS Simulator, id:3B3701D6-8908-4C5B-AE9D-28065AAB3361, OS:17.5, name:iPad Air 13-inch (M2) }
                { platform:iOS Simulator, id:4AA9C940-8B08-497A-8873-91913B1D8A26, OS:17.5, name:iPad Pro 11-inch (M4) }
                { platform:iOS Simulator, id:59DA5271-F055-44DA-9266-C545D4308195, OS:17.5, name:iPad Pro 13-inch (M4) }
                { platform:iOS Simulator, id:5507F622-1A42-4914-AF9A-AA644851A559, OS:17.5, name:iPad mini (6th generation) }
                { platform:iOS Simulator, id:8359DDF7-572A-4AEA-99B4-689EF151872A, OS:17.5, name:iPhone 15 }
                { platform:iOS Simulator, id:10BEF2E3-FEBD-49AD-8A6F-E4F4C559EBF4, OS:17.5, name:iPhone 15 Plus }
                { platform:iOS Simulator, id:2E91DE33-0CAD-4189-BE35-C00ABBA69E19, OS:17.5, name:iPhone 15 Pro }
                { platform:iOS Simulator, id:41104926-C610-474A-9846-5C840E36CE82, OS:17.5, name:iPhone 15 Pro Max }
                { platform:iOS Simulator, id:C4EE5DBE-6048-4BCC-BDEA-8EE7E8C400C6, OS:17.5, name:iPhone SE (3rd generation) }

Download Simulators

Downloading using XCode CLI

xcodebuild -showsdks

Requires newer versions of Xcode and also admin privileges - for CI/CD prefix these download commands with sudo.

Download All Platforms

If in doubt, just download everything:

xcodebuild -downloadAllPlatforms

The output is the same as below individual platform download commands.

Download iOS

xcodebuild -downloadPlatform iOS
Downloading iOS 17.5 Simulator (21F79): 7.5% (551.5 MB of 7.34 GB)

Verify after download:

xcrun simctl list runtimes

Should return something like this (instead of being blank like before):

== Runtimes ==
iOS 17.5 (17.5 - 21F79) - com.apple.CoreSimulator.SimRuntime.iOS-17-5

If it's still empty, try to clear state and re-run the download command again:

sudo rm -rf /Library/Developer/Xcode
sudo rm -rf ~/Library/Developer/Xcode
sudo rm -rf ~/Library/Caches/com.apple.dt.Xcode

Download watchOS

xcodebuild -downloadPlatform watchOS
Downloading watchOS 10.5 Simulator (21T575): 10.9% (428.7 MB of 3.95 GB)
xcrun simctl list runtimes
== Runtimes ==
iOS 17.5 (17.5 - 21F79) - com.apple.CoreSimulator.SimRuntime.iOS-17-5
watchOS 10.5 (10.5 - 21T575) - com.apple.CoreSimulator.SimRuntime.watchOS-10-5

Download tvOS

xcodebuild -downloadPlatform tvOS
xcrun simctl list runtimes
== Runtimes ==
iOS 17.5 (17.5 - 21F79) - com.apple.CoreSimulator.SimRuntime.iOS-17-5
tvOS 17.5 (17.5 - 21L569) - com.apple.CoreSimulator.SimRuntime.tvOS-17-5
watchOS 10.5 (10.5 - 21T575) - com.apple.CoreSimulator.SimRuntime.watchOS-10-5

Download visionOS

xcodebuild -downloadPlatform visionOS
xcrun simctl list runtimes
== Runtimes ==
iOS 17.5 (17.5 - 21F79) - com.apple.CoreSimulator.SimRuntime.iOS-17-5
tvOS 17.5 (17.5 - 21L569) - com.apple.CoreSimulator.SimRuntime.tvOS-17-5
watchOS 10.5 (10.5 - 21T575) - com.apple.CoreSimulator.SimRuntime.watchOS-10-5
visionOS 1.2 (1.2 - 21O5565d) - com.apple.CoreSimulator.SimRuntime.xrOS-1-2```

Download using XCode GUI

open -a Xcode

Navigate to Xcode > Settings > Platforms (or Preferences > Components). Look for iOS 17.5 Simulator and install it.

Download using xcode-install

:octocat: neonichu/xcode-install

This 3rd party is another alternative way to install XCode SDKs from the command line, but it requires Apple Developer credentials.

sudo gem install xcode-install

Prompts for an Apple Developer account username and password (set the $FASTLANE_PASSWORD environment variable). This costs $79 per year at time of writing.

export XCODE_INSTALL_USER=...
export XCODE_INSTALL_PASSWORD=...
xcversion update
xcversion list
xcversion install 'Xcode 15.0' # Adjust version if needed
xcversion simulators
xcversion install-simulator "iOS 17.5"
xcrun simctl create "iPhone 14 (iOS 17.5)" com.apple.CoreSimulator.SimDeviceType.iPhone-14 com.apple.CoreSimulator.SimRuntime.iOS-17-5
xcrun simctl boot "iPhone 14 (iOS 17.5)"

Verify installation:

xcrun simctl list

Copy Profiles to Library

mkdir -p -v ~/Library/MobileDevice/Provisioning\ Profiles

cp -vi *.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/

Load Certificate into Keychain

security list-keychains

Build will find it there later.

CERT=Certificates-SIT.p12

Import certificate and allow codesign and security to read it:

security import "$CERT" \
    -k ~/Library/Keychains/login.keychain-db \
    -P "$CERT_PASSWORD" \
    -T /usr/bin/codesign \
    -T /usr/bin/security

Open KeyChain Access

On your Mac laptop you may need to open keychain access if it's password protected.

In testing this didn't accept my login password, but if I opened it manually first:

open -a "Keychain Access"

then the build proceeded using the already opened keychain.

The Fastlane handling of this is better where is creates a new keychain that isn't password protected.

Build

Make sure you've got XCode set to Developer tools and not just CLI tools or you'll get this error.

sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer

Build:

xcodebuild -workspace "$APP".xcworkspace \
           -scheme SIT \
           -configuration SIT

If you get this error:

xcodebuild: error: Failed to build workspace MyApp with scheme SIT.: This scheme builds an embedded Apple Watch app. watchOS 10.5 must be installed in order to run the scheme
xcodebuild -downloadPlatform watchOS

Print the build settings:

xcodebuild -showBuildSettings # optionally put your -workspace -scheme -configuration switches ...

and destinations:

xcodebuild -workspace "$APP".xcworkspace \
  -scheme SIT \
  -configuration SIT \
  -showdestinations

Build against a specific destination:

xcodebuild clean build -project "$APP".xcodeproj \
                       -scheme SIT \
                       -destination 'platform=iOS Simulator,name=iPhone 15'

Run tests:

xcodebuild test -project "$APP".xcodeproj \
                -scheme YourScheme \
                -destination 'platform=iOS Simulator,name=iPhone 15'

IPA Archives

xcodebuild / Fastlane builds an IPA file:

IPA="./build/$APP.ipa"

This ends up in different paths for different builds, find it like this:

IPA="$(find . -type f -name '*.ipa' | head -n1)"
file "$IPA"
./build/MyApp.ipa: iOS App Zip archive data, at least v1.0 to extract, compression method=store
unzip -l "$IPA"
Archive:  ./build/MyApp.ipa
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  02-11-2025 21:52   Payload/
        0  02-11-2025 21:52   Payload/MyApp.app/
     6201  02-11-2025 21:52   Payload/MyApp.app/blah.nib
     8860  02-11-2025 21:52   Payload/MyApp.app/blah2.nib
    12517  02-11-2025 21:52   Payload/MyApp.app/img.jpg
...
...
<rest of files omitted for brevity>

xcpretty

:octocat: xcpretty/xcpretty

Pipe xcodebuild output into it to make it prettier for human readability.

Used by Fastlane.

xcbeautify

:octocat: cpisciotta/xcbeautify

Faster alternative to xcpretty written in Swift, recommended and used preferentially by Fastlane.

AGV

Apple Generic Versioning (AGV) tool for Xcode projects:

agvtool vers  # -terse

iXGuard

See iXGuard page.

Switching XCode versions

See GitHub Actions - Mac Runner Versions vs XCode Versions.

Download Alternate XCode Versions

XCodes

:octocat: XcodesOrg/xcodes

brew install xcodesorg/made/xcodes

List all possible XCode versions available:

xcodes list

List XCode versions installed:

xcodes installed
15.4 (15F31d) (Selected) /Applications/Xcode.app

Download and switch to 16.1:

xcodes install 16.1

Just download the .xip package (eg. to upload to CI/CD artifacts):

xcodes download 16.1

This requires a iCloud account.

export XCODES_USERNAME=...
export XCODES_PASSWORD=...

And be prepared for the 2FA prompt if enabled on the account.

Apple 2FA

Unfortunately Apple doesn't support authenticator apps which can be used to share QR seeds.

Why doesn't Apple support Authenticator Apps for iCloud 2FA?

2FA - Device Base

Apple’s 2FA is device-based, not time-based one-time passwords (TOTP) using number seeds usually shared as a QR code to authenticator apps.

Apple’s 2FA uses trusted Apple devices to receive push notifications or generate verification codes. Apple’s 2FA uses secure push notifications.

When signing in, Apple sends a real-time notification to trusted devices. These notifications display location-based alerts, helping prevent unauthorized access.

2FA - Security & Control

Apple controls the authentication flow entirely within its ecosystem to reduce phishing risks. Since TOTP codes can be stolen via phishing, Apple prefers to use its push-based system.

2FA - Fallback - SMS or Phone Call

If you don’t have a trusted Apple device, Apple provides an alternative via SMS or phone call, rather than using third-party apps.

If you have Mac you can generate a 2FA code via the CLI:

security find-generic-password -s "iCloud" -w

Since this is such a big problem, switched to using a GitHub-Actions runner with the XCode versions already installed.

See GitHub Actions - Mac Runner Versions vs XCode versions.

Troubleshooting

security: SecKeychainItemImport: Unknown format in import

security import "$cert" \
      -k ~/Library/Keychains/login.keychain-db \
      -T /usr/bin/codesign \
      -T /usr/bin/security \
      ${CERTIFICATE_PASSWORD:+-P "$CERTIFICATE_PASSWORD"}
security: SecKeychainItemImport: Unknown format in import.

Verified the Certificate contains private key using SSL troubleshooting section.

Solution: Ensure $cert is a filename ending in .p12.

security: SecItemCopyMatching: The specified item could not be found in the keychain

You're importing the certificate into one keychain but setting permissions on a different keychain.

XCodeBuild / Fastlane building hanging with no output

Resolved this by adding setup_ci to Fastlane config and then loading the cert to the expected keychain of fastlane_tmp_keychain-db.

The naming format is "$name"-db.

cert="certificate.p12"
keychain="build.keychain"
KEYCHAIN_PATH="$HOME/Library/Keychains/$keychain-db"
echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV"
base64 --decode <<< "$CERTIFICATE" > "$cert"
file "$cert"
md5 "$cert"
security create-keychain -p "" "$keychain"
security default-keychain -s "$keychain"
security unlock-keychain -p "" "$keychain"
security import "$cert" \
    -k "$KEYCHAIN_PATH" \
    -T /usr/bin/codesign \
    ${CERTIFICATE_PASSWORD:+-P "$CERTIFICATE_PASSWORD"}
security set-key-partition-list -S apple-tool:,apple: -k "" "$keychain"

No Runtimes

If after:

xcodebuild -downloadPlatform iOS

You have empty runtimes:

xcrun simctl list runtimes
== Runtimes ==
sudo rm -rf /Library/Developer/Xcode
sudo rm -rf ~/Library/Developer/Xcode
sudo rm -rf ~/Library/Caches/com.apple.dt.Xcode
xcodebuild -downloadPlatform iOS

exportArchive: Rsync failed

After

Archive Succeeded
Successfully stored the archive. You can find it in the Xcode Organizer.

you then get an error like this:

error: exportArchive: Rsync failed
Error Domain=IDEFoundationErrorDomain Code=1 "Rsync failed"

This is caused by rsync failing during creation of the .ipa archive.

xcodebuild wraps the .xcarchive into a .ipa export based on your ExportOptions.plist using rsync.

Rsync may error out on broken symlinks in the build directory.

Clean out the state:

rm -rf build/ ~/Library/Developer/Xcode/DerivedData/*

and then try again.

If it's still breaking this could be caused by use of a toolchain like iXGuard which creates the bitcode but not an export ready archive.

In this case skip the export in Fastlane and do it manually after:

ixguard --config ixguard.yaml -o build/"$NAME"-Guarded.xcarchive build/"$NAME".xcarchive

and then run the export archive:

xcodebuild -exportArchive \
        -archivePath "build/"$NAME"-Guarded.xcarchive" \
        -exportOptionsPlist "./ExportOptions/Production-ExportOptions.plist" \
        -exportPath "."

See the equivalent code in fastlane/Fastfile.

Memes

Headquarters

Headquarters