Over the past six years, I’ve done a lot of iOS development (and written a lot about iOS). I would say it’s been the primary focus of my career. But as a software consultant, I need to be flexible, and I’ve done my fair share of Android development as well.
I’m currently working on a new Android project where I got to use the latest Android Jetpack Components, and I am really impressed.
Google has been busy improving their development tools, languages, and frameworks in the last couple of years, and I believe they have leapfrogged iOS development in providing developers with a solid, well-thought-out framework to develop apps.
Jetpack Changes Things
In the past, Google has been ambivalent about how you should architect your apps. Open-source libraries filled this architectural vacuum. This led to a scenario where two Android projects can differ quite a bit depending on the design patterns and open-source libraries that developers choose.
Now Google has introduced Jetpack, which includes a set of Architecture Components to unify and simplify app development. Providing an answer to the question of how to architect your app makes it easier for new developers to learn best practices.
Because these components are created by Google, they can integrate them all the way from the database layer to the UI. They’ve also made them easy to use by adding support for them in Android Studio.
Google I/O vs. WWDC 2018
A good way to see the latest development practices on each platform is to watch the current year’s videos from Google I/O and WWDC. WWDC in 2018 was a big disappointment for me. It was rumored that they pulled back what they were going to announce this year in favor of speed and reliability improvements to both iOS and Mac OS.
Their “What’s New in Cocoa Touch” video usually offers a good overview of what’s new for iOS. Instead, they spent most of the video talking about the speed and battery improvements they have made behind the scenes.
What was I expecting? That topic could be a blog post in itself. It would have been nice to see a replacement for Core Data, an Auto Layout that was easier to use, better integration between Swift and Cocoa Touch, and improvements to Cocoa Touch itself.
In contrast, when I watched the sessions at Google I/O, I was impressed. In 2017, they introduced Jetpack as an alpha version and encouraged developers to submit feedback on how it could be improved. In 2018, many of the components were good to go. Navigation graphs, LiveData, Room (the database layer on top of SQLite), data binding, MVVM, and so much more. You can see here how it’s matured.
They not only released the components. They also developed many sample apps that used them, so you could see how the components might work in a real app.
1. MVVM vs MVC
- The ViewModel doesn’t contain any UI framework classes, which makes it easy to instantiate it in tests.
- Google added support for data binding from the layout (view) directly to the ViewModel, which is a real code-saver.
- Their implementation of ViewModel also fixes a pain point in Android development: dealing with the lifecycle of the Activity or Fragment. The ViewModel is persistent through rotations of the device and other such events that will cause the Fragment to restart, so you don’t have to write code to save/restore state every time the Fragment goes through its lifecycle events.
- Google introduced data classes in Kotlin. The data class makes it easier to write your Model classes by automatically implementing
equals() hashCode() toString() copy()and more.
On iOS, the Model View Controller (MVC) pattern has a nickname of “Mostly ViewController.” This refers to the bloated size UIViewControllers can grow to if you are not disciplined in factoring away functionality. New developers are on their own to discover this. Also, ViewControllers are a bit tricky to instantiate in tests because they contain UI framework components.
2. LiveData vs. ?
One of the biggest improvements to Android development is the LiveData class. This class encapsulates data that is returned from your database, ViewModel, or even your API.
I can’t tell you how nice it is to set up a query into the database, bind it to the UI, and have it automatically update when new data appears on the database. It’s a lifecycle-aware observable, so when the observing object (like a Fragment or Activity) goes away, a reference to the observable is removed automatically. If your Activity or Fragment is reset when you rotate the device, the Activity or Fragment will reconnect to the LiveData object and get the last value that was stored on the LiveData. Whenever data changes, it will notify the observers.
Those of you familiar with Reactive programming with RxJava or RxKotlin will find it familiar. An advantage over using a Reactive framework is the built-in lifecycle functionality. However, the latest version of RxKotlin and RxJava can integrate with LiveData, so you can have the best of both worlds.
Another advantage of LiveData over a Reactive framework is the built-in support for LiveData throughout the system. LiveData is missing some of the built-in niceties in RxJava or RxKotlin like zip and combineLatest, but Google does provide the MediatorLiveData class to combine multiple LiveData objects into one LiveData object. Writing your own
zip is not hard at all.
There is no equivalent on iOS. Passing data around is very much a manual process done with delegates and callbacks. You can integrate a Reactive framework on iOS, but there is nothing equivalent to the lifecycle-aware functionality.
3. Navigation Architecture vs. Storyboards
This opinion is somewhat debatable, but I think the addition of Storyboards was one of the things that most improved the developer experience on iOS. In one place, you can see the navigation layout of the app. It also reduces the amount of navigation code. The lack of an equivalent for Android development was unfortunate.
Now Google has introduced the Navigation Architecture Component along with Android Studio’s Navigation Editor.
This is where you specify your transitions between Fragments and any data that is passed between them. The other old pain point this solves in Android development is handling the up and back actions. It can get complicated when a Fragment can has multiple entry points or deep linking from a push notification. The Navigation Component handles this automatically.
4. ConstraintLayout vs. AutoLayout
Once iOS needed to support phones of different sizes, they introduced AutoLayout. AutoLayout is a constraint-based layout algorithm. I think it is a very powerful system, but some developers think it is complicated. See my post on Auto Layout Demystified.
Although AutoLayout has a lot of power, you may not need all of it. Simple layouts can be tedious to specify in Xcode’s Interface Builder tool. With the addition of the UIStackView, it has improved, but in my opinion, it is not as easy as Android.
Composing a UI is very simple with Android’s LinearLayout and RelativeLayout. However, they had limitations when working with a complicated UI. They were never as powerful as a true constraint-based system.
In 2017, Google introduced ConstraintLayout as a replacement for RelativeLayout. Unlike AutoLayout on iOS, ConstraintLayout on Android is very simple to understand, and it’s just as capable.
5. RecyclerView vs. UITableView
Another advantage that iOS had over Android is the UITableView. This control performs really well with large datasets because there is very little lag when you scroll. You can also configure it in many different ways (static layout, dynamic layout, indexed, editing features, swipe controls, and more).
Android replaced their ListView with the RecyclerView. The RecyclerView performs much better with large datasets or datasets that change frequently. It still lacks some of the bells and whistles that the UITableView has on iOS, but it is an improvement.
6. Room vs. CoreData
This is not even a fair fight. Both Room and CoreData are abstractions on top of a data store. However, the two technologies take very different approaches.
CoreData on iOS is just an object graph that can have multiple ways of storing its data from in-memory, XML, Binary, or SQLite. It is important to note that even though it can have SQLite as its backing store, CoreData is not a database. You get none of the advantages of SQLite in CoreData. No thread safety, transactions, or SQL Query syntax. The one thing it has going for it is easy migrations.
Room on Android uses SQLite, but it doesn’t hide the fact that you are working with a database. Room improves your experience with SQLite by adding type-safe SQL queries, migrations, and an easy syntax for modeling your data relationships. It also is thread-safe and supports transactions.
Room is not perfect–it is still young. Its one downside is that it could use some improvement with the way it handles foreign keys and many-to-many relationships.
7. Kotlin vs. Swift
Swift is a couple years ahead of Kotlin in development, but the two are very similar. The great thing about both languages is that they are type-safe. The compiler catches a lot of your mistakes before you even run the app for the first time.
If you have ever done Java programming and had to deal with the difference between int and integer, you can appreciate the advantages Kotlin brings to Android (same for the switch from Objective C to Swift). Swift has the slight edge in that it is a little more mature. I wish Kotlin had an easier way to nest multiple let statements and offered better support for array literals, but that is just being picky.
The Android Jetpack components seem to resolve all of the pain points that I had developing older Android apps. I don’t dread working on Android anymore, and I find myself wishing iOS had the same new features that Android now has.
I would be interested in hearing your opinion. Have you developed in both and come to the same conclusion?