Tuesday, November 21, 2017

Real-world native mobile development. From ObjC to Swift and from Java to Kotlin

My team is responsible for our Company's Mobile Apps - Our biggest app is a reasonably sized one (39 ViewControllers at the current time of writing, with about a dozen significant "service" components) with both iOS and Android versions which goes back a few years. The first version was released in about 2013 off the top of my head.

Historical iOS Development in Objective-C

We originally developed the App in question for iOS only, targeting Objective-C (This was before Swift existed.) Later on we built up an Android version using Java (again, predating Kotlin which is only a "stable" option for Android development in the last 6 months or so).

We were quite excited when Apple announced Swift, and immediately played around with it, but we didn't consider it seriously until Swift 2 - We started to port small parts of our iOS app over to Swift 2 in mid 2016 - probably about 4 months before Swift 3 was released. Swift 2 / Xcode 7 was definitely unstable, however we found Swift 3 / Xcode 8 to be pretty stable and nice to work with, and I definitely feel like with Swift 4 and Xcode 9 is mature and reliable now.

Why port Objective-C to Swift?

We felt that we would very much like the benefits of Swift (strong typing, null safety, nicer syntax, etc). As such, we have an informal policy that all new code should be written in Swift (within reason).

While there's a strong argument to be made that old code isn't broken and should be left alone, nobody on the team really wants to have old stuff sticking around forever, and everyone's in agreement that in a perfect world, the entire app would be Swift. Porting everything in one go would be an enormous time investment and generate a huge pile of bugs, so we're not doing that at all. Instead the policy is (informally)

  1. If you're doing new code, do it in Swift
  2. If you're between tasks, and you feel like you want to port some code to Swift, go forth and just do it.
  3. If you're working on some old Objective-C code, you should seriously consider porting it to swift while you're at it

Between 2 and 3, I've found that over the last year or so the app is slowly but surely transitioning it's way over to more and more Swift. We'll still have Objective-C for at least another year or maybe two, but I can definitely see it going away eventually.

Thoughts on Swift

  • Null-safety is fantastic, however there is definitely a learning curve. I found everyone on the team had no problems dealing with the concept of null safety, it's just a bit of a job to learn all the syntax and rules and how they apply everywhere (i.e. nullable generic types and stuff like that)
  • Swift's more concise syntax is great, compared to Objective-C. It's hands down nicer than Java or C# or anything else (except maybe Kotlin, see below)
  • Swift protocols with associated types are weird and very complex. I feel like I understand what's going on and why they're done that way, but I find this very hard to explain to anyone else in a satisfactory manner. The only saving grace is that you tend not to have to use PAT's very much
  • Having programmed C# and C++ for a long time too, I very much like having generics and strong typing available in Swift (Compared to Objective-C)
  • Supporting "mixed projects" where you can have Swift and Objective-C files side by side in the same binary really is a killer feature for porting and upgrading.
    Our company has huge amounts of C++ and C# code, and if Microsoft had somehow pulled off same-project-different-language-per-file compatibility like Apple has here, the world would be a truly different place. It's quite likely all our C++ would have been replaced by C# years ago, rather than hanging around forever. (Now, since C++11 has revitalised the C++ world, it's no longer in danger of going away, but in the mid to late 2000's things were indeed looking dire for C++ for us)
  • Overall, I find Swift nicer to work in than Objective-C, Java or C#, however it is absolutely a much more complicated language. The upside of Swift borrowing lots of features from lots of other languages is that it offers many great, modern options for problem solving and development of safe and high quality code. The downside is there's a boatload of stuff that you have to learn, which has caused basically every developer on my team who's encountered it to feel incompetent for the first few months.

Android Development

As mentioned previously, we'd done our Android stuff entirely in Java. I personally am not a fan of Java, as I find it more verbose and clunky when compared to other languages, however I must say it hasn't been nearly as bad as I thought it would before we started it. A lot of this comes down to the fact that the language itself is really only about 30-40% of the development experience. The rest comes down to platform API's, and while I find Android definitely lags behind iOS in this area, it's still a darn sight better than the standard Java stuff I'd done in the past.

