# Why is it so damn difficult to create a binary framework for your Swift Package

Swift Package Manager (SPM) allows you to

* Share source code (open-source)
    
* Distribute binaries to protect your intellectual property (closed source)
    

Unfortunately, SPM does not help you to create a precompiled binary version of your package products. Although it is a common practice for SDKs to publish binary framework(s) in addition to source code.

Prominent examples are

* [Firebase iOS SDK](https://github.com/firebase/firebase-ios-sdk/releases/tag/8.6.0)
    
* [Facebook iOS SDK](https://github.com/facebook/facebook-ios-sdk/releases/tag/v11.1.0)
    
* [AWS iOS SDK](https://github.com/aws-amplify/aws-sdk-ios/releases/tag/2.25.0) (vending binaries, not code, through SPM)
    

App developers often expect a pre-compiled library version to speed up their build time. Otherwise, the build process for an app has to compile framework code.

Why is it so difficult creating binaries?

# Challenges

You have to remember that

> A frameworks can only contain a single module, so if you are trying to ship a package product that contains multiple targets, this approach won't work.

as [noted in the Swift Forum](https://forums.swift.org/t/how-to-build-swift-package-as-xcframework/41414/2) by Boris Bügling.

Let me explain this statement with an example. A package structure like this

```Swift
// swift-tools-version: 5.6
import PackageDescription

let package = Package(
    name: "MyPackage",
    products: [
        .library(name: "Lib1", targets: ["LibATarget"])
    ],
    dependencies: [
        .package(path: "../OtherPackage")
    ],
    targets: [
        .target(
            name: "LibATarget",
            dependencies: ["TargetB"],
        .target(
            name: "TargetB",
            dependencies: [.product(name: "ExternalLib", package: "OtherPackage")])
```

would require compiling three binary frameworks.

* `LibATarget.xcframework`
    
    * `TargetB.xcframework`
        
        * `ExernalLib.xcframework` (hoping that ExternalLib consists of a single module)
            

If you rely on another package, you might run into situations in which it is impossible to get/create a binary framework for that package dependency. Here is an [example](https://github.com/johnxnguyen/Down/pull/266) concerning a popular Swift package (2k+ stars).

A package structure like the example above is not uncommon. Especially considering the following recommendation given in the Apple Swift Packager Manager [documentation](https://github.com/apple/swift-package-manager/tree/main/Documentation#about-modules):

> As a rule of thumb: more modules is probably better than fewer modules. The package manager is designed to make creating both packages and apps with multiple modules as easy as possible.

# How to generate binary frameworks

You need an Xcode project (`.xcodeproj`) to [compile a binary framework](https://github.com/bielikb/xcframeworks#how-to-create-xcframework-that-contain-ios--ios-simulator-platforms) (`.xcframework`) but the SPM command `swift package generate-xcodeproj` is deprecated and does not work correctly if your Swift Package has resources :(

You can

* manually create an Xcode project and keep it in sync with your folder structure
    
* auto-create an Xcode project via tooling (e.g. [Xcodegen](https://github.com/yonaskolb/XcodeGen) or [Tuist](https://tuist.io/))
    
* develop your custom tooling (e.g. by leveraging [CocoaPods](https://cocoapods.org/))
    

The [AWS SDK for iOS](https://github.com/aws-amplify/aws-sdk-ios) is using `Xcodegen` and then [using a python scrip on CircleCI](https://github.com/aws-amplify/aws-sdk-ios/blob/9b5fc4f07068956e5ff6901ccdd9942220afcdb2/.circleci/config.yml#L411) to create their SDK binaries.

> We will now use Xcodegen to generate the project files that are used to build the SDK releases.

The Firebase iOS SDK team wrote their own [release tooling](https://github.com/firebase/firebase-ios-sdk/tree/master/ReleaseTooling) to generate binary frameworks from CocoaPod specs and zip them. Very impressive!

# Conclusion

If your Swift package is small and simple, try community tools like [swift-create-xcframework](https://github.com/unsignedapps/swift-create-xcframework) or [spm-to-xcframework](https://github.com/mstfy/spm-to-xcframework).

%[https://github.com/unsignedapps/swift-create-xcframework] 

Please note that it is necessary to abstract the bundle access as Xcode won't create the internal static extension `Bundle.module` for XCFrameworks.

If those tools don't work for your Swift Package, you might need to invest in custom tooling. You can get inspired by the examples above.

I certainly hope Apple's Swift Package Manager provides tooling to support SDK maintainers in the future.
