Migrating to Unified API for Components

Updating iOS and Mac Components for the Unified API

PDF for offline use
Related Articles:

Let us know how you feel about this


0/250

last updated: 2016-02

This article covers the steps required to update an existing Xamarin component to support the Unified APIs for Xamarin.IOS and Xamarin.Mac applications.

Overview

Starting February 1st, 2015 Apple requires that all new submissions to the iTunes and the Mac App Store must be 64 bit applications. As a result, any new Xamarin.iOS or Xamarin.Mac application will need to be using the new Unified API instead of the existing Classic MonoTouch and MonoMac APIs to support 64 bit.

Additionally, any Xamarin Component must also support the new Unified APIs to be included in a 64 bit Xamarin.iOS or Xamarin.Mac project. This article will cover the steps required to update an existing component to use the Unified API, while still supporting the Classic APIs.

We will cover the steps required to setup a new Unified Library Project inside the component solution and the step required to share source code between the Classic API and the Unified API versions of the component. We will also cover the additional changes to the source code (such as using CGRect instead of RectangleF) required by the Unified API.

Next, let's take a quick look at the Unified API and the required source code changes to support it.

The Unified API

The new style API makes it easier than ever to share code between Mac and iOS as well as allowing you to support 32 and 64 bit applications with the same binary.

By dropping the MonoMac and MonoTouch prefixes from the namespaces (and just using the top-level), simpler sharing is achieved across Xamarin.Mac and Xamarin.iOS application projects. In addition, these new Unified APIs introduce functionality that allows the same code to run on both 32 or 64 bit systems depending on that host platform and the configuration options you use. For more information, please see the Unified API documentation.

API Versions

To support this simplified code sharing, as well as 64 bit application development, the Xamarin APIs will be split into two versions:

  • Classic API - Limited to 32-bits, and exposed in the monotouch.dll and XamMac.dll assemblies. Namespaces use MonoTouch or MonoMac prefix.
  • Unified API - Support both 32 and 64 bit development with a single api available in the Xamarin.iOS.dll and Xamarin.Mac.dll assemblies. No namespace prefix which simplifies code sharing between iOS and Mac.

As stated above, starting February 1st, 2015 Apple requires that all new submissions to the iTunes and the Mac App Store must be 64 bit applications. As a result, any new Xamarin.iOS or Xamarin.Mac application will need to be using the new Unified API instead of the existing Classic MonoTouch and MonoMac APIs to support 64 bit.

Enterprise Developers can keep using the Classic API or migrate to the Unified API. Both APIs are supported in parallel and Xamarin will continue to support both.

For more background on supporting 32 and 64 bit applications and information about frameworks see the 32 and 64 bit Platform Considerations documentation.

Native Data Types

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, the new Unified API replaces 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.

Along with the new nint data type, the Unified API introduces the nuint and nfloat types, as well providing data types built on top of them where necessary.

To support the Unified API, we must convert int, uint and float data type to their new nint, nuint and nfloat counterparts when calling Unified API methods. In most cases, these values will be implicitly type cast for us, but there are instances where we need to use nint, nuint and nfloat directly.

To learn more about these data type changes, see the Native Types document.

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.

CoreGraphics Types

The point, size and rectangle data types that are used with CoreGraphics use 32 or 64 bits depending on the device they are running on. When Xamarin originally bound the iOS and Mac APIs we used existing data structures that happened to match the data types in System.Drawing (RectangleF for example).

Because of the requirements to support 64 bits and the new native data types, the following adjustments will need to be made to existing code when calling CoreGraphic methods:

  • CGRect - Use CGRect instead of RectangleF when defining floating point rectangular regions.
  • CGSize - Use CGSize instead of SizeF when defining floating point sizes (width and height).
  • CGPoint - Use CGPoint instead of PointF when defining a floating point location (X and Y coordinates).

With the required source code changes out of the way, let's look at converting an existing Xamarin iOS Component to support the Unified API.

Modified API Calls

There are a few instances where there were typos in the API names when they were bound to the original MonoTouch namespace 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:

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

NOTE: While this article does not explicitly cover creating a new component from scratch, the same solution and project layout can be used, as well as, the same modifications to the source code. For more information on creating and submitting a Xamarin Component, please see the Submitting Xamarin Components documentation.

Requirements

