Using SPM with Xcode

As mentioned, SPM currently only supports creating libraries and command-line executables. So you will not be able to write your next macOS app entirely using SPM you will still need Xcode. Yet, SPM can be extremely useful in collaboration with Xcode as a tool to manage your dependencies and keep them up to date. Indeed, SPM is able to generate an Xcode project containing all the targets and dependencies you specified in Package.swift, which you can include within your macOS app Xcode project or workspace. Let's look at this in more detail.

If you want to create an Xcode project with the same definition of your SPM package, just run swift package generate-xcodeproj, which will output the following information:

$ swift package generate-xcodeproj
generated: ./swURL.xcodeproj
If you ran generate-xcodeproj on the last version of your SPM package, you will also see this warning: warning: dependency 'Promises' is not used by any target. No worries, that only means that the Google Promises library was not required and therefore SPM did not include it in the Xcode project.

If you open in Xcode the generated project, you will see it contains all of the files we created previously.

Usually, you will not want to make any changes to this auto-generated project, since you are going to generate it anew every time you have new dependencies. Instead, you'll include it as a subproject within your main Xcode project or workspace.

Do not forget to remove the executable target we added in the Adding more targets section, otherwise, you will not be able to compile the Xcode project that we are going to build in the remainder of this section. In this case, just create a macOS project instead of an iOS project and you will be fine.

To try this out, let's walk through the following steps:

  1. Create a new iOS project in Xcode using the Single View App template.
  2. Locate the swURL.xcodeproj we just created using the Finder, but do not open it.
  3. Drag and drop swURL.xcodeproj from the Finder into your project Navigator area (the leftmost column with a listing of your files).
  4. Go to your iOS app target Build Phases editor and add a new Link Binary With Library phase. Click the "+" button and select swURL.framework in the ensuing dialog.
  5. Modify your ViewController.swift file so it contains the following code:
import UIKit
import swURL

struct Todo: Decodable {
let userId: Int
let id: Int
let title: String
let completed: Bool
}


class ViewController: UIViewController {

func doTask(completion: @escaping (_ data: Todo?, _ error: Error?) -> Void) {

swURL.get(url: URL(string: "https://jsonplaceholder.typicode.com/todos/1")!,
completion: { (result : Todo?, error: Error?) in
completion(result, error)
})
}

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.

doTask { (result : Todo?, error: Error?) -> Void in
print("Hello, world!")
}
}
}

Finally, you can run your app and check the Xcode console to verify it contains "Hello, world!"

As you will notice, the code we used in ViewController is similar to what we used in our command-line target that created previously. The only difference is the absence of the dispatch group synchronization. Indeed, an iOS app is based on a run loop it will be there until the user decides to quit it so there is no need to make you main thread wait for the asynchronous task to complete.

Congratulations! You have just created an iOS app with a dependency that you can easily manage using SPM. Whenever new versions of your dependencies will be available, you will just need to run swift package update and swift package generate-xcodeproj to update your dependencies in your iOS app.