Back to blog

How to create a closed source Objective-C++ framework for Swift Package Manager

Engineering

Mar 15, 2025

Author: Nidal Fakhouri

How to create a closed source Objective-C++ framework for Swift Package Manager

The Swift Package Manager (SPM) is a simple dependency manager, and unlike its predecessors and alternatives like Cocapods, it’s integrated directly in Xcode. With it you can easily add dependencies to your app or own Swift Package.

Publishing your package definition and code to a public repository makes it accessible for other developers to include as dependencies in their projects. You can search for available Swift Packages via the Swift Package Index.

By default the SPM publishes your code open source, but there may be reasons why you would want to distribute your code in a closed source manner as well.

In this article, we’ll explain and show you how. And if you want the code, here's the GitHub repo.

Table of contents

  1. Why we wanted a closed source package on SPM
  2. TL;DR how it all works
  3. GOTCHAS and lessons learned
  4. The tutorial
  5. Final thoughts

Why we needed a closed source package on SPM

For our purposes at Stytch, we wanted to make some proprietary commercial code for our Fraud & Risk Prevention product, an advanced device fingerprinting solution written in Objective-C++ / C++ / C, available on SPM. This approach would allow us to include it in our public, open source iOS SDK and enable our developer community to integrate it directly into their Swift projects as a separate dependency that was independent of the full Stytch iOS SDK.

By keeping the package closed source, we could protect our intellectual property while still allowing the developers to use it easily in their Swift applications.

TL;DR how it all works

Objective-C++

Objective-C++ is a programming language variant that allows you to mix C / C++ and Objective-C code within the same source file. Objective-C has the file extension .m whereas Objective-C++ has the file extension .mm.

XCFramework

An XCFramework is a package format introduced by Apple to simplify the distribution of binary libraries by bundling multiple versions of a library into a single framework. Created using Xcode, an XCFramework includes the necessary frameworks, libraries, and headers to build for multiple platforms such as iOS, macOS, visionOS, tvOS, watchOS, and DriverKit, including Simulator builds. These frameworks can be static or dynamic and include headers.

XCFrameworks address the limitations of traditional frameworks by streamlining development across platforms.

High-level overview

To create a closed source Objective-C++ framework in SPM, we decided to use a XCFramework in a private repo written in Objective-C++. Once compiled, the XCFramework is completely available to Swift code through the interface defined by the Objective-C++ files and their corresponding public headers. We then added the XCFramework to a publicly available Swift package and added that package as a dependency of our Stytch iOS SDK.

Sequence diagram of publishing a closed-source framework on SPM

GOTCHAs and lessons learned

The tutorial below will demonstrate that this is a relatively simple process, however it required some trial and error for us to figure out.

At first I created a static library file (.a), which I wasted a few hours trying to compile a universal binary for, only to then determine that you could only add a XCFramework to a Swift package. In my initial implementation of the static library, I had a bridging header for what I hoped would be a Swift file talking directly to the C++ code, which worked in that context.

But when I realized I had to port this over to a XCFramework, I found that XCFramework files do not support bridging headers. I had to remove the Swift layer and make the framework only Objective-C, C++, and Objective-C++.

This was fine in the end since everything we could implement in Swift we could also achieve in Objective-C. The framework could have Swift files in it, but they would not be able to communicate with the Objective-C files and would simply provide a parallel set of functionality (there is a chance that you can fix this with a .moudlemap file but I did not investigate further).

The tutorial

If you'd like to follow the tutorial with the code side-by-side, check out the GitHub repo where we've uploaded all the files you'll need to create a closed source framework.

Part 1: Commit to private repo A

In Xcode click File->New->Project and select Framework and click Next.

Create a new Framework in XCode.

Add a name under the Product Name field and click Next. For this example I chose “StytchFoo.”

Name your product and organization in Xcode

A new empty project should look like this:

New StytchFoo project

Add the follow files based on your naming convention:

StytchFooPrivate.hpp
StytchFooPrivate.cpp
StytchFooWrapper.h
StytchFooWrapper.mm

Implementation of StytchFooPrivate.hpp

This implementation demonstrates two functions; one that will return a String defined in C++ and another one that we will be able to call from Swift with a String parameter type that will modify that String in C++ and return a new one.

StytchFooPrivate class

Implementation of StytchFooPrivate.cpp

Here is the implementation of those functions described above.

Functions in StytchFooPrivate

Implementation of StytchFooWrapper.h

