Now that Swift Package Manager has started to see some adoption and we have some integration inside of Xcode, I think it's time to take a deeper look at how to use it both as a consumer and as a library author. In this episode we'll create a DiceKit library using SwiftPM, then use it in a command line utility.
Episode Links Swift Package Manager Creating our Own Packages Let's assume we want to make a dice rolling library for others to use. We're going to make a folder to put our library in called DiceKit: $ mkdir DiceKit $ cd DiceKit Then we can create our project: $ swift package init This will use the name of our folder as the name of the package. If you want to change this, you can add the --name flag and specify something else. Note that in this folder there is no xcodeproj. This is because Swift packages leverage settings from Package.swift as well as files on the filesystem to determine what the package contains. Building Packages Since there is no Xcode project, you use the command line to build packages: $ swift build If you inspect the file system now there's a hidden/ignored folder called .build which contains all the built artifacts. Let's create a simple type we can expose in this library. Create a file in Sources/DiceKit called Die.swift. public struct Die { public let numberOfSides: Int public init(numberOfSides: Int) { self.numberOfSides = numberOfSides } public func roll() -> Int { return Int.random(in: 1...numberOfSides) } } Note that this file is already part of the target because we added it to the Sources/DiceKit folder. There's no way for us to run this code yet because we just created a library. Let's create a command line executable that uses this library. Creating a Command Line Swift Executable Change to the parent directory and create a new one to contain our command-line application: cd .. mkdir DiceRoller cd DiceRoller Then we can initialize a new executable package: $ swift init --type executable Note that this creates a main.swift for us, and this is the entry point where our code starts to run when the app is executed. Next let's add a dependency for the DiceKit library. We'll use a local path to refer to the package rather than a published version. In Package.swift: import PackageDescription let package = Package( name: "DiceRoller", dependencies: [ .package(path: "../DiceKit") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( name: "DiceRoller", dependencies: ["DiceKit"]), .testTarget( name: "DiceRollerTests", dependencies: ["DiceRoller"]), ] ) Now when we build it, it will pull in dependencies and build those too: $ swift build Completed resolution in 0.56s [6/6] Linking DiceRoller If we run the app we can see that it works: $ .build/debug/DiceRoller Hello, World! Let's jump back over to main.swift and use our library: import DiceKit let die = Die(numberOfSides: 6) let roll = die.roll() print("🎲> You rolled a \(roll)") Running swift build again, we can see that it works. $ swift build $ .build/debug/DiceRoller 🎲> You rolled a 4 Working in Xcode Projects If we want to build & debug within Xcode we can, but we need to generate an Xcode project. $ swift package generate-xcodeproj Then if we open it we can run it, set breakpoints, etc. Adding Packages from Xcode If you already have an Xcode project and integrate some Swift packages you can do this from the File -> Swift Packages menu and enter in a URL to a library. The best way that I've found to search for these packages is with SwiftPM.co.