When Google announced that Kotlin was going to be officially supported in Android Studio 3.0, like with swift, I was quite excited. I'd seen kotlin before, but never really taken it seriously, and I'd lumped it in with other "experimental" languages like Groovy, Scala and Clojure that look awesome on paper, but don't look stable/widely supported/used enough to use for "real" development. Google promising official support immediately catapulted kotlin out of this group and into the "big league" - at least that was my feeling anyway.

Since Android Studio 3.0 has released, we've started porting parts of our app over to kotlin, following a similar process to what we did with Swift. As with our Swift port, we had pretty much the same reasons - wanting to take advantage of the nicer syntax, strongly safety guarantees and stuff like that. Refer to the "Why port to swift" section above.

Thoughts on Kotlin

  • As with swift, null safety is great. We had been extensively using @NonNull attributes all over our Java already, but Kotlin's support for working with nullable types with smart casting and the ? operator really make a big difference.
  • I definitely appreciate Kotlin's more concise syntax as compared to Java. Type inference is great.
  • Kotlin supports the same-project-different-language-per-file which (as with Swift) a killer feature for porting code. While the auto-converter is certainly far from perfect, it provides an enormous head-start when porting code.
  • I'm stunned by how good Android Studio (IntelliJ) is when dealing with Kotlin. I was expecting it to be like Xcode/Swift were, and have lots of bugs and missing features for the first year or two, but my experience has been pretty much since the Android Studio 3.0 release that it's of equal quality to the Java support. Hat's off to JetBrains here
  • Kotlin has some weird stuff though.
    - WTF Companion Object?
    - Wherefore art thou ternary operator? (Yeah you can use inline if/else, but that looks weird, and even Java has a ternary operator)
    - Returns from lambdas are bonkers. return in a lambda should return from the lambda as that's the common and usual case - the wonky return@thing syntax should be reserved for explicit non-local returns which are out-of-the-ordinary. I feel like they got this entirely backwards
  • Kotlin gets burned by it's extensive Java compatibility a bit - mostly because it inherits the same broken type-erased generics that Java has, which kind of sucks

Kotlin vs Swift

Now here's the best part of any programming blog! Comparing things so we can say which is better. Alas it's not so simple, as they both have ups and downs relative to each other. Here's some things in no particular order.

Syntax

I would personally give a very close win to Swift, but you could go either way here. Both are close (see Swift is like kotlin). Kotlin just has slightly more things which are weird for no seeming purpose. My pet peeve is the val keyword. It's visually incredibly similar to var, and there was strong precedent from many other languages to use let like Swift did, but Kotlin's just made a weird call here. On the other hand, Kotlin does have a lot more 'syntactic-sugar' things built in, which is nice.

Backward Compatibility/Interop

Clear win to Kotlin. One thing which surprised me is how close Kotlin actually is to Java. Not syntactically, but at runtime the majority of Kotlin types are just projections or wrappers around pre-existing Java types like String, HashMap, ArrayList or whatever, which I've found makes Java interop seamless and obvious in 99.9% of cases. Swift on the other hand departs significantly from the Objective-C runtime, defining it's own String, Array and Dictionary types, amongst other things, which can sometimes make things tricky. As evidence of this, the latest version of the Kotlin standard library which you must bundle with your app is 861KB, whereas the Swift 4 stdlib is something like 20+ MB. It's not a fair comparison, as Swift is native code and not JVM bytecode, and also a lot of the Swift std lib is stripped out of your app if not needed, but it does clearly show the point that comparatively, Swift needs a lot more stuff over and above the baseline platform to function.

Modern features/New Paradigms

