Binding Objective-C Libraries, Miguel de Icaza

Post on 10-May-2015

1.944 views 3 download

Tags:

Transcript of Binding Objective-C Libraries, Miguel de Icaza

MonoTouch/iOS Native Interop

Xamarin Inc

Agenda• When to Bind• How to bind • Improving the binding

INTRODUCTION TO BINDINGS

Xamarin.iOS Native Interop

• Consume Objective-C or C code– Integrated existing code– Move performance sensitive code to C/assembly– Adopt third party libraries– Adopt third party controls/frameworks

• At the core of Xamarin.iOS itself

Integration with Native Libraries• iOS Native Libraries

– Core libraries written in C– High-level libraries written in Objective-C

• Consuming C Libraries: – Uses standard .NET Platform/Invoke support

• Consuming Objective-C Libraries:– Binding Projects– “Projects” Objective-C to C#– Provides full integration with native object hierarchy

Mono/Objective-C Bindings• Used by MonoTouch itself– Every Objective-C API is surfaced this way.

• Tool produces C# libraries that:– Map 1:1 to underlying Objective-C libraries– Allow instantiating Objective-C classes– Allow subclassing/overwriting of behavior– Can extend Objective-C with C# idioms

Configuring Objective Sharpie

Header Files + Namespace

Generating the IDL

MonoTouch Binding Project

Creating a Binding• Enumerations and Structures

– Contain core definitions used by the interface

• C# Interface Definition– Defines how to project Objective-C to C#– Name mapping, Overloads

• Curated Extensions:– Contains helper features to simplify development– Add strongly-typed features

Example: C# Interface DefinitionObjective-C@interface MagicBeans : NSObject {

// …}-(id) initWithString:(NSString*)msg;+(NSArray*) getAllBeans();-(void) grow;-(void) harvest:(NSDate *) time;@end

Example: C# Interface DefinitionObjective-C@interface MagicBeans : NSObject {

// …}-(id) initWithString:(NSString*)msg;+(NSArray*) getAllBeans();-(void) grow;-(void) harvest: (NSDate *) time;@end

C# Interface Definition[BaseType (typeof (NSObject))]Interface MagicBean { [Export (“initWithString:”)]

IntPtr Constructor (string msg);

[Static, Export (“getAllBeans”)] MagicBeans [] GetAllBeans ();

[Export (“grow”)] void Grow ();

[Export (“harvest:”)]void Harvest (NSDate time);

}

Binding “MagicBeans” libraryMagicBeans.dll

Enumerations, Structures

C# Interface Definition

Curated Extensions

libMagicBeans.a

C# Source Native Binary .NET Binary

Single deployment unit

Contains:• C# binding• Native Library• Assets (artwork,

audio)

OBJECTIVE SHARPIE

Compiler Driven Bindings• ObjectiveSharpie Tool– Uses LLVM’s Objective-C compiler to parse API– Applies standard Binding rules– Generates Baseline C# IDL for you– Then you modify

• Available Today– http://bit.ly/objective-sharpie

BASICS OF BINDINGS

Basics of Bindings• Binding Classes• Binding Protocols• Methods, Properties

– Type mappings– Arrays– Strings

• Delegate classes (and Events)• Exposing Weak and Strong Types• Binding Blocks

Class Declarations Objective-C C# Mapping Result

Class Declaration

@interface Foo : Bar [BaseType (typeof (Bar))]interfaceFoo

C# class

Class adopting protocol

@interface Foo : Bar <Pro>

[BaseType (typeof (Bar))]interfaceFoo : Pro

C# class, inlined protocol methods

Protocol @protocol Foo <Bar> [BaseType (typeof (Bar))][Model]interface Foo

C# class with methods to override

Category @interface Foo(Cute)

[BaseType (typeof (Foo))]interface Cute

C# extensions method class

Objective-C Method Selectors-(float) getNumber;

• Meaning:– “-” means it is an instance method– Float return type– Selector name is “getNumber”

• C# IDL:[Export (“getNumber”)] float GetNumber ();

