Binding Swift Libraries
Introduction#
An easy to follow guide that will lead you through the process of binding Swift .framework files for use in a Xamarin project.
Remarks#
-
When building a library in Xcode it has an option to include the swift libraries. Don’t! They will be included in your final app as NAME.app/Frameworks/LIBRARY.framework/Frameworks/libswift*.dylib but they must be included as NAME.app/Frameworks/libswift*.dylib
-
You can find this information elsewhere, but it’s worth mention: Don’t include Bitcode in the library. As of right now Xamarin don’t include Bitcode for iOS and Apple requires all libraries to support the same architectures.
Binding a Swift Library in Xamarin.iOS
Binding a Swift Library in Xamarin.iOS follows the same process for Objective-C as shown in https://developer.xamarin.com/guides/ios/advanced_topics/binding_objective-c/, but with some caveats.
- A swift class must inherit from NSObject to be binded.
- Swift compiler will translate class and protocol names into something else unless you use the @objc annotation (e.g. @objc(MyClass)) in your swift classes to specify the explicit objective c name.
- In runtime your APP must include some swift core libraries alongside your binded framework in a folder called Frameworks;
- When the App is pushed to AppStore it must include a SwiftSupport folder alongside your Payload folder. Those are inside the IPA file.
Here you can find a simple sample binding: https://github.com/Flash3001/Xamarin.BindingSwiftLibrarySample
And a full binding sample: https://github.com/Flash3001/iOSCharts.Xamarin
Please find steps below:
1.1 Prepare the Swift classes you want to export
For any Swift class you want to use you either have to inherit from NSObject and make the Objective-C name explicit using the objc annotation. Otherwise the Swift compiler will generate different names. Below is an example code of how a Swift class could look like. Note that it doesn’t matter which class it inherits as long as the root class inherits from NSObject.
//Add this to specify explicit objective c name
@objc(MyClass)
open class MyClass: NSObject {
open func getValue() -> String
{
return "Value came from MyClass.swift!";
}
}
1.2 Build the framework
Disable Bitcode. *
Build for release for Device and Simulator. *
- Not related only to Swift binding.
2. Create a fat library
A framework contains several files, the one that needs to eat a little is NAME.framework/NAME (without extension).
- Copy Release-iphoneos/NAME.framework to NAME.framework
- Create the FAT library using:
- **lipo -create Release-iphonesimulator/NAME.framework/NAME
Release-iphoneos/NAME.framework/NAME -output NAME.framework/NAME**
- Copy the files in Release-iphonesimulator/NAME.framework/Modules/NAME.swiftmodule to NAME.framework/Modules/NAME.swiftmodule (until now it only contained files from the iphoneos)
3. Import the library
I’ll assume you already created the Binding project in File -> New -> iOS -> Binding Library.
Xamarin support importing .frameworks. Just right click ‘Native References’ and click in ‘Add native reference’. Find the newly created fat framework and add it.
4. Create the ApiDefinition based on LIBRARY-Swift.h file inside headers.
You can do it manually, but it won’t be nice. You can use Objetive Sharpie. The tool Xamarin uses to bind its own libraries.
How to use it on https://developer.xamarin.com/guides/cross-platform/macios/binding/objective-sharpie/
The basic command will be something like: sharpie bind -sdk iphoneos9.3 NAME-Swift.h
If you get a System.Reflection.TargetInvocationException
it is probably because you have a different SDK version installed. Run the following command to check with iPhone OS SDK you have installed:
sharpie xcode -sdks
The file NAME-Swift.h is located in NAME.framework/Headers/NAME-Swift.h
Note: The swift classes must inherit from “NSObject”, otherwise NAME-Swift.h won’t import your classes and Objetive Sharpie will convert nothing.
Replace the contents of your binding project ApiDefinition.cs with the newly created one.
5. Change all [Protocol] and [BaseType] to include the class’ name in Objective-C runtime.
In case the original Swift class or protocol doesn’t include the @objc(MyClass) annotation as specified in step 1.1 they will have its internal Objective-C names changed, so you need to map it to the right one.
All names are available in the file NAME-Swift.h in the following format:
SWIFT_CLASS("_TtC11SwiftSample7MyClass")
@interface MyClass : NSObject
And
SWIFT_PROTOCOL("_TtP6Charts17ChartDataProvider_")
@protocol ChartDataProvider
To set the name you use BaseTypeAttribute.Name https://developer.xamarin.com/guides/cross-platform/macios/binding/binding-types-reference/#BaseType.Name property for classes and ProcotolAttribute.Name https://developer.xamarin.com/api/property/MonoTouch.Foundation.ProtocolAttribute.Name/ for protocols.
[BaseType(typeof(NSObject), Name = "_TtC11SwiftSample7MyClass")]
interface MyClass
Doing it manually is not cool. You can use this tool https://github.com/Flash3001/SwiftClassify to insert all the names. (It’s work in progress. But it’s quite simple, just by looking at the code you will get how it works).
6.1 Include all Swift dependencies to run.
If you try to consume the library in an App and try to run it right now it will crash. The error is due to the lack of libswiftCore.dylib
Something like this:
Dyld Error Message:
Library not loaded: @rpath/libswiftCore.dylib
Referenced from: /Users/USER/Library/Developer/CoreSimulator/Devices/AC440891-C819-4050-8CAB-CE15AB4B3830/data/Containers/Bundle/Application/27D2EC87-5042-4FA7-9B80-A24A8971FB48/SampleUsing.app/Frameworks/SwiftSample.framework/SwiftSample
Reason: image not found
Xamarin.iOS doesn’t give official support for binding a Swift library. So you must manually include the swift core libraries in the Frameworks and SwiftSupport folders. The files for the Frameworks folder are different for Simulator and Device. They can be found in /Applications/Xcode.app/Contents/Developer//XcodeDefault.xctoolchain/usr/lib/swift.Toolchains
Instead of manually copying the files inside the Framework folder you can use this library https://github.com/Flash3001/Xamarin.Swift3.Support . It includes every single dependency Swift 3.1 needs, each one in a single NuGet package.
As you can see, the Nuget Package is included in the consumer App, not in the binding itself. If you try to include it in the binding you will get compile errors.
If you are building a Nuget Package you can instruct Nuget to include it as a dependency.
6.2. Finding out which Swift dependencies to include.
An important thing to do is to figure out each package you need to include in your project. A simple binding will usually need:
libswiftCore.dylib
libswiftCoreGraphics.dylib
libswiftCoreImage.dylib
libswiftDarwin.dylib
libswiftDispatch.dylib
libswiftFoundation.dylib
libswiftObjectiveC.dylib
libswiftQuartzCore.dylib
libswiftUIKit.dylib
To list each dependency you can run the following command inside your LibraryName.framework
otool -l -arch armv7 LibraryName | grep libswift
Do not include every package available in NuGet for Swift3 as they might grow your app size.
7. Include SwiftSupport to push App to AppStore.
Apple requires your App to be sent with a SwiftSupport folder alongside your Payload folder. Both are inside your IPA package.
You can use this script https://github.com/bq/ipa-packager to do this work for you.
This process is the only one the library consumer will have to do manually. Every time he/she tries to push the App to AppStore.
Click ‘Sign and Distribute’ and Save to Disk
Create the new IPA using the script before mentioned
If you Unzip the file know, it will contain the SwiftSupport folder.
Remarks
When building a library in Xcode it has an option to include the swift libraries. Don’t! They will be included in your final app as NAME.app/Frameworks/LIBRARY.framework/Frameworks/libswift*.dylib but they must be included as NAME.app/Frameworks/libswift*.dylib
You can find this information elsewhere, but it’s worth mention: Don’t include Bitcode in the library. As of right now Xamarin don’t include Bitcode for iOS and Apple requires all libraries to support the same architectures.
Disclaimer
This guide is originally created by Lucas Teixeira. All credits belong to him. Thank you, Lucas.