Lessons Learned at Q5id
I spent a little less than a year at Q5id. It was the shortest amount of time that I’ve spent with an employer. I specifically picked Q5id because the CEO already had accomplished two successful startup exits, Unicru, which was acquired by Kronos Inc. in 2006, and SureID, which was acquired by Sterling Talent in 2017. Despite this precaution, I ultimately found working for a startup full-time to be beyond my appetite for risk and moved back to employment with a larger, more established company.
In addition to learning more about myself, I picked up some new technical knowledge while at Q5id, particularly around Xamarin. Xamarin is a cross-platform mobile application framework based on the C# and .xaml languages. In Xamarin all project styles are declared in the FormsApp.xaml. Unlike Android, styles can declared and set on a control basis by using the control’s xaml “Style={StaticResource yourFormsAppStyleHere}”.
At Q5id we used the MvvmCross NuGet to manage our architecture. NuGet is the package manager framework for .NET, similar to cocoapods in iOS or Gradle in Android. MvvmCross is a cross-platform MVVM framework for Xamarin Forms & Mono. It has both a MvxApplication and a MvxFormsApplication that you can extend. The former is used to register singletons with the MvvmCross IoCProvider to allow custom start functions for preloading network data when on the splash screen and to handle top level exceptions. The latter is used to handle application lifecycle event callbacks, such as OnStart(), OnSleep(), OnResume(), and OnAppLinkRequestReceived(). You can access the Forms Application programmatically in the Droid or iOS projects by invoking `Xamarin.Forms.Application.Current`. Note that even though MvvmCross has a function called `ConstructAndRegisterSingleton`, the objects instantiated as a result are not singletons. This can be confirmed by logging the result of `GetHashCode()` on any function call within the singleton and observing it can change throughout the lifespan of the app.
You can bind Android and iOS 3rd-party dependencies into your Xamarin project. Doing so causes Visual Studio to automatically wrap the apis in C# interfaces. Occasionally you will need to handle issues where the automatic wrapping fails. To do so you have to occasionally exclude or overwrite methods, fields, or metadata by using the files in the Transforms directory.
- For binding iOS Frameworks you’ll need to install Objective Sharpie . The Microsoft Developer Network provides a pretty nice tutorial for binding a native framework once Objective Sharpie is installed. An approach that I used fairly often when writing libraries for our project was to create a wrapper library for all the dependencies that I wanted. In order to generate the frameworks using cocoapods, I created an empty project, `init cocoapods` in the project directory, and built the xcworkspace file. When you build in Xcode you do it for a certain architecture. Either for the iOS simulator (x86) or for a real ARM device. The frameworks created by building the new workspace can only be used either with the iOS simulator or a real device. To fix this issue you have to create a so called “fat” framework containing architectures for both. The easiest way to do this is by adding an aggregate target (File | New target | Cross-platform | aggregate) to the Pods project. You can change the target’s build configuration to Release and add the framework as just another dependency.
- For binding Android Archives (.aars), .jars, or java libraries you can reference this MSDN tutorial. You can tell exactly what is present in an .aar file (and what will be visible to Xamarin) by executing `unzip yourLib.aar` from the command line. A number of files will be unpacked. You can view the resource and manifest files in cleartext. There will also be a classes.jar that you can unpack using a java decompiler. Once the decompiler is installed you can drag & drop .jar files into it and examine their contents. If the classes are readable, then Xamarin won’t have any issue binding the file automatically. If they’re obfuscated however you will have to manually provide the bindings. When binding your own .aar libraries, when you add a required dependency to the library’s Gradle you will need to import that dependency into your Xamarin Android project. This can be accomplished in one of two ways. The first is to find a pre-bound NuGet for the same dependency in the NuGet Gallery and add the NuGet to your .csproject file. The second way is to create your own binding. During the gradle build process, the gradle build task will download all of the required dependencies to your computer’s `~/.gradle/caches/modules-2/files-2.1` directory. Within this directory you will find all the dependencies as .aars. They may or may not be obfuscated depending on how they were exported by the dependencies creator.
When debugging you can output to the console in C# (with string interpolation) by using the following format: `Debug.WriteLine($“New date {0}”, args.NewDate.ToString());`. This can be used to evaluate expressions in a similar fashion to XCodes `po` statement, or Android Studio’s evaluate expression dialogue. This last tip was particularly useful for me as was learning the ropes for Xamarin.
To summarize, after nearly a year of working with Xamarin I found it to be particularly good for sharing code with between iOS and Android on simple CRUD (Create, READ, Update, Destroy) applications. I found that the more advanced your use case became however (AI, ML, Augmented Reality, IoT, wearables, etc.), the less well suited it was. Microsoft did their best to provide escape hatches for these use cases through custom native library bindings, but I found many of the more nuanced aspects of this process to be undocumented and only resolved through meticulous trial and error. It is for good reason that seasoned .NET developers call this the “dark art” of binding. Next month we’ll take an in-depth look at the Kotlin equivalent of Xamarin, Kotlin Multiplatform Mobile (KMM).
comments powered by Disqus