I've just pushed a new set of samples to our Elements Samples GitHub repository. The new sample, available for Oxygene (Object Pascal), C# and Swift (GitHub links), shows off some concepts for sharing code in an application with bespoke native User Interface for Mac and Windows.
While purposely very simple, the basic structure of the sample is inspired by how I've handled similar challenges when writing Fire and Water: essentially sharing as much of the actual logic and infrastructure as possible, while still creating a custom UI for both platforms.
This sample focuses on the UI aspect, mainly the Main Window of the app, and how to best structure this code. Of course Fire/Water, as would any real-world app, has large amounts of "data mode/business logic" code that can be shared as well, independently of these patterns.
Let's have a look.
All the relevant code is inside the shared project, with the platform-specific projects really just being thin skeletons to let the app be built and started; the Cocoa version has literally no code of its own, and the WPF version has nothing but the
App.xaml, which passes control right into the shared code.
At root level, you see three source files (with some nesting), only two of which really have relevant code.
Aliasessource file declares a couple of type aliases to provide common names between Cocoa and .NET. It's trivially empty here with just a couple of entries — but for a real life project, you might end up adding a few more (for example, Fire/Water maps classes such as
UserControland many more).
AppDelegatefile will be familiar to you if you worked with Cocoa before. I've chosen here to adopt it to serve the same purpose on WPF – as the main entry point and center of the app. Once again, in this simple sample its purpose is very trivial – mainly getting the MainWindowController instantiated (and held onto) in the (shared)
start()method seen below:
You'll notice that both
MainWindowController (which we'll look at next) have additional files nested below them, named
.WPF, respectively. These three files come together to form a single class (using Partial classes, or Extensions in Swift parlance), with the two nested files holding any Cocoa- or WPF-specific code, conditionally compiled via
This is a purely esthetic design choice. Depending on the amount of platform-specific code, you can also simply put everything in the root file. For the Fire/Water project, the amount of shared vs. specific code varies largely between files, but for consistency reasons, I chose to split all UI classes into three, even if sometimes only a single method (or less) is platform specific. I carried this concept over to this sample as well — in fact, you'll find
AppDelegate.WPF.* is completely empty at this stage (and the Cocoa version only contains the
MainWindowController, finally, represents the main (and only) window of this sample application. For separation of concerns, this is split between the Window itself on one hand (for Cocoa represented only by a code-less
.xibfile and for WPF with a
.xamlfile with nested code-behind source file), and the controller on the other. All the logic for the window is implememted in the controller and, in this case, 100% shared between platforms:
You see three public properties (
var, in Swift parlance), which are marked to
Notify on change, so they can be bound to the controls via Cocoa or WPF bindings, respectively. You also see a public "action" method called
calculateResult that is connected to the one button in the window (again from the
.xib and the
xaml.* files, respectively).
As the user changes values in the window, the properties get adjusted automatically, and conversely when
calculateResult sets the
result property, the UI updates to reflect the change immediately, as well. All from platform-independent UI code.
The sample will ship with the next build of Elements, and you can also grab it directly from GitHub here:
And it goes without saying that the sample can be used in Fire, Water and Visual Studio.
You can read more about Elements here.