Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Xamarin: Cross-Platform Mobile Application Development

You're reading from   Xamarin: Cross-Platform Mobile Application Development Master the skills required to develop cross-platform applications from drawing board to app store(s) using Xamarin

Arrow left icon
Product type Course
Published in Aug 2016
Publisher Packt
ISBN-13 9781787120129
Length 1049 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Authors (3):
Arrow left icon
Jonathan Peppers Jonathan Peppers
Author Profile Icon Jonathan Peppers
Jonathan Peppers
George Taskos George Taskos
Author Profile Icon George Taskos
George Taskos
Can Bilgin Can Bilgin
Author Profile Icon Can Bilgin
Can Bilgin
Arrow right icon
View More author details
Toc

Chapter 7. Deploying and Testing on Devices

Deploying to devices is both important and a bit of a hassle when you try it the first time. Testing on a device will commonly display performance issues that aren't present in the simulator/emulator of your application. You can also test things that are only possible on real devices such as GPS, camera, memory limitations, or cellular network connectivity. There are also common pitfalls that exist when developing for Xamarin, which will only surface when testing on a real device.

In this chapter, we will cover the following topics:

  • iOS provisioning
  • Android device settings for debugging
  • The linker
  • Ahead-of-time (AOT) compilation
  • Common memory pitfalls with Xamarin

Before we begin this chapter, it is important to note that an iOS Developer Program membership is required to deploy to iOS devices. Feel free to go back to Chapter 1, Setting Up Xamarin, to walk through this process.

iOS provisioning

Apple has a strict process for deploying applications to iOS devices. While being quite convoluted and sometimes painful for developers, Apple can enable a certain level of security by preventing the average user from side loading potentially malicious applications.

Prerequisites for deploying to iOS

Before we can deploy our application to an iOS device, there are a few things we will need to set up in the iOS Dev Center. We will begin by creating an App ID or a bundle ID for your account. This is the primary identifier for any iOS application.

We will begin by navigating to http://developer.apple.com and performing the following steps:

  1. Click on the iOS Apps icon.
  2. Sign in with your developer account.
  3. Click on Certificates, Identifiers, & Profiles on the right-hand side navigation.
  4. Click on Identifiers.
  5. Click on the plus button to add a new iOS App ID.
  6. In the Name field, enter something meaningful such as YourCompanyNameWildcard.
  7. Select the Wildcard App ID radio button.
  8. In the Bundle ID field, select a reverse domain styled name for your company such as com.yourcompanyname.*.
  9. Click on Continue.
  10. Review the final setting and hit Submit.

Leave this web page open, as we will be using it throughout the chapter.

We just registered a wildcard bundle ID for your account; use this as a prefix for all future applications you wish to identify with this account. Later, when you are preparing to deploy an app to the Apple App Store, you will create an Explicit App ID such as com.yourcompanyname.yourapp. This allows you to deploy the specific app to the store, while the wildcard ID is best used for deploying to devices for testing.

Next, we need to locate the unique identifier on each device you plan on debugging your application on. Apple requires each device to be registered under your account with a limit of 200 devices per developer. The only way to circumvent this requirement is to register for the iOS Developer Enterprise program with a $299 yearly fee that is separate from the standard $99 developer fee.

We will begin by launching Xcode and performing the following steps:

  1. Navigate to Window | Devices in the top menu.
  2. Plug in your target device with a USB cable.
  3. On the left-hand side navigation, you should see your device's name. Click on it to select it.
  4. Notice the Identifier value for your device. Copy it to your clipboard.

The following screenshot shows what your screen should look like with your device selected in Xcode:

Prerequisites for deploying to iOS

Return to http://developer.apple.com (hopefully, it is still open from earlier in the chapter) and perform the following steps:

  1. Navigate to Devices | All on the left-hand side navigation.
  2. Click on the plus button in the top-right corner of the page.
  3. Enter a meaningful name for your device and paste the Identifier value from your clipboard into the UDID field.
  4. Click on Continue.
  5. Review the information you entered and hit Register.

Down the road, when your account is fully set up, you can just click on the Use for Development button in Xcode and skip the second set of steps.

The following screenshot shows you what your device list should look like when complete:

Prerequisites for deploying to iOS

Next, we will need to generate a certificate to represent you as the developer for your account. Prior to Xcode 5, you had to create a certificate signing request using the Keychain app on your Mac. You can find this under Applications, or using the search spotlight on OS X via Command + Space. The newer versions of Xcode make things a lot easier by integrating a lot of this process into Xcode.

Open Xcode and perform the following steps:

  1. Navigate to Xcode | Preferences in the menu at the top.
  2. Select the Accounts tab.
  3. Click on the plus button, on the bottom-left, and then click on Add Apple ID.
  4. Enter the e-mail and password for your developer account.
  5. On creating the account, click on View Details on the bottom-right.
  6. Click on the sync button on the bottom-left.
  7. If this is a new account, Xcode will display a warning that no certificates exist yet. Check each box and click on Request to generate the certificates.

Xcode will now automatically create a developer certificate for your account and install it into your Mac's keychain.

The following screenshot shows what your screen will look after setting up your account:

Prerequisites for deploying to iOS

Creating a provisioning profile

Next, we need to create a provisioning profile. This is the final file that allows applications to be installed on an iOS device. A provisioning profile contains an App ID, a list of device IDs, and finally, a certificate for the developer. You must also have the private key of the developer certificate in your Mac's keychain to use a provisioning profile.

The following are a few types of provisioning profiles:

  • Development: This is used for debug or release builds. You will actively use this type of profile when your applications are in development.
  • Ad Hoc: This is used mainly for release builds. This type of certificate is great for beta testing or distribution to a small set of users. You can distribute to an unlimited number of users using this method with an enterprise developer account.
  • App Store: This is used for release builds for submission to the App Store. You cannot deploy an app to your device using this certificate; it can only be used for store submission.

Let's return to http://developer.apple.com and create a new provisioning profile by performing the following steps:

  1. Navigate to Provisioning Profiles | All on the left-hand side pane.
  2. Click on the plus button at the top-right of the page.
  3. Select iOS App Development and click on Continue.
  4. Select your wildcard App ID created earlier in the chapter and click on Continue.
  5. Select the certificate we created earlier in the chapter and click on Continue.
  6. Select the devices you want to deploy to and click on Continue.
  7. Enter an appropriate Profile Name such as YourCompanyDev.
  8. Click on Generate and your provisioning profile will be created.

The following screenshot shows the new profile that you will end up with on creation. Don't worry about downloading the file; we'll use Xcode to import the final profile.

Creating a provisioning profile

To import the provisioning profile, return to Xcode and perform the following steps:

  1. Navigate to Xcode | Preferences in the menu at the top of the dialog.
  2. Select the Accounts tab.
  3. Select your account and click on View Details.
  4. Click on the sync button on the bottom-left.
  5. After a few seconds, your provisioning profiles will appear.

Xcode will automatically include any provisioning profiles you have created on the Apple developer site. Xcode will also create a few profiles on its own.

In the latest version of Xamarin Studio, you can view these profiles but will not be able to sync them. Navigate to Xamarin Studio | Preferences | Developer Accounts to view the provisioning profiles from Xamarin Studio. You can also view Xamarin's documentation on iOS provisioning on their documentation website at http://docs.xamarin.com/guides/ios/getting_started/device_provisioning/.

Android device settings

Compared to the hassle of deploying your application on iOS devices, Android is a breeze. To deploy an application to a device, you merely have to set a few settings on the device. This is due to Android's openness in comparison to iOS. Android device debugging is turned off for most users, but it can be easily turned on by any developer who might wish to have a go at writing Android applications.

