Jump to content
Lars Fosdal

FMX cross platform approach?

Recommended Posts

Generallly speaking - do you start with the generic (read Win32) application and develop that until it is feature complete before you start on the adaptations for Android/iOS/MacOS - or do you work on all platforms at the same time?

Share this post


Link to post

Several years ago we migrated a 50,000-line Windows VCL app to FMX so we could distribute it on MacOS. 

The migration was pretty easy for us. Most of the differences were changing out VCL component and property names, which is a nuisance, but not difficult.  IMO, though, it's not something you want to do later.   I recommend people develop in FMX from the start. 

But, maybe that's not what you were asking?  For target platform:

Compiling, linking, and opening a debug session to the Mac is really slow for us.   (Some of that is no doubt the 64-bit compiler/linker which is slow.).  It takes so much time when targeting the Mac that we do all our development first on 32-bit Windows (same FMX code base)  so that the iterative write-compile-debug process isn't painful and we can be more productive.

During the development process, after we have some code running on 32-bit FMX Windows,  we compile and run the app on the Mac to make sure it works there too. You'll get pretty good after a while of knowing what code of yours is OS dependent. 

Share this post


Link to post

I am in the lucky situation that I don't need to migrate any VCL, but can start new, light apps with FMX from scratch.

Based on a self-made framework, I'm used to work on all platforms at the same time, meaning one code for all.

Different views, e.g. phone, tablet, desktop, I would separate into different frames or forms wherever you need much difference.

 

Of course this doesn't end up in a thick, rich Windows client, but in light and easy to handle mobile-like apps on all plaforms.

I must confess that I do nor squeeze as much functionality in, as I did under Windows in the past.

So the approach is FMX - mobile first, and so far I think that is OK for most of my apps since it helps to keep apps light and easy.

 

If you need really heavy database related stuff, probably I would do that on VCL, and separate code to mix in some light FMX version too.

 

 

 

Share this post


Link to post

If I was starting a new application targeting multiple platforms, I would develop for all platforms simultaneously while building the app. The reason is that different platforms will have slightly different ways of doing things and if you build the app to work well on one platform then address needs of others, it may affect some screen design decisions you would've made differently earlier. For example, iPhones and iPads don't have a back button but Android devices usually do so if you write the app with the assumption of an Android device first and assume everyone will have a back button, your screen may be too cluttered to add a back button later when you add the iOS platform.

 

That's just one simple example and of course you should be aware of platform differences up front (and never make assumptions, etc.). There are many other considerations such as screen size and orientation, which style will you apply for the different platforms and how do all the different edit controls look and feel. All these things have subtle influences on your screen design. It's much easier to make those decisions in the early stages than to then try to morph a feature-complete app for another platform.

  • Like 2

Share this post


Link to post

I am doing primarily FMX, and it is a simple app, visually.  Currently, I am just doing the "plumbing" - i.e. make all the non-UI stuff solid so that integration with the UI will be as thin as possible - and in the process - building up a cross platform toolbox.

 

The solution needs to collect data over time to build a historical database, and I am thinking about how I should best do this.  

Ideally, I would do it centrally - so that the data only needs to be collected once for multiple concurrent viewers, but where to host it so the collection can happen 24/7 and I don't have to pay an arm and a leg?

 

If I can't do it centrally, the app would need to both be UI and a background task so that data collection continues also when the app is not in use.

 

This is a hobbyist project that I do as an exercise for learning FMX and cross platform, and if I decide to share it with everybody, I will prefer not to be responsible for a "backbone".

I also want it to be as low cost and reliable as possible (there you have an oxymoron 😛) and simple to set up.

Share this post


Link to post
14 hours ago, corneliusdavid said:

The reason is that different platforms will have slightly different ways of doing things and if you build the app to work well on one platform then address needs of others, ..

Right, and this is why I separate those differences into different views and classes, so that in the end you might have still "one source", which might be dynamically switched from platform to platform.

I use mainly frames to define different kinds of views and sub-views, and plug them together as needed.

Very important is to have a strict nomenclature and naming scheme of files, to handle the separations, I use the Postfix if separations by platforms are needed, like

My.SpecialUnit.pas             //<= Common functionality

My.SpecialUnit..Win.pas     //<= Specializations, workarounds, fixes

My.SpecialUnit..iOS.pas

My.SpecialUnit..And.pas

My.SpecialUnit..Macos.pas

My.SpecialUnit..Linux.pas

 

Sometimes I start a new unit, but when it turns out it needs separation, then I rework it to above scheme.

I try to avoid IFDEF's in the code, but only in the uses section.

 

What I meant regarding different platforms is more a visual and behavioral decision, like

on mobile phones use TListView, on tablets use a kind of popup view, and on desktop maybe a TStringGrid.

All approaches show maybe same or similar info, but in a platform dependend way, using the same interface in the caller wherever possible.

 

Share this post


Link to post
On 12/14/2021 at 9:22 AM, Rollo62 said:

If you need really heavy database related stuff, probably I would do that on VCL, and separate code to mix

Yes, for database and other non-GUI and platform-agnostic stuff, keep it simple and write a VCL app to build out and test those features, keeping them completely separate. This is an excellent use case for writing event handlers.

13 hours ago, Rollo62 said:

I try to avoid IFDEF's in the code, but only in the uses section.

This is a nice approach--keeps the code cleaner.

13 hours ago, Rollo62 said:

What I meant regarding different platforms is more a visual and behavioral decision, like

on mobile phones use TListView, on tablets use a kind of popup view, and on desktop maybe a TStringGrid.

That's a great idea--use the best controls for each platform. Grids are a lot easier to work with than ListViews but ListViews are better for small devices.

 

The only thing is that as soon as you view a form in the designer using a different platform view, it creates another .fmx file (e.g. .iPhone.fmx, iPhone47in.fmx, iPhone55in.fmx, .LgXhdpiPh.fmx, .Macintosh.fmx, etc.) so then you have to manage those but still, I like the approach.

Share this post


Link to post
14 hours ago, Rollo62 said:

Very important is to have a strict nomenclature and naming scheme of files, to handle the separations, I use the Postfix if separations by platforms are needed, like

My.SpecialUnit.pas             //<= Common functionality

My.SpecialUnit..Win.pas     //<= Specializations, workarounds, fixes

My.SpecialUnit..iOS.pas

My.SpecialUnit..And.pas

My.SpecialUnit..Macos.pas

My.SpecialUnit..Linux.pas

I would like to throw in a variation of this scheme. Instead of Postfixes for units I suggest separate folders named like the platform, similar (well, almost) to the IDE doing it with the library search paths. Adding a simple search path .\$(Platform) to the project may be sufficient. The platform specific units are named all the same but reside in their platform folders.

 

Before anyone claims that this prohibits to include these units to the project: You cannot do this with the postfix units either. (Well, you could by IFDEFing the DPR, but this is strongly discouraged. You definitely cannot trick the dproj file for that.)

  • Like 2
  • Thanks 1

Share this post


Link to post

 

33 minutes ago, Uwe Raabe said:

Adding a simple search path .\$(Platform) to the project

This sounds really slick at first but would this actually work? The base filenames are the same but if you switch the View in the designer and make any change, the Delphi IDE creates a copy of the .fmx file with a different filename for that view in the same folder, so you'd have to constantly be moving those files to their platform folders.  Or only looking at certain units in certain views.

 

I agree putting {$IFDEF}s in the .dpr is not only a bad idea but difficult to manage as adding/removing units to the project rewrites it. If sub-forms are loaded and managed from the main form (or somewhere else other than the project) then using {$IFDEF}s in uses clauses isn't so bad--but then you don't have all of the used units listed in the project--which are used by some external tools.

Share this post


Link to post

How about using a ProjectGroup with multiple dpr/dprojs that share a code base.

Just select the project to compile in upper righthand window.

 

Also works when running second IDE.  This works fairly well on the VCL side except need different Exe names and only one debug at a time.    

IDE will prompt when time/date by other IDE.  I compiled to an Android and in another IDE compiled a VCL program in the group with the caveat the fmx VCL library didn't load. 

 

One Android Studio uses 3.5 gig-why not load a few lithe Delphi IDEs!:classic_biggrin:  

 

Added able to load fmx library plus  use <ctr> b keys to switch projects!

