Swift vs Kotlin

Despite the title being a sign of a coming fight, this article has no intention to proclaim a winner since there is no battle to be won. The point here is to explore the advantages and weak spots of each language, training is a way of improving and comparing things we know is a good way of learning more and be a better developer.

Before we start, I should point out that I’m not going to evaluate every single difference between them, just the ones I find more interesting. So off we go to our little journey putting those two incredible languages side by side.

Classes

Initialization

Here is a point that curiously is both good and bad in Kotlin--let's check Swift first.

In Swift, classes have no default initializers (constructors), and Structs have a default initializer with every non-private var member as parameters, which turns out to be very practical.

class Foo {

  init(boo: String) {
    //…code
  }

}

struct Boo {
    var boo: String
    var moo: Int
}

var go = Boo(boo: "for", moo: 42)

Kotlin has a very clever and at the same time “verbose” way of declaring something they call primary constructors and secondary constructors.

Following is a simple default constructor which defines a member of some type

class Foo(val boo: String) {
}

You can even declare without brackets if no internal code is needed

class Foo(val boo: String)

In need of changing the primary constructor? Just use the init keyword without params.

Now, the first issue is when we have three or more params--and believe me, that is going to happen sooner or later.

The second issue--which again is both good and bad--is with secondary constructors. This idea of having “secondary” constructors, which is basically overriding, is a little bit cleaner thanks to the primary being in the class declaration. Depending on how we use it, things can get messy. An example would be an overcomplicated constructor with too much responsibility.

class Foo(val boo: String) {
  constructor(val boo: String, val moo: Moo) : this(boo) {
    moo.doSomethingClever()
  }
}
// you can use either this
val kot = Foo(boo = "Hey")
// or this
val lin = Foo(boo = "Hey", moo = "Ho!")

Structs vs Data Classes

Swift Structs are similar to the notion of C/C++ Structs, which I judge to be a simpler concept than Classes since the latter has some extra capabilities, check out the following excerpt from Apple Swift docs:

  • Inheritance enables one class to inherit the characteristics of another.
  • Type casting enables you to check and interpret the type of a class instance at runtime.
  • Deinitializers enable an instance of a class to free up any resources it has assigned.
  • Reference counting allows more than one reference to a class instance.

And one of the most important details is that they are passed by copy, not by reference, that can be a huge time saver or a pain in the back. Knowing when to seize this behavior properly is key to decide which one is best for a given solution.

struct Foo {
  let boo: String
  var moo: Int
}

Now, Kotlin Data Classes are a special type of class whose purpose is to hold data. This makes the class have some members with default behavior (equals, hashCode, toString, copy).

data class Foo(val boo: String, val moo: Int)

Here is the deal though: they are not the same thing. They may be used for similar purposes, but they are different pieces of the puzzle. Structs can be a replacement for Classes until you really need something only a Class can give you--they will avoid pointers in that case for instance. Now Data Classes are normal Classes with some improved behavior for data modeling. It's helpful, but they are still a Class.

Different pieces with different behaviors, that in some contexts can have the same purpose.

Missing in Swift? Abstract Class

Well, some of you will surely disagree with me on the need of OOP concepts in some languages. Some argue that Swift is much more Functional Programming (FP) oriented than the others and that Kotlin should follow same standards.

