Giter Site home page Giter Site logo

bielikb / xcframeworks Goto Github PK

View Code? Open in Web Editor NEW
533.0 30.0 46.0 5.88 MB

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

License: MIT License

Objective-C 24.34% Shell 45.19% Swift 25.18% Ruby 5.30%
xcframework xcode swift swift-interface swift-packages macos catalyst module stability binary

xcframeworks's Introduction

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 and above
  • Swift 5.1 and above
  • Github/Gitlab/Bitbucket account set in Xcode's account preferences

Changelog

What's new Xcode version Swift version Description
Module stable binaries Xcode 11 Swift 5.1 Library evolution allows the library authors to distribute module stable Swift binaries
New version of .swiftinterface interface Xcode 11.4 Swift 5.2 New annotations added to Swift @_inheritsConvenienceInitializers, @_hasMissingDesignatedInitializers. ⚠️ Module interfaces aren't backwards compatible, clients using Swift 5.1.3 and below won't be able to compile Swift binaries compiled with Swift 5.2. More info here
Support for binary dependencies in SwiftPM Xcode 12.0 Swift 5.3 Swift Package Manager now supports declaring binary targets in Package.swift
Debug symbols Xcode 12.0 Swift 5.3 Debug symbols (dSYMs, BCSymbolMaps) can be included within the xcframework through new -debug-symbols <absolute path> flag

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. Please note, that the module stable interfaces are only forward-compatible

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

  • support all Apple platforms and architectures

  • STOP creating & using fat dynamic frameworks. (library author)

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

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 specified by library identifiers. This information is used by Xcode during the linking time => xcodebuild picks the right framework for the platform we're building against. Since Xcode 12.0 the xcframework can contain also debug symbols (dSYMs, BCSymbolMaps).

The structure of xcframework looks as shown below

xcframework

Size of xcframework

During my tests I realized, 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 & their variants - iOS, maccatalyst, macOS, tvOS, watchOS, iPadOS, carPlayOS.

List of destinations

Platform Destination
iOS generic/platform=iOS
iOS Simulator generic/platform=iOS Simulator
maccatalyst generic/platform=macOS,variant=Mac Catalyst
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

This section describes the process of creating the xcframework by archiving & creating the final xcframeworks from 2 archives built for iOS & iOS Simulator.

However, if you're not interested in the details of the process of how the xcframework is created, head directly to section: Create xcframework using fastlane plugin.

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 "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 "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 "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 path using -output argument. Don't forget to add .xcframework extension to your output path.
xcodebuild -create-xcframework \
           -framework My-iOS.framework \
           -debug-symbols <absolute path to dSYM or BCSymbolMaps folder in the xcarchive> # available from XCode 12.0+
           -framework My-iOS_Simulator.framework \
           -debug-symbols <absolute path to dSYM or BCSymbolMaps folder in the xcarchive> # available from XCode 12.0+
           -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

Create xcframework using fastlane plugin

This plugin allows you to generate the xcframework (including all dSYMs & BCSymbolMaps) by specifying the desired destinations. E.g. destination [iOS] will generate xcframework that contains slices for both iOS & iOS Simulator. ⚠️ Currently the plugin doesn't support static libraries.

  1. Add plugin to your project
fastlane add_plugin create_xcframework
  1. Add lane to your Fastfile
desc "Export xcframework"
lane :export_xcframework do
  create_xcframework(
    workspace: 'path/to/your.xcworkspace',
    scheme: 'name of your scheme',
    destinations: ['iOS', 'maccatalyst'],
    xcframework_output_directory: 'Products/xcframeworks'
  )
end

NOTE:

Version 1.1.0 of the fastlane plugin includes support for debug symbols.

You can try out the plugin in this project by calling following command:

