About Cross-Platform Apps

This article is a work in progress

This article is a bit of a misnomer, because Elements does not actually support or encourage the development of cross-platform applications, per se. Instead, Elements is designed to let you write apps for all platforms.

What we mean by that is that in Elements, you won't find the option to go File|New Cross-Platform App and create a single project that will magically build and run on – for example – both iOS and Android. But hat does not mean that you cannot use Elements to build great cross-platform solutions. To the contrary, doing just that is one of many things Elements is great and and designed for.

The way Elements tackles cross-platform development is by letting you create a great application for each of the platforms you want to target – and share a lot of code between the applications, where feasible.

For the purpose of this text, lets assume you want to build an application for the two major mobile platforms — iOS and Android. But all the concepts, ideas and technologies discussed here will apply to other platform combinations, as well, whether you want to write a desktop apps for Mac and Windows, or target the trifecta of iOS, Mac and Apple Watch, for example.

You start out by simply creating to separate application projects, one for iOS and one for Android, just as if you were targeting a single platform. We have tutorials for both of those platforms to get you started with the basics:

When you create your first project, you would start a new solution as you always do (by going via File|New Project, and choosing your platform (e.g. iOS), language (Oxygene, C# or Swift) and application type. Let's call this first (iOS) project "MyApp.iOS" for now.

When you create the second project , you will want to add it to the same solution as the first. There's two ways to do this. Whether you are using Fire or Visual Studio, you can right-click in the Solution Tree, and choose "New Project...". In Fire, you can also create the second project via the "File|New Project" (⌥⌘N) menu again, and just make sure to activate the "Add to existing solution" checkbox on the subsequent dialog:

Let's call this second project MyApp.Android. In either case, you will end up with one Solution that now contains two projects: An iOS app called called MyApp.iOS with a green project icon (Cocoa uses green icons in the IDE), and the second called MyApp.Android with a yellow icon (Java, and thus Android, uses yellow). Right now, these are two separate projects and – aside from being open on the same IDE window – they have nothing in common or shared yet.

These two projects will form the basis of our iOS and Android apps respectively – remember that even though conceptually you may be thinking in terms of "I'm building an app" for iOS and Android, you're really building two individual apps, one for each platform.

In each of these projects, you'll be writing the platform-specific code for your application. This will include the user interface itself and code that ties directly to it (yes, your application's user interface will be platform-specific, more on that in a moment), as well as any code that takes advantage of lower-level platforms-specific features (for example, maybe you want to interact with iCloud on iOS, and use some Google-specific APIs on Android. Even things that are conceptually available on both platforms, such as Push Notifications, might work slightly different on each platform, and require platform-specific handling in your app).

This might seem painful or unnecessary at first, but it is absolutely essential, if you want to create a great native app for each platform that feels polished and at home to the user on both iOS and Android.

I really need to do the the UI separately for each Platform?

The short answer: Yes.

Elements does not (and will not) provide any abstraction layer that will allow you to drag together one set of User Interface (UI) that will just "magically" work, look good and work well on multiple platforms. There's two reasons for that. While it's technically possible to have one set of UI code work on multiple platforms (and some of Elements competitors, including Xamarin Forms and Delphi Mobile, do offer and even focus on that capability), it is virtually impossible to do this and end up with a UI that works well and feels at home on all the platforms.

iOS and Android are different platforms with some vastly different user interface paradigms that go way beyond whether toolbars should be on the bottom or the top of the screen. The only way to create am application that users will enjoy and feel at home with on their platform of choice is to carefully rethink the UI of your app with each platform in mind, and design your UI wit the subtle tweaks that each platform needs.

This does not mean that you cannot share one conceptual design for your app, of course. Things from fundamental structure of your UI, how the user experience flows between different parts of your app, down to design details as to what color schemes and fonts you use, may very well be thought out and designed once, and then implemented (often with subtle nuances) for both platforms. Then again, sometimes even the fundamental flow of your application will differ vastly between the platforms.

A lot depends on what kind of app you are writing, and what existing platform paradigms on both iOS and Android your application needs to embrace to fit it. Some apps may look virtually identical; for other apps, a workflow that works great on one iOS will feel like it is "fighting the platform" om Android, or vice versa.

The Shared Project

But of course we do want to share a lot of code between the two platforms, so for that, lets add a third project to the mix: a Shared Project. Shared projects are also covered more in their own topic, and in the tutorials linked below.

  • Using Shared Projects in Fire or in Visual Studio.

Adding a shared project is simple: just as you did when you added the Android project to the existing solution, invoke the New Project Wizard again, and this time select the "Shared Project" template.

Due to the way templates are structured in the IDE, the Shared Project template will be available underneath any platform and language, but because shared projects do not have an inherent platform (or language, until you start adding code), it doesn't matter which one you pick.

Let's call this project MyApp.Shared, and once again make sure it gets added to the existing solution. You should now have three projects in open, the new one sporting a white "globe" icon (the white indicating that it, and the files within, are not tied to a specific platform).

The Shared Project can be thought of as a container for code (and, in theory, other files) that will be used in both of the other projects. In other words, code that you will write for your app that is not specific to a platform, but can be used on both iOS and Android.

To make that connection, to let Elements know that the MyApp.Shared project should be used by both the iOS and the Android project, you need to add a reference between them. In Fire, simply drag the MyApp.Shared node in the solution tree onto the MyApp.iOS project node, and then repeat the same process to drag it to the MyApp.Android project as well. In Visual Studio, right-click the "References" node in MyApp.iOS, choose "Add Reference..." and then select the shared project. Do the same for the Android project.

Now, any code you add to MyApp.Shared will automatically be available in both the iOS and the Android app. This if course means that any code you write in the shared project needs to be platform-independent, and should not make use of features or types that aren't available on both platforms. Elements provides several ways to achieve that.

Elements RTL

Elements RTL is the main means by which you can write platform-independent code, and is of course the main focus of this whole section of the documentation. Essentially, Elements RTL is a library that provides a wide set of classes (and growing) that are platform independent, and can thus be easily used in shared code, without worrying about each platform's different API.

For example, Strings or container objects such as Arrays or Dictionaries are available on all platforms, but behave differently, normally making to hard to write code that is not tied to a platform – because methods you can Call on Cocoa's string types (such as .lowercasestring()) are different that similar methods on Java strings (e.g. toLower()). By contrast, RemObjects.Elements.RTL.String works and behaves the same on each platform (and for example has a .ToLower() method on all platforms).

To use Elements RTL, all you need to do is add a reference to the library to both of your app projects by going to "Add Reference" and choosing libElements.fx (on Cocoa) or elements.jar (on Java). Elements RTL's types live under the RemObjcts.Elements.RTL namespace, so you can refer to them either by full name (e.g. RemObjcts.Elements.RTL.String or RemObjcts.Elements.RTL.Dictionary) or by using/importing the RemObjcts.Elements.RTL namespace via the uses (Oxygene), using (C#) or import (Swift/Java) keyword.

If you use/import the namespace, all string constants and the C# string keyword will automatically be treated as RemObjecs.Elements.RTL Strings, as well.

Fire and Water also give you the option to automatically add add a reference and use/import of Elements RTL, when starting a new project. Simply check the appropriate option in the New Project dialog.

Refer to the following links for more details

Conditional Compilation

Although Elements RTL can get you a good way towards platform-independent code, sometimes it is easier to just be able to call into a platform API to get a job done. Maybe there's some exiting functionality on both platforms that's not wrapped in Elements RTL (yet), or maybe you want to explicitly do something different on each platform.

All Elements languages allow you to use Conditional Compilation, also sometimes referred to as "ifdefs", to mark specific pieces of code as specific to one (or more) platform. Elements provides a bunch of predefined values you can use to check the platform, and you can also provide your own set of Conditional Defines in Project Settings. You can use the {$IF ...} (Oxygene) or #if ... (C#, Swift and Java) compiler directives to check for the presence of these defines.

You can also use the defined() system function (of #defined() in Swift) for more granular conditional compilation.

method Helpers.AppSignature: String;
begin
  {$IF COCOA}
  result := 'MyApp for iOS';
  {$ELSEIF JAVA}
  result := 'MyApp for Android';
  {$ENDIF}
end;
public string AppSignature()
{
  #if COCOA
  return "MyApp for iOS";
  #elseif JAVA
  return "MyApp for Android";
  #endif
}
func AppSignature() -> String {
  #if COCOA
  return "MyApp for iOS"
  #elseif JAVA
  return "MyApp for Android"
  #endif
}
public string AppSignature()
{
  #if COCOA
  return "MyApp for iOS";
  #elseif JAVA
  return "MyApp for Android";
  #endif
}

The COCOA, JAVA, CLR ands ISLAND defines are provided automatically by the compiler to distinguish between Cocoa (Mac and iOS), Java (including Android), .NET and Island. Refer to the following topics for more details, including a full list of all pre-defined conditionals, and how to define your own.

System Functions and Types

Elements also comes with a range of helpful system functions that can be used to accomplish common (very) low-level tasks. These functions are available on all platforms and in all languages. For example, the writeLn() function can be used to emit some (debug) messages to the console, regardless of platform, and length()can be used to obtain the content size of many common classes such as strings and arrays, without worrying whether the platform's own API would require you to call .count or .Length or something else.

In addition, Elements defines common standard names for common simple types, across all languages and platforms. So for example, you can universally use Integer or Int32 to refer to a 32-bit integer value, or Boolean to refer to a boolean value, independent of the platform's own type names – which often differ subtly,

You can read more in the API sections on:

To do: to be continued

Adding Code to the Shared Project

Connecting Shared and Platform-Dependent Code