The thing is that neither of them is totally oriented towards FP. They serve systems that are equipped for OOP and because of that should offer support for them. So FP may have been the target (couldn't find any resource beside the code itself) but unfortunately Cocoa frameworks has its needs, as does Android.

In that case not having abstract classes is a little bit of a burden. The silver lining is that this presents an opportunity to explore other solutions. For instance, the easiest thing to do is use preconditionFailure(“method must be overridden”) and then inherit the “abstract” class while trying to control every init method to avoid initialization mimicking the abstract class behavior.

Another option is to use protocols and/or split the code you wanted to be executed there in other classes/structs (componentization).

In Kotlin that is supported the same way it is in Java.

abstract class BaseFoo {
  abstract fun hello()
  fun doSomething() {
    // this can be used on my children
  }
}

This means a little less OOP power but doesn't necessarily mean that Swift is bad at it. It just means that some things must be solved differently.

Vars initialization

Swift is like your nice and simple friend that avoids complication and tries to keep things light so everything can always be fun and protects you because you trust each other. But as that kind of friend, he sometimes stops you from doing things you really need to in awkward ways. Maybe it is a lack of deeper knowledge -- I still have lots of ground to cover in both languages -- but it still boggles me.

You can’t declare a member of Class/Struct without initializing it. Actually, there is a way: using the exclamation sign after the type. What that does is force Swift to believe you that you’re going to fill it later, which is kind of a trick.

In this case, lateinit from Kotlin does a better job, doesn’t do anything by force, and conveys a specific meaning to the programmer. It has its own limitations though.

Extension vs Extension

Extension in Swift is pleasant, you can extend the functionality of any class/struct with a simple extension keyword, and voilá there you have some useful code added.

class Foo {
  func doSomething() {}
}

extension Foo{
  func imFromTheExtension() {}
}

//somewhere else

let boo = Foo()
boo.doSomething() //this will work
boo.imFromTheExtension() //this too

Although in Kotlin it can be easy to also extend classes, I still find it to be a little bit awkward. It is less straightforward to use than in Swift.

class Foo {
 fun doSomething() { println("member") }
}

fun Foo.imFromTheExtension(i: Int) { 
  println("extension") 
}

// somewhere else
val boo = Foo()
boo.doSomething() // this will work
boo.imFromTheExtension(42) // this too

The good news is that they are somewhat similar in both of them including some of the rules. For instance, you can’t override existing functionality--a member function always has precedence.

Companion vs Static

Now here is something quite clumsy: Companion. Just to clarify before I continue, although there is a way to do static code in Kotlin it is not static by default. You have to specifically tell the JVM to make things static--otherwise they are just instance methods.

Another crucial point about the companion keyword is that companion is not about static. It is more interesting than that. Companion is good for creating anonymous classes (object expressions) inside other classes--that is its real purpose.

window.addMouseListener(object: MouseAdapter() { override fun mouseClicked(e: MouseEvent) { // ... } override fun mouseEntered(e: MouseEvent) { // ... }})

// same as

class Foo {
  companion object FooMouseAdapter: MouseAdapter {
    override fun mouseClicked(e: MouseEvent) { // ... } 
    override fun mouseEntered(e: MouseEvent) { // ... }
  }
}

In Swift, the only thing that is almost an anonymous class is a Tuple, but is not the same thing. That is not necessarily a bad thing, it’s just different. In its realm the point is to use closures, which are similar to “anonymous functions”.

{ (parameters) -> return type in
   statements
}

So instead of creating a function and passing its name to another function there is the possibility of doing the following

someArray.sorted(by: { (s1: String, s2: String) -> Bool in
  return s1 > s2
})

Are those things similar? Are they the same? Yes and No. What really matters is to know each one and compare the situations where they are used, that helps explore new possibilities in each language.

The Any type

Something that is quite clever in both of them is the concept of the Any type. It is a solution to interface with Cocoa and Android so that an explicit type is not needed.

Since there is no type, a cast will be needed to use the variable properly, but you can inject framework code and assume some things will be there. It is clever but prone to errors. I don't recommend its use as there is no place where a type or generic couldn't be used except if you're in a rush and find it faster to forfeit your future self and use Any instead of Generics.

Named arguments

Maybe the reason Swift has such an enforcing and somewhat verbose way of writing functions/methods by default is that its main purpose was to be used in place of Objective-C, which had that way of building functions using the params names as part of the declaration. I liked it but hurts quality if misused.

In Swift a parameter without an underscore in front of it must be identified with its name before the value, that is by default.

func manyParams(param1: String, param2: Int, param3: Any) {}

func usingTheFuncAbove() {
  manyParams(param1: "Hi", param2: 0, param3: Kotlin())
}

// With underscore

func manyParams(_ param1: String, _ param2: Int, _ param3: Any) {}

func usingTheFuncAbove() {
  manyParams("Hi", 0, Kotlin())
}

Now enforcing something is okay, but what good does it do to allow us to simply ignore that feature using an underscore? I would rather keep the enforcement. I support using variables instead of direct parameters values, which can be targeted as magic numbers.

// Either this one
let width = 2
let height = 4
openBoxPerimiter(width, height)
// Or this one
openBoxPerimiter(width: 2, height: 4)
// But I would avoid
add(2, 3)

Why would I avoid the add(_ x:Int, _ y:Int)? The reason is that I dislike using direct values in function parameters, as I said I support well-named variables instead of hardcoded values.

In Kotlin the opposite is true. The default is that parameters should be named, but they are not mandatory when using the function unless you need to differentiate default params from the others or have a crappy way of coding.

fun manyParams(param1: String = "default", param2: Int, param3: Any) {}

fun usingTheFunAbove() {
  manyParams("Hi", 0, Swift()) //this is still crappy
}

// Do either of the following
fun usingTheFunAbove() {  
  manyParams(param2 = 0, param3 = Swift()) // using default and named params 
}

fun usingTheFunAbove2() {  
  val cleverFlag = 0
  val previousLanguage = Swift() 
  manyParams(cleverFlag, previousLanguage) // using default and good vars
}

In this case, there is a tie, both of them allow the functions/methods to be declared with named arguments and choose when to use them. I have only one reason to prefer Kotlin here: I hate that underscore, it’s confusing, easy to forget and makes code readability awful.

Transpiling?

My wish was that both of them could be almost interchangeable, that could maybe lead to unified language in the future: Swiftlin or Kotlift, who knows?

The real deal would be to program in one and just use in the place of the other, that is almost possible yet there are too many small and big things that match and don’t match so transpiling would currently be annoying.

What would we gain with transpiling? I’m not strongly advocating towards it. I think it would be nice to program once in one language for two strong mobile platforms though. It wouldn’t matter which one, that could have been a better option than those hybrid solutions in my opinion since the native code would be generated with the “sister” language.

Is there more?

Well actually there are a lot of other details we could compare--similarities and differences--and the point here is to bring some of them up and invite all of you to explore them while comparing. That will definitely increase your ability to solve problems with alternatives in slightly analogous situations.

Finally, Swift and Kotlin are two powerful languages used in two of the most widely-used Operating Systems of our time, iOS and Android in case you´re still wondering, Swift is even growing as a popular alternative in backend applications. I finish here with two challenges, stop producing apps with hibrid languages and start coding with Swift and Kotlin, there is a lot to learn with them and in the end some might enjoy it.