Picking up the epic tale of printing a Hello on the terminal, this post details how to encapsulate the static library within a framework.

This is the script. Paste it to the terminal and you shall see a Hello World! message.

Here are the key steps:

  1. Creates a Fat Static Library –which we discussed in a previous article.
  2. Adds a Info.plist with metadata.
  3. Arranges files into a framework structure.
  4. Passes some flags to the client to indicate the path to the framework.

Framework structure

The script produces a framework with this structure:

% tree -F Hello.framework 
Hello.framework/
├── Headers -> Versions/Current/Headers/
├── Hello -> Versions/Current/Hello*
├── Modules -> Versions/Current/Modules/
├── Resources -> Versions/Current/Resources/
└── Versions/
    ├── A/
    │   ├── Headers/
    │   │   └── HelloFramework.h*
    │   ├── Hello*
    │   ├── Modules/
    │   │   ├── Hello.abi.json*
    │   │   ├── Hello.swiftdoc*
    │   │   ├── Hello.swiftmodule/
    │   │   │   ├── arm64.private.swiftinterface*
    │   │   │   ├── arm64.swiftinterface*
    │   │   │   ├── arm64.swiftmodule*
    │   │   │   └── x86_64.swiftmodule*
    │   │   ├── Hello.swiftsourceinfo*
    │   │   └── module.modulemap*
    │   └── Resources/
    │       └── Info.plist*
    └── Current -> A/

This is the basic structure without symbolic links:

Hello.framework/
└── Versions/
    └── A/
        ├── Headers/
        ├── Modules/
        ├── Resources/
        │   └── Info.plist
        └── Executable

Headers

I included a header HelloFramework.h and a module.modulemap for compatibility with C-based languages. They are not really needed in a Swift-only project like this.

Modules

The structure of the Modules folder is the tricky part.

Given that files .abi.json, .swiftdoc, .swiftsourceinfo are the same for both architectures we just put them there. These files contain supplementary information that doesn’t depend on architecture, such as documentation comments, ABI (Application Binary Interface) details, and source information for debugging.

But the module/interface files are different, and the convention is to place them at

 <FW_NAME>.framework/Modules/<FW_NAME>.swiftmodule/<ARCH>.<extension>

Therefore:

Modules/Hello.swiftmodule/
├── arm64.private.swiftinterface
├── arm64.swiftinterface
├── arm64.swiftmodule
├── x86_64.private.swiftinterface
├── x86_64.swiftinterface
└── x86_64.swiftmodule

I learned this from looking inside xcarchive. This arrangement appears on several places, for instance, look at modules at /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/. I tried it and it worked out. I don’t know of official documentation on this.

Resources

This folder contains non-code resources like image, audio, localization, storyboards, etc.

Version

That A could be a B, C, D, .. containing several versions of the framework where there is a need to support old and new clients. The Info.plist is intuitive to read. Then there are a number of symbolic links (->) that provide direct access to the latest version of the framework.

Client flags

To build a fat client compile twice and join the result. Here is the command for ARM64.

swiftc -parse-as-library \
    -o UseHello-arm64 \
    -target arm64-apple-macosx10.9.0 \
    -F. \
    -framework Hello \
    -I Hello.framework/Modules/arm64 \
    UseHello.swift

Mind the flags:

  • -F. adds the current folder to the framework search path.
  • -I Hello.framework/Modules/arm64 adds a search path for Swift module interfaces.
  • -framework Hello linker flag that tells the compiler to link against the Hello framework.

Binary Inspection

Commands to verify the export of the hello function and check the architectures included in the framework:

# was the hello function exported?
% nm Hello.framework/Versions/A/Hello | grep hello | swift demangle
000000000000005c T Hello.Hello.hello() -> ()
0000000000000408 S method descriptor for Hello.Hello.hello() -> ()

# what are the framework architectures?
% lipo -info Hello.framework/Versions/A/Hello
Architectures in file: Hello.framework/Versions/A/Hello are: x86_64 arm64

Using it from SPM

Same as Hello Static Library but passing -framework instead -library.

xcodebuild -create-xcframework \
    -framework Hello.framework \
    -output Hello.xcframework

Conclusion

This article encapsulates a static library into a static framework, detailing the framework structure.

This framework is ready to distribute. However, the optimization process that happens during static linking is too slow for development. That’s why we need dynamic libraries and frameworks for development. I’ll be doing that in Hello Dynamic Framework.

References

There are two main resources that describe the framework structure. They are old and none of them explain the .swiftmodule/ folder arrangement.

Articles

Sessions