Context
I am working on an existing Xcode project that utilizes SPM to manage several internal packages, among other things, that are used within an iOS application.
One of the internal packages contains Objective-C code that defines several extensions of objects and are imported throughout the application.
This package is no longer being actively developed, so I would like to embed it within the iOS application to reduce the compiler workload during a clean build.
Goal
To binarily link a static library that contain object extensions used throughout an existing application in order to reduce compile time.
What I have tried
First, I started by setting up and creating the external project to house the internal package
- Create a new Static Library Xcode project (we’ll call it ExtensionLibrary).
- Copy over the code from the main application to the new ExtensionLibrary project. This code is written in Objective-C and consists of several .h and .m files defining the extensions.
- Import all of the header files of the extensions into the ExtensionLibrary.h file
Example:
#import "SomeExtension.h - Add all of the .m files into the ExtensionLibrary’s targets compile sources under the build phases tab.
- Create a new aggregate target within the ExtensionLibrary.
- Add a run script that performs the following commands.
- Run xcodebuild on the ExtensionLibrary for the iphoneos SDK and iphonesimulator SDK
- Create a universal binary library from the completed builds using lipo
After this, I moved on to linking it to the main application using the following process:
- Deleted the internal package from the main application
- Added the universal binary library ExtensionLibrary.a file and ExtensionLibrary.h file into a new folder
- Added the path to the folder where the .a and .h files were copied to the Header Search Path flag
- Imported the copied over extension library header in the app delegates header file
Example:
#import <ExtensionLibrary.h> - Added the ExtensionLibary.a file to the iOS application’s Link Binary With Libraries Build Phase
Errors
There are two main errors that appear in the main application after doing this process.
- The imported header files for the extensions within the ExtensionLibrary’s header file can not be found in the iOS application.
- The extensions are not available to any of the UIKit objects.
Additional Notes
The main iOS application has the Other Linker flags -ObjC and -all_load set.
The main iOS applications app delegate is written in Objective-C.
The main iOS application is a combination of Swift and Objective-C code, both of which use the extensions defined in the ExtensionLibrary.
The cost to refactor existing code within the main iOS application using the extensions provided in the ExtensionLibrary would be very high, so the linked library needs to work with the existing application with as little changes as possible.
The ExtensionLibrary may undergo future development, so the process to re-link the library should be easily repeatable for future developers who have no experience with this process.
Examples of objects being extended: UIApplication, UIView, UIFont, NSDate, NSArray, and NSError
Question
The main question I have is how do I create and embed a static library of globally available Objective-C extensions into an existing iOS application written in Objective-C and Swift?
Frankly from the steps you provided I can't spot any specific issue. The only part I would do different is the universal binary, since I just can't see a reason to do that (yes, it will require you to re-compile the library when you switch to another platform, but only once. Also you will have "less fat" final binary and freedom to change the source code of it on the fly). Let me just sum up all steps with instructions below:
Sample Project
Our sample project is the iOS project template built by Xcode 13 for iOS applications in Objective-C with the following file structure:
Static Library Project
Provided the IDE used is Xcode, the steps to create a project as part of another project are as follows:
You should end up with the project structure similar to this:
It's not necessary but for static libs I'm used to make one "master header" which has all other library interfaces included (similar to the umbrella headers for frameworks) with the name of the said lib (
SLProject.hin this case), so I remove the implementation file for it (SLProject.m) as well as content of the header itself for now (there is nothing to include). Now, let's create a little category for UIView which prints out the view itself and all other views in the hierarchy (if available):UIView+PrintHierarchy.h
UIView+PrintHierarchy.m
Next, I include this feature in my "master header":
SLProject.h
And ensure that all headers are included in the Copy Files phase of the static library target:
Linking the projects
Now go to the main project target's Build Phases.
-ObjCflag to the main project's target linker options under Build SettingsFor Objective-C part that is it! From here you just import the static library headers wherever you need to and use the methods defined there:
Linking Swift classes
In order to provide access to the same library for Swift classes, it's enough to just add this "master header" to the bridging header in your project:
iOSStaticLibDemo-Bridging-Header.h
And now everything is just made available to the Swift classes as well without any extra imports:
Feel free to use the project created while I was writing these instructions here as a reference.