Hello Static Framework
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.
Static Framework demo
Here are the key steps:
- Creates a Fat Static Library –which we discussed in a previous article.
- Adds a Info.plist with metadata.
- Arranges files into a framework structure.
- 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.
- Anatomy of Framework Bundles (Framework Programming Guide)
- Anatomy of a Framework Bundle (Bundle Programming Guide)
Articles
- Creating a multiplatform binary framework bundle
- Creating a static framework
- Identifying and addressing framework module issues
- Embedding nonstandard code structures in a bundle This one talks about the install_name_tool.
- Placing content in a bundle Describes where to place some files (Info.plist and others)
Sessions