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")
}
Kotlin:
val x = 3
when (x) {
1 -> print("x == 1")
2, 4 -> print("x == 2 or x == 4")
else -> print("x is something else")
}
String interpolation
Swift:
var name = "Mike"
print("Hello \(name)")
The string can also be output in a format:
let str = NSString(format:"%d , %f, %ld, %@", 1, 1.5, 100, "Hello World")
print(str)
Kotlin:
var name = "Mike"
println("Hello $name")
val str = String.format("%d, %f, %d, %s", 1, 1.5, 100, "Hello World")
print(str)
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.
}
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")
Do the same thing in Kotlin:
val sayHello : (String) -> String = { name: String ->
val result = "Hello $name"
print(result)
result
}
sayHello("Mike")
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)")
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}")
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 thevar
property of the class.
class Person {
var name = "Lily"
}
let p1 = Person()
p1.name = "Justin"
print("\(p1.name)")
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
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 }
}
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)
}
}
In Kotlin the extension method could be the top level method, just add the type before .
:
fun String.someMethod() : String {
return this.trim()
}
enum
Swift enum:
enum CompassPoint {
case north
case south
case east
case west
}
Multiple cases can be put in one line, separated by comma:
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
When using the enum, we can drop the type using a shorter dot syntax:
var directionToHead = CompassPoint.west
directionToHead = .east
Swift enum has an allCases
property, which exposes a collection of all the cases.
Kotlin:
enum class Direction {
NORTH, SOUTH, WEST, EAST
}
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
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
}
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)")
}
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
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
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)")
}
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
- Swift book: https://docs.swift.org/swift-book/
Top comments (0)