Native Multi-platform Game: iOS with Kotlin-Native quirks (10 of …)

Ana Redmond
4 min readOct 1, 2018

--

The games for the first app are mostly ported to Kotlin: compiled to iOS using Kotlin-Native and running on an iPad. As we began testing the games on the iPad this week, we started finding some unexpected issues. I came from an Android background, so, some of these issues might be obvious to anyone who has more iOS development experience than me. Here is some of the bigger ones and how we solved them.

Class initialization on iOS vs. Android

Kotlin has many different ways of initializing objects during creation:

  • Declare and initialize simultaneously
  • Initialized in init {} block
  • Initialize in the constructor

Kotlin defines the order of execution for the initializers. However, we ran into a situation for which the order of execution was unclear. In our Android code, we had many cases where the Parent class constructor calls a method that is overridden in the Child class to initialize a variable defined in the child class.

For example, in the following Parent and Child class, the initChild() function is called from the Parent’s init which sets the value of the variable c declared in the child class.

open class Parent {
init{
initChild()
}
open fun initChild() {
...
}
}

class Child:Parent() {
var c:String = "Declare"
init
{

}
override fun initChild() {
c = "Child"
}
}

When I run this code on Android, the value of c ends up as “Child”, whereas on iOS, it is “Declare”. This implies that for Android the child c value is set first, before the superclass init{} -> initChild() is called. But, in iOS, it is the opposite. The superclass init{} -> initChild() function is called first, and then the declaration initializer for variables in the Child class is called, setting the value to “Declare”.

This problem causes errors due to things not being correctly initialized on iOS, for classes that work fine on Android. If this was new code, I would recommend not using this type of initialization, and using the Kotlin provided init {} blocks instead. But, this is ported Android code, and I did not want to make too many changes that would trigger additional changes. So, in this case I solved the problem by moving the declaration of the var c to the Parent, even though it is only used in the Child class.

open class Parent {
var c:String = "Declare"
init
{
initChild()
}
open fun initChild() {
...
}
}

class Child:Parent() {
init {

}
override fun initChild() {
c = "Child"
}
}

With this change, the initialization order is the same in Android and iOS: parent declaration sets c to “Declare”, and then the init{} -> initChild() sets the value to “Child”.

Touch Events on iOS vs. Android

Our games were originally built on Android. When we ran them on iOS, at times the state of the game was incorrect after user touches screen or lifts their finger from the screen. Barb found several such bugs after only a day of testing. I had to try and fix it before she did more testing. After a day of debugging, it appeared that on iOS a touch down event is not automatically followed by a touch move, and a touch up event is not automatically preceded by a touch move, whereas on Android it is. Some of the game code was assuming this behavior and hence would sometime end up in an indeterminate state. Instead of trying to find each of those issues and correcting individually, I worked around it by sending an extra touch move event on touch down or touch up to our games on iOS.

Lists and Arrays on iOS vs Android

Our Android game code often uses Arrays instead of Lists for performance. Some of that crept into the interfaces defined in Kotlin and invoked from Swift. However, Swift has no differentiation between an Array type and a List type. And just to make it really confusing, it is called an Array in Swift and behaves like a List. Kotlin List type maps to a Swift Array type. Kotlin Array types map to a custom class GDKStdLibArray in Swift. So, a simple initialization of a Kotlin Array in Swift looks not so simple:

let gifts = GDKStdlibArray(size:1, init:{_ in
GDKGift(...)
})

It doesn’t use the simplified Swift Array syntax for initialization, getters etc. So, where-ever I needed to call Kotlin code from Swift, I changed the function signature to Kotlin List type instead of Kotlin Array type. Fortunately, since its Kotlin and not Java, changing Array type to List type doesn’t cause any issues. They both have the same function signatures in Kotlin. And my Swift Code now looks like any other Swift Array.

Testing on the math games is well in progress now. Some of these cross-cutting concerns are getting fixed fast. It is time for me to work on Firebase Data Integration next.

--

--

Ana Redmond

Software Designer and Developer. Mother. Building educational apps for my daughters to teach them math concepts at infinut.com