Tuist Tutorial for Xcode
Learn how to use Tuist to create and manage complex Xcode projects and workspaces on-the-fly. By Mark Struzinski.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Tuist Tutorial for Xcode
25 mins
- Getting Started
- Installing Tuist
- Getting Started With Tuist
- Understanding the Tuist Manifest File
- Setting up the Project File
- Defining Your Project
- Defining Your First Target
- Generating Your First Project
- Introducing Focus Mode
- Adding Support for Settings Files
- Setting up External Dependencies
- Moving Files Into Place
- Generating the Framework From Tuist
- Linking the Framework in MovieInfo
- Generating and Updating the Codebase
- Adding a Unit Test Target
- Adding Poster Images
- Adding a Swift Package
- Implementing Poster Images in the List
- Where to Go From Here?
Generating Your First Project
OK, the time has come to use Tuist to generate your first project and workspace! Open Terminal again. Press Control-C or Command-. to stop the current editing session. Xcode will show you a message indicating the workspace no longer exists:
Click Close.
Before generating the project, you'll use Tuist's lint
command to check the validity of your project manifest.
From Terminal, run the following:
tuist lint project
This command will lint the manifest file in the current directory.
You'll see output similar to this:
Loading the dependency graph
Loading project at /Users/ski081/Desktop/WiP
Running linters
Linting the environment
Linting the loaded dependency graph
No linting issues found
Next, in Terminal, enter the following:
tuist generate
You'll get output similar to the following:
Generating workspace MovieInfo.xcworkspace
Generating project MovieInfo
Project generated.
Total time taken: 0.579s
Now, run the following in Terminal to see the directory's new content:
ls -al
You'll see something like this:
drwxr-xr-x@ 10 ski081 staff 320 May 30 22:01 .
drwx------@ 26 ski081 staff 832 May 30 21:51 ..
-rw-r--r--@ 1 ski081 staff 6148 May 29 08:50 .DS_Store
drwxr-xr-x@ 4 ski081 staff 128 May 30 08:23 AdditionalTargetFiles
drwxr-xr-x 3 ski081 staff 96 May 30 22:01 Derived
drwxr-xr-x@ 8 ski081 staff 256 May 30 21:50 MovieInfo
drwxr-xr-x@ 6 ski081 staff 192 May 30 22:01 MovieInfo.xcodeproj
drwxr-xr-x@ 6 ski081 staff 192 May 30 22:01 MovieInfo.xcworkspace
-rw-r--r--@ 1 ski081 staff 409 May 30 22:00 Project.swift
drwxr-xr-x@ 5 ski081 staff 160 May 30 08:24 config
Congratulations! You generated your first project and workspace. :]
Finally, enter the following to open the workspace:
xed .
xed .
opens a workspace in the current directory, if one exists. If not, it will open an Xcode project. So, in either case, it opens the "correct" file.Build and run. The app will behave exactly as before, but there's a huge difference: You've removed the dependency on the project file and can now generate it whenever you want.
Introducing Focus Mode
Before you start adding additional features and projects through Tuist, there's another interesting feature to review: focus mode. Focus lets you open a single target and its dependencies. This lets you ignore any projects or dependencies you don't want to use at the time.
Try this out on your newly generated project setup. From Terminal, enter the following:
tuist focus MovieInfo
This tells Tuist to generate and open only the assets you need to work with MovieInfo
. It will also remove unneeded assets from the Xcode cache and replace them with pre-compiled assets to ensure Xcode doesn't try to index them.
You'll see a message like this:
Generating workspace MovieInfo.xcworkspace
Generating project MovieInfo
Deleted persisted 1622124912.TuistAnalytics.45978508-3C7B-4A87-B3DD-1B5FD4476A55.json
Xcode will open with your target and all dependencies initialized. This isn't useful with a small project like MovieInfo, but you can see how it would benefit larger projects with multiple sub-projects and dependencies.
Adding Support for Settings Files
Next, you'll generate your project and target settings from external .xcconfig files. This is a best practice to give you more control over how Tuist builds the project.
This practice also isolates you from any changes you need to make to the project and target settings in the future. If you needed to change a build setting without an external configuration, Tuist would override it each time it regenerated the file. You'd then need to remember to make that change again in the project or target settings.
By pointing Tuist to an external source, you can make changes there and any new project generation commands will use them.
Tuist uses Settings
to represent configurations that you can use in project and target objects. You'll use it to set up external configuration sources.
Begin another editing session from Terminal:
tuist edit
Open Project.swift and add the following just after the import
:
// 1
let projectSettings = Settings(
// 2
debug: Configuration(xcconfig: Path("config/MovieInfoProject.xcconfig")),
release: Configuration(xcconfig: Path("config/MovieInfoProject.xcconfig")),
defaultSettings: .none)
This does the following:
- Declares a
Settings
object. - Specifies .xcconfig files to use for debug and release configurations. These are already in the starter project, in the config folder.
Next, in the initialization of project
, replace the settings
parameter with your new object:
settings: projectSettings,
Next, you'll apply settings to your target. Right under projectSettings
, create targetSettings
:
let targetSettings = Settings(
debug: Configuration(xcconfig: Path("config/MovieInfoTarget.xcconfig")),
release: Configuration(xcconfig: Path("config/MovieInfoTarget.xcconfig")),
defaultSettings: .none)
Now update the settings
parameter in the initialization of the Target
object, to the following:
settings: targetSettings)
Save Project.swift and exit with Control-C in Terminal. Now, generate your project again with:
tuist generate
Open the workspace. Build and run. The app looks the same as before, but now your build settings live outside of the project so you can apply the same settings every time you generate the project with Tuist.
Setting up External Dependencies
Your next goal is to set up external framework dependencies in the Tuist manifest file. This lets you automatically set up and link any framework code you already have on disk to your project. No more dragging and dropping and fussing with Xcode build settings!
Imagine you've decided to move the networking functionality out of the iOS codebase so you can share it with multiple targets, possibly for a future macOS or watchOS app. The easiest way to do this is to extract the network layer and turn it into a dynamic framework in your project.
Moving Files Into Place
First, you'll need to move the relevant files into a separate location. This lets you reference them easily from the Tuist manifest file.
The starter project has a directory named AdditionalTargetFiles. Move NetworkKit from inside this directory to the root of the project. It should sit alongside the MovieInfo directory and Project.swift.
Once you finish, your directory structure should look like this:
Move the Network directory at MovieInfo/Source/Network and all its contents to the trash.
Generating the Framework From Tuist
In Terminal, switch to the NetworkKit directory and run the edit command:
cd NetworkKit
tuist edit
As before, this opens a project in Xcode. Select Project.swift. You'll see a blank file with an import
at the top.
Now, enter the following:
let projectSettings = Settings(
debug: Configuration(xcconfig: Path("config/NetworkKitProject.xcconfig")),
release: Configuration(xcconfig: Path("config/NetworkKitProject.xcconfig")),
defaultSettings: .none)
let targetSettings = Settings(
debug: Configuration(xcconfig: Path("config/NetworkKitTarget.xcconfig")),
release: Configuration(xcconfig: Path("config/NetworkKitTarget.xcconfig")),
defaultSettings: .none)
let project = Project(
name: "NetworkKit",
organizationName: "Ray Wenderlich",
settings: projectSettings,
targets: [
Target(
name: "NetworkKit",
platform: .iOS,
product: .framework,
bundleId: "<YOUR_BUNDLE_ID_HERE>",
infoPlist: "NetworkKit/Info.plist",
sources: ["NetworkKit/Source/**"],
settings: targetSettings)
])
Don't forget to replace <YOUR_BUNDLE_ID_HERE> with your own values. The bundle ID must be different from the bundle ID you're using for the MovieInfo app.
The code above is similar to your main project definition, with one key difference: Notice that product
in the Target
initializer declares a framework instead of an app product. This generates a dynamic iOS framework.
Save this file and enter Control-C in Terminal to end the editing session.