Edited by Pat Foley
Add shortcut

Share this post


Link to post
8 hours ago, Uwe Raabe said:

I would like to throw in a variation of this scheme. Instead of Postfixes for units I suggest separate folders named like the platform, similar (well, almost) to the IDE doing it with the library search paths. Adding a simple search path .\$(Platform) to the project may be sufficient. The platform specific units are named all the same but reside in their platform folders.

 

Yes, I considered that approach too.

But I am not a big fan of hundrets of sub-folders, especially because Delphi doesn't support relative paths well (except in the .DPR).

If Delphi would allow uses including relative paths, I would go for this approach too, meanwhile I try to abstract by "namespaces".

The advantage of "mainspaces" like above is, that the files are not cluttered all over different sub-folders, but they are nicely sorted side by side.

That way, I can even add further sub-modules, that are related to the domain:

My.SpecialUnit.pas             //<= Common functionality
My.SpecialUnit.SubModule.pas       //<=SubModules
My.SpecialUnit.SubModule.iOS.pas   //<=SubModule with specific workaround for iOS
My.SpecialUnit.SubModule.Other.pas //<=SubModule with implementation for all other platforms
My.SpecialUnit.Win.pas     //<= Specializations, workarounds, fixes
My.SpecialUnit.iOS.pas
My.SpecialUnit.And.pas
My.SpecialUnit.Macos.pas
My.SpecialUnit.Linux.pas 

Of course the folder approach would be nice, because thats even removed IFDEFS, but also make the "path" handling in Delphi more fragile.

 

 

9 hours ago, corneliusdavid said:

The only thing is that as soon as you view a form in the designer using a different platform view, it creates another .fmx file (e.g. .iPhone.fmx, iPhone47in.fmx, iPhone55in.fmx, .LgXhdpiPh.fmx, .Macintosh.fmx, etc.) so then you have to manage those but still, I like the approach.

Thats why I don't use it via the different platform designer in Delphi, this is only differentiating the Form, but hardly to manage different behaviour too.

 

I separate different "Views" in TFrames, and on them I can use whatever I like.

My.SpecialView.Frame.Win.pas            // Maybe this uses StringGrid
My.SpecialView.Frame.Win.dfm            
My.SpecialView.Frame.Tablet.Mobile.pas         // Maybe this uses Popup, (for iOS, Android Tablets)
My.SpecialView.Frame.Tablet.Mobile.dfm            
My.SpecialView.Frame.Phone.Mobile.pas         // Maybe this uses ListView, (for iOS, Android Phones)
My.SpecialView.Frame.Phone.Mobile.dfm            
My.SpecialView.Frame.Macos.pas         // Maybe this uses HtmlGrid, (for Macos)
My.SpecialView.Frame.Macos.dfm            

My.SpecialView.Frame.pas            // Here I can manage to include  the right code in the uses, and forward that as interface
                                    // All the frames above include the same Interface, so they encapsule the same behaviour, only in different views.
                                    // This interface, according to the platform, can be simply forwarded or wrapped if needed
                                    
My.SpecialView.pas                  // Here I can handle the interface, for usage of the certail "view"
                                    // Like forwarding the right interface and Factory
                                    //  
                                    

The whole unit block keeps nicely together, avoiding cluttering into many, unrelated units and folders.

The single views can be nicely edited, by the TFrame designer in a RAD manner, and I can add and visually design as many components as needed for each platform.

I can handle the different "views" by a single interface, so from the users perspective the different platforms doesn't really matter.

The caller doesn't need to care if he is on desktop or mobile (in the best case), he simply can use the same code in many projects.

The views can easily be extended.

Of course the abstractions of different platforms are somewhat difficult, but it turned out that many "standard views" can be identified, and easily unified.

As example: Dialogs, Login, Logger, InternalDebug, View of Lists, and whatsnot

This is what are 1:1 same in all my apps, and work on all platforms in the same way.

 

This way I can replace many "components" by such "view interfaces", and the caller deals only with the interfaces with their expected, well defined behaviour.

All workarounds, fixes and platform differences, version differences are well hidden in the structures behind.

 

 

 

 

 

 

 

 

Edited by Rollo62

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×