skip to Main Content

We can build and run Swift code/projects from the command line via multiple commands without using Xcode. I have heard of xcodebuild, xcrun and swift used for Xcode development. I do use fastlane but I don’t really understand the tools powering it under the hood.

I am an iOS developer who uses a Mac. I develop using Xcode so I haven’t used these command-line tools before.

What are the differences between each command? Are there any cases where I’d be better off using one over the other?

2

Answers


  1. Chosen as BEST ANSWER

    Tl;DR

    xcodebuild and xcrun help build Xcode projects in a headless context, for example in CI setups. swift is the Swift REPL and is largely used for Swift on Server apps. As such, we can build apps without knowing about or using the tools regularly in mobile app development. We use xcodebuild and xcrun under the hood interacting with Xcode even though we don't realise it because they're bundled in Xcode's Command Line tools (documentation archive, but still relevant).

    fastlane is an example CI tool that automates the build process, certificate signing, and interfacing with App Store Connect, using these tools.

    xcodebuild

    xcodebuild is part of Xcode's bundled command-line tools package. From the manpages:

    build Xcode projects and workspaces

    xcodebuild builds one or more targets contained in an Xcode project, or builds a scheme contained in an Xcode workspace or Xcode project.

    xcodebuild has lots of options and use cases. The options are equivalent to certain user actions within the Xcode IDE. Example usage:

    xcodebuild -workspace MyWorkspace.xcworkspace -scheme MyScheme
    

    Builds the scheme MyScheme in the Xcode workspace MyWorkspace.xcworkspace.

    In the command above, we build the workspace without Xcode, using what Xcode runs internally for compilation. Xcode can only be installed on macOS and we have the same limitations for using its command-line tools, including xcodebuild and xcrun.

    xcrun

    xcrun is another Xcode command-line tool part of Xcode's CLI tools. From the manpages:

    run or locate development tools

    xcrun provides a means to locate or invoke coexistence- and platform-aware developer tools from the command-line, without requiring users to modify makefiles or otherwise take inconvenient measures to support multiple Xcode toolchains.

    xcrun [-sdk SDK] -find <tool_name>
    

    xcrun is also commonly used with Xcode-select to manage multiple Xcode versions on the same machine. Every version of Xcode comes bundled with its own development tools, and we can use xcrun to get the current path to them:

    xcrun xcode-select --print-path
    

    swift

    swift is the Swift REPL. swift is a command-line tool that includes the Swift toolchain but can also be installed outside of the Xcode bundled tools. swift is different from xcodebuild and xcrun because it is compiled Swift rather than C. swift is not well documented in the MacOS manpages documentation, however, Apple has documented these tools on its blog:

    Xcode 6.1 introduces yet another way to experiment with Swift in the form of an interactive Read Eval Print Loop, or REPL.

    A REPL is essentially an interactive compilation environment or shell. First, the REPL reads code, then evaluates it, prints, and repeats the process. As you can imagine, there is much less we can develop using a REPL compared to an IDE. However, there are other use cases for Swift than iOS, watchOS, and macOS development.

    swift includes the standard library and doesn't include libraries such as Foundation and UIKit. These Swift libraries are almost certainly needed for iOS or macOS development, so we can't develop apps using the swift REPL alone. However, Swift on Server projects regularly use swift to run Swift code on Linux and even Windows machines.

    For wider adoption, Apple has made swift available on different Operating Systems where Xcode is not readily available. Swift now also has Docker support and Windows. Docker enables us to run swift on any machine regardless of the underlying OS. swift in these applications serves as a scripting language.

    Bonus notes for SoS

    swift is used largely for Swift on Server. Swift on Server has great performance for server applications, with a lower memory footprint, fast startup time, and deterministic performance. Although it is not quite as fast as .NET core for some tasks, this is because Swift is much safer with a rigorous type system, garbage collection with ARC, and fewer optimisations for server-specific apps. Many early adopters praise the improved language type system, memory efficiency, and algorithmic performance. In fact, Swift vapor is comparable to Python and Ruby in efficiency for JSON Serialization tasks. Swift is comparable to Java in very few tasks, but this is likely to change as the language and ecosystem grow.


  2. xcrun

    Think of xcrun as a convenience prefix to invoke various command line tools in the currently active Xcode toolchain.

    Multiple Xcode toolchains can be installed on the same machine (e.g. latest release, beta release and/or legacy releases). Use xcode-select to set which Xcode toolchain path is used. Alternately, the active developer toolchain can be specified by setting the environment variables SDKROOT and DEVELOPER_DIR

    ### open 'bin' directory for the currently active Xcode toolchain 
    open `xcode-select --print-path`/usr/bin
    

    Tools which can be invoked with xcrun include:

    • xcrun simctl <subcommand> ~ iOS Simulator Control
    • xcrun xcodebuild … ~ build/test Xcode projects and workspaces
    • xcrun xctrack <commands> [options] ~ record, import and export Instruments .trace files
    xcrun not_a_tool  help
    
    # xcrun: error: 
    #   sh -c '/Applications/Xcode.app/…/bin/xcodebuild 
    #         -sdk /Applications/Xcode.app/…/MacOSX.sdk 
    #         -find not_a_tool 2> /dev/null' 
    # failed with exit code 17664: (null) (errno=Invalid argument)
    

    xcodebuild

    Think of xcodebuild as single command line tool specialized to harness multiple other tools to manage an overall project build process. An invocation of xcodebuild will execute many tasks just like Build or Test in the Xcode GUI menus do when creating and/or testing a project.

    xcodebuild -help
    

    Note: xcodebuild can be invoked without a proceeding xcrun.

    xcrun xcodebuild -version
    # Xcode 13.2.1
    # Build version 13C100
    
    xcodebuild -version 
    # Xcode 13.2.1
    # Build version 13C100
    

    swift

    Think of swift (by itself) as the interpreted, interactive, scriptable, "REPL" version of the swiftc compiler.

    Then, expand the scope to think of swift as a basis for a extended ecosystem which does not require the presence of Xcode (unless an AppleOS SDK is needed to target Apple hardware).

    swift --help
    
    # SUBCOMMANDS (swift <subcommand> [arguments]):
    #   build:   SwiftPM - Build sources into binary products
    #   package: SwiftPM - Perform operations on Swift packages
    #   run:     SwiftPM - Build and run an executable product
    #   test:    SwiftPM - Build and run tests
    

    Command Line Example

    > swift
    Welcome to Swift version 5.5.2-dev.
    Type :help for assistance.
      1> 8 + 9 
    $R0: Int = 17
      2> :quit
    

    Note: Swift REPL works in conjuction with the LLDB debugger, thus : preceeds commands like help and quit during the REPL session.

    Swift Script Example

    Script example_script.swift File:

    #!/usr/bin/swift
    
    // File: example_script.swift
    // reminder: chmod to executable before invoking the script
    
    import Foundation
    
    func printHelp() {
        print("Please add an argument to the command line. Thanks.")
    }
    
    // --- main ---
    if CommandLine.argc < 2 {
        printHelp()
    } else {
        for i: Int in 1 ..< Int(CommandLine.argc) {
            print( CommandLine.arguments[i] )
        }
    }
    

    Interpretated and Compiled Script Execution:

    ls -l example_script.swift 
    # -rwxr--r--@ 1 user staff  321 Dec 30 16:56 example_script.swift
    
    ./example_script.swift 
    # Please add an argument to the command line. Thanks.
    
    ./example_script.swift a bcd
    # a
    # bcd
    
    ## Let's compile the script...
    swiftc example_script.swift 
    
    ls -l ex*
    # -rwxr-xr-x  1 user staff  55136 Dec 30 16:57 example_script
    # -rwxr--r--  1 user staff    321 Dec 30 16:56 example_script.swift
    
    ./example_script
    # Please add an argument to the command line. Thanks.
    
    ./example_script xyx
    # xyz  
    

    REPL: Read Evaluate Print Loop

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search