Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save pitfield/436fa91114032960d0d1cb220d93d407 to your computer and use it in GitHub Desktop.
Save pitfield/436fa91114032960d0d1cb220d93d407 to your computer and use it in GitHub Desktop.
Create an iOS app with Swift Package Manager dependencies

Create an iOS app with Swift Package Manager dependencies

CAUTION: This information is mostly obsolete. Xcode 11 allows a project to directly declare its Swift Package Manager dependencies.

This gist walks through creating an Xcode project for an iOS app that consumes packages in Swift Package Manager (SPM).

We'll be using Xcode 10.2 and Swift 5.

Scenario

We want to create an iOS app named Swifty that consumes PostgresClientKit, a Swift client library for PostgreSQL. PostgresClientKit itself has two dependencies, BlueSocket and BlueSSLService.

Approach

We'll create an Xcode workspace containing two projects. The first project, Swifty, is an iOS app created using Xcode. The second project, Dependencies, is a framework created as a library package in SPM that has a dependency on PostgresClientKit (and its dependencies in turn). We then add the Dependencies build products to Swifty as embedded binaries.

Steps

Create an empty top-level directory named Swifty.

$ mkdir Swifty
$ cd Swifty

In Xcode, create a workspace named SwiftyWorkspace.xcworkspace in the Swifty folder.

  • File / New / Workspace...
  • Navigate to the Swifty folder
  • Save As: SwiftyWorkspace.xcworkspace
  • Save
$ ls -1
SwiftyWorkspace.xcworkspace

In Xcode, create an iOS project named Swifty.xcodeproj in SwiftyWorkspace.

  • File / New / Project...
  • Template: iOS
  • Application: Single View App
  • Next
  • Product Name: Swifty
  • Next
  • Navigate to the Swifty folder
  • Add to: SwiftyWorkspace
  • Create
$ ls -1
Swifty
SwiftyWorkspace.xcworkspace

$ ls -1 Swifty/
Swifty
Swifty.xcodeproj
SwiftyTests
SwiftyUITests

Next, create a Dependencies package in SPM.

$ mkdir Dependencies
$ cd Dependencies
$ swift package init --type library
Creating library package: Dependencies
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/Dependencies/Dependencies.swift
Creating Tests/
Creating Tests/LinuxMain.swift
Creating Tests/DependenciesTests/
Creating Tests/DependenciesTests/DependenciesTests.swift
Creating Tests/DependenciesTests/XCTestManifests.swift

We can remove some cruft. We need to keep at least one .swift file, but it can be empty.

$ rm -rf README.md Tests Sources/Dependencies/*
$ touch Sources/Dependencies/Placeholder.swift
$ find .
.
./.gitignore
./Package.swift
./Sources
./Sources/Dependencies
./Sources/Dependencies/Placeholder.swift

Now open Package.swift in an editor, and edit the dependencies and targets. It should look like this:

// swift-tools-version:5.0

import PackageDescription

let package = Package(
    name: "Dependencies",
    products: [
        .library(
            name: "Dependencies",
            targets: ["Dependencies"]),
    ],
    dependencies: [
        .package(url: "https://github.com/codewinsdotcom/PostgresClientKit", from: "0.0.0"),
    ],
    targets: [
        .target(
            name: "Dependencies",
            dependencies: ["PostgresClientKit"]),
    ]
)

We can now build the package, resolving the dependencies.

$ swift build
Fetching https://github.com/pitfield/PostgresClientKit
Fetching https://github.com/IBM-Swift/BlueSocket.git
Fetching https://github.com/IBM-Swift/BlueSSLService
Completed resolution in 1.94s
Cloning https://github.com/IBM-Swift/BlueSocket.git
Resolving https://github.com/IBM-Swift/BlueSocket.git at 1.0.46
Cloning https://github.com/IBM-Swift/BlueSSLService
Resolving https://github.com/IBM-Swift/BlueSSLService at 1.0.46
Cloning https://github.com/pitfield/PostgresClientKit
Resolving https://github.com/pitfield/PostgresClientKit at develop
[4/4] Compiling Swift Module 'Dependencies' (1 sources)

And then generate an .xcodeproject.

$ swift package generate-xcodeproj
generated: ./Dependencies.xcodeproj

Back in Xcode, let's add that new Xcode project to our workspace.

  • In the Project Navigator, right-click on the background. Select "Add Files to SwiftyWorkspace...".
  • Navigate to the Dependencies folder and select Dependencies.xcodeproj
  • Add

We can remove some unneeded schemes.

  • Product / Scheme / Manage Schemes...
  • Delete all the schemes except Swifty
  • Close

We can remove some unneeded targets.

  • In the Project Navigator, select the Dependencies project

  • In the editor, remove all targets whose names end in PackageDescription:

    • DependenciesPackageDescription
    • PostgresClientKitPackageDescription
    • SSLServicePackageDescription
    • SocketPackageDescription

Four targets should remain:

  • Dependencies
  • PostgresClientKit
  • SSLService
  • Socket

Finally, we embed the dependencies in Swifty.

  • In the Project Navigator, select the Swifty project
  • In the editor, select the General tab
  • On that tab, click "+" in Embedded Binaries
  • Select all four dependencies under Dependencies / Products (they may be in a different order):
    • Dependencies.framework
    • PostgresClientKit.framework
    • SSLService.framework
    • Socket.framework
  • Add

Building Swifty should now bring in all the dependencies.

  • In the toolbar, confirm the Swifty scheme is selected
  • Product / Build

Open AppDelegate.swift. Import PostgresClientKit.

import PostgresClientKit

In application(_:didFinishLaunchingWithOptions), set the Postgres log level, then log a message. Code completion should work.

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        Postgres.logger.level = .info
        Postgres.logger.info("Log level set to \(Postgres.logger.level)")
        return true
    }

Run the app on a simulator or a device. You should see the logged message in the console (View / Debug Area / Activate Console).

Success!

@chenminhua
Copy link

Thanks for sharing. ❤️

BTW, generate-xcodeproj is Deprecated. https://forums.swift.org/t/rfc-deprecating-generate-xcodeproj/42159

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment