Cross-platform development for mobile with Xamarin

    Introduction


    More recently, Xamarin announced the release of a new version of its tools for cross-platform development of mobile applications, but there are no sane articles in Russian on this topic. A small review appeared on Habré , not related to coding, there were a couple of attempts to talk about this a little more, but things did not go beyond the process of creating a Hello World application. It's a pity. This time we will try to correct this annoying misunderstanding.

    Beginning of work


    The Xamarin installer installs a plug-in for Visual Studio, which allows you to develop applications for popular mobile platforms in a familiar developer environment. A separate Xamarin Studio development environment is also installed, which appears to be a modified version of MonoDevelop. I’m more used to working in Visual Studio, so the examples in this article will be shown using this particular development environment.

    After installation, Visual Studio adds project templates for mobile applications for Android and iOS (it supports the creation of both specialized applications for the iPad and iPhone, as well as universal applications, as well as applications using OpenGL). True, you will have to pay to create iOS applications in Visual Studio. This functionality is available either in trial mode or in the business version of the toolkit, and it costs $ 999 per year.

    After creating the project, we get the same API for each platform that we have with native development, but the syntax will be in C #, in addition, it is possible to use the basic types of the .NET Framework, syntactic sugar and other .NET buns.

    Android development


    After creating the Android project, we get a set of files in which there is a class of the main window and a set of resource files. After long work in Eclipse, the name of the folders in PascalCase is a little annoying, but you can get used to it pretty quickly. There are also differences in working with resource files. In order for the embedded window designer to understand resource files with layouts, their extension is changed to .AXML instead of the usual .XML in Eclipse. This is quite annoying, especially if you draw layouts in Eclipse, and then transfer them to Visual Studio if Eclipse's window designer is more like it.

    The built-in window designer personally seemed uncomfortable to me. Slow, it often brings down the entire IDE, I still don’t understand how to easily switch between the XML view and the UI in it. Here you can certainly say what is written by strangers for predators. I decided for myself that in Eclipse it is more convenient, more familiar to me, and more useful information is placed on the laptop screen in Eclipse. Maybe someone the designer of Visual Studio will like it more, the felt-tip pens are different in taste and color.

    Activities

    The C # code for Mono for Android is very similar to the Java code.
    namespace XamarinDemo.Android
    {
        [Activity(Label = "XamarinDemo.Android", MainLauncher = true, Icon = "@drawable/icon")]
        public class MainActivity : Activity
        {
            int count = 1;
            protected override void OnCreate(Bundle bundle)
            {
                base.OnCreate(bundle);
                SetContentView(Resource.Layout.Main);
                Button button = FindViewById

    Differences that are immediately visible:
    • To register an activity, you do not need to write anything in the manifest. Annotation [Activity] is used for this.
    • In order to make an activity start, you need to specify the MainLauncher parameter for the [Activity] annotation. This will be equivalent to setting action = android.intent.action.MAIN and category = android.intent.category.LAUNCHER in the manifest
    • The main methods of the activity life cycle (and indeed all methods) are named in PascalCase.


    Controls and Event Handling

    To obtain links to controls, as in the Java version, a method is used FindViewById(). I was very pleased with the generic version of this method, which returns an object of the desired type and allows you to get rid of c-casts.
    Instead of listeners, or rather, in addition to them, delegates are used to connect event handlers. You can use listeners, but this is, firstly, not the .NET way, and secondly, it requires writing more code, even compared to Java. In addition, several delegates can be connected:
    Button button = FindViewById

    Not all delegate event handlers are rosy. In one of the projects, there was a problem with an event View.ViewTreeObserver.GlobalLayoutin which, when using the - = operator, delegate handlers were not disabled. Yes, they didn’t turn off at all. I had to use IOnGlobalLayoutListener.

    Using Java libraries in a .NET project

    Mono for Android has the ability to use existing JAR files in a C # application. For these purposes, a special type of project is provided: Java Binding Library.
    In order to use the JAR library you must:
    • Create a Java Binding Library project in the solution.
    • Put the desired JAR file in the Jars folder (there should not be AndroidManifest.xml in the JAR library, because the presence of this file can lead to a number of unpleasant consequences. You can learn more about this here ).
    • Specify Build Action for the JAR file as EmbeddedJar.
    • In the Android application project, add to the References the project created by the Java Binding Library.

    After that, you can use the classes from the JAR file. Keep in mind that package names in C # and in the Java source code may vary slightly. So, for example, the com.example.androiddemolib package from Java code will be renamed to Com.Example.Androiddemolib (that is, it will be converted to PascalCase).
    Good instructions on using Java libraries can be found here .

    Work with databases

    For working with databases, Mono for Android uses namespace classes Mono.Data.Sqlite(used to access SQLite databases) and System.Data.SqlClient(for access to Microsoft SQL Server). Namespace classes System.Data.SqlClientare available only in the Business Edition of development tools. You can also use wrapper classes over the native Java API for Android and third-party developments, such as sqlite-net, in which the asynchronous version of the API is available.
    Using an accessible API is pretty simple. Everything is very similar to the development for desktop PCs:
    namespace XamarinDemo.Android
    {
        [Activity(Label = "XamarinDemo.Android", 
            MainLauncher = true, Icon = "@drawable/icon")]
        public class MainActivity : Activity
        {
            SqliteConnection GetConnection(string path)
            {
                SqliteConnectionStringBuilder builder = 
                    new SqliteConnectionStringBuilder();
                if (!File.Exists(path))
                {
                    FileInfo info = new FileInfo(path);
                    if (!Directory.Exists(info.Directory.FullName))
                    {
                        Directory.CreateDirectory(info.Directory.FullName);
                    }
                    SqliteConnection.CreateFile(path);
                }
                builder.DataSource = path;
                return new SqliteConnection(builder.ToString());
            }
            protected override void OnCreate(Bundle bundle)
            {
                base.OnCreate(bundle);
                SetContentView(Resource.Layout.Main);
                try
                {
                    string path = GetDatabasePath("xamarindemo.sqlite").Path;
                    SqliteConnection connection = GetConnection(path);
                    connection.Open();
                    SqliteCommand command = connection.CreateCommand();
                    command.CommandType = CommandType.Text;
                    command.CommandText = "CREATE TABLE IF NOT EXISTS DemoTable(" +
                        "id INTEGER AUTO_INCREMENT PRIMARY KEY NOT NULL" +
                        ", name VARCHAR(32))";
                    command.ExecuteNonQuery();
                    connection.Close();
                }
                catch (Exception e)
                {
                    System.Diagnostics.Debug.WriteLine(e.ToString());
                }
            }
        }
    }
    


    IOS development



    Controls and Event Handlers

    Creating controls in code is no different from doing the same in Objective-C. Event handlers can be hung in the same way as in Android - with the help of delegates. You can also add handlers as in Objective-C via selectors.
    namespace XamarinDemo.iOS
    {
        public class MyViewController : UIViewController
        {
            UIButton button;
            …
            public MyViewController()
            {
            }
            public override void ViewDidLoad()
            {
                base.ViewDidLoad();
                …
                button = UIButton.FromType(UIButtonType.RoundedRect);
                …
                button.AddTarget(this, 
                	new Selector("ButtonTouchInside"), 
                	UIControlEvent.TouchUpInside);
                …
                button.TouchUpInside += (object sender, EventArgs e) =>
                {
                    button.SetTitle(String.Format(
                		"clicked {0} times", numClicks++), UIControlState.Normal);
                };
                …
                View.AddSubview(button);
            }
            [Export("ButtonTouchInside")]
            void OnButtonTouchInside()
            {
                Console.WriteLine("Hello!");
            }
        }
    }
    


    Using native libraries

    In Xamarin iOS, as well as for Android, the ability to use native libraries is available. For these purposes, there is a special type of project - iOS Binding Project, into which you can add static libraries, after which they will be linked together with the main project.

    In general, to use the native library in a C # project, you need to do the following:
    • Create a static library project in Xcode.
    • If you plan to debug a C # project in a simulator, then in the static library project settings you need to add the i386 architecture to the Valid Architectures list.
    • Write the desired logic (let's say we have one exported class):
      @interface iOSDemoClass : NSObject
      - (NSInteger) addData: (NSInteger) value1 andOtherValue: (NSInteger) value2;
      @end
      …
      @implementation iOSDemoClass
      -(NSInteger) addData: (NSInteger) value1 andOtherValue: (NSInteger) value2
      {
          return value1 + value2;
      }
      @end
      

    • Close a project in Xcode
    • Separate the static library for the required architectures (for example, this way):
      xcodebuild -project iOSDemoLib.xcodeproj \
      -target iOSDemoLib -sdk iphonesimulator \
      -configuration Release clean build
      xcodebuild -project iOSDemoLib.xcodeproj \
      -target iOSDemoLib -sdk iphoneos -arch armv7 \
      -configuration Release clean build
      

    • Make a universal library for several platforms from the libraries obtained in the previous step:
      lipo -create -output ../iOSDemoLib.Binding/libiOSDemoLib.a ../libiOSDemoLib-i386.a ../libiOSDemoLib-iphone-armv7.a
      

    • Create an iOS Binding Project in a C # solution and add a universal static library there. After that, the [libname] .linkwith.cs file will be automatically created with link rules that look something like this:
      [assembly: LinkWith ("libiOSDemoLib.a", LinkTarget.Simulator | LinkTarget.ArmV7, ForceLoad = true, Frameworks = "Foundation")]
      

    • In the ApiDefinition.cs file (created automatically), write the rules for binding native classes to .NET types (I draw attention to the fact that complex method names must contain colons in places where there should be method parameters in Objective-C. That is, in a method with two the parameters will be two colons):
      namespace iOSDemoLib.Binding
      {
          [BaseType (typeof(NSObject))]
          interface iOSDemoClass 
          {
              [Export("addData:andOtherValue:")]
              int AddDataAndOtherValue(int value1, int value2);
          }
      }
      

    • In the application project in which you plan to use the library, add the iOS Binding project to References.
    • Collect, run, be happy.

    In general, it’s very good and accessible about linking native libraries here .

    Work with databases

    For working with databases, iOS uses the same namespaces as Android. The API, respectively, is the same. A slight difference may be in the details. For example, to get the path to the SQLite database file in Android, there is a special API call:
    string path = GetDatabasePath("xamarindemo.sqlite").Path;
    

    In iOS, you need to use standard .NET tools:
    string path = Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.Personal), 
           "xamarindemo.sqlite");
    


    Remote debugging iOS applications from Visual Studio

    Xamarin 2.0 has the ability to debug iOS mobile apps directly from Visual Studio. In order to make this possible, you must have a Mac with Xcode, iOS SDK and Xamarin Studio installed on your local network. No additional settings need to be made; it is enough to select the desired build server from the list of available ones when opening an iOS project in Visual Studio.

    Unfortunately, this approach did not work with a virtual machine running on the same computer as Visual Studio. Although everything works fine with a regular Mac on a local network. Causes or explanations for this have not yet been found, I'm trying to communicate with the developers about this.

    It is also not very clear how to organize UI testing and health checks of applications from Visual Studio. The simulator runs on a Mac, it seems that VNC is indispensable.

    Cross-Class Class Libraries


    Xamarin provides the ability to create libraries that can be used (in the form of code or ready-made assemblies) for several platforms at once. Such libraries are called Portable Class Library (PCL).

    To develop such libraries, a special truncated version of .NET Runtime is used and this is a wonderful tool for the developer, the significance of which is difficult to overestimate, but here, too, is not so simple. By default, in Visual Studio, you cannot specify Android and iOS as supported platforms for a PCL project. But this does not mean that Visual Studio immediately becomes useless in this regard.

    The problem can be solved by creating XML files in a folder C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.0\Profile\Profile104\SupportedFrameworksfor x64 systems or in a folder C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.0\Profile\Profile104\SupportedFrameworksfor x86 systems.
    Android file MonoAndroid, Version = v1.6 + .xml

    iOS file VSMonoTouch, Version = v1.0 + .xml

    After creating these two files and restarting Visual Studio, you can specify Android and iOS as supported platforms for new PCL projects or add these platforms to existing projects through Properties -> Library -> Target Framework.
    But this is not all the nuances.

    If you want support for Android and iOS in a PCL project, then you have to abandon support for Xbox 360 and Windows Phone 7 (but you can support Windows Phone 7.5+). For Xbox, you will have to create another project and add files from the existing PCL project in the form of links. Or, as an option, leave all platforms from Microsoft (including the Xbox 360) in one PCL project, and leave only iOS and Android in another.

    There is still such a problem that a PCL project added from Visual Studio under Windows will not participate in the solution assembly if it is opened in Xamarin Studio under OS X. The project will be inactive and a message (not built in active configuration) will be displayed . This problem is solved by removing the project from the solution and adding it again.

    New language features and C # 5 support


    In the development for Android, it is relevant to transfer work with files, databases, a network, etc., that is, with lengthy operations, to a separate stream. In C # 5, special features are provided for implementing asynchronous operations, namely async / await . Unfortunately, the current version of Mono for Android and MonoTouch do not have these features. Because of this, many quite interesting libraries cannot be used in the form that is provided for them. For example, to work with the asynchronous API of the sqlite-net library, you have to do a few feints with your ears. The good news is that these features should be available in a few months with the transition of Xamarin to C # 5.

    Additional components and libraries


    Xamarin, in addition to selling software development tools, opened a store selling third-party components, many of which really make life easier for developers. The store offers both paid and free libraries and themes. The usefulness of, for example, the free Xamarin.Auth , Xamarin.Social and Xamarin.Mobile is hard to overestimate. It is possible to publish your own components in this store.

    Unpleasant moments


    Of the problem points, the most noticeable are:
    • Terrible brakes when debugging Android applications, when on a fairly powerful machine with Core i7 and 8 GB of RAM, when you stop at the breakpoint, Visual Studio freezes for 10-15 seconds. This can be repeated at every step of the debugger.
    • The absence in the list of devices available for debugging applications, some devices connected to the computer (which, however, are visible through adb devices and on which Eclipse runs applications perfectly)
    • Arbitrary debugging of the debugger and the need to restart the application in this regard.
    • Lack of articles and training materials (yes, there are some materials on the official website, but they do not cover all popular tasks)
    • The presence of bugs, which you learn SUDDENLY! after talking on the forum. Although at the same official forum, developers quite vigorously answer questions and well done in general.
    • The price of the Business edition is very biting. In addition, a license for a year, and not forever (the situation is slightly a bit discounted when buying a license for several platforms at once).


    Pleasant moments


    • When purchasing a license for several platforms at once or for several workplaces at once, a discount is provided.
    • With the release of the new version, which will support async \ await, development will really become easier and it will be possible to use a bunch of useful libraries without changing their code.


    conclusions


    The Xamarin toolkit yes, it really works, and if you plan to develop several applications a year or if the planned profit from the developed application is more than $ 2k, then the Xamarin SDK can clearly make your life easier and save you from double work on code for each platform.

    On the other hand, for an Indy developer, the price of $ 1k for each platform, for example, seems excessive to me because there are many nuances that you need to know and / or feel for yourself before starting development, there are bugs that are not known in which mode will be fixed and it is not known how much their presence can slow down the development of your project specifically.

    Some cool links!




    UPD: Clarification from northicewind regarding license cost:
    The license is unlimited, but includes one year of free updates. I wrote to them in support for clarifications. Here is their answer
    Your license is perpetual. You have to purchase a renewal every year to get new updates.

    Also popular now: