Binding types reference guide
This document describes the list of attributes that you can use to annotate your API contract files to drive the binding and the code generated
Xamarin.iOS and Xamarin.Mac API contracts are written in C# mostly as interface definitions that define the way that Objective-C code is surfaced to C#. The process involves a mix of interface declarations plus some basic type definitions that the API contract might require. For an introduction to binding types, see our companion guide Binding Objective-C Libraries.
Type definitions
Syntax:
[BaseType (typeof (BTYPE))
interface MyType : [Protocol1, Protocol2] {
IntPtr Constructor (string foo);
}
Every interface in your contract definition that has the
[BaseType]
attribute declares the base type for
the generated object. In the above declaration a MyType
class C# type will
be generated that binds to an Objective-C type called MyType
.
If you specify any types after the typename (in the sample above Protocol1
and Protocol2
) using the interface inheritance syntax the contents of those
interfaces will be inlined as if they had been part of the contract for MyType
.
The way that Xamarin.iOS surfaces that a type adopts a protocol is by inlining all
of the methods and properties that were declared in the protocol into the type
itself.
The following shows how the Objective-C declaration for UITextField
would be
defined in a Xamarin.iOS contract:
@interface UITextField : UIControl <UITextInput> {
}
Would be written like this as a C# API contract:
[BaseType (typeof (UIControl))]
interface UITextField : UITextInput {
}
You can control many other aspects of the code generation by applying other
attributes to the interface as well as configuring the [BaseType]
attribute.
Generating events
One feature of the Xamarin.iOS and Xamarin.Mac API design is that we map
Objective-C delegate classes as C# events and callbacks. Users can choose in a
per-instance basis whether they want to adopt the Objective-C programming
pattern, by assigning to properties like Delegate
an instance of a class that
implements the various methods that the Objective-C runtime would call, or by
choosing the C#-style events and properties.
Let us see one example of how to use the Objective-C model:
bool MakeDecision ()
{
return true;
}
void Setup ()
{
var scrollView = new UIScrollView (myRect);
scrollView.Delegate = new MyScrollViewDelegate ();
...
}
class MyScrollViewDelegate : UIScrollViewDelegate {
public override void Scrolled (UIScrollView scrollView)
{
Console.WriteLine ("Scrolled");
}
public override bool ShouldScrollToTop (UIScrollView scrollView)
{
return MakeDecision ();
}
}
In the above example, you can see that we have chosen to overwrite two
methods, one a notification that a scrolling event has taken place, and the
second that is a callback that should return a boolean value instructing the
scrollView
whether it should scroll to the top or not.
The C# model allows the user of your library to listen to notifications using the C# event syntax or the property syntax to hook up callbacks that are expected to return values.
This is how the C# code for the same feature looks like using lambdas:
void Setup ()
{
var scrollview = new UIScrollView (myRect);
// Event connection, use += and multiple events can be connected
scrollView.Scrolled += (sender, eventArgs) { Console.WriteLine ("Scrolled"); }
// Property connection, use = only a single callback can be used
scrollView.ShouldScrollToTop = (sv) => MakeDecision ();
}
Since events do not return values (they have a void return type) you can
connect multiple copies. The ShouldScrollToTop
is not an event, it is instead a
property with the type UIScrollViewCondition
which has this
signature:
public delegate bool UIScrollViewCondition (UIScrollView scrollView);
It returns a bool
value, in this case the lambda syntax allows us to just
return the value from the MakeDecision
function.
The binding generator supports generating events and properties that link a
class like UIScrollView
with its UIScrollViewDelegate
(well call these the Model
class), this is done by annotating your [BaseType]
definition with the Events
and Delegates
parameters (described below).
In addition to annotating the [BaseType]
with those
parameters it is necessary to inform the generator of a few more components.
For events that take more than one parameter (in Objective-C the convention
is that the first parameter in a delegate class is the instance of the sender
object) you must provide the name that you would like for the generated
EventArgs
class to be. This is done with the [EventArgs]
attribute on the method declaration in your Model class. For example:
[BaseType (typeof (UINavigationControllerDelegate))]
[Model][Protocol]
public interface UIImagePickerControllerDelegate {
[Export ("imagePickerController:didFinishPickingImage:editingInfo:"), EventArgs ("UIImagePickerImagePicked")]
void FinishedPickingImage (UIImagePickerController picker, UIImage image, NSDictionary editingInfo);
}
The above declaration will generate a UIImagePickerImagePickedEventArgs
class that derives from EventArgs
and packs both parameters, the UIImage
and
the NSDictionary
. The generator produces this:
public partial class UIImagePickerImagePickedEventArgs : EventArgs {
public UIImagePickerImagePickedEventArgs (UIImage image, NSDictionary editingInfo);
public UIImage Image { get; set; }
public NSDictionary EditingInfo { get; set; }
}
It then exposes the following in the UIImagePickerController
class:
public event EventHandler<UIImagePickerImagePickedEventArgs> FinishedPickingImage { add; remove; }
Model methods that return a value are bound differently. Those require both a
name for the generated C# delegate (the signature for the method) and also a
default value to return in case the user does not provide an implementation.
For example, the ShouldScrollToTop
definition is this:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UIScrollViewDelegate {
[Export ("scrollViewShouldScrollToTop:"), DelegateName ("UIScrollViewCondition"), DefaultValue ("true")]
bool ShouldScrollToTop (UIScrollView scrollView);
}
The above will create a UIScrollViewCondition
delegate with the signature
that was shown above, and if the user does not provide an implementation, the
return value will be true.
In addition to the [DefaultValue]
attribute,
you can also use the [DefaultValueFromArgument]
attribute that directs the generator to return the value of the specified
parameter in the call or the [NoDefaultValue]
parameter that instructs the generator that there is no default value.
BaseTypeAttribute
Syntax:
public class BaseTypeAttribute : Attribute {
public BaseTypeAttribute (Type t);
// Properties
public Type BaseType { get; set; }
public string Name { get; set; }
public Type [] Events { get; set; }
public string [] Delegates { get; set; }
public string KeepRefUntil { get; set; }
}
BaseType.Name
You use the Name
property to control the name that this type will bind to in
the Objective-C world. This is typically used to give the C# type a name that is
compliant with the .NET Framework Design Guidelines, but which maps to a name in
Objective-C that does not follow that convention.
Example, in the following case we map the Objective-C NSURLConnection
type to
NSUrlConnection
, as the .NET Framework Design Guidelines use "Url" instead of
"URL":
[BaseType (typeof (NSObject), Name="NSURLConnection")]
interface NSUrlConnection {
}
The specified name is used as the value for the generated
[Register]
attribute in the binding. If Name
is not specified, the type's short
name is used as the value for the [Register]
attribute in the generated
output.
BaseType.Events and BaseType.Delegates
These properties are used to drive the generation of C#-style events in the
generated classes. They are used to link a given class with its Objective-C
delegate class. You will encounter many cases where a class uses a delegate
class to send notifications and events. For example a BarcodeScanner
would have
a companion BardodeScannerDelegate
class. The BarcodeScanner
class would
typically have a Delegate
property that you would assign an instance of
BarcodeScannerDelegate
to, while this works, you might want to expose to your
users a C#-like style event interface, and in those cases you would use the
Events
and Delegates
properties of the [BaseType]
attribute.
These properties are always set together and must have the same number of
elements and be kept in sync. The Delegates
array contains one string for each
weakly-typed delegate that you want to wrap, and the Events
array contains one
type for each type that you want to associate with it.
[BaseType (typeof (NSObject),
Delegates=new string [] { "WeakDelegate" },
Events=new Type [] {typeof(UIAccelerometerDelegate)})]
public interface UIAccelerometer {
}
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UIAccelerometerDelegate {
}
BaseType.KeepRefUntil
If you apply this attribute when new instances of this class are created, the
instance of that object will be kept around until the method referenced by the
KeepRefUntil
has been invoked. This is useful to improve the usability of your
APIs, when you do not want your user to keep a reference to an object around to
use your code. The value of this property is the name of a method in the
Delegate
class, so you must use this in combination with the Events
and
Delegates
properties as well.
The following example show how this is used by UIActionSheet
in
Xamarin.iOS:
[BaseType (typeof (NSObject), KeepRefUntil="Dismissed")]
[BaseType (typeof (UIView),
KeepRefUntil="Dismissed",
Delegates=new string [] { "WeakDelegate" },
Events=new Type [] {typeof(UIActionSheetDelegate)})]
public interface UIActionSheet {
}
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UIActionSheetDelegate {
[Export ("actionSheet:didDismissWithButtonIndex:"), EventArgs ("UIButton")]
void Dismissed (UIActionSheet actionSheet, nint buttonIndex);
}
DesignatedDefaultCtorAttribute
When this attribute is applied to the interface definition it will generate
a [DesignatedInitializer]
attribute on the default (generated) constructor,
which maps to the init
selector.
DisableDefaultCtorAttribute
When this attribute is applied to the interface definition it will prevent the generator from producing the default constructor.
Use this attribute when you need the object to be initialized with one of the other constructors in the class.
PrivateDefaultCtorAttribute
When this attribute is applied to the interface definition it will flag the default constructor as private. This means that you can still instantiate object of this class internally from your extension file, but it just wont be accessible to users of your class.
CategoryAttribute
Use this attribute on a type definition to bind Objective-C categories and to expose those as C# extension methods to mirror the way Objective-C exposes the functionality.
Categories are an Objective-C mechanism used to extend the set of
methods and properties available in a class. In practice, they are
used to either extend the functionality of a base class (for example
NSObject
) when a specific framework is linked in (for example UIKit
),
making their methods available, but only if the new framework is
linked in. In some other cases, they are used to organize features
in a class by functionality. They are similar in spirit to C#
extension methods.
This is what a category would look like in Objective-C:
@interface UIView (MyUIViewExtension)
-(void) makeBackgroundRed;
@end
The above example is found on a library that would extend instances of
UIView
with the method makeBackgroundRed
.
To bind those, you can use the [Category]
attribute on
an interface definition. When using the [Category]
attribute, the
meaning of the [BaseType]
attribute changes from being
used to specify the base class to extend, to being the type to extend.
The following shows how the UIView
extensions are bound and turned
into C# extension methods:
[BaseType (typeof (UIView))]
[Category]
interface MyUIViewExtension {
[Export ("makeBackgroundRed")]
void MakeBackgroundRed ();
}
The above will create a MyUIViewExtension
a class
that contains the MakeBackgroundRed
extension method. This means
that you can now call MakeBackgroundRed
on any UIView
subclass,
giving you the same functionality you would get on Objective-C.
In some cases you will find static members inside categories like in the following example:
@interface FooObject (MyFooObjectExtension)
+ (BOOL)boolMethod:(NSRange *)range;
@end
This will lead to an incorrect Category C# interface definition:
[Category]
[BaseType (typeof (FooObject))]
interface FooObject_Extensions {
// Incorrect Interface definition
[Static]
[Export ("boolMethod:")]
bool BoolMethod (NSRange range);
}
This is incorrect because to use the BoolMethod
extension you need an instance of FooObject
but you are binding an ObjC static extension, this is a side effect due to the fact of how C# extension methods are implemented.
The only way to use the above definitions is by the following ugly code:
(null as FooObject).BoolMethod (range);
The recommendation to avoid this is to inline the BoolMethod
definition inside the FooObject
interface definition itself, this will allow you to call this extension like it is intended FooObject.BoolMethod (range)
.
[BaseType (typeof (NSObject))]
interface FooObject {
[Static]
[Export ("boolMethod:")]
bool BoolMethod (NSRange range);
}
We will issue a warning (BI1117) whenever we find a [Static]
member inside a [Category]
definition. If you really want to have [Static]
members inside your [Category]
definitions you can silence the warning by using [Category (allowStaticMembers: true)]
or by decorating either your member or [Category]
interface definition with [Internal]
.
StaticAttribute
When this attribute is applied to a class it will just generate a static
class, one that does not derive from NSObject
, so the
[BaseType]
attribute is ignored. Static classes are
used to host C public variables that you want to expose.
For example:
[Static]
interface CBAdvertisement {
[Field ("CBAdvertisementDataServiceUUIDsKey")]
NSString DataServiceUUIDsKey { get; }
Will generate a C# class with the following API:
public partial class CBAdvertisement {
public static NSString DataServiceUUIDsKey { get; }
}
Protocol/Model definitions
Models are typically used by protocol implementation. They differ in that the runtime will only register with Objective-C the methods that actually have been overwritten. Otherwise, the method will not be registered.
This in general means that when you subclass a class that
has been flagged with the ModelAttribute
, you should not call
the base method. Calling that method will throw the following
exception: Foundation.You_Should_Not_Call_base_In_This_Method. You are supposed to implement the entire behavior
on your subclass for any methods you override.
AbstractAttribute
By default, members that are part of a protocol are not mandatory. This
allows users to create a subclass of the Model
object by merely deriving from
the class in C# and overriding only the methods they care about. Sometimes the
Objective-C contract requires that the user provides an implementation for this
method (those are flagged with the @required
directive in Objective-C). In those
cases, you should flag those methods with the [Abstract]
attribute.
The [Abstract]
attribute can be applied to either methods or properties and
causes the generator to flag the generated member as abstract and the class to
be an abstract class.
The following is taken from Xamarin.iOS:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UITableViewDataSource {
[Export ("tableView:numberOfRowsInSection:")]
[Abstract]
nint RowsInSection (UITableView tableView, nint section);
}
DefaultValueAttribute
Specifies the default value to be returned by a model method if the user does not provide a method for this particular method in the Model object
Syntax:
public class DefaultValueAttribute : Attribute {
public DefaultValueAttribute (object o);
public object Default { get; set; }
}
For example, in the following imaginary delegate class for a Camera
class, we
provide a ShouldUploadToServer
which would be exposed as a property on the
Camera
class. If the user of the Camera
class does not explicitly set a the
value to a lambda that can respond true or false, the default value return in
this case would be false, the value that we specified in the DefaultValue
attribute:
[BaseType (typeof (NSObject))]
[Model][Protocol]
interface CameraDelegate {
[Export ("camera:shouldPromptForAction:"), DefaultValue (false)]
bool ShouldUploadToServer (Camera camera, CameraAction action);
}
If the user sets a handler in the imaginary class, then this value would be ignored:
var camera = new Camera ();
camera.ShouldUploadToServer = (camera, action) => return SomeDecision ();
See also: [NoDefaultValue]
, [DefaultValueFromArgument]
.
DefaultValueFromArgumentAttribute
Syntax:
public class DefaultValueFromArgumentAttribute : Attribute {
public DefaultValueFromArgumentAttribute (string argument);
public string Argument { get; }
}
This attribute when provided on a method that returns a value on a model class will instruct the generator to return the value of the specified parameter if the user did not provide their own method or lambda.
Example:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface NSAnimationDelegate {
[Export ("animation:valueForProgress:"), DelegateName ("NSAnimationProgress"), DefaultValueFromArgumentAttribute ("progress")]
float ComputeAnimationCurve (NSAnimation animation, nfloat progress);
}
In the above case if the user of the NSAnimation
class chose to use any of
the C# events/properties, and did not set NSAnimation.ComputeAnimationCurve
to a
method or lambda, the return value would be the value passed in the progress
parameter.
See also: [NoDefaultValue]
, [DefaultValue]
IgnoredInDelegateAttribute
Sometimes it makes sense not to expose an event or delegate property from a Model class into the host class so adding this attribute will instruct the generator to avoid the generation of any method decorated with it.
[BaseType (typeof (UINavigationControllerDelegate))]
[Model][Protocol]
public interface UIImagePickerControllerDelegate {
[Export ("imagePickerController:didFinishPickingImage:editingInfo:"), EventArgs ("UIImagePickerImagePicked")]
void FinishedPickingImage (UIImagePickerController picker, UIImage image, NSDictionary editingInfo);
[Export ("imagePickerController:didFinishPickingImage:"), IgnoredInDelegate)] // No event generated for this method
void FinishedPickingImage (UIImagePickerController picker, UIImage image);
}
DelegateNameAttribute
This attribute is used in Model methods that return values to set the name of the delegate signature to use.
Example:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface NSAnimationDelegate {
[Export ("animation:valueForProgress:"), DelegateName ("NSAnimationProgress"), DefaultValueFromArgumentAttribute ("progress")]
float ComputeAnimationCurve (NSAnimation animation, float progress);
}
With the above definition, the generator will produce the following public declaration:
public delegate float NSAnimationProgress (MonoMac.AppKit.NSAnimation animation, float progress);
DelegateApiNameAttribute
This attribute is used to allow the generator to change the name of the property generated in the host class. Sometimes it is useful when the name of the FooDelegate class method makes sense for the Delegate class, but would look odd in the host class as a property.
Also this is really useful (and needed) when you have two or more overload methods that makes sense to keep them named as is in the FooDelegate class but you want to expose them in the host class with a better given name.
Example:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface NSAnimationDelegate {
[Export ("animation:valueForProgress:"), DelegateApiName ("ComputeAnimationCurve"), DelegateName ("Func<NSAnimation, float, float>"), DefaultValueFromArgument ("progress")]
float GetValueForProgress (NSAnimation animation, float progress);
}
With the above definition, the generator will produce the following public declaration in the host class:
public Func<NSAnimation, float, float> ComputeAnimationCurve { get; set; }
EventArgsAttribute
For events that take more than one parameter (in Objective-C the convention
is that the first parameter in a delegate class is the instance of the sender
object) you must provide the name that you would like for the generated
EventArgs class to be. This is done with the [EventArgs]
attribute on the method
declaration in your Model
class.
For example:
[BaseType (typeof (UINavigationControllerDelegate))]
[Model][Protocol]
public interface UIImagePickerControllerDelegate {
[Export ("imagePickerController:didFinishPickingImage:editingInfo:"), EventArgs ("UIImagePickerImagePicked")]
void FinishedPickingImage (UIImagePickerController picker, UIImage image, NSDictionary editingInfo);
}
The above declaration will generate a UIImagePickerImagePickedEventArgs
class that derives from EventArgs
and packs both parameters, the UIImage
and the NSDictionary
. The generator produces this:
public partial class UIImagePickerImagePickedEventArgs : EventArgs {
public UIImagePickerImagePickedEventArgs (UIImage image, NSDictionary editingInfo);
public UIImage Image { get; set; }
public NSDictionary EditingInfo { get; set; }
}
It then exposes the following in the UIImagePickerController
class:
public event EventHandler<UIImagePickerImagePickedEventArgs> FinishedPickingImage { add; remove; }
EventNameAttribute
This attribute is used to allow the generator to change the name of an event or property generated in the class. Sometimes it is useful when the name of the Model class method makes sense for the model class, but would look odd in the originating class as an event or property.
For example, the UIWebView
uses the following bit from the
UIWebViewDelegate
:
[Export ("webViewDidFinishLoad:"), EventArgs ("UIWebView"), EventName ("LoadFinished")]
void LoadingFinished (UIWebView webView);
The above exposes LoadingFinished
as the method in the UIWebViewDelegate
, but
LoadFinished
as the event to hook up to in a UIWebView
:
var webView = new UIWebView (...);
webView.LoadFinished += delegate { Console.WriteLine ("done!"); }
ModelAttribute
When you apply the [Model]
attribute to a type definition in your contract API,
the runtime will generate special code that will only surface invocations to
methods in the class if the user has overwritten a method in the class. This
attribute is typically applied to all APIs that wrap an Objective-C delegate
class.
The runtime will also generate an Objective-C class that matches the name of the corresponding protocol.
It's possible to customize the name of the Objective-C class in two ways:
Setting
AutoGeneratedName = true
:[Model (AutoGeneratedName = true)]
This will make the runtime generate an unique name for the Objective-C type. The name is currently based on the assembly name and the full name of the model's type (this may change in the future).
Explicitly specifying the name:
[Model (Name = "CustomName")]
It's recommended to use AutoGeneratedName = true
. In .NET, the name is
always generated (unless it's explicitly specified as in 2. above), and the
AutoGeneratedName
property does not exist anymore.
NoDefaultValueAttribute
Specifies that the method on the model does not provide a default return value.
This works with the Objective-C runtime by responding
false
to the Objective-C runtime request to determine if the
specified selector is implemented in this class.
[BaseType (typeof (NSObject))]
[Model][Protocol]
interface CameraDelegate {
[Export ("shouldDisplayPopup"), NoDefaultValue]
bool ShouldUploadToServer ();
}
See also: [DefaultValue]
, [DefaultValueFromArgument]
Protocols
The Objective-C protocol concept does not really exist in C#. Protocols are similar to C# interfaces but they differ in that not all of the methods and properties declared in a protocol must be implemented by the class that adopts it. Instead some of the methods and properties are optional.
Some protocols are generally used as Model classes, those should be bound
using the [Model]
attribute.
[BaseType (typeof (NSObject))]
[Model, Protocol]
interface MyProtocol {
// Use [Abstract] when the method is defined in the @required section
// of the protocol definition in Objective-C
[Abstract]
[Export ("say:")]
void Say (string msg);
[Export ("listen")]
void Listen ();
}
Starting with Xamarin.iOS 7.0 a new and improved protocol
binding functionality has been incorporated. Any definition
that contains the [Protocol]
attribute will actually generate
three supporting classes that vastly improve the way that you
consume protocols:
// Full method implementation, contains all methods
class MyProtocol : IMyProtocol {
public void Say (string msg);
public void Listen (string msg);
}
// Interface that contains only the required methods
interface IMyProtocol: INativeObject, IDisposable {
[Export ("say:")]
void Say (string msg);
}
// Extension methods
static class IMyProtocol_Extensions {
public static void Optional (this IMyProtocol this, string msg);
}
}
The class implementation provides a complete abstract class that you can override individual methods of and get full type safety. But due to C# not supporting multiple inheritance, there are scenarios where you might require a different base class, but still want to implement an interface.
This is where the generated interface definition comes in. It is an interface that has all the required methods from the protocol. This allows developers that want to implement your protocol to merely implement the interface. The runtime will automatically register the type as adopting the protocol.
Notice that the interface only lists the required methods and does expose the optional methods. This means that classes that adopt the protocol will get full type checking for the required methods, but will have to resort to weak typing (manually using Export attributes and matching the signature) for the optional protocol methods.
To make it convenient to consume an API that uses protocols, the binding tool also will produce an extensions method class that exposes all of the optional methods. This means that as long as you are consuming an API, you will be able to treat protocols as having all the methods.
If you want to use the protocol definitions in your API, you will need to write skeleton empty interfaces in your API definition. If you want to use the MyProtocol in an API, you would need to do this:
[BaseType (typeof (NSObject))]
[Model, Protocol]
interface MyProtocol {
// Use [Abstract] when the method is defined in the @required section
// of the protocol definition in Objective-C
[Abstract]
[Export ("say:")]
void Say (string msg);
[Export ("listen")]
void Listen ();
}
interface IMyProtocol {}
[BaseType (typeof(NSObject))]
interface MyTool {
[Export ("getProtocol")]
IMyProtocol GetProtocol ();
}
The above is needed because at binding time the IMyProtocol
would not exist, that is why you need to provide an empty
interface.
Adopting protocol-generated interfaces
Whenever you implement one of the interfaces generated for the protocols, like this:
class MyDelegate : NSObject, IUITableViewDelegate {
nint IUITableViewDelegate.GetRowHeight (nint row) {
return 1;
}
}
The implementation for the required interface methods gets exported with the proper name, so it is equivalent to this:
class MyDelegate : NSObject, IUITableViewDelegate {
[Export ("getRowHeight:")]
nint IUITableViewDelegate.GetRowHeight (nint row) {
return 1;
}
}
This will work for all required protocol members, but there is a special case with optional selectors to be aware of.
Optional protocol members are treated identically when using the base class:
public class UrlSessionDelegate : NSUrlSessionDownloadDelegate {
public override void DidWriteData (NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
but when using the protocol interface it is required to add the [Export]. The IDE will add it via autocomplete when you add it starting with override.
public class UrlSessionDelegate : NSObject, INSUrlSessionDownloadDelegate {
[Export ("URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:")]
public void DidWriteData (NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
There is a slight behavior difference between the two at runtime:
- Users of the base class (NSUrlSessionDownloadDelegate in example) provides all required and optional selectors, returning reasonable default values.
- Users of the interface (INSUrlSessionDownloadDelegate in example) only respond to the exact selectors provided.
Some rare classes can behave differently here. In almost all cases however it is safe to use either.
Protocol inlining
While you bind existing Objective-C types that have been declared as adopting
a protocol, you will want to inline the protocol directly. To do this, merely
declare your protocol as an interface without any [BaseType]
attribute and list the protocol in the list of base interfaces for your interface.
Example:
interface SpeakProtocol {
[Export ("say:")]
void Say (string msg);
}
[BaseType (typeof (NSObject))]
interface Robot : SpeakProtocol {
[Export ("awake")]
bool Awake { get; set; }
}
Member definitions
The attributes in this section are applied to individual members of a type: properties and method declarations.
AlignAttribute
Used to specify the alignment value for property return types. Certain
properties take pointers to addresses that must be aligned at certain boundaries
(in Xamarin.iOS this happens for example with some GLKBaseEffect
properties that
must be 16-byte aligned). You can use this property to decorate the getter, and
use the alignment value. This is typically used with the OpenTK.Vector4
and
OpenTK.Matrix4
types when integrated with Objective-C APIs.
Example:
public interface GLKBaseEffect {
[Export ("constantColor")]
Vector4 ConstantColor { [Align (16)] get; set; }
}
AppearanceAttribute
The [Appearance]
attribute is limited to iOS 5, where the Appearance manager was
introduced.
The [Appearance]
attribute can be applied to any method or property that
participate in the UIAppearance
framework. When this attribute is applied to a
method or property in a class, it will direct the binding generator to create a
strongly-typed appearance class that is used to style all the instances of this
class, or the instances that match certain criteria.
Example:
public interface UIToolbar {
[Export ("setBackgroundImage:forToolbarPosition:barMetrics:")]
[Appearance]
void SetBackgroundImage (UIImage backgroundImage, UIToolbarPosition position, UIBarMetrics barMetrics);
[Export ("backgroundImageForToolbarPosition:barMetrics:")]
[Appearance]
UIImage GetBackgroundImage (UIToolbarPosition position, UIBarMetrics barMetrics);
}
The above would generate the following code in UIToolbar:
public partial class UIToolbar {
public partial class UIToolbarAppearance : UIView.UIViewAppearance {
public virtual void SetBackgroundImage (UIImage backgroundImage, UIToolbarPosition position, UIBarMetrics barMetrics);
public virtual UIImage GetBackgroundImage (UIToolbarPosition position, UIBarMetrics barMetrics)
}
public static new UIToolbarAppearance Appearance { get; }
public static new UIToolbarAppearance AppearanceWhenContainedIn (params Type [] containers);
}
AutoReleaseAttribute (Xamarin.iOS 5.4)
Use the [AutoReleaseAttribute]
on methods and properties to wrap the method
invocation to the method in an NSAutoReleasePool
.
In Objective-C there are some methods that return values that are added to
the default NSAutoReleasePool
. By default, these would go to your thread
NSAutoReleasePool
, but since Xamarin.iOS also keeps a reference to your objects as
long as the managed object lives, you might not want to keep an extra reference
in the NSAutoReleasePool
which will only get drained until your thread returns
control to the next thread, or you go back to the main loop.
This attribute is applied for example on heavy properties (for example
UIImage.FromFile
) that returns objects that have been added to the default
NSAutoReleasePool
. Without this attribute, the images would be retained as long
as your thread did not return control to the main loop. Uf your thread was some
sort of background downloader that is always alive and waiting for work, the
images would never be released.
ForcedTypeAttribute
The [ForcedTypeAttribute]
is used to enforce the creation of a managed type even
if the returned unmanaged object does not match the type described in the binding
definition.
This is useful when the type described in a header does not match the returned type
of the native method, for example take the following Objective-C definition from NSURLSession
:
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
It clearly states that it will return an NSURLSessionDownloadTask
instance, but yet it
returns a NSURLSessionTask
, which is a superclass and thus not convertible to
NSURLSessionDownloadTask
. Since we are in a type-safe context an InvalidCastException
will happen.
To comply with the header description and avoid the InvalidCastException
, the
[ForcedTypeAttribute]
is used.
[BaseType (typeof (NSObject), Name="NSURLSession")]
interface NSUrlSession {
[Export ("downloadTaskWithRequest:")]
[return: ForcedType]
NSUrlSessionDownloadTask CreateDownloadTask (NSUrlRequest request);
}
The [ForcedTypeAttribute]
also accepts a boolean value named Owns
that is false
by default [ForcedType (owns: true)]
. The owns parameter is used to follow
the Ownership Policy
for Core Foundation objects.
The [ForcedTypeAttribute]
is only valid on parameters, properties,
and return value.
BindAsAttribute
The [BindAsAttribute]
allows binding NSNumber
, NSValue
and NSString
(enums) into more accurate C# types. The attribute can be used to create better, more accurate, .NET API over the native API.
You can decorate methods (on return value), parameters and properties with BindAs
. The only restriction is that your member must not be inside a [Protocol]
or [Model]
interface.
For example:
[return: BindAs (typeof (bool?))]
[Export ("shouldDrawAt:")]
NSNumber ShouldDraw ([BindAs (typeof (CGRect))] NSValue rect);
Would output:
[Export ("shouldDrawAt:")]
bool? ShouldDraw (CGRect rect) { ... }
Internally we will do the bool?
<-> NSNumber
and CGRect
<-> NSValue
conversions.
The current supported encapsulation types are:
NSValue
NSNumber
NSString
NSValue
The following C# data types are supported to be encapsulated from/into NSValue
:
- CGAffineTransform
- NSRange
- CGVector
- SCNMatrix4
- CLLocationCoordinate2D
- SCNVector3
- SCNVector4
- CGPoint / PointF
- CGRect / RectangleF
- CGSize / SizeF
- UIEdgeInsets
- UIOffset
- MKCoordinateSpan
- CMTimeRange
- CMTime
- CMTimeMapping
- CATransform3D
NSNumber
The following C# data types are supported to be encapsulated from/into NSNumber
:
- bool
- byte
- double
- float
- short
- int
- long
- sbyte
- ushort
- uint
- ulong
- nfloat
- nint
- nuint
- Enums
NSString
[BindAs]
works in conjuntion with enums backed by a NSString constant so you can create better .NET API, for example:
[BindAs (typeof (CAScroll))]
[Export ("supportedScrollMode")]
NSString SupportedScrollMode { get; set; }
Would output:
[Export ("supportedScrollMode")]
CAScroll SupportedScrollMode { get; set; }
We will handle the enum
<-> NSString
conversion only if the provided enum type to [BindAs]
is backed by a NSString constant.
Arrays
[BindAs]
also supports arrays of any of the supported types, you can have the following API definition as an example:
[return: BindAs (typeof (CAScroll []))]
[Export ("getScrollModesAt:")]
NSString [] GetScrollModes ([BindAs (typeof (CGRect []))] NSValue [] rects);
Would output:
[Export ("getScrollModesAt:")]
CAScroll? [] GetScrollModes (CGRect [] rects) { ... }
The rects
parameter will be encapsulated into a NSArray
that contains an NSValue
for each CGRect
and in return you will get an array of CAScroll?
which has been created using the values of the returned NSArray
containing NSStrings
.
BindAttribute
The [Bind]
attribute has two uses one when applied to a method or property
declaration, and another one when applied to the individual getter or setter in
a property.
When used for a method or property, the effect of the [Bind]
attribute is to
generate a method that invokes the specified selector. But the resulting
generated method is not decorated with the [Export]
attribute, which means that
it can not participate in method overriding. This is typically used in
combination with the [Target]
attribute for implementing Objective-C extension
methods.
For example:
public interface UIView {
[Bind ("drawAtPoint:withFont:")]
SizeF DrawString ([Target] string str, CGPoint point, UIFont font);
}
When used in a getter or setter, the [Bind]
attribute is used to alter the
defaults inferred by the code generator when generating the getter and setter
Objective-C selector names for a property. By default when you flag a property
with the name fooBar
, the generator would generate a fooBar
export for the
getter and setFooBar:
for the setter. In a few cases, Objective-C does not
follow this convention, usually they change the getter name to be isFooBar
.
You would use this attribute to inform the generator of this.
For example:
// Default behavior
[Export ("active")]
bool Active { get; set; }
// Custom naming with the Bind attribute
[Export ("visible")]
bool Visible { [Bind ("isVisible")] get; set; }
AsyncAttribute
Only available on Xamarin.iOS 6.3 and newer.
This attribute can be applied to methods that take a completion handler as their last argument.
You can use the [Async]
attribute on methods whose
last argument is a callback. When you apply
this to a method, the binding generator will generate a
version of that method with the suffix Async
. If the callback
takes no parameters, the return value will be a Task
, if the
callback takes a parameter, the result will be a
Task<T>
.
[Export ("upload:complete:")]
[Async]
void LoadFile (string file, NSAction complete)
The following will generate this async method:
Task LoadFileAsync (string file);
If the callback takes multiple parameters, you
should set the ResultType
or ResultTypeName
to specify the
desired name of the generated type which will hold all the
properties.
delegate void OnComplete (string [] files, nint byteCount);
[Export ("upload:complete:")]
[Async (ResultTypeName="FileLoading")]
void LoadFiles (string file, OnComplete complete)
The following will generate this async method, where
FileLoading
contains properties to access both files
and
byteCount
:
Task<FileLoading> LoadFile (string file);
If the last parameter of the callback is an NSError
, then
the generated Async
method will check if the value is not
null, and if that is the case, the generated async method will
set the task exception.
[Export ("upload:onComplete:")]
[Async]
void Upload (string file, Action<string,NSError> onComplete);
The above generates the following async method:
Task<string> UploadAsync (string file);
And on error, the resulting Task will have the exception
set to an NSErrorException
that wraps the resulting NSError
.
AsyncAttribute.ResultType
Use this property to specify the value for the returning
Task
object. This parameter takes an existing type, thus it
needs to be defined in one of your core api definitions.
AsyncAttribute.ResultTypeName
Use this property to specify the value for the returning
Task
object. This parameter takes the name of your desired
type name, the generator will produce a series of properties,
one for each parameter that the callback takes.
AsyncAttribute.MethodName
Use this property to customize the name of the generated async methods. The default is to use the name of the method and append the text "Async", you can use this to change this default.
DesignatedInitializerAttribute
When this attribute is applied to a constructor it will generate the same
[DesignatedInitializer]
in the final platform assembly. This is to help
the IDE indicate which constructor should be used in subclasses.
This should map to Objective-C/clang use of __attribute__((objc_designated_initializer))
.
DisableZeroCopyAttribute
This attribute is applied to string parameters or string properties and
instructs the code generator to not use the zero-copy string marshaling for
this parameter, and instead create a new NSString instance from the C# string.
This attribute is only required on strings if you instruct the generator to use
zero-copy string marshaling using either the --zero-copy
command
line option or setting the assembly-level attribute ZeroCopyStringsAttribute
.
This is necessary in cases where the property is declared in Objective-C to
be a retain
or assign
property instead of a copy
property. These typically
happen in third-party libraries that have been wrongly "optimized" by
developers. In general, retain
or assign
NSString
properties are incorrect
since NSMutableString
or user-derived classes of NSString
might alter the
contents of the strings without the knowledge of the library code, subtly
breaking the application. Typically this happens due to premature
optimization.
The following shows two such properties in Objective-C:
@property(nonatomic,retain) NSString *name;
@property(nonatomic,assign) NSString *name2;
DisposeAttribute
When you apply the [DisposeAttribute]
to a class, you provide a code snippet
that will be added to the Dispose()
method implementation of the class.
Since the Dispose
method is automatically generated by the bgen
tool, you need to use the [Dispose]
attribute to inject some code in the
generated Dispose
method implementation.
For example:
[BaseType (typeof (NSObject))]
[Dispose ("if (OpenConnections > 0) CloseAllConnections ();")]
interface DatabaseConnection {
}
ExportAttribute
The [Export]
attribute is used to flag a method or property to be exposed to
the Objective-C runtime. This attribute is shared between the binding tool and
the actual Xamarin.iOS and Xamarin.Mac runtimes. For methods, the parameter is passed
verbatim to the generated code, for properties, a getter and setter Exports are
generated based on the base declaration (see the section on the [BindAttribute]
for information on how to alter the behavior of the binding tool).
Syntax:
public enum ArgumentSemantic {
None, Assign, Copy, Retain.
}
[AttributeUsage (AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)]
public class ExportAttribute : Attribute {
public ExportAttribute();
public ExportAttribute (string selector);
public ExportAttribute (string selector, ArgumentSemantic semantic);
public string Selector { get; set; }
public ArgumentSemantic ArgumentSemantic { get; set; }
}
The selector represents the name of the underlying Objective-C method or property that is being bound.
ExportAttribute.ArgumentSemantic
FieldAttribute
This attribute is used to expose a C global variable as a field that is loaded on demand and exposed to C# code. Usually this is required to get the values of constants that are defined in C or Objective-C and that could be either tokens used in some APIs, or whose values are opaque and must be used as-is by user code.
Syntax:
public class FieldAttribute : Attribute {
public FieldAttribute (string symbolName);
public FieldAttribute (string symbolName, string libraryName);
public string SymbolName { get; set; }
public string LibraryName { get; set; }
}
The symbolName
is the C symbol to link with. By default this
will be loaded from a library whose name is inferred from the namespace where
the type is defined. If this is not the library where the symbol is looked up,
you should pass the libraryName
parameter. If you're linking a
static library, use __Internal
as the libraryName
parameter.
The generated properties are always static.
Properties flagged with the Field attribute can be of the following types:
NSString
NSArray
nint
/int
/long
nuint
/uint
/ulong
nfloat
/float
double
CGSize
System.IntPtr
- Enums
Setters are not supported for enums backed by NSString constants, but they can be manually bound if needed.
Example:
[Static]
interface CameraEffects {
[Field ("kCameraEffectsZoomFactorKey", "CameraLibrary")]
NSString ZoomFactorKey { get; }
}
InternalAttribute
The [Internal]
attribute can be applied to methods or properties and it has the
effect of flagging the generated code with the internal
C# keyword making the
code only accessible to code in the generated assembly. This is typically used
to hide APIs that are too low-level or provide a suboptimal public API that you
want to improve upon or for APIs that are not supported by the generator and
require some hand-coding.
When you design the binding, you would typically hide the method or property using this attribute and provide a different name for the method or property, and then on your C# complementary support file, you would add a strongly-typed wrapper that exposes the underlying functionality.
For example:
[Internal]
[Export ("setValue:forKey:")]
void _SetValueForKey (NSObject value, NSObject key);
[Internal]
[Export ("getValueForKey:")]
NSObject _GetValueForKey (NSObject key);
Then, in your supporting file, you could have some code like this:
public NSObject this [NSObject idx] {
get {
return _GetValueForKey (idx);
}
set {
_SetValueForKey (value, idx);
}
}
IsThreadStaticAttribute
This attribute flags the backing field for a property to be annotated with
the .NET [ThreadStatic]
attribute. This is useful if the field is a thread
static variable.
MarshalNativeExceptions (Xamarin.iOS 6.0.6)
This attribute will make a method support native (Objective-C) exceptions.
Instead of calling objc_msgSend
directly, the invocation will go through a
custom trampoline which catches ObjectiveC exceptions and marshals them into
managed exceptions.
Currently only a few objc_msgSend
signatures are supported (you will find out
if a signature isn't supported when native linking of an app that uses the
binding fails with a missing xamarin__objc_msgSend symbol), but more can be
added at request.
NewAttribute
This attribute is applied to methods and properties to have the generator
generate the new
keyword in front of the declaration.
It is used to avoid compiler warnings when the same method or property name is introduced in a subclass that already existed in a base class.
NotificationAttribute
You can apply this attribute to fields to have the generator produce a strongly-typed helper Notifications class.
This attribute can be used without arguments for notifications that carry no
payload, or you can specify a System.Type
that references another interface in
the API definition, typically with the name ending with "EventArgs". The
generator will turn the interface into a class that subclasses EventArgs
and
will include all of the properties listed there. The [Export]
attribute should
be used in the EventArgs
class to list the name of the key used to look up the
Objective-C dictionary to fetch the value.
For example:
interface MyClass {
[Notification]
[Field ("MyClassDidStartNotification")]
NSString DidStartNotification { get; }
}
The above code will generate a nested class MyClass.Notifications
with the
following methods:
public class MyClass {
[..]
public Notifications {
public static NSObject ObserveDidStart (EventHandler<NSNotificationEventArgs> handler)
public static NSObject ObserveDidStart (NSObject objectToObserve, EventHandler<NSNotificationEventArgs> handler)
}
}
Users of your code can then easily subscribe to notifications posted to the NSDefaultCenter by using code like this:
var token = MyClass.Notifications.ObserverDidStart ((notification) => {
Console.WriteLine ("Observed the 'DidStart' event!");
});
Or to set a specific object to observe. If you pass null
to objectToObserve
this method will behave just like its other peer.
var token = MyClass.Notifications.ObserverDidStart (objectToObserve, (notification) => {
Console.WriteLine ("Observed the 'DidStart' event on objectToObserve!");
});
The returned value from ObserveDidStart
can be used to easily stop receiving
notifications, like this:
token.Dispose ();
Or you can call NSNotification.DefaultCenter.RemoveObserver
and pass the token. If your notification contains parameters, you
should specify a helper EventArgs
interface, like this:
interface MyClass {
[Notification (typeof (MyScreenChangedEventArgs)]
[Field ("MyClassScreenChangedNotification")]
NSString ScreenChangedNotification { get; }
}
// The helper EventArgs declaration
interface MyScreenChangedEventArgs {
[Export ("ScreenXKey")]
nint ScreenX { get; set; }
[Export ("ScreenYKey")]
nint ScreenY { get; set; }
[Export ("DidGoOffKey")]
[ProbePresence]
bool DidGoOff { get; }
}
The above will generate a MyScreenChangedEventArgs
class with the
ScreenX
and ScreenY
properties that will fetch the data from the
NSNotification.UserInfo
dictionary using the keys ScreenXKey
and ScreenYKey
respectively and apply the proper conversions. The [ProbePresence]
attribute is used for the generator to probe if the key is set in the
UserInfo
, instead of trying to extract the value. This is used for
cases where the presence of the key is the value (typically for
boolean values).
This allows you to write code like this:
var token = MyClass.NotificationsObserveScreenChanged ((notification) => {
Console.WriteLine ("The new screen dimensions are {0},{1}", notification.ScreenX, notification.ScreenY);
});
In some cases, there is no constant associated with the value passed
on the dictionary. Apple sometimes uses public symbol constants and
sometimes uses string constants. By default the [Export]
attribute
in your provided EventArgs
class will use the specified name as a
public symbol to be looked up at runtime. If this is not the case,
and instead it is supposed to be looked up as a string constant then
pass the ArgumentSemantic.Assign
value to the Export attribute.
New in Xamarin.iOS 8.4
Sometimes, notifications will begin life without any arguments, so the
use of [Notification]
without arguments is acceptable. But
sometimes, parameters to the notification will be introduced. To
support this scenario, the attribute can be applied more than once.
If you are developing a binding, and you want to avoid breaking existing user code, you would turn an existing notification from:
interface MyClass {
[Notification]
[Field ("MyClassScreenChangedNotification")]
NSString ScreenChangedNotification { get; }
}
Into a version that lists the notification attribute twice, like this:
interface MyClass {
[Notification]
[Notification (typeof (MyScreenChangedEventArgs)]
[Field ("MyClassScreenChangedNotification")]
NSString ScreenChangedNotification { get; }
}
NullAllowedAttribute
When this is applied to a property it flags the property as allowing the
value null
to be assigned to it. This is only valid for reference types.
When this is applied to a parameter in a method signature it indicates that
the specified parameter can be null and that no check should be performed for
passing null
values.
If the reference type does not have this attribute, the binding tool will
generate a check for the value being assigned before passing it to Objective-C
and will generate a check that will throw an ArgumentNullException
if the value
assigned is null
.
For example:
// In properties
[NullAllowed]
UIImage IconFile { get; set; }
// In methods
void SetImage ([NullAllowed] UIImage image, State forState);
OverrideAttribute
Use this attribute to instruct the binding generator that the binding for
this particular method should be flagged with an override
keyword.
PreSnippetAttribute
You can use this attribute to inject some code to be inserted after the input parameters have been validated, but before the code calls into Objective-C.
Example:
[Export ("demo")]
[PreSnippet ("var old = ViewController;")]
void Demo ();
PrologueSnippetAttribute
You can use this attribute to inject some code to be inserted before any of the parameters are validated in the generated method.
Example:
[Export ("demo")]
[Prologue ("Trace.Entry ();")]
void Demo ();
PostGetAttribute
Instructs the binding generator to invoke the specified property from this class to fetch a value from it.
This property is typically used to refresh the cache that points to reference objects that keep the object graph referenced. Usually it shows up in code that has operations like Add/Remove. This method is used so that after elements are added or removed that the internal cache be updated to ensure that we are keeping managed references to objects that are actually in use. This is possible because the binding tool generates a backing field for all reference objects in a given binding.
Example:
[BaseType (typeof (NSObject))]
public interface NSOperation {
[Export ("addDependency:")][PostGet ("Dependencies")]
void AddDependency (NSOperation op);
[Export ("removeDependency:")][PostGet ("Dependencies")]
void RemoveDependency (NSOperation op);
[Export ("dependencies")]
NSOperation [] Dependencies { get; }
}
In this case, the Dependencies
property will be invoked after
adding or removing dependencies from the NSOperation
object, ensuring that we
have a graph that represents the actual loaded objects, preventing both memory
leaks as well as memory corruption.
PostSnippetAttribute
You can use this attribute to inject some C# source code to be inserted after the code has invoked the underlying Objective-C method
Example:
[Export ("demo")]
[PostSnippet ("if (old != null) old.DemoComplete ();")]
void Demo ();
ProxyAttribute
This attribute is applied to return values to flag them as being proxy
objects. Some Objective-C APIs return proxy objects that can not be
differentiated from user bindings. The effect of this attribute is to flag the
object as being a DirectBinding
object. For a scenario in Xamarin.Mac, you can see
the discussion on this bug.
ReleaseAttribute (Xamarin.iOS 6.0)
This can be applied to return types to indicate that the generator should
call Release
on the object before returning it. This is only needed when a
method gives you a retained object (as opposed to an autoreleased object, which
is the most common scenario)
Example:
[Export ("getAndRetainObject")]
[return: Release ()]
NSObject GetAndRetainObject ();
Additionally this attribute is propagated to the generated code, so that the Xamarin.iOS runtime knows it must retain the object upon returning to Objective-C from such a function.
SealedAttribute
Instructs the generator to flag the generated method as sealed. If this attribute is not specified, the default is to generate a virtual method (either a virtual method, an abstract method or an override depending on how other attributes are used).
StaticAttribute
When the [Static]
attribute is applied to a method or property, this generates a
static method or property. If this attribute is not specified, then the
generator produces an instance method or property.
TransientAttribute
Use this attribute to flag properties whose values are transient, that is, objects that are created temporarily by iOS but are not long-lived. When this attribute is applied to a property, the generator does not create a backing field for this property, which means that the managed class does not keep a reference to the object.
WrapAttribute
In the design of the Xamarin.iOS/Xamarin.Mac bindings, the [Wrap]
attribute is used
to wrap a weakly-typed object with a strongly-typed object. This comes into play
mostly with Objective-C delegate objects which are typically declared as being
of type id
or NSObject
. The convention used by
Xamarin.iOS and Xamarin.Mac is to expose those delegates or data sources as being of
type NSObject
and are named using the convention "Weak" + the name being
exposed. An id delegate
property from Objective-C would be exposed as an
NSObject WeakDelegate { get; set; }
property in the API contract file.
But typically the value that is assigned to this delegate is of a strong
type, so we surface the strong type and apply the [Wrap]
attribute, this means
that users can choose to use weak types if they need some fine-control or if
they need to resort to low-level tricks, or they can use the strongly-typed
property for most of their work.
Example:
[BaseType (typeof (NSObject))]
interface Demo {
[Export ("delegate"), NullAllowed]
NSObject WeakDelegate { get; set; }
[Wrap ("WeakDelegate")]
DemoDelegate Delegate { get; set; }
}
[BaseType (typeof (NSObject))]
[Model][Protocol]
interface DemoDelegate {
[Export ("doDemo")]
void DoDemo ();
}
This is how the user would use the weakly-typed version of the Delegate:
// The weak case, user has to roll his own
class SomeObject : NSObject {
[Export ("doDemo")]
void CallbackForDoDemo () {}
}
var demo = new Demo ();
demo.WeakDelegate = new SomeObject ();
And this is how the user would use the strongly-typed version, notice that
the user takes advantage of C#'s type system and is using the override keyword
to declare its intent and does not have to manually decorate the method
with [Export]
, since we did that work in the binding for the user:
// This is the strong case,
class MyDelegate : DemoDelegate {
override void Demo DoDemo () {}
}
var strongDemo = new Demo ();
demo.Delegate = new MyDelegate ();
Another use of the [Wrap]
attribute is to support strongly-typed version
of methods. For example:
[BaseType (typeof (NSObject))]
interface XyzPanel {
[Export ("playback:withOptions:")]
void Playback (string fileName, [NullAllowed] NSDictionary options);
[Wrap ("Playback (fileName, options?.Dictionary")]
void Playback (string fileName, XyzOptions options);
}
When the [Wrap]
attribute is applied on a method inside a type decorated
with a [Category]
attribute, you need to include This
as
the first argument since an extension method is being generated. For example:
[Wrap ("Write (This, image, options?.Dictionary, out error)")]
bool Write (CIImage image, CIImageRepresentationOptions options, out NSError error);
The members generated by [Wrap]
are not virtual
by default, if you need a virtual
member you can set to true
the optional isVirtual
parameter.
[BaseType (typeof (NSObject))]
interface FooExplorer {
[Export ("fooWithContentsOfURL:")]
void FromUrl (NSUrl url);
[Wrap ("FromUrl (NSUrl.FromString (url))", isVirtual: true)]
void FromUrl (string url);
}
[Wrap]
can also be used directly in property getters and setters.
This allows to have full control on them and adjust the code as needed.
For example, consider the following API definition that uses smart enums:
// Smart enum.
enum PersonRelationship {
[Field (null)]
None,
[Field ("FMFather", "__Internal")]
Father,
[Field ("FMMother", "__Internal")]
Mother
}
Interface definition:
// Property definition.
[Export ("presenceType")]
NSString _PresenceType { get; set; }
PersonRelationship PresenceType {
[Wrap ("PersonRelationshipExtensions.GetValue (_PresenceType)")]
get;
[Wrap ("_PresenceType = value.GetConstant ()")]
set;
}
Parameter attributes
This section describes the attributes that you can apply to the parameters in
a method definition as well as the [NullAttribute]
that applies to a property as a
whole.
BlockCallback
This attribute is applied to parameter types in C# delegate declarations to notify the binder that the parameter in question conforms to the Objective-C block calling convention and should marshal it in this way.
This is typically used for callbacks that are defined like this in Objective-C:
typedef returnType (^SomeTypeDefinition) (int parameter1, NSString *parameter2);
See also: CCallback.
CCallback
This attribute is applied to parameter types in C# delegate declarations to notify the binder that the parameter in question conforms to the C ABI function pointer calling convention and should marshal it in this way.
This is typically used for callbacks that are defined like this in Objective-C:
typedef returnType (*SomeTypeDefinition) (int parameter1, NSString *parameter2);
See also: BlockCallback.
Params
You can use the [Params]
attribute on the last array parameter of
a method definition to have the generator inject a "params" in
the definition. This allows the binding to easily allow for
optional parameters.
For example, the following definition:
[Export ("loadFiles:")]
void LoadFiles ([Params]NSUrl [] files);
Allows the following code to be written:
foo.LoadFiles (new NSUrl (url));
foo.LoadFiles (new NSUrl (url1), new NSUrl (url2), new NSUrl (url3));
This has the added advantage that it does not require users to create an array purely for passing elements.
PlainString
You can use the [PlainString]
attribute in front of string parameters to
instruct the binding generator to pass the string as a C string, instead of
passing the parameter as an NSString
.
Most Objective-C APIs consume NSString
parameters, but a handful of APIs
expose a char *
API for passing strings, instead of the NSString
variation.
Use [PlainString]
in those cases.
For example, the following Objective-C declarations:
- (void) setText: (NSString *) theText;
- (void) logMessage: (char *) message;
Should be bound like this:
[Export ("setText:")]
void SetText (string theText);
[Export ("logMessage:")]
void LogMessage ([PlainString] string theText);
RetainAttribute
Instructs the generator to keep a reference to the specified parameter. The
generator will provide the backing store for this field or you can specify a
name (the WrapName
) to store the value at. This is useful to hold a reference to
a managed object that is passed as a parameter to Objective-C and when you know
that Objective-C will only keep this copy of the object. For instance, an API
like SetDisplay (SomeObject)
would use this attribute as it is likely that the
SetDisplay could only display one object at a time. If you need to keep track of
more than one object (for example, for a Stack-like API) you would use the
[RetainList]
attribute.
Syntax:
public class RetainAttribute {
public RetainAttribute ();
public RetainAttribute (string wrapName);
public string WrapName { get; }
}
TransientAttribute
This attribute is applied to parameters and is only used
when transitioning from Objective-C to C#. During those
transitions the various Objective-C NSObject
parameters are
wrapped into a managed representation of the object.
The runtime will take a reference to the native object and keep the reference until the last managed reference to the object is gone, and the GC has a chance to run.
In a few cases, it is important for the C# runtime to not keep a reference to the native object. This sometimes happens when the underlying native code has attached a special behavior to the lifecycle of the parameter. For example: the destructor for the parameter will perform some cleanup action, or dispose some precious resource.
This attribute informs the runtime that you desire the object to be disposed if possible when returning back to Objective-C from your overwritten method.
The rule is simple: if the runtime had to create a new managed representation from the native object, then at the end of the function, the retain count for the native object will be dropped, and the Handle property of the managed object will be cleared. This means that if you kept a reference to the managed object, that reference will become useless (invoking methods on it will throw an exception).
If the object passed was not created, or if there was already an outstanding managed representation of the object, the forced dispose does not take place.
Property attributes
NotImplementedAttribute
This attribute is used to support an Objective-C idiom where a property with a getter is introduced in a base class, and a mutable subclass introduces a setter.
Since C# does not support this model, the base class needs to have both the setter and the getter, and a subclass can use the OverrideAttribute.
This attribute is only used in property setters, and is used to support the mutable idiom in Objective-C.
Example:
[BaseType (typeof (NSObject))]
interface MyString {
[Export ("initWithValue:")]
IntPtr Constructor (string value);
[Export ("value")]
string Value {
get;
[NotImplemented ("Not available on MyString, use MyMutableString to set")]
set;
}
}
[BaseType (typeof (MyString))]
interface MyMutableString {
[Export ("value")]
[Override]
string Value { get; set; }
}
Enum attributes
Mapping NSString
constants to enum values is a easy way to create better
.NET API. It:
- allows code completion to be more useful, by showing only the correct values for the API;
- adds type safety, you cannot use another
NSString
constant in a incorrect context; and - allows to hide some constants, making code completion show shorter API list without losing functionality.
Example:
enum NSRunLoopMode {
[DefaultEnumValue]
[Field ("NSDefaultRunLoopMode")]
Default,
[Field ("NSRunLoopCommonModes")]
Common,
[Field (null)]
Other = 1000
}
From the above binding definition the generator will create the enum
itself and will
also create a *Extensions
static type that includes two-ways conversion methods
between the enum values and the NSString
constants. This means the constants remains
available to developers even if they are not part of the API.
Examples:
// using the NSString constant in a different API / framework / 3rd party code
CallApiRequiringAnNSString (NSRunLoopMode.Default.GetConstant ());
// converting the constants from a different API / framework / 3rd party code
var constant = CallApiReturningAnNSString ();
// back into an enum value
CallApiWithEnum (NSRunLoopModeExtensions.GetValue (constant));
DefaultEnumValueAttribute
You can decorate one enum value with this attribute. This will become the constant being returned if the enum value is not known.
From the example above:
var x = (NSRunLoopMode) 99;
Call (x.GetConstant ()); // NSDefaultRunLoopMode will be used
If no enum value is decorated then a NotSupportedException
will be thrown.
ErrorDomainAttribute
Error codes are bound as an enum values. There's generally an error domain for them and it's not always easy to find which one applies (or if one even exists).
You can use this attribute to associate the error domain with the enum itself.
Example:
[Native]
[ErrorDomain ("AVKitErrorDomain")]
public enum AVKitError : nint {
None = 0,
Unknown = -1000,
PictureInPictureStartFailed = -1001
}
You can then call the extension method GetDomain
to get the domain constant of
any error.
FieldAttribute
This is the same [Field]
attribute used for constants inside type. It can also
be used inside enums to map a value with a specific constant.
A null
value can be used to specify which enum value should be returned if a
null
NSString
constant is specified.
From the example above:
var constant = NSRunLoopMode.NewInWatchOS3; // will be null in watchOS 2.x
Call (NSRunLoopModeExtensions.GetValue (constant)); // will return 1000
If no null
value is present then an ArgumentNullException
will be thrown.
Global attributes
Global attributes are either applied using the [assembly:]
attribute modifier
like the [LinkWithAttribute]
or can be used anywhere,
like the availability attributes.
LinkWithAttribute
This is an assembly-level attribute which allows developers to specify the linking flags required to reuse a bound library without forcing the consumer of the library to manually configure the gcc_flags and extra mtouch arguments passed to a library.
Syntax:
// In properties
[Flags]
public enum LinkTarget {
Simulator = 1,
ArmV6 = 2,
ArmV7 = 4,
Thumb = 8,
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple=true)]
public class LinkWithAttribute : Attribute {
public LinkWithAttribute ();
public LinkWithAttribute (string libraryName);
public LinkWithAttribute (string libraryName, LinkTarget target);
public LinkWithAttribute (string libraryName, LinkTarget target, string linkerFlags);
public bool ForceLoad { get; set; }
public string Frameworks { get; set; }
public bool IsCxx { get; set; }
public string LibraryName { get; }
public string LinkerFlags { get; set; }
public LinkTarget LinkTarget { get; set; }
public bool NeedsGccExceptionHandling { get; set; }
public bool SmartLink { get; set; }
public string WeakFrameworks { get; set; }
}
This attribute is applied at the assembly level, for example, this is what the CorePlot bindings use:
[assembly: LinkWith ("libCorePlot-CocoaTouch.a", LinkTarget.ArmV7 | LinkTarget.ArmV7s | LinkTarget.Simulator, Frameworks = "CoreGraphics QuartzCore", ForceLoad = true)]
When you use the [LinkWith]
attribute, the specified libraryName
is embedded
into the resulting assembly, allowing users to ship a single DLL that contains
both the unmanaged dependencies as well as the command line flags necessary to
properly consume the library from Xamarin.iOS.
It's also possible to not provide a libraryName
, in which case the
LinkWith
attribute can be used to only specify additional linker flags:
[assembly: LinkWith (LinkerFlags = "-lsqlite3")]
LinkWithAttribute constructors
These constructors allow you to specify the library to link with and embed into your resulting assembly, the supported targets that the library supports and any optional library flags that are necessary to link with the library.
Note that the LinkTarget
argument is inferred by Xamarin.iOS and does not need to be set.
Examples:
// Specify additional linker:
[assembly: LinkWith (LinkerFlags = "-sqlite3")]
// Specify library name for the constructor:
[assembly: LinkWith ("libDemo.a");
// Specify library name, and link target for the constructor:
[assembly: LinkWith ("libDemo.a", LinkTarget.Thumb | LinkTarget.Simulator);
// Specify only the library name, link target and linker flags for the constructor:
[assembly: LinkWith ("libDemo.a", LinkTarget.Thumb | LinkTarget.Simulator, SmartLink = true, ForceLoad = true, IsCxx = true);
LinkWithAttribute.ForceLoad
The ForceLoad
property is used to decide whether or not the -force_load
link
flag is used for linking the native library. For now, this should always be
true.
LinkWithAttribute.Frameworks
If the library being bound has a hard requirement on any frameworks (other
than Foundation
and UIKit
), you should set the Frameworks
property to a string
containing a space-delimited list of the required platform frameworks. For
example, if you are binding a library that requires CoreGraphics
and CoreText
,
you would set the Frameworks
property to "CoreGraphics CoreText"
.
LinkWithAttribute.IsCxx
Set this property to true if the resulting executable needs to be compiled using a C++ compiler instead of the default, which is a C compiler. Use this if the library that you are binding was written in C++.
LinkWithAttribute.LibraryName
The name of the unmanaged library to bundle. This is a file with the extension ".a" and it can contain object code for multiple platforms (for example, ARM and x86 for the simulator).
Earlier versions of Xamarin.iOS checked the LinkTarget
property to determine
the platform your library supported, but this is now auto-detected, and the
LinkTarget
property is ignored.
LinkWithAttribute.LinkerFlags
The LinkerFlags
string provides a way for binding authors to specify any
additional linker flags needed when linking the native library into the
application.
For example, if the native library requires libxml2 and zlib, you would set
the LinkerFlags
string to "-lxml2 -lz"
.
LinkWithAttribute.LinkTarget
Earlier versions of Xamarin.iOS checked the LinkTarget
property to determine
the platform your library supported, but this is now auto-detected, and the
LinkTarget
property is ignored.
LinkWithAttribute.NeedsGccExceptionHandling
Set this property to true if the library that you are linking requires the GCC Exception Handling library (gcc_eh)
LinkWithAttribute.SmartLink
The SmartLink
property should be set to true to let Xamarin.iOS determine
whether ForceLoad
is required or not.
LinkWithAttribute.WeakFrameworks
The WeakFrameworks
property works the same way as the Frameworks
property,
except that at link-time, the -weak_framework
specifier is passed
to gcc for each of the listed frameworks.
WeakFrameworks
makes it possible for libraries and applications to weakly
link against platform frameworks so that they can optionally use them if they
are available but do not take a hard dependency on them which is useful if your
library is meant to add extra features on newer versions of iOS. For more
information on weak linking, see Apple's documentation on Weak Linking.
Good candidates for weak linking would be Frameworks
like Accounts,
CoreBluetooth
, CoreImage
, GLKit
, NewsstandKit
and Twitter
since they are only
available in iOS 5.
AdviceAttribute
Use this attribute to give developers a hint about other APIs that might be more convenient for them to use. For example, if you provide a strongly-typed version of an API, you could use this attribute on the weakly-typed attribute to direct the developer to the better API.
The information from this attribute is shown in the documentation and tools can be developed to give user suggestions on how to improve
RequiresSuperAttribute
This is a specialized subclass of the [Advice]
attribute that can be used
to hint to the developer that overriding a method requires a call to
the base (overridden) method.
This corresponds to clang
__attribute__((objc_requires_super))
ZeroCopyStringsAttribute
Only available in Xamarin.iOS 5.4 and newer.
This attribute instructs the generator that the binding for this specific
library (if applied with [assembly:]
) or type should use the fast
zero-copy string marshaling. This attribute is equivalent to passing the
command line option --zero-copy
to the generator.
When using zero-copy for strings, the generator effectively uses the same C#
string as the string that Objective-C consumes without incurring the creation of
a new NSString
object and avoiding copying the data from the C# strings to the
Objective-C string. The only drawback of using Zero Copy strings is that you
must ensure that any string property that you wrap that happens to be flagged as
retain
or copy
has the [DisableZeroCopy]
attribute set. This is
require because the handle for zero-copy strings is allocated on the stack and
is invalid upon the function return.
Example:
[ZeroCopyStrings]
[BaseType (typeof (NSObject))]
interface MyBinding {
[Export ("name")]
string Name { get; set; }
[Export ("domain"), NullAllowed]
string Domain { get; set; }
[DisablZeroCopy]
[Export ("someRetainedNSString")]
string RetainedProperty { get; set; }
}
You can also apply the attribute at the assembly level, and it will apply to all the types of the assembly:
[assembly:ZeroCopyStrings]
Strongly-typed dictionaries
With Xamarin.iOS 8.0 we introduced support for easily creating
strongly-typed classes that wrap NSDictionaries
.
While it has always been possible to use the DictionaryContainer data type together with a manual API, it is now a lot simpler to do this. For more information, see Surfacing Strong Types.
StrongDictionary
When this attribute is applied to an interface, the generator will produce a class with the same name as the interface that derives from DictionaryContainer and turns each property defined in the interface into a strongly-typed getter and setter for the dictionary.
This automatically generates a class that can be instantiated from an
existing NSDictionary
or that has been created new.
This attribute takes one parameter, the name of the class containing the keys that are used to access the elements on the dictionary. By default each property in the interface with the attribute will lookup a member in the specified type for a name with the suffix "Key".
For example:
[StrongDictionary ("MyOptionKeys")]
interface MyOption {
string Name { get; set; }
nint Age { get; set; }
}
[Static]
interface MyOptionKeys {
// In Objective-C this is "NSString *MYOptionNameKey;"
[Field ("MYOptionNameKey")]
NSString NameKey { get; }
// In Objective-C this is "NSString *MYOptionAgeKey;"
[Field ("MYOptionAgeKey")]
NSString AgeKey { get; }
}
In the above case, the MyOption
class will produce a string property
for Name
that will use the MyOptionKeys.NameKey
as the key into the
dictionary to retrieve a string. And will use the
MyOptionKeys.AgeKey
as the key into the dictionary to retrieve an
NSNumber
which contains an int.
If you want to use a different key, you can use the export attribute on the property, for example:
[StrongDictionary ("MyColoringKeys")]
interface MyColoringOptions {
[Export ("TheName")] // Override the default which would be NameKey
string Name { get; set; }
[Export ("TheAge")] // Override the default which would be AgeKey
nint Age { get; set; }
}
[Static]
interface MyColoringKeys {
// In Objective-C this is "NSString *MYColoringNameKey"
[Field ("MYColoringNameKey")]
NSString TheName { get; }
// In Objective-C this is "NSString *MYColoringAgeKey"
[Field ("MYColoringAgeKey")]
NSString TheAge { get; }
}
Strong dictionary types
The following data types are supported in the StrongDictionary
definition:
C# Interface Type | NSDictionary Storage Type |
---|---|
bool |
Boolean stored in an NSNumber |
Enumeration values | integer stored in an NSNumber |
int |
32-bit integer stored in an NSNumber |
uint |
32-bit unsigned integer stored in an NSNumber |
nint |
NSInteger stored in an NSNumber |
nuint |
NSUInteger stored in an NSNumber |
long |
64-bit integer stored in an NSNumber |
float |
32-bit integer stored as an NSNumber |
double |
64-bit integer stored as an NSNumber |
NSObject and subclasses |
NSObject |
NSDictionary |
NSDictionary |
string |
NSString |
NSString |
NSString |
C# Array of NSObject |
NSArray |
C# Array of enumerations |
NSArray containing NSNumber values |