Binding Swift Libraries — Xamarin.iOS

This post was originally posted on Stack Overflow Documentation and many other people contributed to it, unfortunately it was not possible to find out who they are. If you contributed to it please let me know so I can give the proper credit.


An easy to follow guide that will lead you through the process of binding Swift .framework files for use in a Xamarin project.


1. 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

2. 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 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.

1. A swift class must inherit from NSObject to be binded.

2. 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.

3. In runtime your APP must include some swift core libraries alongside your binded framework in a folder called Frameworks;

4. 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


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
libswiftCoreFoundation.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

Unzip your .IPA

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.