4 keys for writing cross-platform apps

These days, most every application has the potential to be a cross-platform application. That’s great for users but often difficult for developers. Targeting more than one platform opens a whole warehouse of Pandora’s boxes: how to handle paths, how to write desktop application UIs, how to compile a binary for another platform, and lots more. Here are four key concepts to guide you when writing successful cross-platform applications.

Microsoft Windows is always the exception

No other operating system requires making exceptions like Microsoft Windows does. Linux and macOS are similar enough that most of their behaviors are compatible. But Windows has many behaviors that deviate from both of them, which makes for complex cross-platform development.

Line breaks

On Linux and macOS, the newline character (\n) is the standard line-break character for text files. On Windows, it’s the return and newline characters (\r\n). Many editors on Windows can be configured to use the Linux or Mac style by default, and that’s a good choice for consistency’s sake.

Web form text input from Windows machines to a server (whatever OS it runs) is particularly susceptible to the line-break exception. Text entries submitted from a browser on Windows will have \r\n line endings. You’ll need to ensure your web framework automatically converts those to \n for the sake of consistency.

File path separators

Windows can use the backslash (\) as a path separator. On Linux and macOS, the backslash is, in almost every context worth mentioning, an escape character. Paths on those platforms are joined with the forward-slash (/). The good news is that most any recent edition of Windows can also use the forward slash for a path separator.

To keep things normalized, software developers should avoid manually constructing paths out of strings whenever possible, and use some kind of object-oriented path-handling method instead. For instance, Python has the pathlib module in its standard library, which converts paths into objects instead of strings. The proper path separator can then be applied programmatically, regardless of what OS you’re on.

Case sensitivity in file systems

Windows has something of an entrenched culture of case-insensitive file systems. That is, the operating system itself respects case sensitivity, but the default behavior for the FAT, FAT32, and NTFS file systems in Windows is to ignore case.

This isn’t much of a problem as long as all the filenames used in your application are case-insensitively unique—that is, you don’t have myfile and MyFile in the same directory. That’s a good strategy to follow at any rate.

Platform-native UIs are hard—use the web if you can

For all the hate thrown at Electron, it solves an important problem: How to write truly cross-platform apps that present consistently. Electron solves this issue by using web technology to render an app’s front end, essentially making apps into locally hosted web applications.

Most objections over Electron center on things like the size of the deliverable (which can reach into the hundreds of megabytes), or that a web front end isn’t robust enough for certain jobs, or the mere fact that the front end has to be built with HTML, CSS, and JavaScript.

The first objection is completely legitimate. Electron builds tend to run big, as Electron’s design requires an entire, standalone web browser instance to be bundled with it. Hence the rise of alternatives like Tauri, which uses the Rust language and delivers a far smaller package. The movement is away from monolithic browser-included deliverables and toward leveraging the operating system’s existing web-view components whenever possible.

The second complaint—that some apps need more powerful behavior than a web browser can deliver— has been growing less true with time. In many cases, it’s less about the capabilities of the browser, and more about finding a front-end framework that lets you use those capabilities well. For interactive 3D graphics, for instance, the three.js library delivers results as impressive as most full desktop apps.

The third gripe—overreliance on HTML, CSS, and JavaScript—is also valid. Writing web applications is an art and science all its own, distinct from other kinds of application development. The staggering range of choices for front-end interactivity—Angular? Vue? React? Vanilla JavaScript?—can leave prospective developers bewildered. The good news is there’s a massive culture of tutorials, tooling, reusable components, documented practices, and functional examples for creating apps with web UIs. Tools like HTMX simplify writing apps that use common behavioral patterns, and some languages offer libraries that programmatically generate front-end code (Anvil for Python is one example).

When cross-compiling, use the other platform

Cross-compiling is the practice (and art) of compiling a program on one platform so that it runs on another one. It’s also one of the more complex undertakings when delivering an application across platforms.

The single biggest rule of cross-compiling, no matter what the language, is it’s always easier to compile directly on the target platform—sometimes by orders of magnitude. The other big rule is, if you can pay someone else to do it for you, it’s worth it.

Some languages have the tooling and features to make cross-compiling less painful. Rust, for instance, offers cross-compiling as semi-native functionality in its toolchain. But you’ll still need to bring in some additional bits to complete the experience—for example, a proper linker.

One big problem with cross-platform compiling is how asymmetrical it can be. If you’re a macOS user, it’s easy to set up and maintain Windows or Linux virtual machines on the Mac. If you use Linux or Windows, it’s harder to emulate macOS on those platforms. Not impossible, just more difficult—the biggest reason being the legal issues, as macOS’s EULA does not allow it to be used on non-Apple hardware. The easiest workaround (though hardly the cheapest) is to simply buy a separate Macintosh system and use that. Another option is to use tools like osxcross to perform cross-compilation on a Linux, FreeBSD, or OpenBSD system.

Another common option, one most in line with modern software delivery methods, is to use a system like GitHub Actions (via GitHub-hosted runners) or Azure Pipelines to build your software on any of their supported target platforms. (Both GitHub and Azure support macOS.) The downside is paying for the use of the service, but if you’re already invested in either platform, it’s often the most economical and least messy approach. Plus, it keeps the burden of system maintenance out of your hands.

Keep an eye on application development platforms

The way we write and deploy apps is always in flux. Who would have anticipated the container revolution, for instance? Or predicted the dominant language for machine learning and AI (and many other things) would be Python? To that end, it’s always worth keeping an eye on the future, since cross-platform deployment is fast becoming a must-have feature.

Consider WebAssembly. It’s shaping up as a key cross-platform runtime, and a way to achieve portability without having to sacrifice too much performance. And it’s even being eyed as a possible substitute for container technology in the right use cases. The Wasm runtime world is still in its early stages, in all its incarnations. But if your target is “as many platforms as possible,” it’s worth looking into what Wasm can offer.

Source

Yorum yapın