bundle exec fastlane export_xcframework

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
Can’t use framework compiled with Swift 5.2 in Swift 5.1.3 project error - thrown at linking time The xcframework was generated using the Swift 5.2 and above. Module stable interfaces are not backwards-compatible. Update your Xcode to Xcode 11.4 and above or generate module stable binary using Xcode 11.3 and below
Incompatible module error - thrown at linking time The module built for iOS Simulator shares the same arch slice as the new M1. Exclude arm64 slice when building your xcframework by specifying following build setting: EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64

Distribution of xcframeworks

  • manually

Distribution of xcframeworks using the native dependency manager Swift Package Manager or the 3rd party dependency managers does come with several reported integration issues. Manual integration seems sofar to be the safest/bullet-proof integration option.

  • Swift Package Manager

    • binary targets are supported since Xcode 12.0
    • define binary target in your Swift Package manifest.
    • zipped xcframework filename should contain the version number
    • compute the binary checksum by calling swift package compute-checksum <xcframework filename.
    • use the computed binary checksum in your Swift Package manifest, when referencing the xcframework remotely.
  • CocoaPods

    • supported since v1.9.1, several important bug-fixes came with the version 1.10.0
    • As a vendor, you might consider limiting the cocoapods pods audience to avoid any unnecessary issues by specifying the minimum cocoapods version required: eg `spec.cocoapods_version = '>= 1.10.0'``
    • use vendored_frameworks to specify you xcframework(s) in your podspec. e.g. spec.vendored_frameworks = 'DynamicFramework.xcframework'
    • specify paths to your dSYMs and xcframework in spec.preserve_paths = [...], since Xcode 12 the xcframeworks can contain symbol files, so there's no need to distribute the symbol files explicitly
  • Carthage

    • Since version 0.37.0 Carthage produces xcframeworks for open-sourced depdendencies when --use-xcframeworks flag is passed. It doesn't support fetching already existing 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/

Distribute binary frameworks as Swift packages

https://developer.apple.com/wwdc20/10147 https://developer.apple.com/documentation/swift_packages/distributing_binary_frameworks_as_swift_packages

ABI Stability & Module Stability - swift.org

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

Library evolution in Swift - swift.org

https://swift.org/blog/library-evolution/

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/

Alex Grebenyuk - XCFrameworks (case-study about Distribution of xcframeworks as Swift Packages for specific case)

https://kean.blog/post/xcframeworks-caveats

xcframeworks's People

Contributors

bielikb avatar igor-makarov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xcframeworks's Issues

Readme xcodebuild syntax issue

The following content in the readme isn't valid syntax and won't succeed:

-destination destination="generic/platform=iOS Simulator" \
...
-destination destination="generic/platform=iOS" \

It should be:

-destination "generic/platform=iOS Simulator" \
...
-destination "generic/platform=iOS" \

I think there's a total of 3 mentions of the incorrect syntax.

`lipo` is not deprecated and the suggestion to not use it is misleading

View my explanation here: https://developer.apple.com/forums/thread/666335?answerId=685927022#685927022

I think the misunderstanding is that Xcode simply has a better UX now for creating frameworks so if you're using Xcode to build your code you no longer need to add manual build phases and manually invoke lipo. But the notion of fat binaries has not gone anywhere and, if you're doing this manually (anyone not using Xcode to build their libraries, and even some who are) then you still need to be putting fat binaries into the top level xcframework. It can't happen any other way (=

S

X

I am able to create the framework but have 1GB size for XCFramework.

My own pods have around 2100 Files

In XCFramework,

  • 500 MB for Simulator Platform
  • 500 MB for iOS Platform

Anyway to reduce the size of XCFramework?

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

"No Such Module" while using a XCFramework of a framework that uses CocoaPods

Hi! I have created a project that consumes a library using CocoaPods.

I have created an XCFramework of that project using bundle exec fastlane export_xcframework. It worked, but when I try to consume the framework from my SPM project it says that there's "No such module StreamAMGSDK". This error appears in the .swiftinterfacefile:

// swift-interface-format-version: 1.0
// swift-compiler-version: Apple Swift version 5.6.1 effective-4.1.50 (swiftlang-5.6.0.323.66 clang-1316.0.20.12)
// swift-module-flags: -target arm64-apple-ios14.0 -enable-objc-interop -enable-library-evolution -swift-version 4 -enforce-exclusivity=checked -O -module-name IMPlayer
import StreamAMGSDK // Error appears here <<<<<<<<
import Swift
import _Concurrency
@_hasMissingDesignatedInitializers public class IMPlayerFactory {
  final public let shared: IMPlayer.IMPlayerFactory
  public func createPlayer(frame: CoreGraphics.CGRect, youboraAccountCode: Swift.Int? = nil, partnerId: Swift.Int) -> StreamAMGSDK.AMGPlayKit
  @objc deinit
}

There's something I can do to make this XCFramework work by having this library included?

Distributing XCFramework via SPM

Hi Boris,

Swift 5.2 was released recently and if I got it right, distributing XCFramework via SPM was part of changes here. Do you know something about it? How I should format Swift.package to make it work?

Best regards,
Alex.

Help pls with understood how make xcframeowrks?

xcode 13.4 trying move from fat to xcframework.

  1. I create the package by: 'swift package init --type library`;
  2. Move our sources to Source folder;
  3. Generate archive for iOS:
