NicholasTD07 / xcframeworks

Demonstration of creating and integrating xcframeworks and their co-op with static libraries and Swift packages

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

xcframeworks repo

This is a demonstration of creating and integrating the xcframeworks and their co-op with static libraries and Swift packages within the same Xcode project.

Table of contents

Pre-requisities

  • Xcode 11
  • Swift 5.1 toolchain - run sudo xcode-select -s path/to/Xcode11 in terminal.
  • Github/Gitlab/Bitbucket account set in Xcode's account preferences

Introduction: New .xcframework format

Requirements

  • Xcode11
  • Swift 5.1 and above

Motivation & consequences

  • introduce standard format to gain module stability for your Swift frameworks & libraries. Library author & client of a library are no longer required to use the same version of compiler

  • provide seamless experience when creating & integrating the module stable frameworks

  • support all Apple platforms and architectures NOTE: while fat framework can support module stability, the lipo command line tool, that is used to fuse the frameworks together, is not officially supported by Apple. Also using lipo tools falls short in cases of fusing two platforms with similar architectures together - e.g. arm6 architecture can be found in iOS + watchOS.

  • STOP creating & using fat frameworks == no more lipo.

  • STOP slicing frameworks by stripping the architectures in your projects' targets' custom build-phase.

Contents of xcframework

This format bundles module-stable frameworks (.swiftinterface) for the platforms of interest.

The Info.plist contains all available frameworks in a bundle. This information is used by Xcode during the linking time => xcodebuild picks the right framework for the platform we're building against

The structure of xcframework looks as shown below xcframework

Size of xcframework

The size of an xcframework was smaller than the size of an corresponding fat framework. I tested swift only & mixed frameworks. Generally the lipo commandline tool adds a bit of overhead for all contained architectures.

Platforms

xcframework supports all Apple platforms - iOS, macOS, tvOS, watchOS, iPadOS, carPlayOS.

List of destinations

Platform Destination
iOS generic/platform=iOS
iOS Simulator generic/platform=iOS Simulator
iPadOS generic/platform=iPadOS
iPadOS Simulator generic/platform=iPadOS Simulator
macOS generic/platform=macOS
tvOS generic/platform=tvOS
watchOS generic/platform=watchOS
watchOS Simulator generic/platform=watchOS Simulator
carPlayOS generic/platform=carPlayOS
carPlayOS Simulator generic/platform=carPlayOS Simulator

How to create .xcframework that contain iOS + iOS Simulator platforms

1. Archive your scheme for desired platforms (destinations)

1.1 Pass SKIP_INSTALL=NO && BUILD_LIBRARY_FOR_DISTRIBUTION=YES to archive your scheme

xcodebuild archive \
-workspace MyWorkspace.xcworkspace \
-scheme MyScheme \
-destination destination="generic/platform=iOS" \
-archivePath "archives/MyScheme-iOS" \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES

1.2 iOS Simulator - archive your scheme for iOS Simulator platform by specifying correct destination destination="generic/platform=iOS Simulator" & point archivePath to architecture specific path, e.g. archives/MyScheme-iOS-Simulator.

xcodebuild archive \
..
-destination destination="generic/platform=iOS Simulator" \
-archivePath "archives/MyScheme-iOS-Simulator" \
..

1.3 iOS - archive your scheme for iOS by specifying destination="generic/platform=iOS" & point archivePath to device specific path. The architecture specific path will ensure the archive from step 2. wont be overwritten, e.g. MyScheme-iOS

xcodebuild archive \
..
-destination destination="generic/platform=iOS" \
-archivePath "archives/MyScheme-iOS" \
..

Locations

Binaries in .xcarchive are located under:

  • Products/Library/Frameworks folder for dynamic frameworks
  • Products/usr/local/lib folder for static libraries

2. Create .xcframework from built archives

xcodebuild allows you to create xcframework by specifying frameworks, libraries or even can add headers to the libraries. -create-xcframework

