My Experience building an app for Android and iOS with Qt

I just “finished”[1] an app that runs on iOS and Android using Qt 5.6. It’s written mostly in C++ and QML. Yes, I’m skipping other platforms, like Windows Phone, Windows Desktop, Mac, Linux, etc. Truth be told, I did try to target Desktop, but it only worked for testing. But, for mobile, there are only two that are worth the work.

(I recently updated this app, and I wrote a much less interesting addendum about that.)

Why Qt

Admittedly, writing in C++ might be a deal breaker for some people, but I actually prefer C++. I’ve got experience in Java (starting from Java 1.0), and I’ve done a few apps in Objective-C and Swift. But, in the end, I still write my best code in C++, and I’ve got several years of using Qt, so it was an easy choice for me. (I’d never used Qt 5, though, so there was still some learning to do.)

Qt has also been in the cross platform game for a lot longer than most of the others. Xamarin, PhoneGap, Cocos, etc. are all relatively new. The newer parts of Qt (namely QML) still had some quirks, but on the whole, it was nice to have a stable, mature system to work with, using a language that I’m already very familiar with.

Doesn’t Cross Platform suck?

If you ask iOS or Android programmers about cross-platform development, the majority of the responses are that it’s crap (at best). In my opinion, that’s just not so. Obviously, it depends on the toolkit, too, but, from my one experience, it’s not too bad. Unquestionably, you are limited to the lowest common denominator, or even less. But, for many cases, mine in particular, that’s enough. For a small development team, the trade-offs are worth the restrictions.

What was harder?

With Qt, it’s almost like building a regular application. If you’ve ever developed for one platform, there are always unexpected problems because one platform doesn’t work like the other. Although I’ve worked with Qt and iOS before, I hit a few things writing for Qt on iOS, as well as learning QML (Qt’s UI language) and how it interacts with C++. In some ways, it’s like the interaction between Swift and Objective-C, but more of a pain in the ass. I also hit some problems with how the same code ran on different hardware. Things like network buffering, and fonts. Which leads us to testing…

Testing was a bit of a chore. Even if you only code once, you have to test everywhere. I assume most developers have at least their own device to test with. This is often insufficient, especially now that iOS has 4 iPhone sizes, and Android has many more. Xcode lets you see how it works on every screen size, but on Android, I could only get one usable simulator (which was fairly low resolution). I had an Android tablet, so that let me see it at a much higher resolution. I had two older devices (an iPhone 4, and Nexus S), which, due to my limited feature set, I ended up supporting.

My test devices, development and beta

Releasing also took a while to figure out. This is where you have to do some problem solving that’s not related to coding. If you’re releasing for iOS, you have to figure how to make a build and sign it for the app store. For Android, you have to do the same for Google Play. You’d have to do this anyway, but then you have the added complexity of passing those parameters though the Qt build system. It’s not too hard, but it was an added task.

What’s easier

Well, the biggest is the most obvious. I only had to write one app. I could have written it in about 1/3 to 1/2 the time if I just targeted iOS because I know iOS better than QML and mobile Qt. On the other hand, it was quicker than I could have written an app for Android because 1) I don’t know Android as well, and 2) Java is a pain in the ass for me to use.

But, the hidden cost of most software is maintenance. Even if I was more knowledgeable in the ways of Android, and I could have built the Android version in the same fraction of the time as I estimate it would have taken me in iOS, I’d have two code bases, in two languages, with two totally different sets of bugs. Any features would have to be implemented twice. Protocol changes to the backend would have to be tested twice.

As mentioned before, the feature set is limited, but in some ways, that’s a good thing. By default, iOS just sets the target at the latest SDK. By the time you realize that you could have supported an older device, it may or not take some juggling. Ditto for Android. I was pleasantly surprised to find that my build supported my iOS 7.x device and my Jelly Bean Nexus S

GUI Layout was also a breeze. By “breeze”, I mean, a pain in the ass, but less a pain in the ass as Xcode’s Auto Layout. As I remember, Java’s layout language is equally painful. I spent a few days trying to get the layouts right in QML (Qt’s Layout language). (They have a WYSISWG tool, but it’s not very mature (more on that later)). But, getting a GUI to work across many different platforms is no easy task, and now that I’ve fallen through some of the traps, I think I can get through it more quickly next time. I’m still not there on iOS.

What didn’t work

It’s not native. In the majority of cases, Qt does its own painting, so it doesn’t look like a native app. In general, iOS users expect more of a uniform experience.

Getting fonts to work across platforms is also something I haven’t figured out yet. On iOS, virtually all devices are “retina” resolution now, so one font size works across all devices. But what looks good on iOS, only looks good on some Android devices.

Qt Creator is the worst IDE I’ve ever used. Qt Designer was almost useless. I basically just used it for compiling and deploying, and emacs for editing. I’d actually prefer to use emacs for everything, but I didn’t take the time to generate the proper makefiles to work across all three platforms (iOS, Android, and the iOS Simulator).

Conclusion

Obviously, if you don’t know C++, using Qt is going to be a long road. (You can do it all in QML and Javascript, but that might not be enough.) For me, it turned out to be something like:

1.6(work) + 2(testing)[2] + 1.5(deployment) + 1.1(maintenance)

compared to building an iOS app from scratch (because I’m pretty familiar with that). But, Android would have been unpleasant because it’s in Java, and mostly an unknown. With what I know now, that 1.6x work multiplier would be more like 1.2. Your mileage is certain to vary, but for me, this was the best choice to build a cross platform app, and an even better choice in the future. If I had a lot of C++ code, I might even use this even if I only had to support iOS, since I wouldn’t have to write Objective-C++ wrappers.

Oh, if you’re interested, the code is on github, and the apps are in iTunes and Google Play:

[1]: When is something ever “finished”? In this case, the app is for an event at the end of this month, but for some apps, the lifetime is much longer.

[2]: That number really should be higher because I skimped on Android testing. I just didn’t have enough devices to be thorough.