Ganttis + Xamarin.iOS

How can we adapt DlhSoft’s Swift-based Gantt chart components and integrate them into a Xamarin app?

DlhSoft
Ganttis

--

Photo by Tim Johnson on Unsplash

About one year ago we’ve released Ganttis (and recently we’ve upgraded it to version 2) — consisting of a powerful set of macOS and iOS components that allow developers to easily add Gantt charts into their apps.

We’ve built those components using Swift 5, deployed them using Apple’s newest .xcframework format, and therefore it’s very easy to add them into modern Xcode projects, regardless of the app’s supported targets and platforms (macOS, iOS, simulator, Mac Catalyst.)

Of course, sometimes you’re not free to use Xcode and Swift for everything. Xamarin tools — provided by Microsoft — have their own advantages. They allow developers to build native iOS and Android apps, among other platforms, from a single source code base using another great language: C#.

But… can Ganttis’ iOS components be used within a Xamarin app?

That’s the question we’ve got from some of you, that’s the question we aim to answer today. The short answer is yes (see this repo for the full example), while the long one — how it can be done — consists of the tutorial-like sections below.

Summary

To be able to use GanttisTouch components you will need to follow these high level steps:

  1. Create adapter classes (using Xcode and Swift) to support Objective C integration for Ganttis components
  2. Set up a Bindings library under your Xamarin solution, mapping the Objective C adapters (and their underlying components) to C# interfaces
  3. Use the API bindings within your C# code to create native Ganttis views in your Xamarin project

Xamarin.iOS

First of all, let’s assume we are working on a Xamarin.iOS project, also known as native Xamarin iOS app.

If you would want to use Xamarin Forms instead, you should know that it’s not easy to use native platform components there. Instead, we recommend you to follow this article and do it the other way around: add Forms pages into a more powerful native project, that would eventually allow you to also include Ganttis components and integrate them to your Forms pages too, of course.

(You may also have separate Xamarin.Android and/or other targets, and one or more .NET Standard/shared code projects for common UI and logic, but for now let’s just focus on Xamarin.iOS; other concerns are simply out of scope for this post.)

Back to our original assumption. We’ll start with a Xamarin.iOS project named, for example, GanttisXamarinSample. In Visual Studio for Mac the solution would initially look like this:

Objective C adapters

As you know, Xamarin.iOS solutions can define bindings to native iOS APIs only if the latter are supported in the legacy Objective C world. Pure Swift components, like ours, are — obviously — not visible as Objective C objects, so we’d need to write wrappers first — we’re calling them adapters — in order to enable Xamarin bindings to them.

This Adapters sub-project should be created as a Cocoa Touch Framework with Xcode. It should reference Ganttis.xcframework (embedding and signing it into the output product) and have its iOS targets set up appropriately:

Important: in the following sections we’ll only consider the iOS simulator platform — replace it with iOS device upon deployment* — but if you want to support both devices and simulators with the same output framework you should create a “fat” one, like mentioned here (step 12.)

* Note that to properly run the app on the device, you might also need to add “ --optimize=-remove-dynamic-registrar” flag in your Visual Studio app project’s options, under iOS Build section, mtouch arguments.

(At the time of writing, Xamarin.iOS doesn’t support XCFramework references directly, as in our opinion, it eventually should.)

Wrapper classes

The actual wrapper classes (with objc support) within the Adapters project should be defined as follows:

  • Item, Dependency: classes that support passing Gantt chart item values (e.g. labels, start and finish dates) and dependencies between them;
  • GanttChartAdapter: inheriting from DlhSoft’s GanttChart, but constructed using Item and Dependency instance arrays. (Can add settings and miscellaneous properties as well.)
  • Observer: protocol to be supported by external classes in order to be notified when changes occur (e.g. when an end user drags a task bar within the Gantt chart area.)

