Unified API Overview
Xamarin's Unified API makes it possible to share code between Mac and iOS and support 32 and 64-bit applications with the same binary. The Unified API is used by default in new Xamarin.iOS and Xamarin.Mac projects.
Important
The Xamarin Classic API, which preceded the Unified API, has been deprecated.
- The last version of Xamarin.iOS to support the Classic API (monotouch.dll) was Xamarin.iOS 9.10.
- Xamarin.Mac still supports the Classic API, but it is no longer updated. Since it is deprecated, developers should move their applications to the Unified API.
Updating Classic API-based Apps
Follow the relevant instructions for your platform:
- Update Existing Apps
- Update Existing iOS Apps
- Update Existing Mac Apps
- Update Existing Xamarin.Forms Apps
- Migrating a Binding to the Unified API
Tips for Updating Code to the Unified API
Regardless of what applications you are migrating, check out these tips to help you successfully update to the Unified API.
Library Split
From this point on, our APIs will be surfaced in two ways:
- Classic API: Limited to 32-bits (only) and exposed in the
monotouch.dll
andXamMac.dll
assemblies. - Unified API: Support both 32 and 64 bit development with a single API available in the
Xamarin.iOS.dll
andXamarin.Mac.dll
assemblies.
This means that for Enterprise developers (not targeting the App Store), you can continue using the existing Classic APIs, as we will keep maintaining them forever, or you can upgrade to the new APIs.
Namespace Changes
To reduce the friction to share code between our Mac and iOS products, we are changing the namespaces for the APIs in the products.
We are dropping the prefix "MonoTouch" from our iOS product and "MonoMac" from our Mac product on the data types.
This makes it simpler to share code between the Mac and iOS platforms without resorting to conditional compilation and will reduce the noise at the top of your source code files.
- Classic API: Namespaces use
MonoTouch.
orMonoMac.
prefix. - Unified API: No namespace prefix
Runtime Defaults
The Unified API by default uses the SGen garbage collector and the New Reference Counting system for tracking object ownership. This same feature has been ported to Xamarin.Mac.
This solves a number of problems that developers faced with the old system and also ease memory management.
Note that it is possible to enable New Refcount even for the Classic API, but the default is conservative and does not require users to make any changes. With the Unified API, we took the opportunity of changing the default and give developers all the improvements at the same time that they refactor and re-test their code.
API Changes
The Unified API removes deprecated methods and there are a few instances where there were typos in the API names when they were bound to the original MonoTouch and MonoMac namespaces in the Classic APIs. These instances have been corrected in the new Unified APIs and will need to be updated in your component, iOS and Mac applications. Here is a list of the most common ones you might run into:
Classic API Method Name | Unified API Method Name |
---|---|
UINavigationController.PushViewControllerAnimated() |
UINavigationController.PushViewController() |
UINavigationController.PopViewControllerAnimated() |
UINavigationController.PopViewController() |
CGContext.SetRGBFillColor() |
CGContext.SetFillColor() |
NetworkReachability.SetCallback() |
NetworkReachability.SetNotification() |
CGContext.SetShadowWithColor |
CGContext.SetShadow |
UIView.StringSize |
UIKit.UIStringDrawing.StringSize |
For a full list of changes when switching from the Classic to the Unified API, please see our Classic (monotouch.dll) vs Unified (Xamarin.iOS.dll) API differences documentation.
Updating to Unified
Several old/broken/deprecated API in classic are not
available in the Unified API. It can be easier to
fix the CS0616
warnings before starting your (manual
or automated) upgrade since you'll have the [Obsolete]
attribute message (part of the warning) to guide you to the right API.
Note that we are publishing a diff of the classic vs unified API changes that can be used either before or after your project updates. Still fixing the obsoletes calls in Classic will often be a time saver (less documentation lookups).
Follow these instructions to update existing iOS apps, or Mac apps to the Unified API. Review the remainder of this page, and these tips for additional information on migrating your code.
NuGet
NuGet packages that previously supported Xamarin.iOS via the Classic API published their assemblies using the Monotouch10 platform moniker.
The Unified API introduces a new platform identifier for compatible packages - Xamarin.iOS10. Existing NuGet packages will need to be updated to add support for this platform, by building against the Unified API.
Important
If you have an error in the form "Error 3 Cannot include both 'monotouch.dll' and 'Xamarin.iOS.dll' in the same Xamarin.iOS project - 'Xamarin.iOS.dll' is referenced explicitly, while 'monotouch.dll' is referenced by 'xxx, Version=0.0.000, Culture=neutral, PublicKeyToken=null'" after converting your application to the Unified APIs, it is typically due to having either a component or NuGet Package in the project that has not been updated to the Unified API. You'll need to remove the existing component/NuGet, update to a version that supports the Unified APIs and do a clean build.
The Road to 64 Bits
For background on supporting 32 and 64 bit applications and information about frameworks see the 32 and 64 bit Platform Considerations.
New Data Types
At the core of the difference, both Mac and iOS APIs use an architecture-specific data types that are always 32 bit on 32 bit platforms and 64 bit on 64 bit platforms.
For example, Objective-C maps the NSInteger
data type to int32_t
on 32 bit systems and
to int64_t
on 64 bit systems.
To match this behavior, on our Unified API, we are
replacing the previous uses of int
(which in .NET
is defined as always being System.Int32
) to a new
data type: System.nint
. You can think of the "n"
as meaning "native", so the native integer type of the
platform.
We are introducing nint
, nuint
and nfloat
as well providing data types built on
top of them where necessary.
To learn more about these data type changes, see the Native Types document.
How to detect the architecture of iOS apps
There might be situations where your application needs to know if it is running on a 32 bit or a 64 bit iOS system. The following code can be used to check the architecture:
if (IntPtr.Size == 4) {
Console.WriteLine ("32-bit App");
} else if (IntPtr.Size == 8) {
Console.WriteLine ("64-bit App");
}
Arrays and System.Collections.Generic
Because C# indexers expect a type of int
, you'll have to explicitly cast nint
values to int
to access the elements in a collection or array. For example:
public List<string> Names = new List<string>();
...
public string GetName(nint index) {
return Names[(int)index];
}
This is expected behavior, because the cast from int
to nint
is lossy on 64 bit, an implicit conversion is not done.
Converting DateTime to NSDate
When using the Unified APIs, the implicit conversion of DateTime
to NSDate
values is no longer performed. These values will need to be explicitly converted from one type to another. The following extension methods can be used to automate this process:
public static DateTime NSDateToDateTime(this NSDate date)
{
// NSDate has a wider range than DateTime, so clip
// the converted date to DateTime.Min|MaxValue.
double secs = date.SecondsSinceReferenceDate;
if (secs < -63113904000)
return DateTime.MinValue;
if (secs > 252423993599)
return DateTime.MaxValue;
return (DateTime) date;
}
public static NSDate DateTimeToNSDate(this DateTime date)
{
if (date.Kind == DateTimeKind.Unspecified)
date = DateTime.SpecifyKind (date, /* DateTimeKind.Local or DateTimeKind.Utc, this depends on each app */)
return (NSDate) date;
}
Deprecated APIs and Typos
Inside Xamarin.iOS classic API (monotouch.dll) the [Obsolete]
attribute
was used in two different ways:
- Deprecated iOS API: This is when Apple hints to you to stop using an API because it's being superseded by a newer one. The Classic API is still fine and often required (if you support the older version of iOS).
Such API (and the
[Obsolete]
attribute) are included into the new Xamarin.iOS assemblies. - Incorrect API Some API had typos on their names.
For the original assemblies (monotouch.dll and XamMac.dll) we kept the old code available for compatibility but they have been removed from the Unified API assemblies (Xamarin.iOS.dll and Xamarin.Mac)
NSObject subclasses .ctor(IntPtr)
Every NSObject
subclass has a constructor that accepts an IntPtr
. This is how we can instantiate a new managed instance from a native ObjC handle.
In classic this was a public
constructor. However it was easy to misuse this feature in user code, e.g. creating several managed instances for a single ObjC instance or creating a managed instance that would lack the expected managed state (for subclasses).
To avoid those kind of problems the IntPtr
constructors are now protected
in unified API, to be used only for subclassing. This will ensure the correct/safe API is used to create managed instance from handles, i.e.
var label = Runtime.GetNSObject<UILabel> (handle);
This API will return an existing managed instance (if it already exists) or will create a new one (when required). It is already available in both classic and unified API.
Note that the .ctor(NSObjectFlag)
is now also protected
but this one was rarely used outside of subclassing.
NSAction Replaced with Action
With the Unified APIs, NSAction
has been removed in favor of the standard .NET Action
. This is a big improvement because Action
is a common .NET type, whereas NSAction
was specific to Xamarin.iOS. They both do exactly the same thing, but they were distinct and incompatible types and resulted in more code having to be written to achieve the same result.
For example, if your existing Xamarin application included the following code:
UITapGestureRecognizer singleTap = new UITapGestureRecognizer (new NSAction (delegate() {
ShowDropDownAnimated (tblDataView);
}));
It can now be replaced with a simple lambda:
UITapGestureRecognizer singleTap = new UITapGestureRecognizer (() => ShowDropDownAnimated(tblDataView));
Previously that would be a compiler error because an Action
can't be assigned to NSAction
, but since UITapGestureRecognizer
now takes an Action
instead of an NSAction
it is valid in the Unified APIs.
Custom delegates replaced with Action<T>
In unified some simple (e.g. one parameter) .net delegates were replaced with Action<T>
. E.g.
public delegate void NSNotificationHandler (NSNotification notification);
can now be used as an Action<NSNotification>
. This promote code reuse and reduce code duplication inside both Xamarin.iOS and your own applications.
Task<bool> replaced with Task<Boolean,NSError>>
In classic there were some async APIs returning Task<bool>
. However some of them where are to use when an NSError
was part of the signature, i.e. the bool
was already true
and you had to catch an exception to get the NSError
.
Since some errors are very common and the return value was not useful this pattern was changed in unified to return a Task<Tuple<Boolean,NSError>>
. This allows you to check both the success and any error that could have happened during the async call.
NSString vs string
In a few cases some constants had to be changed from string
to NSString
, e.g. UITableViewCell
Classic
public virtual string ReuseIdentifier { get; }
Unified
public virtual NSString ReuseIdentifier { get; }
In general we prefer the .NET System.String
type. However, despite Apple guidelines, some native API are comparing constant pointers (not the string itself) and this can only work when we expose the constants as NSString
.
Objective-C Protocols
The original MonoTouch did not have full support for ObjC
protocols and some, non-optimal, API were added to support the
most common scenario. This limitation does not exists anymore
but, for backward compatibility, several APIs are kept around
inside monotouch.dll
and XamMac.dll
.
These limitations have been removed and cleaned up on the Unified APIs. Most changes will look like this:
Classic
public virtual AVAssetResourceLoaderDelegate Delegate { get; }
Unified
public virtual IAVAssetResourceLoaderDelegate Delegate { get; }
The I
prefix means unified expose an interface, instead of a specific type, for the ObjC protocol. This will ease cases where you do not want to subclass the specific type that Xamarin.iOS provided.
It also allowed some API to be more precise and easy to use, e.g.:
Classic
public virtual void SelectionDidChange (NSObject uiTextInput);
Unified
public virtual void SelectionDidChange (IUITextInput uiTextInput);
Such API are now easier to us, without referring to documentation, and your IDE code completion will provide you with more useful suggestions based on the protocol/interface.
NSCoding Protocol
Our original binding included an .ctor(NSCoder) for every
type - even if it did not support the NSCoding
protocol. A single Encode(NSCoder)
method was
present in the NSObject
to encode the object.
But this method would only work if the instance conformed to
NSCoding protocol.
On the Unified API we have fixed this. The new
assemblies will only have the .ctor(NSCoder)
if
the type conforms to NSCoding
. Also such types
now have an Encode(NSCoder)
method which conforms
to the INSCoding
interface.
Low Impact: In most cases this change won’t affect applications as the old, removed, constructors could not be used.
Further Tips
Additional changes to be aware of are listed in the tips for updating apps to the Unified API.