The following is required to complete the steps presented in this article:

  • Xamarin License - A current license of Xamarin.iOS or Xamarin.Mac is required.
  • Xcode 7 and iOS - Apple's Xcode 7 and the latest iOS API need to be installed and configured on the developer's computer.
  • Xamarin Studio or Visual Studio - The latest version of Xamarin Studio or Visual Studio should be installed and configured on the development computer. Xamarin Studio and an Apple Mac is required for developing a Xamarin.Mac application.

Preparing Your Solution

The easiest way to support both the Classic and the Unified API in your component bundle is to add a new Unified API Library project to your Component Solution and share the source code with the existing Classic API project.

Let's do the following:

  1. Open your existing Component Solution in Xamarin Studio or Visual Studio.
  2. In the Solution Explorer, right-click on the solution and select Add > Add New Project...:
  3. From the New Project dialog box, select iOS > App > Unified API > iOS Library Project:
  4. Enter a Name for the new project and click the OK button.

With the new project added to the solution, let's share the C# source code with the existing Classic API version of the component.

Sharing Source Code

To make it easy the develop and maintain both the Classic and the Unified API version of our component, we are going to share the source code between the two versions. Later in this article, we'll show how to use conditional compilation instructions to to support required modification in this shared code.

Let's do the following:

  1. In the Solution Explorer, right-click on the new project we just created and add any folders required to match the structure of our existing Classic API version of the component.
  2. Also in the Solution Explorer, ensure that all of the same References have been made in the new project as the existing one (with the exception of the MonoTouch and MonoMac APIs):
  3. Right-click on a Folder inside of the new project (or the project itself if folders are not being used), and select Add > Add Files...:
  4. From the Add File dialog box, navigate to the same location in the existing Classic API version of the component, select all of the files and click the Open button:
  5. From the Add File to Folder dialog box, select Add a link to the file, Use the same action for all selected files and click the OK button:
  6. Repeat these steps for all folders/source code in the existing version of the component.

With all of the component's source code shared between the Classic and Unified API versions, let's look at modifying the using statements with #if...#endif statements to support both APIs.

Modify the Using Statements

For all of the shared source code in our component, we will need to add #if...#endif conditional compilation statements to make sure that the code is using the correct API for the correct project (Classic or Unified). We also need to handle the differences in the new native data types and the changes to calling CoreGraphics routines by using the following code in our conditional compilation code:

#if __UNIFIED__
...

// Mappings Unified CoreGraphic classes to MonoTouch classes
using RectangleF = global::CoreGraphics.CGRect;
using SizeF = global::CoreGraphics.CGSize;
using PointF = global::CoreGraphics.CGPoint;
#else
...

// Mappings Unified types to MonoTouch types
using nfloat = global::System.Single;
using nint = global::System.Int32;
using nuint = global::System.UInt32;
#endif

Let's do the following:

  1. In Xamarin Studio or Visual Studio, under our Unified Library Project, double-click a shared source code file in the Solution Explorer to open it for editing.
  2. In our using statements at the top of the file, locate any calls to Classic APIs (these are using MonoTouch or MonoMac API prefixes and will usually be marked in red):
  3. Select the Classic API calls, cut them and copy them below the rest of the using statements:
  4. Add the following conditional compilation instructions around the Classic API calls (for any Unified API call, simply strip off the MonoTouch or MonoMac prefixes):
  5. Next, let's add the using statements from above to handle the native data types and the CoreGraphics changes for the Unified API:
  6. Save the changes to the file.
  7. Repeat the above steps for any shared source code file.

With all of these changes in place, we are ready to do the first compile of the Unified API version of the component and test it.

Note: The amount and the specific using statements will depend on the given application and the specific Apple APIs being called in any C# source code file. As a rule, find any instance of using MonoTouch... or using MonoMac... and strip those off the MonoTouch or MonoMac API prefixes for the Unified API version of the component.

Compiling the Unified Component

After we get all of our shared source code modified to use conditional compilation, we are ready to attempt to compile the Unified API version of the component. Aside from the simple modifications that we made when we adjusted the using statements, there will be other cases where the code needs to be modified to compile, and more than likely, our first several compile attempts will fail.

Let's do our first build of the Unified Library Project to find any instances where an implicit type conversions are required (such as an int to a nint) or where the calling structure of a method or delegate has changed.

Keep adjusting the code, adding #if...#endif statements as required to fix any issues and re-compiling until we get a successful compile.

Testing the Classic Version of the Component

At this point we should stop working on the Unified API version of the component and thoroughly re-test the Classic API version to ensure that we have not introduced any compiler errors or breaking changes into the source code.

