Kotlin/Native iOS

3. Coroutines and Immutability of K/N

Yuya Horita
Quick Code
5 min readDec 28, 2018

--

Coroutines

What is coroutines? This official guide is very helpful for understanding what is coroutines and how to use coroutines.

This chapter’s topic is to use coroutines in K/N and its current status.

Updating Gradle

To use Coroutines in K/N, update dependencies insharedNative/build.gradle .

kolinx-coroutines-core-common and kotlinx-coroutines-core-native are new. One more, in settings.gradle add a line enableFeaturePreview('GRADLE_METADATA') .

Then, Sync Now .

Use Coroutines from iOS

Now, you can use coroutines APIs in common module. In actual.kt , define this CoroutineDispatcher .

Coroutine dispatcher determines what thread the corresponding coroutine uses for its execution.

Next, define CoroutineScope . We need a scope that runs on iOS event loop.

The scope is used to generate a coroutine. I don’t want it visible from iOS so the MainScope class is defined with internal . But the access level is depended on you.

CoroutineScope is a scope for new coroutines. This means every coroutine builder inherits the scope’s coroutineContext . Thanks to this both context elements and cancellation are automatically propagated. For example, the scope’s cancellation invokes cancellation of coroutines launched inside the scope.

Let’s try calling coroutines. Define showHelloCoroutine function in actual.kt and helloCoroutine suspend function in common.kt . The launch function is an extended function of CoroutineScope class. This is one of coroutine builders.

We can call it like the following.

Here I separated to two functions. One is like a coroutine wrapper function and the other is a suspend function.

In most cases, suspend functions have business logic and we want to share them. So here, I defined helloCoroutine suspend function in common.kt to use from Android.

On the other hand, suspend functions cannot use directly from iOS yet so I prepared smth like a wrapper function for the suspend function.

Suspend functions are not exported to a framework in this time. But there is an issue about this.

HTTP Request with Ktor Client.

The above example is a little boring, just printing on console. We will implement HTTP request with Ktor, here.

Updating Gradle

(Updated on January 16, 2019)

Simple Request

Create a API class in common.kt

The HttpClient class is included in Ktor library. The request suspend function calls client.call with Gethttp method.

Next, define a wrapper function for this suspend function as an extended function of Api class in actual.kt .

This passes https://tools.ietf.org/rfc/rfc8216.txt as a parameter. What is registered for RFC8216 ? You will see soon.

Let’s try call it in ViewController.swift

Kotlin lambda which returns Unit is exported as a closure which return KotlinUnit , not Void. The last return is required because of this.

Anyway, this will be performed asynchronously and print HTTP Live Streaming document. We could use http request from iOS 🎉

Currently, Coroutines support only for main thread. But, supporting for multi-threaded coroutines is issued .

K/N immutability

Frozen

In K/N, immutability is a runtime property. This can be applied using freeze() function in kotlin.native.concurrent . We can check the frozen status using isFrozen .

This is a part of kotlin.native.concurrent package. isFrozen property is defined here. Any? type has isFrozen property apparently.

K/N ensures the important invariant, mutable XOR global . This means the object is either immutable or accessible from the single thread.

Some objects are frozen by default, like

  • Primitive types such as kotlin.String, kotlin.Int
  • object singletons
  • enum class

These status can be checked easily, like the following. In actual.kt ,

If a mutating operation is applied to a frozen object, InvalidMutabilityException is thrown.

IncorrectDereferenceException

A class object is not frozen by default. And K/N ensures mutable XOR global .

What happens if we access a not frozen object? Try this, defining the following function in actual.kt .

In ViewController.swift .

This code crashes with

IncorrectDereferenceException is thrown when the object is accessed from another thread.

Fix this crash by freeze(), like

This will work and the following will be shown on console

These reference may be helpful for our understanding

@ThreadLocal and @SharedImmutable

When you use top level global variables of non-primitive types, ThreadLocal or SharedImmutable annotation sometimes may be helpful.

Try with simple example. Define this top level variable in actual.kt.

And access it from different thread in ViewController.swift.

A class object is not frozen so this throws exception. How about using this?

There is no change even though this hello object is frozen !!

In fact, for top level variables, @SharedImmutable or @ThreadLocal annotation is needed.

  • SharedImmutable: make the object frozen (immutable) and accessible from another thread
  • ThreadLocal: make the object state thread local and mutable (the changed state is not reflected to other threads).

Then, the following lines are shown on console successfully.

These annotation may go away in upcoming releases.

Summary

I introduced coroutines and immutability of K/N.

I want to show an example using reactive programming and architecture framework in next chapter. (coming soon)

Reference

--

--

Yuya Horita
Quick Code

Master of Nuclear Physics, CyberAgent, Inc. FRESH LIVE. M3. Software Engineer. Twitter: https://twitter.com/horita_yuya ,GitHub: https://github.com/horita-yuya