Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Link/Embed an Objective-C Static Library of Extensions to an Xcode 13 project?

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

  1. Create a new Static Library Xcode project (we’ll call it ExtensionLibrary).
  2. 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.
  3. Import all of the header files of the extensions into the ExtensionLibrary.h file Example: #import "SomeExtension.h
  4. Add all of the .m files into the ExtensionLibrary’s targets compile sources under the build phases tab.
  5. Create a new aggregate target within the ExtensionLibrary.
  6. 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:

  1. Deleted the internal package from the main application
  2. Added the universal binary library ExtensionLibrary.a file and ExtensionLibrary.h file into a new folder
  3. Added the path to the folder where the .a and .h files were copied to the Header Search Path flag
  4. Imported the copied over extension library header in the app delegates header file Example: #import <ExtensionLibrary.h>
  5. 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?

like image 999
SherlockLock Avatar asked Oct 25 '25 11:10

SherlockLock


1 Answers

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:

enter image description here

Static Library Project

Provided the IDE used is Xcode, the steps to create a project as part of another project are as follows:

  1. While having the main project opened, open the File / New / Project menu enter image description here
  2. Choose the iOS / Static Library template and go the next step enter image description here
  3. Give the project a name (I gave SLProject) and go to the next step
  4. On this step choose the root folder of your main project and ensure you have the main project specified as the target for the Add To: option enter image description here

You should end up with the project structure similar to this:

enter image description here

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.h in 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

@import UIKit;

NS_ASSUME_NONNULL_BEGIN

@interface UIView (PrintHierarchy)

- (void)tdw_printHierarchy;

@end

NS_ASSUME_NONNULL_END

UIView+PrintHierarchy.m

#import "UIView+PrintHierarchy.h"

@implementation UIView (PrintHierarchy)

- (void)tdw_printHierarchy {
    NSLog(@"%@", self);
    if (self.superview) {
        [self.superview tdw_printHierarchy];
    }
}

@end

Next, I include this feature in my "master header":

SLProject.h

#import "UIView+PrintHierarchy.h"

And ensure that all headers are included in the Copy Files phase of the static library target:

enter image description here

Linking the projects

Now go to the main project target's Build Phases.

  1. First, you need to link the main project with the static library target under the Link Binary With Libraries phase (click + and find the target of the static library project we just created).
  2. I also usually add the projects my current project depends on in the Dependencies section enter image description here
  3. Since we deal with category methods compiled as part of a static library, due to a known issue, don't forget to add -ObjC flag to the main project's target linker options under Build Settings enter image description here

For 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:

#import <SLProject/SLProject.h>
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view tdw_printHierarchy];
}


@end

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

#import <SLProject/SLProject.h>

And now everything is just made available to the Swift classes as well without any extra imports:

import UIKit

class SwiftView: UIView {
    
    func someMethod() {
        tdw_printHierarchy();
    }
}

Feel free to use the project created while I was writing these instructions here as a reference.

like image 98
The Dreams Wind Avatar answered Oct 28 '25 01:10

The Dreams Wind



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!