GanttChartAdapter’s initializer should map the items and dependencies it receives into structures supported by the underlying component. It can also set up header and content settings and define itself as an item observer to be able to pass changes to the external object whenever they occur.

(It shouldn’t be difficult to write the necessary Swift code for the adapter classes even if you’re a full time C# developer: as you will certainly see, these programming languages are highly similar in most of their use cases.)

Of course, you should add more or less classes and properties mapping underlying components to exported APIs, as actually needed in your app.

Finally, however, you may want to also set up an (already purchased — or just a placeholder for a) DlhSoft Ganttis license, to be initialized automatically with a License class instance — simply passing the appropriate license string to the base library.

Adapters framework

When you’re ready, build the Adapters target using Xcode (having the correct platform selected — simulator if you are testing the solution, or iOS device if you’re ready to deploy your app and you’re not using a “fat” framework as already suggested during this section’s introduction.)

Find the .framework output directory by right clicking it in the Products list and selecting Show in Finder there, and copy it — to easily reference it later — to the main Visual Studio solution’s folder that you’ve started from.

Bindings

Back in Visual Studio, the next step is to create a Xamarin Bindings library (we’ve named it GanttisBindings) and add it into your solution. Then add the previously prepared GanttisAdapters.framework to the Native References virtual folder of the new Bindings project. (GanttisTouch bytes are already included within the “umbrella” framework.)

Under ApiDefinition.cs you should then define the C# interfaces that you want to use in order to access the underlying Objective C classes prepared within the Adapters framework. Follow the official documentation to prepare them — with or without using Objective Sharpie tool referenced there — but remember:

because your target classes have (also) been defined in Swift, although marked as objc integrable, they have auto-generated names like _TtC15GanttisAdapters4Item for Item — this is the default behavior of Xcode building system, required since names of Objective C classes should be unique, while Swift classes may be grouped within modules.

You need to place those names under [Name] attribute instances, defining one for each interface within the API definitions namespace.

Hint: to obtain the actual names, you can look into the Swift headers generated within Adapters.framework folder, under Headers subfolder.

Of course, the C# interface names themselves may be different than those of the adapter types — we decided to use the original type names from the inner Ganttis framework.

(Note that you may safely delete the associated Structs.cs file — if it was generated for you by Visual Studio and you don’t plan to use it.)

Loading GanttChart instances

It’s now time to consume what we’ve prepared: to use our Bindings in order to create Adapter instances, and use the inner Ganttis framework for our needs.

Under the main GanttisXamarinSample project, add a project reference to the Bindings library and write using GanttisAdapters statements under the C# source code files where you need access to the new APIs.

Let’s then add a GanttChart component instance into our app’s main window. To be easier, let’s just create the instance and add it as a subview of the root view of the app, controlled by the custom ViewController defined by Xamarin.iOS project template (under ViewController.cs file):

use C# to create GanttChartItem and GanttChartDependency objects, create a GanttChart instance collecting that data, and add layout constraints for it to use all the space available within its container;

set up ViewController class to implement IGanttChartItemObserver to be notified whenever change events occur: TimeDidChangeFor method will be called automatically, but it needs to be marked with an appropriate [Export] attribute (like in ApiDefinitions) — since it’s an optional method in the wrapped Swift’s protocol.

When you’ll finally run the app (you may safely disregard Xamarin’s ManifestResourceWithNoCulture deprecation warning that may appear upon building), you should see that DlhSoft’s native iOS GanttChart works in your Xamarin project as if it were a C# view — using data (Gantt chart items that turn into bars, and dependencies shown as arrow lines) defined with C# too!

When the end user drags the bars around, the delegate method — TimeDidChangeFor(item) — is also called appropriately.

and… one more thing!

To remove the DlhSoft licensing warning alert, remember that we’ve also prepared a License class that will now become very handy. Simply add and instantiate a static readonly field of type License in your AppDelegate class (AppDelegate.cs) and you’re done!

--

--