Irbisa · Cheatsheet
Xcode Build & Modularization
Targets, schemes, xcconfig, SPM, static vs dynamic linking, XCFramework, build times, thinning
What is the difference between a target, a scheme and a build configuration, and between
.xcodeprojand.xcworkspace?How do you choose an app's minimum deployment target, and how do you use newer APIs without raising it?
import SwiftUI struct RootView: View { var body: some View { if #available(iOS 18, *) { // runtime check: only this branch may use the iOS 18 Tab API…What are a signing certificate, a provisioning profile and entitlements, and how do they fit together?
What is the difference between CocoaPods, Carthage and Swift Package Manager, and why have most teams moved to SPM?
What does this function do in a Debug build and in a Release build, and why do the two differ?
func loadItem(id: Int?) -> String { assert(id != nil, "id is required") #if DEBUG print("loading \(String(describing: id))") #endif precondition((id ?? 0) >= 0, "negative id") return "item-\(id ?? -1)"} print(loadItem(id: nil))What are
.xcconfigfiles for, and in what order does Xcode resolve a build setting?// Config/Base.xcconfig: shared by every configurationSWIFT_VERSION = 6.0IPHONEOS_DEPLOYMENT_TARGET = 17.0CODE_SIGN_STYLE = Automatic // Config/Staging.xcconfig: assigned to the Staging configuration…What happens when you press Build in Xcode, from the task graph to the signed app?
In
Package.swift, what is the difference between a target, a product and a package dependency, and why can another package not import your target?// swift-tools-version: 6.0import PackageDescription let package = Package( name: "Networking", platforms: [.iOS(.v17)],…How does Swift Package Manager resolve dependency versions, and why should
Package.resolvedbe committed?// swift-tools-version: 6.0import PackageDescription let package = Package( name: "AppModules", dependencies: […What is an XCFramework, and why did it replace fat frameworks built with
lipo?set -euo pipefail # One archive per platform variant: device and simulator are both arm64,# so lipo cannot merge them into one binary.xcodebuild archive -scheme Analytics \ -destination "generic/platform=iOS" -archivePath build/ios \…SwiftLint and a code generator run on every build even when nothing changed: what is the build system missing, and how do you fix it?
# Run Script phase, wrong: no inputs or outputs, so it runs on every build,# and rewriting Strings.swift recompiles everything that uses it.# swiftlint --fix# Tools/gen-strings Resources/Localizable.xcstrings > Sources/Generated/Strings.swift # Run Script phase, right.…What is app thinning, and why do the universal
.ipasize, the per-device download size and the installed size differ?set -euo pipefail xcodebuild archive -workspace App.xcworkspace -scheme App \ -configuration Release -destination 'generic/platform=iOS' \ -archivePath build/App.xcarchive…What is a privacy manifest (
PrivacyInfo.xcprivacy), and what happens to a build without one?<?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>NSPrivacyTracking</key> <false/>…How does a build get from Xcode to the App Store, and what happens in TestFlight and App Review along the way?
How do you set up CI/CD for an iOS app, and how do you handle code signing on a CI machine?
# fastlane/Fastfiledefault_platform(:ios) platform :ios do lane :beta do setup_ci # temporary keychain on CI runners, no-op locally…How do static and dynamic linking differ on iOS, and why did cold launch get slower after twelve modules became dynamic frameworks?
FeedandProfileare dynamic frameworks that both depend onAnalyticsin this manifest: what goes wrong at runtime, and how do you fix it?let package = Package( name: "App", products: [ .library(name: "Analytics", type: .static, targets: ["Analytics"]), .library(name: "Feed", type: .dynamic, targets: ["Feed"]), .library(name: "Profile", type: .dynamic, targets: ["Profile"]), ], targets: [ .target(name: "Analytics"), // holds AnalyticsTracker.shared .target(name: "Feed", dependencies: ["Analytics"]), .target(name: "Profile", dependencies: ["Analytics"]), ])A one-line change in a feature module costs twelve minutes to build: how do you find what is actually rebuilt and fix it?
set -euo pipefailSIM='generic/platform=iOS Simulator' # 1. Where does the time go? Per-task durations (open the Build Timeline in Xcode too)xcodebuild -workspace App.xcworkspace -scheme App -destination "$SIM" \ build -showBuildTimingSummary…Every type in a feature package ended up
publicjust to make the app compile: what does that cost, and what should the module expose?import SwiftUI // Package.swift: one product, two targets that share internalslet package = Package( name: "Checkout", products: [.library(name: "Checkout", targets: ["Checkout"])],…Checkout must open Profile's address editor and Profile must open Checkout's payment sheet, and SwiftPM rejects the cycle: how do you break it?
import Foundation // Package.swift: interface targets break the cyclelet package = Package( name: "Features", products: […What are ABI stability and module stability, and what does turning on
BUILD_LIBRARY_FOR_DISTRIBUTIONcommit an SDK to?// Framework built with BUILD_LIBRARY_FOR_DISTRIBUTION = YES // Not frozen: you may add a case in 2.0public enum PaymentState: Sendable { case pending case settled(id: String)…Why do large teams generate their Xcode project with Tuist or XcodeGen, and how would you migrate a 200-target project without freezing feature work?
import ProjectDescription // Project.swift: the target graph as reviewable source, no pbxproj in gitlet project = Project( name: "App", settings: .settings(configurations: […