This document discusses how to create bindings between Objective-C and C# code using Xamarin.iOS. It covers when to create bindings, how the binding process works, and ways to improve bindings. Key points include:
- Bindings allow existing Objective-C/C code and libraries to be consumed from C# in Xamarin.iOS apps. They map Objective-C APIs and objects to equivalent C# interfaces and classes.
- The binding process involves generating C# interfaces that map to Objective-C headers, then building a binding library that surfaces the native APIs in C#.
- Features like notifications, properties, enums and structures are discussed. Advanced topics cover improving bindings through extensions, strong
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. 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. 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
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
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
– http://bit.ly/objective-sharpie
19. Class Declarations
Objective-C C# Mapping Result
Class
Declarati
on
@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
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. 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. 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. Binding Correctness
• [Export] definitions might have errors
– Transcription errors
– Accidental setters, or wrong getters
• Create a unit test
– Subclass ApiCtorInitTest
24. Testing your APIs
[TestFixture]
public class BindingCtorTest : ApiCtorInitTest {
protected override Assembly Assembly {
get {
return typeof (CCAccelAmplitude).Assembly; }
}
}
}
25. Core Type Mappings
Objective-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
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. 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. 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. 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
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 and
System.Drawing.SizeF
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
37. Binding Notification – Two Steps
Define 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; }
38. Generated Notifications
public 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);
});
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. • Add () methods, for initializing collections
• Exposing high-level APIs
• Strong typed APIs for Dictionaries
• Implement ToString ()
• Iterators and IEnumerable
42. Curated Extensions
Usage
• Improve the C# experience
• Expose some common
methods
(enumerators, LINQ, strong
types)
• Re-surface internal methods
Example
partial class MagicBeans {
// Enable code like this:
//
// foreach (var j in myBean){
// }
//
public IEnumerator<Bean> GetEnumerator()
{
foreach (var bean in GetAllBeans())
yield return bean;
}
}
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. 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
45. NSDictionary vs Strong Types
With NSDictionary
var options = new NSDictionary (
AVAudioSettings.AVLinearPCMIsFloatKey,
new NSNumber (1),
AVAudioSettings.AVEncoderBitRateKey,
new NSNumber (44000));
new AVAssetReaderAudioMixOutput
(tr, options);
With Strong Types
var options = new AudioSettings () {
AVLinearPCMFloat = true,
EncoderBitRate = 44000
};
new AVAssetReaderAudioMixOutput (tr, options)
46. Using DictionaryContainer
• DictionaryContainer provides bridge
– Subclass DictionaryContainer
– Provide a constructor that takes NSDictionary
– Provide constructor with fresh
NSMutableDictionary
• Use GetXxxValue and SetXxxValue
47. DictionaryContainer Sample
public 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. Expose Iterators
• Useful for LINQ
• Lets your objects be consumed by LINQ
• Implement GetEnumerator
• Use yield to invoke underlying APIs