Binding Objective-C Libraries, Miguel de Icaza


Published on

Published in: Technology, Education
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Binding Objective-C Libraries, Miguel de Icaza

  1. 1. MonoTouch/iOS Native InteropXamarin Inc
  2. 2. Agenda• When to Bind• How to bind• Improving the binding
  4. 4. 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
  5. 5. 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
  6. 6. 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
  7. 7. Configuring Objective Sharpie
  8. 8. Header Files + Namespace
  9. 9. Generating the IDL
  10. 10. MonoTouch Binding Project
  11. 11. 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
  12. 12. Example: C# Interface DefinitionObjective-C@interface MagicBeans : NSObject {// …}-(id) initWithString:(NSString*)msg;+(NSArray*) getAllBeans();-(void) grow;-(void) harvest:(NSDate *) time;@end
  13. 13. Example: C# Interface DefinitionObjective-C@interface MagicBeans : NSObject {// …}-(id) initWithString:(NSString*)msg;+(NSArray*) getAllBeans();-(void) grow;-(void) harvest: (NSDate *) time;@endC# 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);}
  14. 14. Binding “MagicBeans” libraryMagicBeans.dllEnumerations,StructuresC# InterfaceDefinitionCuratedExtensionslibMagicBeans.aC# Source Native Binary .NET BinarySingle deployment unitContains:• C# binding• Native Library• Assets (artwork,audio)
  16. 16. 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–
  18. 18. Basics of Bindings• Binding Classes• Binding Protocols• Methods, Properties– Type mappings– Arrays– Strings• Delegate classes (and Events)• Exposing Weak and Strong Types• Binding Blocks
  19. 19. Class DeclarationsObjective-C C# Mapping ResultClassDeclaration@interface Foo : Bar [BaseType (typeof (Bar))]interfaceFooC# classClassadoptingprotocol@interface Foo : Bar<Pro>[BaseType (typeof (Bar))]interfaceFoo : ProC# class, inlinedprotocol methodsProtocol @protocol Foo <Bar> [BaseType (typeof (Bar))][Model]interface FooC# class with methodsto overrideCategory @interfaceFoo(Cute)[BaseType (typeof (Foo))]interface CuteC# extensions methodclass
  20. 20. 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 ();
  21. 21. 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)
  22. 22. 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; }
  23. 23. Binding Correctness• [Export] definitions might have errors– Transcription errors– Accidental setters, or wrong getters• Create a unit test– Subclass ApiCtorInitTest
  24. 24. Testing your APIs[TestFixture]public class BindingCtorTest : ApiCtorInitTest {protected override Assembly Assembly {get {return typeof (CCAccelAmplitude).Assembly; }}}}
  25. 25. Core Type MappingsObjective-C C#BOOL, GLBoolean boolNSString * C# string or NSStringchar * [PlainString] stringNSInteger, NSUInteger int, uintCGRect, CGPoint, CGSize RectangleF, PointF, SizeFid NSObjectSEL ObjCRuntime.Selectordispatch_queue_t CoreFoundation.DispatchQueueCGFloat, GLFloat float
  26. 26. Arrays• NSArray represents arrays– Untyped, no code completion• When binding, use strong types instead– “NSArray” becomes “UIView *+”– Requires a trip to the documentation
  28. 28. 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
  29. 29. 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
  30. 30. 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
  31. 31. SmartLink effect on Samples-2,0004,0006,000All codeSmart Linking
  32. 32. Size Savings0100,000200,000300,000400,000500,000Savings
  34. 34. 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 andSystem.Drawing.SizeF
  35. 35. 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
  36. 36. Observing APIUsage:var obs = NSFileHandle.Notifications.ObserveReadCompletion ((sender, args) => {Console.WriteLine (args.AvailableData);Console.WriteLine (args.UnixError);});Stop notifications:obs.Dispose ();
  37. 37. Binding Notification – Two StepsDefine Notification Payload• Optional• Interface without [Export]• Use EventArgs in type namepublic interface NSFileHandleReadEventArgs {[Export ("NSFileHandleNotificationDataItem")]NSData AvailableData { get; }[Export ("NSFileHandleError”)]int UnixErrorCode { get; }}Annotate Fields• Annotate fields with[Notification] attribute• Use type if you have apayload[Field ("NSFileHandleReadCompletionNotification")][Notification (typeof (NSFileHandleReadEventArgs))]NSString ReadCompletionNotification { get; }
  38. 38. Generated Notificationspublic class NSFileHandle {public class Notifications {// Return value is a token to stop receiving notificationspublic staticNSObject ObserveReadCompletion(EventHandler<MonoTouch.Foundation.NSFileHandleReadEventArgs> handler)}}Usage:NSFileHandle.Notifications.ObserveReadCompletion ((sender, args) => {Console.WriteLine (args.AvailableData);Console.WriteLine (args.UnixError);});
  39. 39. 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.
  41. 41. • Add () methods, for initializing collections• Exposing high-level APIs• Strong typed APIs for Dictionaries• Implement ToString ()• Iterators and IEnumerable
  42. 42. Curated ExtensionsUsage• Improve the C# experience• Expose some commonmethods(enumerators, LINQ, strongtypes)• Re-surface internal methodsExamplepartial class MagicBeans {// Enable code like this://// foreach (var j in myBean){// }//public IEnumerator<Bean> GetEnumerator(){foreach (var bean in GetAllBeans())yield return bean;}}
  43. 43. 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
  44. 44. Strong typed wrappers• Discovery of properties with IDE’s CodeCompletion.• 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
  45. 45. 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)
  46. 46. Using DictionaryContainer• DictionaryContainer provides bridge– Subclass DictionaryContainer– Provide a constructor that takes NSDictionary– Provide constructor with freshNSMutableDictionary• Use GetXxxValue and SetXxxValue
  47. 47. 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);}}
  48. 48. Expose Iterators• Useful for LINQ• Lets your objects be consumed by LINQ• Implement GetEnumerator• Use yield to invoke underlying APIs