This will be the publicly visible interface to Swift, I named the files StytchFooWrapper, but named the actual Objective-C object only StytchFoo because I could not have a StytchFoo.h file as that would have collided with the public header for the framework. Therefore StytchFoo will be the name of the object we refer to in Swift code.

Here we will define a parallel interface as the StytchFooPrivate.hpp file.

StytchFooWrapper file

Implementation of StytchFooWrapper.mm

Here in the implementation file we hold a private reference to the StytchFooPrivate C++ object. A standard Objective-C file will have the extension .m but notice that the file extension for this file is .mm, which makes it a Objective-C++ file where we can write both Objective-C and C++ in one file.

StytchFooPrivate C++ object

Next we need to make sure the right header files are public to the framework.

Select the project file in the left hand menu called the project navigator. Make sure your framework target is selected under the TARGETS section, here its highlight with a yellow toolbox icon. Then from the top menu select Build Phases.

Unfold the Headers section and you will see that the StytchFooWrapper.h is not under the Public section.

Project navigator menu

Drag the StytchFooWrapper.h file into the public section.

Drag StytchFooWrapper.h file into the public section

Delete the Copy Bundle Resources section by clicking the trash can icon on the right to prevent source files from being copied into the final XCFramework for distribution. Your build phases should only include the following sections.

Delete the Copy Bundle Resources.

Then open the StytchFoo.h header file and add #import <StytchFoo/StytchFooWrapper.h> .

StytchFoo.h header file

Now let's add a script that will build the framework for distribution.

Right click on the project file and select New Empty File.

New script file to build the framework.

Create a script called BuildXCFramework.sh, this will create a universal binary .XCFramework that can be run on device and simulator.

Create BuildXCFramework.sh

Implementation of BuildXCFramework.sh

Adjust the values as needed for your naming scheme.

Implement BuildXCFramework.sh

Navigate to your project directory and type the following commands in the terminal:

chmod +x BuildXCFramework.sh

To modify permissions on the script to run it, you only need to run it once.

./BuildXCFramework.sh

To run the script each time you want to build the framework, run this:

StytchFoo % chmod +x BuildXCFramework.sh
StytchFoo % ./BuildXCFramework.sh

After you run the BuildXCFramework.sh script you can check your project directory and you should see a Product directory with the built .xcframework in it. We will need to copy it into the Swift package for step 2.

.XCFframework.sh file

Part 2: Commit to public repo B

Create a public repo and navigate to the empty repo and type:

swift package init --name stytch-foo --type library

This will create a new Swift package named stytch-foo.

In my case I named the directory StytchFooPackage.

Copy your .xcframework file generated in the private repo in step 1 and copy it into the stytch-foo directory of your newly created package. It should look like this:

New swift package stytch-foo

You can double click on the Package.swift file to open the package in Xcode.

Modify your Package.swift file to follow this example file given. We must add a .binaryTarget to the package for the .xcframework.

adding a .binaryTarget to the pacakge

There must be an empty Swift file matching the package name, I could not figure out why.

empty swift file

Commit these changes to your public repo and your Swift package is live!

Part 3: Commit to repo C, your application repo

In Xcode click File->New->Project and select App and click Next.

Select App with repo c

Add a name under the Product Name field and click Next. For this example I chose “StytchFooDemo.”

StytchFooDemo for product name.

The default project at creation should look like this:

Project creation for StytchFooDemo

In Xcode click File->New->Add Package Dependencies.

Once the window opens add the url to the repo to the top left of the below window and it will come up as an option to select. Select the package as seen below and click Add Package.

Add the package.

Click Add Package again.

Add package

Here is where we tie it all together!

Now we can add the import statement import StytchFoo and use our StytchFoo object defined in part 1 of the tutorial in StytchFooWrapper.h and StytchFooWrapper.mm.

If you look in the console in the attached image you will see the log statements that match the implementation of StytchFooPrivate.cpp from step 1. Therefore this demonstrates that this Swift code is calling into C++ code.

Final step

Final thoughts

Building a closed source Objective-C++ framework for Swift Package Manager provides a practical way to distribute proprietary functionality securely and efficiently. By leveraging XCFrameworks, we encapsulated our code in a robust, reusable package that integrates seamlessly into Swift projects while protecting our intellectual property. There are many potential approaches to solving this problem, and this article outlines just one path.

If solving problems and reading topics like this interest you, check out our careers page.

Happy coding!

Build with Stytch

APIs and SDKs for authentication, authorization, security and fraud prevention

Read the doccs

Share this article

How to create a closed source Objective-C++ framework for Swift Package Manager