We will begin by opening the Settings application. You might have to locate this by looking through all the applications on the device as follows:

  1. Scroll down and click on the section labeled Developer options.
  2. In the action bar at the top, you might have to toggle a switch to the ON position. This varies on each device.
  3. Scroll down and check USB Debugging.
  4. A warning confirmation message will appear. Then, click on OK.

Tip

Note that some newer Android devices have made it a little more difficult for the average user to turn on USB debugging. You have to click on the Developer options item seven times to turn this option on.

The following screenshot shows what your device will look like during the process:

Android device settings

After enabling this option, all you have to do is plug in your device via USB and debug an Android application in Xamarin Studio. You will see your device listed in the Select Device dialog. Note that if you are on Windows or have a nonstandard device, you might have to visit your device vendor's website to install drivers. Most Samsung and Nexus devices install their drivers automatically. On Android 4.3 and higher, there is also a confirmation dialog on the device that appears before beginning a USB debugging session.

The following screenshot shows you what your device will look like for a Samsung Galaxy SII in the Select Device dialog. Xamarin Studio will display the model number, which is not always a name that you can recognize. You can view this model number in your device's settings.

Android device settings

Understanding the linker

To keep Xamarin applications small and lightweight for mobile devices, Xamarin has created a feature for their compiler called the linker. Its main purpose is to strip unused code out of the core Mono assemblies (such as System.dll) and platform-specific assemblies (such as Mono.Android.dll and monotouch.dll). However, it can also give you the same benefits if it is set up to run on your own assemblies. Without running the linker, the entire Mono framework can be around 30 megabytes. This is why linking is enabled by default in device builds, which enables you to keep your applications small.

The linker uses static analysis to work through the various code paths in an assembly. If it determines a method or class that is never used, it removes the unused code from that assembly. This can be a time-consuming process, so builds running in the simulator skip this step by default.

Xamarin applications have the following three main settings for the linker:

  • Don't Link: In this setting, the linker compilation step is skipped. This is best used for builds running in the simulator or if you need to diagnose a potential issue with the linker.
  • Link SDK Assemblies Only: In this setting, the linker will only be run on the core Mono assemblies such as System.dll, System.Core.dll, and System.Xml.dll.
  • Link All Assemblies: In this setting, the linker is run against all the assemblies in your application, which include any class libraries or third-party assemblies you are using.

These settings can be found in the Project options of any Xamarin.iOS or Xamarin.Android application. These settings are generally not present on class libraries, as it is generally associated with an iOS or Android application that will be deployed.

The linker can also cause potential issues at runtime as there are cases in which its analysis determines incorrectly that a piece of code is unused. This can happen if you are using features in the System.Reflection namespace instead of accessing the method or property directly. This is one reason why it is important for you to test your application on physical devices, as linking is enabled for device builds.

To demonstrate this issue, let's take a look at the following code example:

//Just a simple class for holding info
public class Person
{
  public int Id { get; set; }
  public string Name { get; set; }
}

//Then somewhere later in your code
var person = new Person { Id = 1, Name = "Chuck Norris" };
var propInfo = person.GetType().GetProperty("Name");
string value = propInfo.GetValue(person) as string;
Console.WriteLine("Name: " + value);

Running the preceding code will work fine using the options for Don't Link or Link SDK Assemblies Only. However, if you try to run this, when using Link All Assemblies, you will get an exception similar to the following:

Unhandled Exception:
System.ArgumentException: Get Method not found for 'Name' at System.Reflection.MonoProperty.GetValue (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] index, System.Globalization.CultureInfo culture) at System.Reflection.PropertyInfo.GetValue (System.Object obj)

Since the Name property's getter was never used directly from code, the linker stripped it from the assembly. This caused the reflection code to fail at runtime.