xcodebuild archive -scheme TestFramework \
            -destination "generic/platform=iOS" \
            -archivePath ./Archive-iOS \
            -derivedDataPath ".build" \
            SKIP_INSTALL=NO \
            BUILD_LIBRARY_FOR_DISTRIBUTION=YES
  1. Generate archive for iOS Simulator:
xcodebuild archive -scheme TestFramework \
            -destination "generic/platform=iOS Simulator" \
            -archivePath ./Archive-iOSSimulator \
            -derivedDataPath ".build" \
            SKIP_INSTALL=NO \
            BUILD_LIBRARY_FOR_DISTRIBUTION=YES
  1. Generate the xcframework by docs:
xcodebuild -create-xcframework \
-framework './Archive-iOS.xcarchive/Products/Library/Frameworks/TestFramework.framework' \
-framework './Archive-iOSSimulator.xcarchive/Products/Library/Frameworks/TestFramework.framework' \
-output './Archive-iOS.xcarchive.xcframework'

got error: error: unable to read the file -> ./Archive-iOS.xcarchive/Products/Library/Frameworks/TestFramework.framework/TestFramework

Ofc, because on steps 3,4 I haven't this framework in archive. Where can I found correct documentation with step by step for make xcframeworks?
What I did wrong?

Add bitcode to XCFramework and not able to run the scrip for creating xcframework

HI,

Have you tried adding bitcode support to xcframework. I was not able to do so and was facing the issue. Also, I was not able to run your script for creating xcframework. I copied that script and added the same in my project under the build phases tab in the run script section. I am facing the following error "Output dir was not set. try to run ./scripts/create_xcframeworks.sh Products". Can you help me with that?

PRODUCT_NAME was misdefined

Getting PRODUCT_NAME was misdefined: Sample. Please, provide :product_name option error, even through providing the product_name option externally

create_xcframework(
workspace: "Myapp.xcworkspace",
scheme: "Myapp-Universal",
product_name: 'Myapp', # optional if scheme doesnt match the name of your framework
destinations: ['iOS'],
xcframework_output_directory: '../../bin'
)

Stack

Key Value
OS 10.15.7
Ruby 2.7.2
Bundler? false
Git git version 2.15.0
Installation Source /usr/local/Cellar/fastlane/2.174.0/libexec/bin/fastlane
Host Mac OS X 10.15.7 (19H2)
Ruby Lib Dir /usr/local/Cellar/[email protected]/2.7.2/lib
OpenSSL Version OpenSSL 1.1.1i 8 Dec 2020
Is contained false
Is homebrew true
Is installed via Fabric.app false
Xcode Path /Applications/Xcode_12.4.app/Contents/Developer/
Xcode Version 12.4

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.