Clear win to Swift. While I find Kotlin to be great to work with, it doesn't try to depart from Java nearly as much as Swift does from ObjC. As above this makes for much better interop and is easier to learn, BUT there's not as much opportunity to push things forward. Take for example Swift's value types. They can enable paradigms and ideas that you simply can't express in Kotlin. Likewise, Generics in Kotlin are held back by Java compatibility and so are half-assed like the Java ones. Swift makes the choice to break compatibility (Objective-C code literally can't see Swift generic code at all) in order to have "proper" generics without doing things halfway. I find swift to be a fundamentally different thing to Objective-C (or even C#/Java) whereas Kotlin doesn't seem to enable any fundamental new things that you couldn't already do in Java - everything's just much much nicer.

Learning curve

Java->Kotlin is way easier than Objc->Swift. See above. Even though Kotlin has more crazy stuff like companion object, the fact that there's not as much "distance" between it and Java outweighs it all.

App/Runtime stability

Both have been great so far.

IDE/Development Experience

Definite win to Kotlin. I think Swifth with Xcode 9 is definitely good, but the work JetBrains have done for IntelliJ/Android studio is simply fantastic (Around language support anyway. Some of the other things in IntelliJ like Build Configurations and the Git integration are awful, but it's unfair to penalise Kotlin for that)

Compilation Performance

Clear win to Kotlin. It seems a bit slower to compile than Java, but Swift compilation times are more in line with C++ (i.e. Ages)

Productivity

I'd probably say Swift here but you could go either way. They honestly both feel very good, and so much of productivity is subjective and down more to the platform API's than the language itself.

Runtime performance

Both are great. While in theory Swift should be slightly faster than Objective-C and Kotlin should be slightly slower than Java, in practice for all normal app-level stuff we do like rendering UI, network requests, JSON parsing and so forth, I can't tell the difference between code written in ObjC vs Swift or Kotlin vs Java. Because they both have great interop, if you did happen to have a piece of code which actually was performance sensitive, you could just write that one small piece in the other language anyway. If you're worried about performance issues from moving to Swift or Kotlin, don't be.

Porting code

A LANDSLIDE win for Kotlin here. It's relative closeness to Java, combined with the auto-converter in Android Studio make it fairly easy to port most things. The auto-converter screws things up quite a bit, but when it goes wrong I've found the problems tend to be quite easy to fix, once you've done it a few times and learn the patterns of how it goes wrong. Swift doesn't have an auto-converter, so you need to go through your objective-C code and manually port across things line by line. For example, I ported a significant chunk of code from Java to Kotlin in about a day. That same code would have taken me at least 2 days to do ObjC->Swift, most likely 4+.

I have an Objective-C app, should I port it to Swift?

Maybe, however it will be a fair bit of work spread out over a long time, and will bring about a lot of change. Most of the changes will improve your code and make it more readable, stable and maintainable, but some will not. In particular anywhere you depended on the dynamic behaviour of Objective-C. My team and I chose to port ultimately because we like the language better - mostly for the cleaner syntax and static typing. Compared to ObjC, it's more similar to C# (which everyone on the team had prior knowledge of), so that helps too. These reasons don't hold for everyone though. Porting to Swift seems more like a long term thing - Apple is pushing the community hard towards Swift, and it is following. There will come a time (if it hasn't already) where having objective-C code means it's harder to find developers, harder to find code samples, help and documentation online, and other such things, so it makes sense to move to swift for those reasons.

I have a Java app, should I port it to Kotlin?

Yes>. To me, it seems like a no brainer. Kotlin is better than java in many many ways, but doesn't seem to have really any downsides. You (like me) may not like some of the weird things it has like Companion Object, but in my experience those are all fairly superficial and you can get past them without much worry. The auto-converter makes porting your code take a lot less time, and it's actually quite fun.

Conclusion

If I had to sum it up, it goes back to my point before that Kotlin really does feel like super nice modern Java. I like it and it's great to work with but it doesn't bring any significant new paradigms to the table.

Swift on the other hand is a significant departure Objective-C, and with Structs, ARC, and in the future the Memory ownership model stuff, seems to be moving more into the space of Rust and things like that. Don't forget that Objective-C is a dynamic language and actually has more in common with Ruby or JavaScript than you might think.

What's my preference? Superficially Kotlin and Swift are very similar.
Assuming you could somehow magically eliminate all the platform specific differences between iOS and Android - say perhaps you were writing server-side software on linux or something - I think I'd chose Swift. There's a variety of reasons, but the thing that I guess I place the most weight on is the modern features/paradigms (see above). From that point of view, Kotlin and Swift are actually very different.