A direct face-off between the mobile platforms
A quick search for "android vs. ios" will turn up many articles arguing the benefits of one platform over the other, with most focusing on points such as market share, usability and device fragmentation. There are some posts that offer the "developer's perspective" on the platforms, but very few provide in-depth technical comparisons for anything more than basic features of a trivial app. And there's a reason for this lack of detailed comparisons: companies with sufficiently complex mobiles apps usually have one person or team building for Android, and a different person or team building for iOS. The two platforms use different programming languages (Java and Objective-C), have different software development kits (SDKs), and utilize different development tools, so it's no surprise that different human resources are used for each project as well.
GQueues is an online task manager, which previously only had an HTML5 mobile web app. Recently I just finished developing both the GQueues app for Android and the GQueues app for iPhone & iPad. While these apps are not as complex as a first-person 3D shooter game, they certainly would not be considered trivial - storing and managing thousands of tasks for a user, supporting multiple accounts, providing background syncing to the web and offering complex filtering, sorting and grouping options. From this experience I hope to offer a fairly unique perspective analyzing and comparing the creation of the same GQueues app on both platforms.
I have 12 years of experience as a software engineer, but this was my first time making an Android app, and my first data-focused iOS app (I made two games for iOS 3 back in 2010, but they mostly dealt with animations and Bluetooth connectivity). The last time I wrote any significant Java code was in grad school classes, and my Objective-C coding was limited to the two games. So I basically started with an equal blank slate on both platforms.
In short, I was able to pick up Android development twice as fast as iOS. With Android I spent a week reading books, following tutorials and creating test apps that implemented all the core functionality that would be required for the GQueues app. After that I had the foundation to start architecting the app and wrote my first lines of code for the project. Within another week I was comfortably developing on Android without having to rely on a resource to implement each new feature.
With iOS, I followed the same process, but it took two weeks of experimentation before I felt ready to start the initial app code base. Much of this time was spent exploring all the intricacies of the Core Data APIs. Figuring out how to setup and centrally manage PersistentStoreCoordinators and ManagedObjectContexts for each user in a thread-safe manner took some work, but was critical to supporting multiple accounts within the app (and probably worth a separate blog post of its own). Even more time consuming was developing a scalable architecture for the FetchedResultsControllers that backed the tables of tasks, queues and categories that users would view and manipulate. Another 2 weeks of development passed before I was comfortably coding on iOS.
Overall, the quality of the Android documentation corpus (official docs, third-party tutorials, books, code samples, StackOverflow posts) is much higher in my opinion. I gleaned many architectural best practices from reputable open-source Android apps, such as the 2012 Google I/O app, which Google graciously made available to developers. Moreover, the Android platform itself is open-source, so I was able to dive into the platform code when necessary to figure out particularly troubling issues. While there is a proliferation of iOS documentation, much of it has become outdated with the significant changes made in iOS 5 and 6, including the introduction of Automatic Reference Counting (ARC). Thus, many code samples (including Apple's official samples) and ways to approach problems are inaccurate and should actually be ignored in favor of newer methods. Sifting through it all just took more time.
As shown in the Stats Overview above, developing the Android app was about 10% faster than the iOS app, even though as part of the Android app development I re-wrote the entire syncing code for the backend servers that was previously used with the HTML5 mobile web app. Creating an app that eschewed the generic, antiquated interface provided by iOS 6 added additional time, but even considering that, Android development just moved faster.
The usefulness of the books in the lists above was limited, because, as is with all tech books, their content was somewhat outdated and for the most part only dealt with introductory level concepts. However, plowing through them in a couple days at the beginning of the projects was a fairly quick way to digest core functionality. Online resources were by far the most valuable for both projects.
I'll share just a few comments about the developments tools of each platform, as this topic already has a lot of coverage. I'm not a huge fan of either Eclipse or XCode - they both have strong and weak areas (and really I just prefer my Vim text editor). Search in Eclipse is ridiculously slow and cumbersome. Documentation search in XCode Organizer is infuriatingly sluggish. Filtering by log tags in Eclipse (with the logcat integration in the Android plugin) is super useful. Code completion is really good in both IDEs. Interface Builder in Xcode is useless (more on that later). XCode Instruments, however, are extremely useful in profiling, measuring and debugging. Google had not yet released Android Studio when I first developed the GQueues Android app, but I'm looking forward to testing it out as I make updates.
As far as testing code while you write it, the Android emulators are a complete waste of time (it's really a joke how slow they are). In my development cycle I always deployed to real Android devices for testing - it was much quicker. The iOS Simulator, on the other hand, is very fast and made the dev cycle much more efficient. For each new piece of code I would start my testing with the simulator and only switch to device testing once it was more fully formed.
With Android I had test devices on each version of Android the app supported (except Gingerbread), and then relied heavily on my beta testers to get decent device coverage later. Obviously with iOS this was much less of a concern and I just used the oldest and newest devices supported for testing.
Test Devices
A requirement of the GQueues app was that it work on phones and tablets of all sizes, with the layout optimized for the different form factors. With the myriad of devices running Android, it's no surprise that the Android platform has mature components to help developers support the variety of dimensions. RelativeLayouts, which provide the ability to position one view in relation to another, have been available since the first version of Android and are key to creating flexible, responsive layouts. Additionally, with Android, all layouts are specified in XML, which is actually a very clean, simple and efficient way to design screens - a feature I only fully appreciated after creating layouts in iOS.
Auto Layout, Apple's analog to RelativeLayouts, is quite new (introduced in iOS 6) and its integration with Interface Builder (IB) is horrendous. I spent days fighting Auto Layout in IB, as has every iOS 6 developer, building precise constraints for views only to have IB completely change all my specifications because of its "smart" system to constantly maintain unambiguous layouts. I learned all the tips and tricks to deal with this fragile part of IB to no avail. I finally ended the pain by taking all layouts out of IB and simple wrote them long-hand with pages of boiler-plate code. If you avoid IB and Apple's geeked-out ASCII art style of layout coding, the implementation of Auto Layout is actually very powerful and straightforward to use. Supposedly Apple has improved much of this with iOS 7, but I have yet to test it out.
When an app must be optimized for both small and large devices, the key to success is dynamic composition of views based on available screen real estate - known as an "Adaptive Layout." Tablets may display two or three views all on one screen, whereas a phone may only show one view at a time. Android supports this design technique through the use of Fragments, which are independent, self-contained modules that can be dropped into Activities as needed. By utilizing Fragments it was very easy to setup different layouts for GQueues by adjusting a few lines of XML for various screen sizes. To me, fragments were a very natural solution because they were modeled after the well-known Object-Oriented principles of high cohesion and loose coupling.
iOS supports the use of multiple ViewControllers on a screen through Custom Container View Controllers (you can also use a Master-Detail template, but the widths are static and customization prohibited). I found Apple's documentation of this nascent feature convoluted and incomplete, with the best resources being Ray's iOS 5 tutorials and the WWDC videos. After more work than seemed necessary I was able to architect GQueues layouts to present multiple views simultaneously for the iPad and only a single view for the iPhone.
Suffice it to say, supporting device rotation on Android takes a huge amount a work and is the source of many bugs, whereas iOS requires just a pinch of effort while the platform does the rest. With Android, when you rotate a device it essentially terminates the entire stack of views and recreates each one when rotation completes. So to support rotation with the GQueues app I needed to ensure the current state of everything could be properly saved at anytime and restored on resume. With iOS, the platform manages almost all rotation concerns for you. The only real issue I had to worry about after rotation was adjusting the position of any views that weren't fully handled by auto layout.
There are a couple very common layouts on the web that were extremely difficult to implement for the GQueues app on both Android and iOS. One example I'll mention was displaying tags on the task details screen. Each tag is a variable width and the tags should wrap to the next line when necessary. On the web this is achieved easily by setting the float value in CSS. Neither Android or iOS support this "Flow Layout" natively - which meant I had to add a lot of code to manually calculate and position tags to achieve this "flow" appearance. The Android code I ended up writing was based on the talk by Romain Guy and the open source flow layout by Artem Votincev. On iOS I took a similar approach using the total container width and making calculations for each tag to set the auto layout parameters as necessary. In both cases the amount of work required was surprising.
A common complaint about the Android ecosystem is extreme fragmentation. Carriers are very slow to push out updates, so there are huge cohorts of devices running older versions of Android, which in turn means developers must refrain from using the latest features of the OS if they want broad device coverage. However, I found the Android community has made great strides in addressing this issue by providing libraries that back-port many of the newest features. By using Android's official Support Library and Jake Wharton's ActionBarSherlock library I was able to use nearly every feature desired in Jelly Bean (4.2) while still supporting devices running Android 2.2.
With iOS, support for older versions of the OS is pretty much non-existent, though not nearly as crucial. In my planning stages I did spend a fair amount of time considering which versions of iOS I would support. While 83% of devices were reportedly running iOS 6 at the time, I had concerns about excluding owners of the original iPad, particularly because my parents and sister - hard-core GQueues users - all had the device. Ultimately I decided to only support iOS 6+ so that I could use Auto Layout exclusively without having to implement any of the deprecated layout techniques, which would have increased development time significantly. I solved the original iPad dilemma (at least for my family), by giving them 4th generation iPads to replace their antiquated devices :)
At the heart of both GQueues mobile apps is the data - storing it on the device and syncing it with the web. The Android and iOS platforms have very disparate systems for managing data. Android offers ContentProviders, which are subclassable interfaces that sit on top of SQLite databases and act as structured frameworks for all the app's data needs. ContentProviders have a high learning curve and it required a lot of upfront work to get one running for GQueues. Once setup though, it was easy to expand and customize as I built out the full data model.
As background info, the GQueues web service is backed by Google App Engine's Datastore, which is a highly scalable and distributed NoSQL data storage system. SQLite, on the other hand, is a standard relational database, which obviously doesn't scale very well, but that's irrelevant for the app since it's only storing a single user's data. (As an aside, I made the architectural decision to create separate databases for each user, which was paramount to providing quick and simple support for multiple accounts in the app, but the implementation details deserve a post of their own). Anyway, a huge advantage with Android was the ability to create SQLite Views to back Smart Queues. Figuring out the very complex table joins and subqueries needed to support Smart Queues took some work, but enabled more efficiency and faster loading of Smart Queues since the filtering was not happening in the code.
With the iOS app I used Core Data, which is the platform's "schema-driven object graph management and persistence framework." Essentially it can be thought of as a NoSQL datastore, although interestingly, it is in fact backed by a SQLite database (well, that was the most reasonable choice of several options). iOS does allow users to create SQLite databases directly, but it's all done with regular C code and lacks built-in integration with other iOS components. Core Data also has a high learning curve, but ultimately I chose it over a standard SQLite database because you get so much functionality "for free" - including caching, data model migration support and the use of a NSFetchedResultsController, which makes providing data for the tables in the user interface much easier.
A key to managing any dataset, of course, but particularly vital with syncing, is the use of transactions - atomic, consistent, isolated, durable units of work (ACID). On Android transactions were very straightforward and implemented the same way as most relational databases management systems, thus, maintaining data integrity was not too difficult. Plus, by strategically taking advantage of SQLite's support for UNIQUE ON CONFLICT REPLACE clauses with certain columns during table creation, updating records atomically during the sync process required virtually no work at all.
Transactions with Core Data (an in-memory object graph) are not fully supported in the strictest sense. By using separate child ManagedObjectContexts for background thread processing (thread confinement) along with strategic placement of @synchronized blocks I was able to implement proper handling of data updates and syncing while avoiding incorrect overwriting. iOS's suggestion for efficiently updating or creating objects was only marginally helpful, and overall I found using Core Data cumbersome despite its purported advantages. Additionally, whereas SQLite in Android enabled fast loading Smart Queues, in the iOS app all filtering had to be performed in code, which is slower even with the use of extensive caching.
Adding fairly robust full text search capabilities to the Android app was simple. I modeled my implementation after search in the Google I/O app, using the FTS3 feature of SQLite to create a virtual table populated by several triggers set on the table that stored a user's tasks. From there it was just a matter of designing the search interface and adding storage for search history.
Core Data in iOS does not support full text search natively, so I chose to implement basic search functionality through the use of LIKE clauses in a predicate for task descriptions and notes. It is certainly not as powerful as full text search, but I reasoned it still covered a large percentage of real life use cases.
I'll only mention a few APIs that I used in building various features of the GQueues apps for comparison purposes.
Regular Expressions were critical for implementing the Quick Add parsing in GQueues and fortunately both Android and iOS provided RegEx support natively. Android's Pattern and Matcher APIs have been available since version 1 and include support for a fairly extensive set of regular expression syntax including look-ahead and look-behind assertions. iOS introduced the NSRegularExpression class in iOS 4, and I was happy to discover that my pain-stakingly crafted expressions in Android could be used almost verbatim in iOS.
In designing the app's interface I wanted the user to be able to swipe between pages of task details. In Android I used a ViewPager along with the fledgling FragmentStatePagerAdapter, which is truly in the experimental stage and only available through the Support Library. I spent several days getting an initial implementation hooked up to the data, and several more fighting bugs related to duplicate menu options and proper handling when the data changes. This was all way more difficult than I expected and I would have left this out of the app except that swiping through tasks is such a great user experience. The UIPageViewController on iOS was significantly easier to use, though it still had several quirks I had to work through, and adding my own caching system was necessary to make swiping between the complicated views actually usable.
Android provides a state of the art speech-to-text API that is very easy to use. With 20 lines of code I was able to integrate the RecognizerIntent into GQueues and provide a customized voice input feature. Unfortunately, iOS has yet to provide an API for the tech behind SIRI, so developers are left to use third-party libraries or rely on the keyboard's microphone option for voice input. I surveyed a variety of third-party options, including Nuance (the provider of voice recognition for SIRI), but found the free libraries lacking and the paid services cost-prohibitive. So ultimately the GQueues app relies on people using the built-in keyboard microphone option, which is quite adequate, as long as users remember it's there.
Through the use of Intents Android makes it simple to integrate your app into others installed on a person's device. Again, with a small amount of code I was able to allow people to create GQueues tasks from inside other apps by supporting the ACTION_SEND intent. Android also obviously provides a full widget platform and I built a few initial widgets, with plans to add more in future updates. iOS is clearly behind in the areas of cross-app integration and home screen widgets, completely lacking support for both features.
As noted in the Stats Overview, I beta tested both apps with real users for a little over a month. The testing volunteers in both groups were fantastic, helping uncover dozens of bugs, providing suggestions for additional features and offering feedback on user-interface elements that weren't quite right. The beta program, which I managed with private Google Groups, also helped me ensure that I was launching apps that would truly be useful to people. Near the end of each beta period I gathered more structured feedback through a survey which helped me judge if the apps were ready to launch.
On-boarding Android testers was as easy as posting a link to an APK which they could download to their device (well, they also had to flip a switch in Settings to allow installs of apps not on Google Play). Google has made it even easier now to test apps with real users by supporting alpha and beta versions in the Developers Console and staged rollouts. I'm looking forward to using both features as I launch updates to the Android app.
iOS beta testing was much harder, despite using the service TestFlight, which does simplify the process a great deal. In keeping with Apple's insatiable appetite for control, the UDID of every device used for testing must be added to the certificate used to sign the beta version of the app. Consequently, every time I needed to add beta testers, whether a single person or a new cohort, I had to create and distribute a new build of the app. On top of that, Apple limits you to 100 registered test devices per year, so I had to carefully allocate my spots, which is the reason I had half as many iOS testers as Android.
Of course no Android / iOS comparison would be complete without mentioning the publishing process. Posting the GQueues app on Google Play was a real joy. I was able to launch the app as soon as I decided it was ready. I clicked the button and within 30 minutes it was available on Google Play worldwide and customers were installing it on their devices. Publishing on the App Store, as almost every iOS developer can attest, is a demoralizing experience. After months of intense, rigorous coding, I was forced to submit my creation to Apple and wait 7 days at which point the reviewer spent 2 minutes looking at the app before giving the almost obligatory initial rejection. I then spent a day making required changes and submitted again so I could wait another 8 days before finally getting approved. Of course there are many truly horrific App Store submission stories out there which make my experience seem like a walk in the park. Regardless though, I felt very anxious and uncomfortable relinquishing so much control over my business to such a temperamental third-party.
As you can see from my analysis above, in my experience building the GQueues mobile apps, one platform does not clearly outshine the other. Both Android and iOS have areas where they are really strong and mature, and other aspects that definitely need some improvement. Looking at the history of both platforms, it seems like Android may currently have more momentum, not only in terms of marketshare, but considering the huge improvements to their UI in the last two years and steady enhancements to their development platform. Apple though is the king of secrecy and I'm sure they are hard at work building what they believe to be the next revolution in mobile computing (besides finally killing off skeuomorphism). In either case, when I think about the emergence of the app eco-system in the last 6 years, I feel fortunate to be able to develop on these platforms during this time of such rapid advances in mobile technology.
I love building products! And Python. And dark chocolate. When I'm not leading the team at GQueues, I can be found running ultras on the trails of the Rocky Mountains.