1. Specify all frameworks or libraries that you want to add into .xcframework
2. Specify the outpath paht using -output argument. Don't forget to add .xcframework extension to your output path.
xcodebuild -create-xcframework \
           -framework My-iOS.framework \
           -framework My-iOS_Simulator.framework \
           -output My.xcframework

Module stability is gained with Xcode 11 + Swift 5.1, once your module declares .swiftinterface file, that describes the public interface of your framework along with linker flags, used toolchain and other info. Swift interface can be found under your framework's swiftmodule folder. .swiftinterface file is autogenerated when xcframework is created.

swift-interface

Generate .xcframeworks for iOS + iOS Simulator using create_xcframeworks.sh script

The archiving and creation of .xcframework is excercised by create_xcframeworks.sh script.

This script takes 1 parameter that defines output directory. Output directory will create subfolder for archives and xcframeworks.

The script will:

  • archive the scheme StaticLibrary & create the .xcframework
  • archive the scheme DynamicFramework & create the .xcframework

Generated xcframework

Usage

./scripts/create_xcframeworks.sh OUTPUT_DIRECTORY_NAME

eg.

./scripts/create_xcframeworks.sh Products

Testing & Troubleshooting

Make sure to always build & run your generated xcframework before distributing it to your clients. Few of the problems will unveil just at the compile or run time, so don't rely solely on the success of the xcframework creation.

Here's the list of compiler errors I got across when integrating built xcframework into Xcode project.

Problem Severity Description Solution
Redundant conformance of x to NSObjectProtocol error - thrown at dynamic linking time Your class is already subclassed from NSObject, which conforms to NSObjectProtocol Check protocol conformances of your classes and remove redundant conformance to NSObjectProtocol
Use of unimplemented initializer 'init()' for class error - thrown at dynamic linking time Objective-C ABI public classes need to provide public init Provide public init override for your public class: override public init()
@objc' class method in extension of subclass of Class X requires iOS 13.0.0 error Rules for interoperability with Objective-C has changed since iOS 13.0.0. and currently doesn't support @objc interoperability in class extensions. There's open question on Swift forums Move/Remove @objc declaration from your Swift class extension
scoped imports are not yet supported in module interfaces warning Read more about Swift import declarations here: https://nshipster.com/import/ Import the module instead of specific declaration. For example: change import class MyModule.MyClass to import MyModule

Distribution of xcframeworks


How to integrate .xcframework in your project

  1. Drag & drop .xcframework manually into your project's target

Drag & drop xcframework

  1. Embed & sign .xcframework in your project's target

Embed & sign .xcframework


What's in XCFrameworks workspace

XCFrameworks workspace consists of:

  • StaticLibrary project - represents static library project

  • DynamicFramework project - represents project that builds dylib

  • Swift Package - Swift Package for internal development (within Sample project)

  • TextAttributes - external Swift Package

  • Sample - Sample project that includes all of the dependencies mentioned above.

swift-interface


Materials

Binary Frameworks in Swift

https://developer.apple.com/videos/play/wwdc2019/416/

ABI Stability & Module Stability - swift.org

https://swift.org/blog/abi-stability-and-more/

Library evolution for stable ABIs

https://github.com/apple/swift-evolution/blob/master/proposals/0260-library-evolution.md

Library evolution - Docs

https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst

Swift Unwrapped - Swift 5.1 with Doug Gregor (Library evolution, ...)

https://spec.fm/podcasts/swift-unwrapped/308610

Alexis Beingessner- How Swift Achieved Dynamic Linking Where Rust Couldn't

https://gankra.github.io/blah/swift-abi/

Presentation about Dependency management in Xcode 11

https://www.slideshare.net/BorisBielik/dependency-management-in-xcode-11-153424888

About

Demonstration of creating and integrating xcframeworks and their co-op with static libraries and Swift packages

License:MIT License


Languages

Language:Shell 43.0%Language:Swift 24.2%Language:Objective-C 19.5%Language:Ruby 13.3%