Objective-C Method Selectors+(float) add:(int) first and:(int) second;

• Meaning:– “+” means it is a static method– Float return type– Takes two int arguments– Selector name is “add:and:”

• C# IDL:[Export (“add:and:”)]float Add (int first, int second)

Objective-C Property Selectors@property (readwrite) int foo;

• Meaning:– Property is read/write– int return type– Selector pair is: “foo” (get) and “setFoo:” (set)

• C# IDL:[Export (“foo”)]int Foo { get; set; }

Binding Correctness• [Export] definitions might have errors– Transcription errors– Accidental setters, or wrong getters

• Create a unit test– Subclass ApiCtorInitTest

Testing your APIs[TestFixture]public class BindingCtorTest : ApiCtorInitTest { protected override Assembly Assembly { get { return typeof (CCAccelAmplitude).Assembly; } } }}

Core Type MappingsObjective-C C#

BOOL, GLBoolean bool

NSString * C# string or NSString

char * [PlainString] string

NSInteger, NSUInteger int, uint

CGRect, CGPoint, CGSize RectangleF, PointF, SizeF

id NSObject

SEL ObjCRuntime.Selector

dispatch_queue_t CoreFoundation.DispatchQueue

CGFloat, GLFloat float

Arrays• NSArray represents arrays– Untyped, no code completion

• When binding, use strong types instead– “NSArray” becomes “UIView []”– Requires a trip to the documentation

LINKING LIBRARIES

Linking Libraries• Use assembly-level attribute LinkWith