Even though potential issues can arise in your code, the option of Link All Assemblies is still quite useful. There are a few optimizations that can only be performed in this mode, and Xamarin can reduce your application to the smallest possible size. If performance or a tiny download size is the requirement for your application, you can try this option. However, thorough testing should be performed to verify that no problems are caused by linking your assemblies.

To resolve issues in your code, Xamarin has included a complete set of workarounds to prevent specific parts of your code from being stripped away.

Some of the options include the following:

  • Mark class members with [Preserve]. This will force the linker to include the attributed method, field, or property.
  • Mark an entire class with [Preserve(AllMembers=true)]. This will preserve all code within the class.
  • Mark an entire assembly with [assembly: Preserve]. This is an assembly-level attribute that will preserve all code contained within it.
  • Skip an entire assembly by modifying Additional mtouch arguments in your project options. Use --linkskip=System to skip an entire assembly. This can be used on assemblies that you do not have the source code for.
  • Custom linking via an XML file. This is the best option to use when you need to skip linking on a specific class or method that you do not have the source code for. Use –-xml=YourFile.xml in Additional mtouch arguments.

The following is a sample XML file that demonstrates custom linking:

<linker>
  <assembly fullname="mscorlib">
    <type fullname="System.Environment">
      <field name="mono_corlib_version" />
        <method name="get_StackTrace" /> 
    </type>
  </assembly>
  <assembly fullname="My.Assembly.Name">
    <type fullname="MyTypeA" preserve="fields" />
       <method name=".ctor" />
    </type>
    <type fullname="MyTypeB" />                         
      <method signature="System.Void MyFunc(System.Int32 x)" />
        <field signature="System.String _myField" />
    </type>
  </assembly>
</linker>

Custom linking is the most complicated option and is usually the last resort. Luckily, most Xamarin applications will not have to work around many linker issues.

Understanding AOT compilation

The runtime behind Mono and .NET on Windows is based on a just in time (JIT) compiler. C# and other .NET languages are compiled into Microsoft Intermediate Language (MSIL). At runtime, MSIL is compiled into a native code to run on whatever type of architecture is running your application. Xamarin.Android follows this exact pattern. However, due to Apple's restrictions on dynamically generated code, a JIT compiler is not allowed on iOS.

To work around this restriction, Xamarin has developed a new option called Ahead-of-time (AOT) compilation. In addition to making .NET possible on iOS, AOT has other benefits such as a shorter startup time and potentially better performance.

AOT also has some limitations that are generally related to C# generics. To compile an assembly ahead of time, the compiler will need to run some static analysis against your code to determine the type of information. Generics throw a wrench into this situation.

There are a few cases that are not supported by AOT, but they are completely valid in C#. The first is a generic interface, which is as follows:

interface MyInterface<T> 
{
  T GetMyValue();
}

The compiler cannot determine the classes that can implement this interface ahead of time, especially when multiple assemblies are involved. The second limitation is related to the first. You cannot override virtual methods that contain generic parameters or return values.

The following is a simple example:

class MyClass<T>
{
  public virtual T GetMyValue() 
  {
    //Some code here
  }
}

class MySubClass : MyClass<int>
{
  public override int GetMyValue()
  {
    //Some code here
  }
}

Again, the static analysis of the compiler cannot determine which classes can override this method at compile time.

Another limitation is that you cannot use DllImport in a generic class, as shown in the following code:

class MyGeneric<T>
{
  [DllImport('MyImport")]
  public static void MyImport();
}

If you are not familiar with the language feature, DllImport is a way to call native C/C++ methods from C#. Using them inside generic classes is not supported.

These limitations are another good reason why testing on devices is important since the preceding code will work fine on other platforms that can run C# code but not Xamarin.iOS.

Avoiding common memory pitfalls

Memory on mobile devices is certainly not an unlimited commodity. Because of this, memory usage in your application can be much more important than on desktop applications. At times, you might find the need to use a memory profiler, or improve your code to use memory more efficiently.

The following are the most common memory pitfalls:

  • The garbage collector (GC) is unable to collect large objects fast enough to keep up with your application
  • Your code inadvertently causes a memory leak
  • A C# object is garbage collected but is later attempted to be used by native code

Garbage collector

Let's take a look at the first problem where the GC cannot keep up. Let's say we have a Xamarin.iOS application with a button for sharing an image on Twitter as follows:

twitterShare.TouchUpInside += (sender, e) =>
{
  var image = UImage.FromFile("YourLargeImage.png");
  //Share to Twitter
};

Now let's assume the image is a 10 MB image from the user's camera roll. If the user clicks on the button and cancels the Twitter post rapidly, there could be a possibility of your application running out of memory. iOS will commonly force close apps using too much memory, and you don't want users to experience this with your app.

The best solution is to call Dispose on the image when you are finished with it as follows:

var image = UImage.FromFile('YourLargeImage.png");
//Share to Twitter
image.Dispose();

An even better approach would be to take advantage of the C# using statement as follows:

using(var image = UImage.FromFile('YourLargeImage.png"))
{
  //Share to Twitter
}

The C# using statement will automatically call Dispose in a try-finally block, so the object will get disposed even if an exception is thrown. I recommend that you take advantage of the using statement for any IDisposable class where possible. It is not always necessary for small objects such as NSString, but is always a good idea for larger, more heavyweight UIKit objects.

Tip

A similar situation can occur on Android with the Bitmap class. Although slightly different, it is best to call both the Dispose and Recycle methods on this class along with using the BitmapFactory.Options settings for InPurgeable and InInputShareable.

Memory leaks

Memory leaks are the next potential issues. C# being a managed, garbage-collected language prevents a lot of memory leaks, but not all of them. The most common leaks in C# are caused by events.

Let's assume that we have a static class with an event as follows:

static class MyStatic
{
  public static event EventHandler MyEvent;
}

Now, let's say we need to subscribe to the event from an iOS controller as follows:

public override void ViewDidLoad()
{
  base.ViewDidLoad();

  MyStatic.MyEvent += (sender, e) =>
  {
    //Do something
  };
}

The problem here is that the static class will hold a reference to the controller until the event is unsubscribed. This is a situation that a lot of developers might miss. To fix this issue on iOS, I would subscribe to the event in ViewWillAppear and unsubscribe ViewWillDisappear. On Android, use OnStart and OnStop, or OnPause and OnResume.

You would correctly implement this event as follows:

public override void ViewWillAppear()
{
  base.ViewWillAppear();
  MyStatic.MyEvent += OnMyEvent;
}

public override void ViewWillDisappear()
{
  base.ViewWillDisappear ();
  MyStatic.MyEvent -= OnMyEvent;
}

However, an event is not a surefire cause of a memory leak. Subscribing to the TouchUpInside event on a button inside the ViewDidLoad method, for example, is just fine. Since the button lives in memory just as long as the controller does, everything can get garbage collected without any problems.

Accessing objects disposed by GC

For the final issue, the garbage collector can sometimes remove a C# object. Later, an Objective-C object attempts to access it.

The following is an example to add a button to UITableViewCell:

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
  var cell = tableView.DequeueReusableCell('MyCell");
  //Remaining cell setup here

  var button = UIButton.FromType(UIButtonType.InfoDark);
  button.TouchUpInside += (sender, e) =>
  {
    //Do something
  };
  cell.AccessoryView = button;
  return cell;
}

We add the built-in info button as an accessory view to the cell. The problem here is that the button will get garbage collected, but its Objective-C counterpart will remain in use as it is displayed on the screen. If you click on the button after some period of time, you will get a crash that looks something similar to the following:

mono-rt: Stacktrace:
mono-rt:   at <unknown>
mono-rt:   at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) 
mono-rt:   at MonoTouch.UIKit.UIApplication.Main (string[],string,string)  
... Continued ...
=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries 
used by your application.
================================================================

It is not the most descriptive error message, but in general, you know that something went wrong in the native Objective-C code. To resolve the issue, create a custom subclass of UITableViewCell, and create a dedicated member variable for the button as follows:

public class MyCell : UITableViewCell
{
  UIButton button;
  public MyCell()
  {
    button = UIButton.FromType(UIButtonType.InfoDark);
    button.TouchUpInside += (sender, e) => 
    {
      //Do something
    };
    AccessoryView = button;
  }
}

Now, your GetCell implementation will look something like the following code:

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
  var cell = tableView.DequeueReusableCell('MyCell") as MyCell;
  //Remaining cell setup here
  return cell;
}

Since the button is not a local variable, it will no longer get garbage collected sooner than needed. A crash is avoided, and in some ways this code is a bit cleaner. Similar situations can happen on Android with the interaction between C# and Java; however, it is less likely since both are garbage collected languages.

Garbage collector

Let's take a look at the first problem where the GC cannot keep up. Let's say we have a Xamarin.iOS application with a button for sharing an image on Twitter as follows:

twitterShare.TouchUpInside += (sender, e) =>
{
  var image = UImage.FromFile("YourLargeImage.png");
  //Share to Twitter
};

Now let's assume the image is a 10 MB image from the user's camera roll. If the user clicks on the button and cancels the Twitter post rapidly, there could be a possibility of your application running out of memory. iOS will commonly force close apps using too much memory, and you don't want users to experience this with your app.

The best solution is to call Dispose on the image when you are finished with it as follows:

var image = UImage.FromFile('YourLargeImage.png");
//Share to Twitter
image.Dispose();

An even better approach would be to take advantage of the C# using statement as follows:

using(var image = UImage.FromFile('YourLargeImage.png"))
{
  //Share to Twitter
}

The C# using statement will automatically call Dispose in a try-finally block, so the object will get disposed even if an exception is thrown. I recommend that you take advantage of the using statement for any IDisposable class where possible. It is not always necessary for small objects such as NSString, but is always a good idea for larger, more heavyweight UIKit objects.

Tip

A similar situation can occur on Android with the Bitmap class. Although slightly different, it is best to call both the Dispose and Recycle methods on this class along with using the BitmapFactory.Options settings for InPurgeable and InInputShareable.

Memory leaks

Memory leaks are the next potential issues. C# being a managed, garbage-collected language prevents a lot of memory leaks, but not all of them. The most common leaks in C# are caused by events.

Let's assume that we have a static class with an event as follows:

static class MyStatic
{
  public static event EventHandler MyEvent;
}

Now, let's say we need to subscribe to the event from an iOS controller as follows:

public override void ViewDidLoad()
{
  base.ViewDidLoad();

  MyStatic.MyEvent += (sender, e) =>
  {
    //Do something
  };
}

The problem here is that the static class will hold a reference to the controller until the event is unsubscribed. This is a situation that a lot of developers might miss. To fix this issue on iOS, I would subscribe to the event in ViewWillAppear and unsubscribe ViewWillDisappear. On Android, use OnStart and OnStop, or OnPause and OnResume.

You would correctly implement this event as follows:

public override void ViewWillAppear()
{
  base.ViewWillAppear();
  MyStatic.MyEvent += OnMyEvent;
}

public override void ViewWillDisappear()
{
  base.ViewWillDisappear ();
  MyStatic.MyEvent -= OnMyEvent;
}

However, an event is not a surefire cause of a memory leak. Subscribing to the TouchUpInside event on a button inside the ViewDidLoad method, for example, is just fine. Since the button lives in memory just as long as the controller does, everything can get garbage collected without any problems.

Accessing objects disposed by GC

For the final issue, the garbage collector can sometimes remove a C# object. Later, an Objective-C object attempts to access it.

The following is an example to add a button to UITableViewCell:

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
  var cell = tableView.DequeueReusableCell('MyCell");
  //Remaining cell setup here

  var button = UIButton.FromType(UIButtonType.InfoDark);
  button.TouchUpInside += (sender, e) =>
  {
    //Do something
  };
  cell.AccessoryView = button;
  return cell;
}

We add the built-in info button as an accessory view to the cell. The problem here is that the button will get garbage collected, but its Objective-C counterpart will remain in use as it is displayed on the screen. If you click on the button after some period of time, you will get a crash that looks something similar to the following:

mono-rt: Stacktrace:
mono-rt:   at <unknown>
mono-rt:   at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) 
mono-rt:   at MonoTouch.UIKit.UIApplication.Main (string[],string,string)  
... Continued ...
=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries 
used by your application.
================================================================

It is not the most descriptive error message, but in general, you know that something went wrong in the native Objective-C code. To resolve the issue, create a custom subclass of UITableViewCell, and create a dedicated member variable for the button as follows:

public class MyCell : UITableViewCell
{
  UIButton button;
  public MyCell()
  {
    button = UIButton.FromType(UIButtonType.InfoDark);
    button.TouchUpInside += (sender, e) => 
    {
      //Do something
    };
    AccessoryView = button;
  }
}

Now, your GetCell implementation will look something like the following code:

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
  var cell = tableView.DequeueReusableCell('MyCell") as MyCell;
  //Remaining cell setup here
  return cell;
}

Since the button is not a local variable, it will no longer get garbage collected sooner than needed. A crash is avoided, and in some ways this code is a bit cleaner. Similar situations can happen on Android with the interaction between C# and Java; however, it is less likely since both are garbage collected languages.

Memory leaks

Memory leaks are the next potential issues. C# being a managed, garbage-collected language prevents a lot of memory leaks, but not all of them. The most common leaks in C# are caused by events.

Let's assume that we have a static class with an event as follows:

static class MyStatic
{
  public static event EventHandler MyEvent;
}

Now, let's say we need to subscribe to the event from an iOS controller as follows:

public override void ViewDidLoad()
{
  base.ViewDidLoad();

  MyStatic.MyEvent += (sender, e) =>
  {
    //Do something
  };
}

The problem here is that the static class will hold a reference to the controller until the event is unsubscribed. This is a situation that a lot of developers might miss. To fix this issue on iOS, I would subscribe to the event in ViewWillAppear and unsubscribe ViewWillDisappear. On Android, use OnStart and OnStop, or OnPause and OnResume.

You would correctly implement this event as follows:

public override void ViewWillAppear()
{
  base.ViewWillAppear();
  MyStatic.MyEvent += OnMyEvent;
}

public override void ViewWillDisappear()
{
  base.ViewWillDisappear ();
  MyStatic.MyEvent -= OnMyEvent;
}

However, an event is not a surefire cause of a memory leak. Subscribing to the TouchUpInside event on a button inside the ViewDidLoad method, for example, is just fine. Since the button lives in memory just as long as the controller does, everything can get garbage collected without any problems.

Accessing objects disposed by GC

For the final issue, the garbage collector can sometimes remove a C# object. Later, an Objective-C object attempts to access it.

The following is an example to add a button to UITableViewCell:

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
  var cell = tableView.DequeueReusableCell('MyCell");
  //Remaining cell setup here

  var button = UIButton.FromType(UIButtonType.InfoDark);
  button.TouchUpInside += (sender, e) =>
  {
    //Do something
  };
  cell.AccessoryView = button;
  return cell;
}

We add the built-in info button as an accessory view to the cell. The problem here is that the button will get garbage collected, but its Objective-C counterpart will remain in use as it is displayed on the screen. If you click on the button after some period of time, you will get a crash that looks something similar to the following:

mono-rt: Stacktrace:
mono-rt:   at <unknown>
mono-rt:   at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) 
mono-rt:   at MonoTouch.UIKit.UIApplication.Main (string[],string,string)  
... Continued ...
=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries 
used by your application.
================================================================

It is not the most descriptive error message, but in general, you know that something went wrong in the native Objective-C code. To resolve the issue, create a custom subclass of UITableViewCell, and create a dedicated member variable for the button as follows:

public class MyCell : UITableViewCell
{
  UIButton button;
  public MyCell()
  {
    button = UIButton.FromType(UIButtonType.InfoDark);
    button.TouchUpInside += (sender, e) => 
    {
      //Do something
    };
    AccessoryView = button;
  }
}

Now, your GetCell implementation will look something like the following code:

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
  var cell = tableView.DequeueReusableCell('MyCell") as MyCell;
  //Remaining cell setup here
  return cell;
}

Since the button is not a local variable, it will no longer get garbage collected sooner than needed. A crash is avoided, and in some ways this code is a bit cleaner. Similar situations can happen on Android with the interaction between C# and Java; however, it is less likely since both are garbage collected languages.

Accessing objects disposed by GC

For the final issue, the garbage collector can sometimes remove a C# object. Later, an Objective-C object attempts to access it.

The following is an example to add a button to UITableViewCell:

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
  var cell = tableView.DequeueReusableCell('MyCell");
  //Remaining cell setup here

  var button = UIButton.FromType(UIButtonType.InfoDark);
  button.TouchUpInside += (sender, e) =>
  {
    //Do something
  };
  cell.AccessoryView = button;
  return cell;
}

We add the built-in info button as an accessory view to the cell. The problem here is that the button will get garbage collected, but its Objective-C counterpart will remain in use as it is displayed on the screen. If you click on the button after some period of time, you will get a crash that looks something similar to the following:

mono-rt: Stacktrace:
mono-rt:   at <unknown>
mono-rt:   at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) 
mono-rt:   at MonoTouch.UIKit.UIApplication.Main (string[],string,string)  
... Continued ...
=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries 
used by your application.
================================================================

It is not the most descriptive error message, but in general, you know that something went wrong in the native Objective-C code. To resolve the issue, create a custom subclass of UITableViewCell, and create a dedicated member variable for the button as follows:

public class MyCell : UITableViewCell
{
  UIButton button;
  public MyCell()
  {
    button = UIButton.FromType(UIButtonType.InfoDark);
    button.TouchUpInside += (sender, e) => 
    {
      //Do something
    };
    AccessoryView = button;
  }
}

Now, your GetCell implementation will look something like the following code:

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
  var cell = tableView.DequeueReusableCell('MyCell") as MyCell;
  //Remaining cell setup here
  return cell;
}

Since the button is not a local variable, it will no longer get garbage collected sooner than needed. A crash is avoided, and in some ways this code is a bit cleaner. Similar situations can happen on Android with the interaction between C# and Java; however, it is less likely since both are garbage collected languages.

Summary

In this chapter, we started out learning the process of setting up iOS provision profiles to deploy to iOS devices. Next, we looked at the required device settings for deploying your application to an Android device. We discovered the Xamarin linker and how it can make your applications smaller and more performance-oriented. We went through the various settings to resolve problems caused by your code and the linker, and we explained AOT compilation on iOS and the limitations that occur. Finally, we covered the most common memory pitfalls that can occur with Xamarin applications.

Testing your Xamarin application on mobile devices is important for various reasons. Some bugs are only displayed on the device due to the platform limitations that Xamarin has to work around. Your PC is much more powerful, so you will see different performances using the simulator rather than on a physical device. In the next chapter, we'll create a real web service using Windows Azure to drive our XamChat application. We will use a feature called Azure Mobile Services and implement push notifications on iOS and Android.

You have been reading a chapter from
Xamarin: Cross-Platform Mobile Application Development
Published in: Aug 2016
Publisher: Packt
ISBN-13: 9781787120129
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime