Swift static library in Objective-C
14 Jan 2018Xcode 9 added the ability to create static libraries with Swift code. Everything works great if you have an all Swift project, you link your library, import the module and away you go. To get your shiny new static library to be visible in Objective-C you need to do a little more work.
These instructions are based on a setup where you will build the static library as a subproject of your main app, you’ll need to make adjustments as required if this does not mirror your setup.
Step 1 - Create/configure the subproject
- Create the subproject using
File
->New
->Project
->Cocoa Touch Static Library
.
- Give the project a suitable name and click
Next
- Add the static library project to your existing project
Step 2 - Link the static library with the main project
- Navigate to the target’s
General
tab for your main app
- Add the static library to
Linked Frameworks and Libraries
using the+
button
For a pure Swift project you should be done now. For a mixed project carry on.
Step 3 - Add the generated swift header to the search path
- Navigate to the
Build Phases
of the static library
- Add a new
Run Script Phase
with the following code and give it a sensible name
At this point you should be able to import the static library using #import <FeatureA/FeatureA-Swift.h>
and start using your Swift code that is annotated with @objc
.
Step 4 - Create a ModuleMap
Depending on how your project is set up you can run into issues where the generated swift file for your main project imports the new static library. The issue here is that it will import using module syntax (@import FeatureA;
), which your project will not currently understand.
- In your static library project create a file called
module.modulemap
and populate it with something like the following
- Navigate to the
Build Phases
of the static library project
- Add the
module.modulemap
file to theCopy Files
phase of your static library
Wrap up
Following the steps above should get you working with a Swift static library within a mixed Swift/Objective-C project much quicker than I did.
Extras
Whenever I read a post like this I’m obviously grateful for the tips, especially if they help resolve the thing that was blocking me, but I’m always left wondering how someone worked it out in the first place. Here’s some rough retrospective notes on the things I did to figure out the above.
Check search paths
I examined all the search paths - you can view these in Xcode by searching in Build Settings
. The one that I was mainly investigating was Header Search Path
, this setting is blank by default.
I noticed that the Static Library project sets its Copy Files
subpath as include/$(PRODUCT_NAME)
so I made the assumption that this directory will be searched.
Examine derived data
I started to look in the Build Products Dir
to see if I could find my generated Swift.h
file but couldn’t see anything. You can get to the Products
folder by expanding the Products
folder in Xcode and then right clicking an item (like the static library) and selecting Show in Finder
.
At this point I was started to think that maybe the generated header just wasn’t being created and this wasn’t meant to be.
Examined the build logs
I was just looking to see if there were any errors I had missed or anything else to help me out. This is when I noticed a phase called Copy FeatureA-Swift.h ...
- BINGO. The header is indeed being created it’s just being put in a place that is not on the search path.
This is when I added the Run Phase Script
from above. It basically copies any files following the name *-Swift.h
from the DerivedSources
folder to the include
folder.
ModuleMap
In my particular project I ran into the problem where the main project’s generated Swift file imported the library. This meant that the library was being imported using module syntax. This step was not too difficult, I just read a few blog posts on creating a module.modulemap file and got it done.
Extra wrap up
I’ve tried to give some insight into the things I did to resolve the issue but reading the above can still be misleading. Taken at face value it looks like I just followed a few systematic steps to get to a solution but the reality was that this debugging took many hours, lot’s off googling, lot’s of swearing and muttering to myself, a few good nights of sleep and a lot of perseverance. Hopefully someone might find this useful (probably just me in a few weeks time when I have forgotten the above).