Bridging Elements RTL and Native Classes

The code in your typical Elements application will fall into two categories with regard to Elements RTL use.

One portion of code will be written to be cross-platform, and possibly shared between different versions of your app – for example for iOS and Android. In this code, you will (try to) avoid using any platform-specific APIs, and not tackle any platform-specific problems. You will implement core "business logic" of your app – what that exactly means of course depends on your kind of app. It could be network communication, data processing, and the like.

The second portion will be platform-specific, and you’ll be writing separate versions of it for each platform. This portion will contain both high-level visual code (you’ll be creating a distinct user experience for your app for each platform), but also lower-level platform specific code (your app might integrate with different platform paradigms – for example with something like iCloud or GameCenter on iOS).

The tricky part can be connecting these two portions of your code, especially as they will not really be two distinct and siloed areas of your app, but mixed throughout your entire project. This is where Elements RTL’s "toll free bridging" comes in.

Mapped Types

Because many parts of Elements RTL use Mapped Types, it is really easy to connect your cross-platform code with platform-specific code and APIs in your apps. And not only easy, but it can also be done with no runtime overhead. With mapped types, even though the Elements RTL classes may appear to be specific, platform-independent classes to the code you write, under the hood and at runtime, they are represented by core platform classes – different classes on each platform.

Let’s for example take dictionaries. Elements RTL provides a Dictionary<K,V> type that’s available to all languages, on all platforms. Elements RTL's Dictionary is a generic class that represents a collection of key/value pairs, and provides a consistent API to work with the data inside said dictionary – to add values, query them, and so forth. This API is identical on all platforms, so if you write code that uses Elements RTL's Dictionary, that code will (assuming you don’t use any other platform-specific APIs) compile for .NET, for Cocoa, and for Java.

So you can write a unit of code that leverages Dictionary (and, in real life, other Elements RTL classes), and that unit can then be shared between the different versions of your app, simply by referencing it from each project (or using a Shared Project).

But this shared code will, in most likelihood, not live on its own. You’ll be writing platform-specific code that will interact with it, and that code might require you to pass your data to platform-specific APIs provided by the platform – APIs that know nothing about Elements RTL.

Since these Elements RTL classes are mapped, at runtime they are instances of actual classes provided by the platform. For example, on iOS (as well as Mac OS and watchOS), Elements RTL's Dictionary actually maps to Foundation's NSMutableDictionary, so all the code you write using Elements RTL’s dictionary class actually gets compiled down to use NSMutableDictionary under the hood.

Because the Elements compiler knows this, you can pass the Elements RTL version of a class to an API that expects the platform version, or vice versa. For example, your shared class could expose a property of type RemObjects.Elements.RTL.Dictionary, and in your platform-specific code you could pass its value to a pure Cocoa API that expects an NSDictionary. In the Android version of your app, you could use the exact same class, exposing the exact same property, and pass it to an API that expects a java.util.HashMap class, instead.

type
  MyBusinessData = public class
  public
    property infos: RemObjects.Elements.RTL.Dictionary<String, String> read ...;
  end;
public class MyBusinessData 
{
    public RemObjects.Elements.RTL.Dictionary<String, String> infos { get ...; set ... }
}
public class MyBusinessData {

    public var infos: RemObjects.Elements.RTL.Dictionary<String, String> { ... }
}
public class MyBusinessData 
{
    public RemObjects.Elements.RTL.Dictionary<String, String> getInfos() { ... }
}
method SomeWhereInMyiOSApp.SomeWhere();
begin
  var b: MyBusinessData := ...;
  var infos: NSDictionary := b.infos // bridges toll-free
  NSUbiquitousKeyValueStore.defaultStore.setDictionary(info) forKey("BusinessDataInfo"); // save to iCloud
end;
public class SomeWhereInMyiOSApp 
{
    public void SomeWhere() 
    {

        MyBusinessData b = ...;
        NSDictionary infos = b.infos; // bridges toll-free
        NSUbiquitousKeyValueStore.defaultStore.setDictionary(info) forKey("BusinessDataInfo"); // save to iCloud
    }
}
public class SomeWhereInMyiOSApp {

    public func SomeWhere() {

        let b: MyBusinessData = ...
        let infos: NSDictionary = b.infos // bridges toll-free
        NSUbiquitousKeyValueStore.defaultStore.setDictionary(info, forKey: "BusinessDataInfo") // save to iCloud
    }
}
public class SomeWhereInMyiOSApp 
{
    public void SomeWhere() 
    {

        MyBusinessData b = ...;
        NSDictionary infos = b.getInfos; // bridges toll-free
        NSUbiquitousKeyValueStore.defaultStore.setDictionary(info) forKey("BusinessDataInfo"); // save to iCloud
    }
}

Of course this is just one example, but the same concept applies to pretty much all Elements RTL classes that represent data, where there is a well-defined platform-specific class that matches. From complex collection classes such as lists, dictionaries and stacks, down to simple types such as Strings or URLs.

With this, Elements RTL not only makes it easy to write cross-platform code that you can share between all versions of your app, it also makes it easy to integrate this code with the rest of your apps, and with each platform's native APIs.

See Also