In the last article we’ve introduced Kotlin, a modern language for the JVM and now a Google approved language for Android development. We have shown how simple it is to use Kotlin on the server side to develop SmartFoxServer Extensions.
Now it’s the time to see how to integrate the SFS2X Java API with Kotlin to build a client application.
» Setting up the project
In the previous episode we have talked about Jetbrain’s IntelliJ, a multi-language IDE that is also provided as a “Community Edition”, free of charge. This is the easiest way to get started, as the IDE supports Java and Kotlin out of the box without additional downloads.
We also need a copy of the latest SmartFoxServer client API for Java, which we will unzip in a folder of our choice. Once IntelliJ is installed we can go ahead and create a new project, from File > New > Project, choose Kotlin (Java) as the project type and continue with the procedure.
Next, we need to add the SFS2X client API to our project: right-click on the project’s name, select Open Module Settings, and select the Libraries item from the left-side column. From the second column click the plus (+) sign and navigate to the folder where you have previously unzipped the API.
» A basic Kotlin/SFS2X client
We can now write our client code:
import sfs2x.client.SmartFox import sfs2x.client.core.BaseEvent import sfs2x.client.core.IEventListener import sfs2x.client.core.SFSEvent import sfs2x.client.requests.LoginRequest import sfs2x.client.util.ConfigData class SFSEventListener : IEventListener { override fun dispatch(evt: BaseEvent?) { when (evt?.type) { SFSEvent.CONNECTION -> onConnection(evt) SFSEvent.CONNECTION_LOST -> println("Connection lost") SFSEvent.LOGIN -> println("Logged in as: ${sfs.mySelf.name}") SFSEvent.LOGIN_ERROR -> { val errorMsg = evt.arguments.get("errorMessage"); println("Login failed: ${errorMsg}") } } } fun onConnection(evt:BaseEvent) { val success = evt.arguments.get("success") as Boolean if (success) { println("Connected to server...") sfs.send(LoginRequest("", "", cfg.zone)) } else println("Connection failed!") } } val sfs = SmartFox() val cfg = ConfigData() val listener = SFSEventListener() fun main(args: Array<String>) { println("SFS2X API Version: ${sfs.version}") cfg.host = "127.0.0.1" cfg.port = 9933 cfg.zone = "BasicExamples" sfs.setClientDetails("Kotlin", "") sfs.addEventListener(SFSEvent.CONNECTION, listener) sfs.addEventListener(SFSEvent.CONNECTION_LOST, listener) sfs.addEventListener(SFSEvent.LOGIN, listener) sfs.addEventListener(SFSEvent.LOGIN_ERROR, listener) // Start connection sfs.connect(cfg) }
Before we start with the main() method notice a few stark differences with Java. We don’t have to enclose our main() method and variables in a class. This approach resembles more dynamic languages such as Python or Javascript, at least on the surface.
You can also notice that variables can be declared in an apparently type-less fashion using the val or var keywords. The former represents an immutable value (equivalent to Java’s final modifier) while the latter is used for mutable values.
We said that this approach is “apparently type-less” because in reality it isn’t. Kotlin is a statically typed language that supports type inference, which means that the compiler will auto-detect what types are being used even without explicit declaration. In those rare exceptions where type detection is not possible an error will be generated. In any case we can always be explicit with our type declarations using this syntax:
val sfs:SmartFox = SmartFox()
(Also notice how objects are instantiated without the use of the new keyword, as in Java)
With this out of the way we can now go back to our main() method. The first line outputs the version of the API in use. Here we can see another interesting feature in Kotlin: string templates, which allows to write complex expressions directly in a string without the need for concatenation or placeholders.
The rest of the code should be straightforward: we set up the basic settings for the connection and add several event listeners, all pointing to the same instance of our SFSEventListener class. This in turn overrides the dispatch() method, just like in Java, and uses a when block to dispatch different events to their handlers. You can think of this as the equivalent of the switch statement in Java.
Finally in the when block we have used a mix of lambdas and method references to show that both are supported, just like in Java 8 and most modern languages supporting the functional paradigm.
In a nutshell, for those not entirely familiar with these concepts:
- lambdas: are anonymous functions that can be passed “on the spot”, typically they contain just a few lines of code. For longer code you may want to use the next type.
- method references: these are references to methods declared somewhere else in the code, and can be passed around just like any other variable. These are useful when the method contains more than just a few lines of code.
» Next steps and resources
To learn more about Kotlin and its capabilities we highly recommend several interesting resources:
- Google I/O ’17 Introduction to Kotlin (YT video)
- Official Kotlin language website
- Try Kotlin online (try Kotlin without installing anything)
- The Kotlin reference (also available as downloadable PDF)
In the next installment we will put all we have learned so far to good use, by building a client and server example in Kotlin.
Stay tuned.