It’s not a rare case when software engineers are involved in the development of a few projects simultaneously, for one or even a few products.
Usually, it requires additional dancing around the projects, VCS repositories and their dependencies set up.
This writing is mostly about tools, such as Xcode, AppCode, PODs, and how to set up comfortable multi projects workspace, using them.
For a better understanding of the scene and the actions on it, let’s emulate some real-life requirements.
The task is to develop an SDK, Sample, and Client projects that will be sold as different products.
- SDK is a private library project and stays as a separate product. Has it own dependencies, such as networking and media libraries;
- Sample is a public application project and is a part of SDK product, direct responsibilities of which is to show API use cases. Depends on SDK library;
- Client is a private application project and is another product, that depends on SDK library and other libraries that SDK doesn’t.
- During ongoing development, New Clients can be introduced, that will use all or just a specific set of SDK features.
Here and continuously over the article: Product — something that can be sold; Project — implementation of the services that Product offers. Product can consist of multiple Projects.
Projects Structure & Dependencies
For incoming requirements, one of the possible implementations will be to create three separate Xcode projects and corresponding version control repositories for them:
It is more complex compared to the mono repository setup, but the most flexible and unconnected setup, where each project has isolated VCS history, Releases, Wikis and independent repository privacy settings.
While Swift Package Manager is still pretty new and not supported by all libraries, Carthage requires a lot of manual Xcode setups, CocoaPods is probably the most usable and popular external iOS dependency manager out there.
So, in addition to the existing structure, the Podfile with external dependency list may be added to SDK and Client projects.
On early stages of development you can think that the easiest way to add internal dependency will be to simply drop library Framework file to the Application project folder:
And link it to the Application in the project settings:
And you will be right, it’s easy. But be ready to face some consequences by choosing it:
- If you are involved to develop all of three projects, be ready to have up to 3 Xcode instances opened at the same time;
- If you used to develop with JetBrains tools, be ready to have opened up to another 3 AppCode instances(unfortunately it’s not possible to build UI in AppCode);
- Add few opened emulators and your work machine will start to breathe fire;
- Client’s Podfile must include all dependencies that SDK’s Podfile has, which means if there is a change it must be applied to SDK’s file as well as to Client’s one;
- Copying files from SDK to each project that depends on it (copying could be automated with Xcode pre-build script, but it is still not the best option), just to test simple changes during development. And it is good if changes are functional and may be checked with the unit or environment Tests, but what it SDK exposes UI components or even
What you will really want, is it to be as flexible and comfortable as you were during regular project development, using the same tools that you get used to:
- Continue to have all projects in separate repositories;
- Have one opened Xcode or AppCode window with all needed projects inside, for example, SDK and Client;
- Possibility to setup internal dependencies without copying anything manually;
- Possibility to build projects individually, for example, to build Client together with SDK or only SDK without Client;
- Possibility to setup CocoaPods external dependency independently for each project;
- Possibility to change SDK external dependency in one place;
- Possibility to reuse workspace setup for CI, public CocoaPods package publishing and Application publishing with ease;
- Possibility to manipulate with VCS repository inside that window, for all included projects within it;
- Possibility to track changes between the projects automatically, without additional manual compilation.
And Xcode has a tool that will help to achieve all of that — Workspaces.
Sample and SDK projects will be used as an example below. Everything that will be done with the Sample project can be applied to the Main Application or any other flavor application that depends on SDK.
Workspaces allow you to use multiple projects from one Xcode instance as well as allow to link those projects among them.
To create a workspace, simply open File -> New -> Workspace menu and follow the instructions there. Let’s create one and add it to the Sample project folder:
To add projects to the newly created workspace, simply drop their
.xcodeproj files to Xcode window. Then you can choose a target project you want to run, include dependencies and all that stuff that you used to see in the regular project setup.
CocoaPods is using workspaces to connect all dependencies with your project, and if you are using it, you are for sure using workspaces in your day to day life.
And CocoaPods setup is exactly the main questioned part that left:
- How to deal with CocoaPods workspace that is generated automatically?
- How to use all dependencies that SDK has, in Sample and main App, without any copy-pasting to the POD files?
- How to add dependencies only to Sample or main App, avoiding SDK?
Let’s answer all of these questions, step by step.
First of all, you need to make a POD from SDK project. To do this run following command inside of SDK project dir:
pod spec create
It will create a template
.podspecfile, where you can provide a name, version, description and other information about the library, including source files, headers, frameworks and what is most important dependencies that it will be using.
s.framework = "Foundation"
Now we are able to use this SDK as local POD dependency.
Next step will be to use the SDK library in the Sample project. Run the following command, from a terminal, inside of App or Sample project dir:
It will create a
Podfile file with some smart default values. This Podfile is a specification that describes the dependencies of the targets of one or more Xcode projects:
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'target 'sample' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!# Pods for sampletarget 'sampleTests' do
# Pods for testing
endtarget 'sampleUITests' do
# Pods for testing
Let’s edit it like this:
use_frameworks!workspace 'sample'def sample_pods
podspec :path => '../sdk/sdk.podspec'
endtarget 'sample' do
endtarget 'sdk' do
- Here the
workspace, created previously, is specified. It already links two local projects — SDK and Sample (see Workspaces chapter);
- Then, we are defining a variable,
def sdk_pods, that points to SDK
.podspecfile which contains all of SDKs dependencies;
- Another variable,
def sample_pods, defines all
poddependencies that will be reachable only from the Sample project;
- Next step is to create a
‘sample’target, linked to Sample
projectand depended on both
- And at last, create
‘sdk’target, linked to SDK
projectand depended only on
Note, you must include SDK POD dependencies to all targets that will use it.
Now, install the PODs by running another terminal command :
All POD preparations are done and the workspace is ready for usage.
It’s also worth saying, that pointing directly to the
.podspec is one of the possible options. Cocoapods allows to use a folder as a local path to POD repository, so you could have something like this instead:
pod 'sdk', :path => '../sdk'
But note, it is still required to define a podspec file for SDK, and you will be required to reinstall pods to apply SDK changes. Using podspec directly saves us from that.
The last thing is to link SDK Framework to the Sample project. Open Workspace in Xcode, select Sample project file and go to:
General tab -> Linked Frameworks and Libraries
Press the plus button and type “sdk” in the search field. Then select “sdk.framework” and press “Add” button:
“Linked Frameworks and Libraries” should look like this:
The final look of your project’s structure will be something like:
That’s it. Now you are able to use Xcode Workspace with multiple projects that have different CocoaPods dependencies from one Xcode window.