iOS V.S. Android (Language Characters)
After a long journey in iOS native stack development, my recent incoming projects will be leaning back to Android side again (previously before I started working on iOS in recent years, I’ve been working on Android for around 3 years). After a roughly going through the contents in google’s official Android developer portal, it is a very instinctive thing for a developer with a similar background like me to come out the idea of making a comparison on the two platforms.
By having a systematically comparison of the two platform, one can understand clearly about the difference and similarities of the two and hence, become easier to memorize and handle the development techniques on both of them.
Since the knowledge map of Android and iOS developement contains a long list of topics, which might include for example: OS Characters, Language, Architecture Components, Engineering Tools & Automations, Challenges and Advantages, and etc. When I was writing this article, I was planning to have several post as a series to cover this. In this post, I would like to start with the comparison from the language aspect.
Comparison
The characters of the two languages are summarized from their official documents: https://kotlinlang.org and https://swift.org. Sections are devided following the language document section strcutures provided on the two document portal.
1. Basics
1.1 Variables
Swift
:- let/var
static let
for thread safety lazy.- support
get/set/willSet/didSet
Kotlin
:- val/var
lateinit var
for lazy properties with thread safety.const
can be used to declare a compile time constant.- support
getter/setter
1.2 Data Classes
Swift
:- Struct: passed by value, copy on modify
Kotlin
:- Data Class: auto generate hash/equal/toString
- Inline Class: aka wrapper class, provides a wrapping on the single wrapped in properties.
1.3 Protocols
Swift
:- Protocol: support default implementation via protocol’s extension
Kotlin
:- Interface:
- support SAM (which can be used as lambda expressions or function input params)
- support interface delegation and hence provide default implementations. (note: members overridden in this way do not get called from the members of the delegate object, refer to this issue)
- Interface:
// add singleton
object GoldColor: AquariumFish {
override val color: String
get() = "gold"
}
// example of interface delegation for fishAction
class PrintFishAction(val food: String) : FishAction {
override fun eat() {
println(food)
}
}
// 此处函数完全通过interface delegation 替代
// 类似于swift 中的extension default implementation for protocol
class Plecostomus: AquariumFish by GoldColor,
FishAction by PrintFishAction("testing food")
1.4 Optional Handlings
Swift
:- if: optional unwrapping
- guard: check
?
for optional chaining!
force unwrapping??
short circuit valuation
Kotlin
:- if: the clause and clause after if block will automatically unwrap the optional.
variable?.let { }
conduct nil check.?:
for short circuit.!!
: for force unwrap.
1.5 Control Flow
Generally, the control condition in Swift does not require a braket but for those in kotlin need. e.g. if (xxxx != xxx) {}
Swift
:- for/while/do-while/if-else
- switch
Kotlin
:- for/while/if-else
- use
when
as a conditional switch
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> {
print("x is neither 1 nor 2")
}
}
1.6 Visibilities
Swift
:- scopes: private \ fileprivate \ internal \ public \ open
Kotlin
:- scopes: private \ protected \ internal \ public \ open
note:
protected
in kotlin is not available on package level declaration,private
in kotlin is same asfileprivate
in Swift.- internal/public/open has same visibility definition accross the two languages.
1.7 Nested Classes
Swift
:- inner class: can be initialized with out the outter class instance.
Kotlin
:- inner class: specified with
inner
keyword, needs to initialize with the outter class instance. - nested class: can be initialized with out the outter class instance.
- inner class: specified with
1.8 Closures/Lambdas
In both language, code blocks in self-conatining form are provided to act as function/function parameter/variables in code. In Swift it is called closure and in kotlin it is called lambdas (actually lambda is a more commonly used term to describe this in consideration of other languages like javascript).
Swift
:- implicit return: only applicable for clousre with a simple expression
- implicit param name: $0, $1
- support passing trailing closure
someFunctionThatTakesAClosure() { // trailing closure's body goes here }
- use
@escaping
to denote a escaping closure.
Kotlin
:- implicit return: support all levels of
- implicit param name: only available for single params
- support passing trailing lambda
val product = items.fold(1) { acc, e -> acc * e }
- support default scope functions: let/run/with/apply/also, there are some trivial differences between those scope lambda, please refer to this article.
- for lambdas in kotlin can be bound to a typealias
typealias ClickHandler = (Button, ClickEvent) -> Unit
1.9 Coding Conventions
Swift
: swift coding guidelines- xcode auto format: select the code and ctrl+i
Kotlin
: kotlin coding conventions- android studio format: select the code and cmd+alt+l
1.10 Operators
Swift
:- support overloading existing operators.
- support the customisation of prefix/infix/postfix operators, taking the following as the example.
struct Vector2D {
var x = 0.0, y = 0.0
}
infix operator +-: AdditionPrecedence
extension Vector2D {
static func +- (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y - right.y)
}
}
Kotlin
:- provides a map between operators and correpsonding functions, you can override the function and hence override the operators.
- you can customize your new infix method
data class Point(val x: Int, val y: Int)
operator fun Point.unaryMinus() = Point(-x, -y)
val point = Point(10, 20)
fun main() {
println(-point) // prints "Point(x=-10, y=-20)"
}
1.11 Annotations (Attibutes)
- For swift: annotations are basically compiler attibutes and used in compilation time, there is no flexibility to customize your own.
- For kotlin: the annotations are API capabilities which can be used in both compile time and runtime. It is quite common to use annotations to generate classes/methods during compilation time and for the purpose of dependency injections.
infix fun Int.shl(x: Int): Int {
this + x
}
// the function call below is identical
1 shl 2
1.shl(2)
2. Advanced
2.1 Dynamic Capabilities
- Swift: heavily focuses on compile time validation, the reflection capability provided in Swift is much limited than other languages.
- Swift officially there are APIs provided limited use cases like
Mirror
in section debugging and reflection - Since in iOS both Swift and ObjC are under the same runtime, hence swift can make use of ObjC capabilities: method swizzling, kvo, kvc, etc.
- Swift officially there are APIs provided limited use cases like
- Kotlin: similar to java, you can dynamically inject dependencies, invoke methods and retrieve properties via reflections.
2.2 Generics
- Swift: using generics are quite straight forward in Swift, where it can be both applied in class and methods. generics can be added with class or protocol constraints.
- In protocols, generics needs to be declared in form of associated types.
- Kotlin: not about the concept of
in
andout
,in
is used in generics types for input parameters (aka consumer),out
is used in generics types for output parameters (aka producer). Can refer this article to enhance your understanding.- for
in
as consumer [? super in Java] - for
out
as producer [? extend in Java]
- for