DEV Community

Dandan Meng
Dandan Meng

Posted on

Swift vs Kotlin

This article is aiming to help an Android developer get a quick knowledge of the Swift programming language. (Writing from this perspective.)

I didn’t mean to write a very long and detailed article to cover everything, just a quick one to get you onboarded.

This article is also suitable for:

  • Experienced programmer (who knows other programming languages) wants to learn Swift.
  • iOS developer (who knows Swift) wants to learn Kotlin.
  • iOS beginner developers.
  • Developer hasn’t used Swift for a while and wants to get a quick refresher memo.

Basic types

Swift Kotlin
Bool Boolean
Array Array, List, MutableList
Set Set
Dictionary Map

Other types are basically the same.

Syntax

Swift Kotlin
variables (not totally equals, see elaboration below) let/var val/var
named arguments at: 0 at = 0
function func name() → returnType fun name(): returnType
absence of a value nil null
unwrapped type String! -
if if number != nil if(number != null)
provide default value if nil or null xxx ?? “default string” ? : ”default string”
if it has value, do {} if let number = Int(”333”) {} ?.let {}
for loop for i in 1...5 {} for (i in 1..5) {}
for loop for i in 1..<5 {} for (i in 1 until 5) {}
do while loop repeat {} while do {} while ()
this instance self this
value object struct data class
as? as?
as! as
try? -
try! -
class initializer initializer constructor
init a mutable list var someInts: [Int] = [] val someInts = mutableListOf()
init a empty dictionary/map var namesOfIntegers: [Int: String] = [:] val namesOfIntegers = mutableMapOf()

Constants and Variables