Do the following:

  1. In the Solution Explorer, right-click on the solution and select Clean my-solution-name:
  2. Select the Classic API test project in the Solution Explorer, right-click on it and select Set As Startup Project:
  3. Build and debug the project.

Again, we should thoroughly test the component on both the iOS Simulator and on real hardware to ensure that we have not broken any of the Classic API behavior. When we are satisfied that the original version of the component is working correctly, we are ready to test the new Unified API version.

Adding a Unified Test Project to our Solution

Now that we have the new Unified API version of the component successfully compiling and we have thoroughly tested the existing Classic API version, we are ready to add an example iOS (or Mac) Unified Test project to our component solution.

Just like adding the Unified API version of the component itself, we will add a new project and match the structure and references of our existing Classic API Test application version. However, instead of sharing the source code between the two versions via file linking (like we did with the component), we'll actually copy over the code to the new project.

We are coping the code instead of linking because a Unified API specific example application is required when we ship our component to Xamarin. Later, we'll use our test app as the example in the component bundle.

Let's do the following:

  1. In the Solution Explorer, right-click on the solution and select Add > Add New Project...:
  2. From the New Project dialog box, select iOS > App and the type of project to create under Unified API(for example iPhone > Single View Application):
  3. Enter a Name for the project and click the OK button.
  4. Double-Click the new project's name in the Solution Explorer to open the Project Options dialog box:
  5. We'll want to match the Default Namespace, iOS Bundle Signing and Info.plist settings of our original Classic API test application.
  6. Click the OK button when finished.
  7. Remove the default MainStoryboard.storyboard files and the view controller class that was added by default.
  8. Like we did for the component itself, let's match any References and add any Folders required to match the structure of the original test project.
  9. Again, select the main project (or a folder), right-click and select Add files...
  10. Select all of the files from the same location in the original application and click the Open button.
  11. From the Add File to Folder dialog box, select Copy the file to the directory, Use the same action for all selected files and click the OK button:
  12. Repeat the above steps to copy over all of the source code, images and other resources for the test application.
  13. Edit all of the C# source code files and remove the MonoTouch or MonoMac API prefixes. Also make any needed changes for type conversions (int to nint) and for CoreGraphics (CGRect for RectangleF):
  14. Save the changes to the files.
  15. In the Solution Explorer, right-click on the project and select Set As Startup Project:

Like we did with the component itself, we will need to compile several times and fix any remaining issues with the code to make it compatible with the Unified APIs. Once we get a successful compile, we are ready to begin testing both the example app and the Unified API copy of the component.

Testing the Unified Version of the Component

Just like we did with the Classic API version of the component and its associated Test application, we need to thoroughly test the new Unified API version of the component and its test application. Tests should be run both on the iOS Simulator and on real hardware.

Every feature should be tested and we should look for any odd or broken behavior caused by calling the new APIs, specifically in any areas where we had to make adjustments beyond the simple using statements.

After we have the component and the test application fully tested, we are ready to bundle our component and submit it to the Xamarin Component Store.

Bundling the Component

Once you have completed and fully tested both versions your component, you are ready to bundle and ship them to Xamarin. For more information on preparing a component and submitting it to the Xamarin Component Store for approval, please see the Component Store Submission Guide document.

Modifying your YAML File

You will need to modify your YAML file to include a new key ios-unified, you'll point this to the release bin directory of the Unified API version of the component and the compiled .DLL. You will also need to include a Unified API example application. For this we can use the Unified Test app that we created above. Again, this is why we copied the code from the Classic API version of the test app instead of using file linking.

Simply, copy the Unified API version of the test app into your samples directory and modify its References to point to the compiled .DLL in the component's bin directory (instead of pointing to the component's project file).

Here is an example of a modified component YAML file with the two new entries added:

Submitting the Component

With all of these changes in place, you submit the component as normal by issuing either of the following commands in the Terminal:

  • Windows - xamarin-component.exe package <foldername>
  • Mac - mono xamarin-component.exe package <foldername>

See the Component Store Submission Guide document for more information on submitting a component.

Summary

In this article, we have covered the steps required to update an existing Xamarin Component that will work with the Unified 64 bit APIs for Xamarin.iOS and Xamarin.Mac. We have shown how to deliver the classic APIs along with the new, Unified APIs inside the same component bundle and how to share code between the two version of the component.

Xamarin Workbook

If it's not already installed, install the Xamarin Workbooks app first. The workbook file should download automatically, but if it doesn't, just click to start the workbook download manually.