– [assembly:LinkWith (…)

• Specify static and dynamic dependencies– Frameworks (for required dependencies)– WeakFrameworks (for optional dependencies)– Pass system linker flags

• Libraries referenced are bundled into DLL– Simplifies distribution (single DLL contains all resources)– Unpacked before the final build

Native Libraries• FAT libraries– One libFoo.a may contain x86, arm, thumb code– Not all libraries build have all targets– Make sure you build all the supported targets– See monotouch-bindings’ Makefile for samples

• IDE automatically examines fat libraries– And produces the proper LinkWith attribute

New: SmartLink• By default, all native code is linked-in

• SmartLink merges Mono and System linker– Only code that is directly referenced is included– Caveat: dynamic Objective-C code can fail– Big savings

SmartLink effect on Samples

ATMHud

BeebleS

DK

Couchbase

Datatra

ns

DropBoxS

ync -

DropBoxS

yncSa

mpleiOS

DropBoxS

ync -

DropBoxS

yncSa

mpleMTD

Flurry

Analytics

GCDiscree

tNotificati

on

GoogleAnaly

tics

GoogleMap

s

MBProgre

ssHud

MGSplitV

iewContro

ller

RedLas

er

SDSe

gmen

tedContro

l

TestF

light

TimesS

quare

WEP

opover

ZipArch

ive -

2,000

4,000

6,000

All codeSmart Linking

Size Savings

ATMHud

BeebleS

DK

Couchbase

Datatra

ns

DropBoxS

ync -

DropBoxS

yncSa

mpleiOS

DropBoxS

ync -

DropBoxS

yncSa

mpleMTD

Flurry

Analytics

GCDiscree

tNotificati

on

GoogleAnaly

tics

GoogleMap

s

MBProgre

ssHud

MGSplitV

iewContro

ller

RedLas

er

SDSe

gmen

tedContro

l

TestF

light

TimesS

quare

WEP

opover

ZipArch

ive0

100,000200,000300,000400,000500,000

Savings

ADVANCED BINDING FEATURES

Binding Public Variables• Mapped to properties in C#• Use the Field attribute.• Provide get or get/set

[Field (“FooServiceKey”, “__Internal”)]NSString ServiceKey { get; set; }

• Use “__Internal” for binding libraries• Supports NSArray, NSString, int, long, float, double, IntPtr and

System.Drawing.SizeF

Notifications• Basic API:– NSNotificationCenter takes a string + method– Invokes method when notification is posted– Contains an NSDictionary with notification data

• We want a strongly typed API:– Simplify discovery of available notifications– Simplify discovery of parameters , consumption

Observing APIUsage:

var obs = NSFileHandle.Notifications.ObserveReadCompletion ((sender, args) => { Console.WriteLine (args.AvailableData); Console.WriteLine (args.UnixError);});

Stop notifications:obs.Dispose ();

Binding Notification – Two StepsDefine Notification Payload• Optional• Interface without [Export]• Use EventArgs in type name

public interface NSFileHandleReadEventArgs { [Export ("NSFileHandleNotificationDataItem")] NSData AvailableData { get; }

[Export ("NSFileHandleError”)] int UnixErrorCode { get; }}

Annotate Fields• Annotate fields with

[Notification] attribute• Use type if you have a

payload

[Field ("NSFileHandleReadCompletionNotification")][Notification (typeof (NSFileHandleReadEventArgs))]NSString ReadCompletionNotification { get; }

Generated Notificationspublic class NSFileHandle { public class Notifications {

// Return value is a token to stop receiving notifications public static NSObject ObserveReadCompletion (EventHandler<MonoTouch.Foundation.NSFileHandleReadEventArgs> handler) }}

Usage:

NSFileHandle.Notifications.ObserveReadCompletion ((sender, args) => { Console.WriteLine (args.AvailableData); Console.WriteLine (args.UnixError);});

Async• Easy Async, turn any method of the form:

void Foo (arg1, arg2,.., argN, callback cb)• Just add [Async] to the contract and it does:

void Foo (arg1, arg2, .., argN, callback cb)Task FooAsync (arg1, arg2, .., argN)

• Callback must be a delegate type.

CURATED EXTENSIONS

• Add () methods, for initializing collections• Exposing high-level APIs• Strong typed APIs for Dictionaries• Implement ToString ()• Iterators and IEnumerable

Curated ExtensionsUsage• Improve the C# experience

• Expose some common methods (enumerators, LINQ, strong types)

• Re-surface internal methods

Examplepartial class MagicBeans {

// Enable code like this: // // foreach (var j in myBean){ // } //

public IEnumerator<Bean> GetEnumerator() { foreach (var bean in GetAllBeans())

yield return bean; }}

NSDictionary As Options • Many APIs in iOS take NSDictionary “options”– Annoying to find acceptable keys and values– Requires a trip to the documentation– Often docs are bad, see StackOverflow or Google– Invalid values are ignored or– Generate an error with no useful information

Strong typed wrappers• Discovery of properties with IDE’s Code

Completion.• Let the IDE guide you• Only valid key/values allowed• Skip a trip to the documentation• Avoid ping-pong in docs• Avoid error guesswork for your users

NSDictionary vs Strong TypesWith NSDictionaryvar options = new NSDictionary (

AVAudioSettings.AVLinearPCMIsFloatKey,new NSNumber (1),

AVAudioSettings.AVEncoderBitRateKey,new NSNumber (44000));

new AVAssetReaderAudioMixOutput (tr, options);

With Strong Typesvar options = new AudioSettings () {

AVLinearPCMFloat = true,EncoderBitRate = 44000

};

new AVAssetReaderAudioMixOutput (tr, options)

Using DictionaryContainer• DictionaryContainer provides bridge– Subclass DictionaryContainer– Provide a constructor that takes NSDictionary– Provide constructor with fresh

NSMutableDictionary• Use GetXxxValue and SetXxxValue

DictionaryContainer Samplepublic class AudioSettings : DictionaryContainer{ public AudioSettings () : base (new NSMutableDictionary ()) {} public AudioSettings (NSDictionary dictionary) : base (dictionary) {}

public AudioFormatType? Format { set { SetNumberValue (AVAudioSettings.AVFormatIDKey, (int?) value); } get { return (AudioFormatType?)GetInt32Value (AVAudioSettings.AVFormatIDKey); }}

Expose Iterators• Useful for LINQ• Lets your objects be consumed by LINQ• Implement GetEnumerator• Use yield to invoke underlying APIs