Swift:

  • let you can not assign value again. If the type is struct, you can not update any var properties of the instance. (If it's a class, then you can update var properties).
  • var can both assign values or update properties of the instance.
  • var also makes an array mutable.

Kotlin:

  • val means final in java, you can not assign values again, but you can update the public vars of the instance.
  • var means you can assign values again.
  • collections types’ immutable or mutable are controlled by the data type.

Switch case

Swift:

var x = 3
switch x {
    case 1: print("x == 1")
    case 2, 4: print("x == 2 or x == 4")
    default: print("x is something else")  
}
Enter fullscreen mode Exit fullscreen mode

Kotlin:

val x = 3
when (x) {
    1 -> print("x == 1")
    2, 4 -> print("x == 2 or x == 4")
    else -> print("x is something else")
}
Enter fullscreen mode Exit fullscreen mode

String interpolation

Swift:

var name = "Mike"
print("Hello \(name)")
Enter fullscreen mode Exit fullscreen mode

The string can also be output in a format:

let str = NSString(format:"%d , %f, %ld, %@", 1, 1.5, 100, "Hello World")
print(str)
Enter fullscreen mode Exit fullscreen mode

Kotlin:

var name = "Mike"
println("Hello $name")

val str = String.format("%d, %f, %d, %s", 1, 1.5, 100, "Hello World")
print(str)
Enter fullscreen mode Exit fullscreen mode

Function and Closure

The Swift function has an argument label:

func someFunction(argumentLabel parameterName: Int) {
    // In the function body, parameterName refers to the argument value
    // for that parameter.
}
Enter fullscreen mode Exit fullscreen mode

while parameterName is used inside the function, argumentLabel is used by the caller.

When an argument label is not provided, parameterName also plays the role of argumentLabel.

If you don't want to specify an argument label at all, the argument label can be omitted by using underscored _.

Closure

Closure is similar to lambda in Kotlin.

A simple Swift example:

let sayHello = { (name: String) -> String in
    let result = "Hello \(name)"
    print(result)
    return result
}

sayHello("Mike")
Enter fullscreen mode Exit fullscreen mode

Do the same thing in Kotlin:

val sayHello : (String) -> String = { name: String -> 
    val result = "Hello $name"
    print(result)
    result
}

sayHello("Mike")
Enter fullscreen mode Exit fullscreen mode

Common:

  • The type can be inferred from the context.
  • Can be passed as arguments as another function’s parameter to achieve nested functions (high-order functions).
  • if the closure/lambda is the last parameter, we can put it outside the function’s parentheses. if it’s the only parameter, we don’t need to write parentheses().

Differences:

  • In Swift, only single expression closure can omitting the return keyword; In Kotlin, the last expression will be treated as the return value, no return keyword in lambda.
  • Swift has shorthand argument names such as $0, $1, $2.

Custom types

Swift Kotlin
class class
protocol interface
extension extension methods

class

The class of Swift and Kotlin looks quite the same.

The inheritance is using : and the subclass function could override the super class's function.

There is an order requirement: the class need to be put before any protocols.

The only thing different is the constructor, in swift it’s initializer:

class Person {
    let name: String
    init(name: String = "") {
        self.name = name
    }
}
let p1 = Person()
print("\(p1.name)") // default name: ""

let p2 = Person(name: "haha")
print("\(p2.name)")
Enter fullscreen mode Exit fullscreen mode

In Kotlin, we could achieve the same result by writing like this:

class Person(val name: String = "") {
}
val p1 = Person()
print("${p1.name}") // default name: ""

val p2 = Person(name="haha")
print("${p2.name}")
Enter fullscreen mode Exit fullscreen mode

struct

Struct is value type.

struct vs class:

  • class can inherit.
  • struct is value type, copies never share data; class is reference type, all copies will share the same instance.
  • class has deinit.
  • class instance can be let , and you can change the var property of the class.
class Person {
    var name = "Lily"
}

let p1 = Person()
p1.name = "Justin"

print("\(p1.name)")
Enter fullscreen mode Exit fullscreen mode

This is ok.

But if Person is a struct:

struct Person {
    var name = "Lily"
}

let p1 = Person()
p1.name = "Justin"
// Compiler error: Cannot assign to property: `p1` is a `let` constant
Enter fullscreen mode Exit fullscreen mode

When using struct, we have to use **var** p1 = Person().

protocol

protocol is just like interface in Kotlin.

We could define function and properties as contracts.

The properties could be:

protocol SomeProtocol {
    var mustBeSettable: Int { get set }
    var doesNotNeedToBeSettable: Int { get }
}
Enter fullscreen mode Exit fullscreen mode

There are slight differences in protocol and interface, like the class implementing the protocol doesn’t need to use the override keyword on functions.

extension

extension is also different. In Swift, extension is more like a place to hold the functions and properties.

extension String {
    func trimmed() -> String {
        self.trimmingCharacters(in: .whitespacesAndNewlines)
    }

    mutating func trim() {
        self = self.trimmed()
    }

    var lines: [String] {
        self.components(separatedBy: .newlines)
    }
}
Enter fullscreen mode Exit fullscreen mode

In Kotlin the extension method could be the top level method, just add the type before .:

fun String.someMethod() : String {
    return this.trim()
}
Enter fullscreen mode Exit fullscreen mode

enum

Swift enum:

enum CompassPoint {
    case north
    case south
    case east
    case west
}
Enter fullscreen mode Exit fullscreen mode

Multiple cases can be put in one line, separated by comma:

enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
Enter fullscreen mode Exit fullscreen mode

When using the enum, we can drop the type using a shorter dot syntax:

var directionToHead = CompassPoint.west
directionToHead = .east
Enter fullscreen mode Exit fullscreen mode

Swift enum has an allCases property, which exposes a collection of all the cases.

Kotlin:

enum class Direction {
    NORTH, SOUTH, WEST, EAST
}
Enter fullscreen mode Exit fullscreen mode

In enum we can also define properties and functions, which is the same for Swift and Kotlin.

Optionals

While the syntax of Swift’s optional type and Kotlin’s nullable type looks similar (a question mark following the type), they are different.

Swift’s optional type is more like Java’s Optional.

You will always need to unwrap it before using the value.

var someString : String? = nil
print(someString?.count) // print nil
print(someString!.count) // Fatal error: Unexpectedly found nil while unwrapping an Optional value
Enter fullscreen mode Exit fullscreen mode

When the variable has value, we want to use it:

var someString : String? = "Hello"

if (someString != nil) {
    print("\(someString) with length \(someString?.count)")
    // print: Optional("Hello") with length Optional(5)

    print("\(someString!) with length \(someString!.count)")
    // print: Hello with length 5
}
Enter fullscreen mode Exit fullscreen mode

Note that when used directly, the variable will always be an Optional.

Actually in Swift there is a simpler way to do this, using if let:

if let someStringValue = someString {
    print("\(someStringValue) with length \(someStringValue.count)")
}
Enter fullscreen mode Exit fullscreen mode

The someStringValue is the unwrapped value from someString, and the block is only entered when it's not nil.

In Kotlin:

var someString : String? = null
print(someString?.length) // print null
print(someString!!.length) // NullPointerException
Enter fullscreen mode Exit fullscreen mode

The difference is mainly when the variable has value:

var someString : String? = "Hello"
if(someString != null) {
   print("$someString with length: ${someString.length}")  
}
// print: Hello with length: 5
Enter fullscreen mode Exit fullscreen mode

In Kotlin, after we check the variable is not null, we could use the value directly, in the following code inside {} , the compiler knows the variable is not null.

If let and guard let

As we see in the above sample in the Optional section, the if let can unwrap the Optional value, and only execute the block when it has value.

The guard let does the opposite: the else block will be entered only when the value is nil .

func printSquare(of number: Int?){
    guard let number = number else {
        print("Oops we got nil")
        return
    }

    print("\(number) * \(number) is \(number * number)")
}
Enter fullscreen mode Exit fullscreen mode

guard let can be used to do the argument checking and early return.

After the guard, the number is no longer optional, it has value.

Last note

I won’t suggest spending much time to learn a language syntax in every detail.

Just grab some basics and you are ready to go, you will explore more on your daily work.

Anyway, I hope this article is handy and useful to you.

References

Top comments (0)