Kotlin DSL — know your limits

Kotlin DSL —defining mandatory parameters

Or why I wrote another Android library

Since Kotlin was introduced, multiple libraries have opt-in for DSL implementation of their API.
Kotlin DSL is a great tool, it makes your API more readable and easier to use, and if you haven’t gotten around to play with it, I strongly encourage you to give it a try.

But this article is not meant to explain how to implement an API based on Kotlin DSL, there are some great articles about it out there.
Nor am I going talk about how awesome Kotlin DSL is (and it is).
Instead, I’ll focus on the problems I faced when I tried to apply it to my own use cases.

First

https://medium.com/media/850a7346dec798d01d2c96efa93a19a1/href

A nice example for DSL can be found in an Android library called Anko, which allows laying out UI components programmatically (might look familiar to Flutter/Jetpack devs):

verticalLayout {
editText {
hint = "Name"
textSize = 24f
}
button("Say Hello") {
onClick { toast(“Hello!”) }
}
}

Looks pretty easy to understand, right?

So, where’s the catch?

Let’s take a closer look:

button("Say Hello") {
onClick { toast("Hello!") }
}

As you can see, the button text is supplied as a parameter since it’s mandatory for the creation of a button, as opposed to the onClickListener which is optional and therefore defined inside the lambda block.

For a single parameter, this still seems quite readable, but what if there were multiple mandatory parameters?

After giving DSL a try, what I soon found out is that I had the same problem as with plain old Builders — receiving mandatory parameters.

What’s the solution then?

The obvious solution is to verify all mandatory parameters are set as part of the final object build, but that’s a runtime solution and a sign of bad API in my books.

Another solution I would use in builders was to define mandatory parameters in the constructor.
The same trick works for DSL, but I found it contradicts one of the purposes of DSL — being readable.

The final option I tried is using Kotlin Contracts, which I quickly ruled out for two reasons:
1. I didn’t feel it was mature enough.
2. They only apply for top-level functions, not exactly what I was looking for.

https://medium.com/media/b524e2fd23b6d1b3287bb1bc5bdd2f55/href

My goal was clear — a compile-time verification of mandatory parameters.
I wanted a simple solution to this common problem.

Introducing DSLint

Eventually, I decided to be my own best friend and implement a solution to this problem using a custom Android linter library (sorry non-Android devs).

So here’s how it works, let’s say I was to implement a Person builder using DSL and wanted to make sure the person’s name was set.
All I need to do is annotate the class and the name property and let my custom linter do the magic:

@DSLint
class Person {

@set:DSLMandatory
var name: String
}

Clean and simple.
I made it open source, so you can either use it in your project or clone the repo and create your custom solution based on it.

You can grab a look here:

ironSource/dslint

To sum up

As I previously said, DSL is a great tool and I encourage you to try it but also be aware of its limits.
I hope you enjoyed reading and I look forward to hearing about the challenges you faced when trying out DSL and how you approached them.

Click the 👏 to say “thanks!” and help others find this article.

To be up-to-date with great news on Kt. Academy, subscribe to the newsletter, observe Twitter and follow us on medium.

If you need a Kotlin workshop, check how we can help you: kt.academy.


Kotlin DSL — know your limits was originally published in Kt. Academy on Medium, where people are continuing the conversation by highlighting and responding to this story.

Continue Reading Kotlin DSL — know your limits

Interface Builder Is Dead Long Live SwiftUI

But nothing happened! Why? Did I have done something wrong? Let’s debug it. So the code is valid, the debugger is telling me that I increment the variable.

Basically, I have to notify the view that the framework needs to call the body again. Does this mean that the framework will rebuild the UI every time I click the button? Probably not, one thing it’s the definition of your UI, and another is how the UIKit interprets and optimize your definition to render the changes.

How to update a Declarative UI?

@State is a wrapper around an Int that every time the value changes it notify the Host(the UIKit interpreter), and the framework will call body again to see if something changes it will interpret the new description and will draw the changes. That’s a bit different from what we are used to, instead of having instances of the elements and changing the value, we provide a description and tell the framework when it changed. That includes notifying when you want to toggle a button, change the content of a Textfield, or present an ActionSheet instead by calling a function and changing the state of UI elements in UIKit.

With this in mind, let’s see what the other benefits are. There is a live preview initialized by code if you are in macOS Catalina, the good thing is that you can simulate the various state of your App by passing the parameters that you want. So you can preview directly in XCode the multiple states of your App. No need anymore to fake navigation or go through your App, and you can set the views in any state in the preview. Now it’s not perfect, it needs to build the App, and in the Beta, it takes time to render or reload the UI, but still less than rebuilding the App and going through the different flows.

I recommend you to check out “Building Custom Views with SwiftUI” from WWDC2019 at least up to minute 23. It will help you understand how the layout is rendered and what sizes as each view and which position it will have. You will also use “.layoutPriority()” that seems more intuitive than the nightmare that content hugging and content compression resistance priorities are in UIKit.

What are the bad parts of SwiftUI?

View class is clutter with hundreds of methods and modifiers, that gives flexibility on building the framework but really a downside for discoverability. Even with compatibility with UIKit, that means you can use SwiftUI inside UIKit ViewControllers or the other way around, UIViews inside SwiftUI Components. This SDK will only be distributed starting with iOS 13, and it’s not backward compatible, so that means, you need to target update or new release of your App, past September 2020 to cover most of the ios 13 users as recommended by Apple. Today, you can’t do everything in SwiftUI, it’s not really important, it’s good enough, the bases are here to stay, and it will give Apple time to mature the API and the tools. Maybe it will never replace UIKit, and we will have to learn how to live with both.

To sum up, I feel that SwiftUI will allow iOS dev to produce better UI by being straightforward to “test” and verify the implementation. Is it ready yet for developers to start to learn? No, the API it’s still changing, and it’s a good thing, that’s what Beta is for. When will it be ready for prime time? If Apple keep is consistency, around XCode 11.2 or 11.3.

In the meanwhile you can check our example App, EndZone in github.

Continue Reading Interface Builder Is Dead Long Live SwiftUI

Interface Builder Is Dead Long Live SwiftUI

But nothing happened! Why? Did I have done something wrong? Let’s debug it. So the code is valid, the debugger is telling me that I increment the variable.

Basically, I have to notify the view that the framework needs to call the body again. Does this mean that the framework will rebuild the UI every time I click the button? Probably not, one thing it’s the definition of your UI, and another is how the UIKit interprets and optimize your definition to render the changes.

How to update a Declarative UI?

@State is a wrapper around an Int that every time the value changes it notify the Host(the UIKit interpreter), and the framework will call body again to see if something changes it will interpret the new description and will draw the changes. That’s a bit different from what we are used to, instead of having instances of the elements and changing the value, we provide a description and tell the framework when it changed. That includes notifying when you want to toggle a button, change the content of a Textfield, or present an ActionSheet instead by calling a function and changing the state of UI elements in UIKit.

With this in mind, let’s see what the other benefits are. There is a live preview initialized by code if you are in macOS Catalina, the good thing is that you can simulate the various state of your App by passing the parameters that you want. So you can preview directly in XCode the multiple states of your App. No need anymore to fake navigation or go through your App, and you can set the views in any state in the preview. Now it’s not perfect, it needs to build the App, and in the Beta, it takes time to render or reload the UI, but still less than rebuilding the App and going through the different flows.

I recommend you to check out “Building Custom Views with SwiftUI” from WWDC2019 at least up to minute 23. It will help you understand how the layout is rendered and what sizes as each view and which position it will have. You will also use “.layoutPriority()” that seems more intuitive than the nightmare that content hugging and content compression resistance priorities are in UIKit.

What are the bad parts of SwiftUI?

View class is clutter with hundreds of methods and modifiers, that gives flexibility on building the framework but really a downside for discoverability. Even with compatibility with UIKit, that means you can use SwiftUI inside UIKit ViewControllers or the other way around, UIViews inside SwiftUI Components. This SDK will only be distributed starting with iOS 13, and it’s not backward compatible, so that means, you need to target update or new release of your App, past September 2020 to cover most of the ios 13 users as recommended by Apple. Today, you can’t do everything in SwiftUI, it’s not really important, it’s good enough, the bases are here to stay, and it will give Apple time to mature the API and the tools. Maybe it will never replace UIKit, and we will have to learn how to live with both.

To sum up, I feel that SwiftUI will allow iOS dev to produce better UI by being straightforward to “test” and verify the implementation. Is it ready yet for developers to start to learn? No, the API it’s still changing, and it’s a good thing, that’s what Beta is for. When will it be ready for prime time? If Apple keep is consistency, around XCode 11.2 or 11.3.

In the meanwhile you can check our example App, EndZone in github.

Continue Reading Interface Builder Is Dead Long Live SwiftUI

Interface Builder Is Dead Long Live SwiftUI

But nothing happened! Why? Did I have done something wrong? Let’s debug it. So the code is valid, the debugger is telling me that I increment the variable.

Basically, I have to notify the view that the framework needs to call the body again. Does this mean that the framework will rebuild the UI every time I click the button? Probably not, one thing it’s the definition of your UI, and another is how the UIKit interprets and optimize your definition to render the changes.

How to update a Declarative UI?

@State is a wrapper around an Int that every time the value changes it notify the Host(the UIKit interpreter), and the framework will call body again to see if something changes it will interpret the new description and will draw the changes. That’s a bit different from what we are used to, instead of having instances of the elements and changing the value, we provide a description and tell the framework when it changed. That includes notifying when you want to toggle a button, change the content of a Textfield, or present an ActionSheet instead by calling a function and changing the state of UI elements in UIKit.

With this in mind, let’s see what the other benefits are. There is a live preview initialized by code if you are in macOS Catalina, the good thing is that you can simulate the various state of your App by passing the parameters that you want. So you can preview directly in XCode the multiple states of your App. No need anymore to fake navigation or go through your App, and you can set the views in any state in the preview. Now it’s not perfect, it needs to build the App, and in the Beta, it takes time to render or reload the UI, but still less than rebuilding the App and going through the different flows.

I recommend you to check out “Building Custom Views with SwiftUI” from WWDC2019 at least up to minute 23. It will help you understand how the layout is rendered and what sizes as each view and which position it will have. You will also use “.layoutPriority()” that seems more intuitive than the nightmare that content hugging and content compression resistance priorities are in UIKit.

What are the bad parts of SwiftUI?

View class is clutter with hundreds of methods and modifiers, that gives flexibility on building the framework but really a downside for discoverability. Even with compatibility with UIKit, that means you can use SwiftUI inside UIKit ViewControllers or the other way around, UIViews inside SwiftUI Components. This SDK will only be distributed starting with iOS 13, and it’s not backward compatible, so that means, you need to target update or new release of your App, past September 2020 to cover most of the ios 13 users as recommended by Apple. Today, you can’t do everything in SwiftUI, it’s not really important, it’s good enough, the bases are here to stay, and it will give Apple time to mature the API and the tools. Maybe it will never replace UIKit, and we will have to learn how to live with both.

To sum up, I feel that SwiftUI will allow iOS dev to produce better UI by being straightforward to “test” and verify the implementation. Is it ready yet for developers to start to learn? No, the API it’s still changing, and it’s a good thing, that’s what Beta is for. When will it be ready for prime time? If Apple keep is consistency, around XCode 11.2 or 11.3.

In the meanwhile you can check our example App, EndZone in github.

Continue Reading Interface Builder Is Dead Long Live SwiftUI

Interface Builder Is Dead Long Live SwiftUI

But nothing happened! Why? Did I have done something wrong? Let’s debug it. So the code is valid, the debugger is telling me that I increment the variable.

Basically, I have to notify the view that the framework needs to call the body again. Does this mean that the framework will rebuild the UI every time I click the button? Probably not, one thing it’s the definition of your UI, and another is how the UIKit interprets and optimize your definition to render the changes.

How to update a Declarative UI?

@State is a wrapper around an Int that every time the value changes it notify the Host(the UIKit interpreter), and the framework will call body again to see if something changes it will interpret the new description and will draw the changes. That’s a bit different from what we are used to, instead of having instances of the elements and changing the value, we provide a description and tell the framework when it changed. That includes notifying when you want to toggle a button, change the content of a Textfield, or present an ActionSheet instead by calling a function and changing the state of UI elements in UIKit.

With this in mind, let’s see what the other benefits are. There is a live preview initialized by code if you are in macOS Catalina, the good thing is that you can simulate the various state of your App by passing the parameters that you want. So you can preview directly in XCode the multiple states of your App. No need anymore to fake navigation or go through your App, and you can set the views in any state in the preview. Now it’s not perfect, it needs to build the App, and in the Beta, it takes time to render or reload the UI, but still less than rebuilding the App and going through the different flows.

I recommend you to check out “Building Custom Views with SwiftUI” from WWDC2019 at least up to minute 23. It will help you understand how the layout is rendered and what sizes as each view and which position it will have. You will also use “.layoutPriority()” that seems more intuitive than the nightmare that content hugging and content compression resistance priorities are in UIKit.

What are the bad parts of SwiftUI?

View class is clutter with hundreds of methods and modifiers, that gives flexibility on building the framework but really a downside for discoverability. Even with compatibility with UIKit, that means you can use SwiftUI inside UIKit ViewControllers or the other way around, UIViews inside SwiftUI Components. This SDK will only be distributed starting with iOS 13, and it’s not backward compatible, so that means, you need to target update or new release of your App, past September 2020 to cover most of the ios 13 users as recommended by Apple. Today, you can’t do everything in SwiftUI, it’s not really important, it’s good enough, the bases are here to stay, and it will give Apple time to mature the API and the tools. Maybe it will never replace UIKit, and we will have to learn how to live with both.

To sum up, I feel that SwiftUI will allow iOS dev to produce better UI by being straightforward to “test” and verify the implementation. Is it ready yet for developers to start to learn? No, the API it’s still changing, and it’s a good thing, that’s what Beta is for. When will it be ready for prime time? If Apple keep is consistency, around XCode 11.2 or 11.3.

In the meanwhile you can check our example App, EndZone in github.

Continue Reading Interface Builder Is Dead Long Live SwiftUI

Interface Builder Is Dead Long Live SwiftUI

But nothing happened! Why? Did I have done something wrong? Let’s debug it. So the code is valid, the debugger is telling me that I increment the variable.

Basically, I have to notify the view that the framework needs to call the body again. Does this mean that the framework will rebuild the UI every time I click the button? Probably not, one thing it’s the definition of your UI, and another is how the UIKit interprets and optimize your definition to render the changes.

How to update a Declarative UI?

@State is a wrapper around an Int that every time the value changes it notify the Host(the UIKit interpreter), and the framework will call body again to see if something changes it will interpret the new description and will draw the changes. That’s a bit different from what we are used to, instead of having instances of the elements and changing the value, we provide a description and tell the framework when it changed. That includes notifying when you want to toggle a button, change the content of a Textfield, or present an ActionSheet instead by calling a function and changing the state of UI elements in UIKit.

With this in mind, let’s see what the other benefits are. There is a live preview initialized by code if you are in macOS Catalina, the good thing is that you can simulate the various state of your App by passing the parameters that you want. So you can preview directly in XCode the multiple states of your App. No need anymore to fake navigation or go through your App, and you can set the views in any state in the preview. Now it’s not perfect, it needs to build the App, and in the Beta, it takes time to render or reload the UI, but still less than rebuilding the App and going through the different flows.

I recommend you to check out “Building Custom Views with SwiftUI” from WWDC2019 at least up to minute 23. It will help you understand how the layout is rendered and what sizes as each view and which position it will have. You will also use “.layoutPriority()” that seems more intuitive than the nightmare that content hugging and content compression resistance priorities are in UIKit.

What are the bad parts of SwiftUI?

View class is clutter with hundreds of methods and modifiers, that gives flexibility on building the framework but really a downside for discoverability. Even with compatibility with UIKit, that means you can use SwiftUI inside UIKit ViewControllers or the other way around, UIViews inside SwiftUI Components. This SDK will only be distributed starting with iOS 13, and it’s not backward compatible, so that means, you need to target update or new release of your App, past September 2020 to cover most of the ios 13 users as recommended by Apple. Today, you can’t do everything in SwiftUI, it’s not really important, it’s good enough, the bases are here to stay, and it will give Apple time to mature the API and the tools. Maybe it will never replace UIKit, and we will have to learn how to live with both.

To sum up, I feel that SwiftUI will allow iOS dev to produce better UI by being straightforward to “test” and verify the implementation. Is it ready yet for developers to start to learn? No, the API it’s still changing, and it’s a good thing, that’s what Beta is for. When will it be ready for prime time? If Apple keep is consistency, around XCode 11.2 or 11.3.

In the meanwhile you can check our example App, EndZone in github.

Continue Reading Interface Builder Is Dead Long Live SwiftUI

Interface Builder Is Dead Long Live SwiftUI

But nothing happened! Why? Did I have done something wrong? Let’s debug it. So the code is valid, the debugger is telling me that I increment the variable.

Basically, I have to notify the view that the framework needs to call the body again. Does this mean that the framework will rebuild the UI every time I click the button? Probably not, one thing it’s the definition of your UI, and another is how the UIKit interprets and optimize your definition to render the changes.

How to update a Declarative UI?

@State is a wrapper around an Int that every time the value changes it notify the Host(the UIKit interpreter), and the framework will call body again to see if something changes it will interpret the new description and will draw the changes. That’s a bit different from what we are used to, instead of having instances of the elements and changing the value, we provide a description and tell the framework when it changed. That includes notifying when you want to toggle a button, change the content of a Textfield, or present an ActionSheet instead by calling a function and changing the state of UI elements in UIKit.

With this in mind, let’s see what the other benefits are. There is a live preview initialized by code if you are in macOS Catalina, the good thing is that you can simulate the various state of your App by passing the parameters that you want. So you can preview directly in XCode the multiple states of your App. No need anymore to fake navigation or go through your App, and you can set the views in any state in the preview. Now it’s not perfect, it needs to build the App, and in the Beta, it takes time to render or reload the UI, but still less than rebuilding the App and going through the different flows.

I recommend you to check out “Building Custom Views with SwiftUI” from WWDC2019 at least up to minute 23. It will help you understand how the layout is rendered and what sizes as each view and which position it will have. You will also use “.layoutPriority()” that seems more intuitive than the nightmare that content hugging and content compression resistance priorities are in UIKit.

What are the bad parts of SwiftUI?

View class is clutter with hundreds of methods and modifiers, that gives flexibility on building the framework but really a downside for discoverability. Even with compatibility with UIKit, that means you can use SwiftUI inside UIKit ViewControllers or the other way around, UIViews inside SwiftUI Components. This SDK will only be distributed starting with iOS 13, and it’s not backward compatible, so that means, you need to target update or new release of your App, past September 2020 to cover most of the ios 13 users as recommended by Apple. Today, you can’t do everything in SwiftUI, it’s not really important, it’s good enough, the bases are here to stay, and it will give Apple time to mature the API and the tools. Maybe it will never replace UIKit, and we will have to learn how to live with both.

To sum up, I feel that SwiftUI will allow iOS dev to produce better UI by being straightforward to “test” and verify the implementation. Is it ready yet for developers to start to learn? No, the API it’s still changing, and it’s a good thing, that’s what Beta is for. When will it be ready for prime time? If Apple keep is consistency, around XCode 11.2 or 11.3.

In the meanwhile you can check our example App, EndZone in github.

Continue Reading Interface Builder Is Dead Long Live SwiftUI

Interface Builder Is Dead Long Live SwiftUI

But nothing happened! Why? Did I have done something wrong? Let’s debug it. So the code is valid, the debugger is telling me that I increment the variable.

Basically, I have to notify the view that the framework needs to call the body again. Does this mean that the framework will rebuild the UI every time I click the button? Probably not, one thing it’s the definition of your UI, and another is how the UIKit interprets and optimize your definition to render the changes.

How to update a Declarative UI?

@State is a wrapper around an Int that every time the value changes it notify the Host(the UIKit interpreter), and the framework will call body again to see if something changes it will interpret the new description and will draw the changes. That’s a bit different from what we are used to, instead of having instances of the elements and changing the value, we provide a description and tell the framework when it changed. That includes notifying when you want to toggle a button, change the content of a Textfield, or present an ActionSheet instead by calling a function and changing the state of UI elements in UIKit.

With this in mind, let’s see what the other benefits are. There is a live preview initialized by code if you are in macOS Catalina, the good thing is that you can simulate the various state of your App by passing the parameters that you want. So you can preview directly in XCode the multiple states of your App. No need anymore to fake navigation or go through your App, and you can set the views in any state in the preview. Now it’s not perfect, it needs to build the App, and in the Beta, it takes time to render or reload the UI, but still less than rebuilding the App and going through the different flows.

I recommend you to check out “Building Custom Views with SwiftUI” from WWDC2019 at least up to minute 23. It will help you understand how the layout is rendered and what sizes as each view and which position it will have. You will also use “.layoutPriority()” that seems more intuitive than the nightmare that content hugging and content compression resistance priorities are in UIKit.

What are the bad parts of SwiftUI?

View class is clutter with hundreds of methods and modifiers, that gives flexibility on building the framework but really a downside for discoverability. Even with compatibility with UIKit, that means you can use SwiftUI inside UIKit ViewControllers or the other way around, UIViews inside SwiftUI Components. This SDK will only be distributed starting with iOS 13, and it’s not backward compatible, so that means, you need to target update or new release of your App, past September 2020 to cover most of the ios 13 users as recommended by Apple. Today, you can’t do everything in SwiftUI, it’s not really important, it’s good enough, the bases are here to stay, and it will give Apple time to mature the API and the tools. Maybe it will never replace UIKit, and we will have to learn how to live with both.

To sum up, I feel that SwiftUI will allow iOS dev to produce better UI by being straightforward to “test” and verify the implementation. Is it ready yet for developers to start to learn? No, the API it’s still changing, and it’s a good thing, that’s what Beta is for. When will it be ready for prime time? If Apple keep is consistency, around XCode 11.2 or 11.3.

In the meanwhile you can check our example App, EndZone in github.

Continue Reading Interface Builder Is Dead Long Live SwiftUI

Interface Builder Is Dead Long Live SwiftUI

But nothing happened! Why? Did I have done something wrong? Let’s debug it. So the code is valid, the debugger is telling me that I increment the variable.

Basically, I have to notify the view that the framework needs to call the body again. Does this mean that the framework will rebuild the UI every time I click the button? Probably not, one thing it’s the definition of your UI, and another is how the UIKit interprets and optimize your definition to render the changes.

How to update a Declarative UI?

@State is a wrapper around an Int that every time the value changes it notify the Host(the UIKit interpreter), and the framework will call body again to see if something changes it will interpret the new description and will draw the changes. That’s a bit different from what we are used to, instead of having instances of the elements and changing the value, we provide a description and tell the framework when it changed. That includes notifying when you want to toggle a button, change the content of a Textfield, or present an ActionSheet instead by calling a function and changing the state of UI elements in UIKit.

With this in mind, let’s see what the other benefits are. There is a live preview initialized by code if you are in macOS Catalina, the good thing is that you can simulate the various state of your App by passing the parameters that you want. So you can preview directly in XCode the multiple states of your App. No need anymore to fake navigation or go through your App, and you can set the views in any state in the preview. Now it’s not perfect, it needs to build the App, and in the Beta, it takes time to render or reload the UI, but still less than rebuilding the App and going through the different flows.

I recommend you to check out “Building Custom Views with SwiftUI” from WWDC2019 at least up to minute 23. It will help you understand how the layout is rendered and what sizes as each view and which position it will have. You will also use “.layoutPriority()” that seems more intuitive than the nightmare that content hugging and content compression resistance priorities are in UIKit.

What are the bad parts of SwiftUI?

View class is clutter with hundreds of methods and modifiers, that gives flexibility on building the framework but really a downside for discoverability. Even with compatibility with UIKit, that means you can use SwiftUI inside UIKit ViewControllers or the other way around, UIViews inside SwiftUI Components. This SDK will only be distributed starting with iOS 13, and it’s not backward compatible, so that means, you need to target update or new release of your App, past September 2020 to cover most of the ios 13 users as recommended by Apple. Today, you can’t do everything in SwiftUI, it’s not really important, it’s good enough, the bases are here to stay, and it will give Apple time to mature the API and the tools. Maybe it will never replace UIKit, and we will have to learn how to live with both.

To sum up, I feel that SwiftUI will allow iOS dev to produce better UI by being straightforward to “test” and verify the implementation. Is it ready yet for developers to start to learn? No, the API it’s still changing, and it’s a good thing, that’s what Beta is for. When will it be ready for prime time? If Apple keep is consistency, around XCode 11.2 or 11.3.

In the meanwhile you can check our example App, EndZone in github.

Continue Reading Interface Builder Is Dead Long Live SwiftUI

Interface Builder Is Dead Long Live SwiftUI

But nothing happened! Why? Did I have done something wrong? Let’s debug it. So the code is valid, the debugger is telling me that I increment the variable.

Basically, I have to notify the view that the framework needs to call the body again. Does this mean that the framework will rebuild the UI every time I click the button? Probably not, one thing it’s the definition of your UI, and another is how the UIKit interprets and optimize your definition to render the changes.

How to update a Declarative UI?

@State is a wrapper around an Int that every time the value changes it notify the Host(the UIKit interpreter), and the framework will call body again to see if something changes it will interpret the new description and will draw the changes. That’s a bit different from what we are used to, instead of having instances of the elements and changing the value, we provide a description and tell the framework when it changed. That includes notifying when you want to toggle a button, change the content of a Textfield, or present an ActionSheet instead by calling a function and changing the state of UI elements in UIKit.

With this in mind, let’s see what the other benefits are. There is a live preview initialized by code if you are in macOS Catalina, the good thing is that you can simulate the various state of your App by passing the parameters that you want. So you can preview directly in XCode the multiple states of your App. No need anymore to fake navigation or go through your App, and you can set the views in any state in the preview. Now it’s not perfect, it needs to build the App, and in the Beta, it takes time to render or reload the UI, but still less than rebuilding the App and going through the different flows.

I recommend you to check out “Building Custom Views with SwiftUI” from WWDC2019 at least up to minute 23. It will help you understand how the layout is rendered and what sizes as each view and which position it will have. You will also use “.layoutPriority()” that seems more intuitive than the nightmare that content hugging and content compression resistance priorities are in UIKit.

What are the bad parts of SwiftUI?

View class is clutter with hundreds of methods and modifiers, that gives flexibility on building the framework but really a downside for discoverability. Even with compatibility with UIKit, that means you can use SwiftUI inside UIKit ViewControllers or the other way around, UIViews inside SwiftUI Components. This SDK will only be distributed starting with iOS 13, and it’s not backward compatible, so that means, you need to target update or new release of your App, past September 2020 to cover most of the ios 13 users as recommended by Apple. Today, you can’t do everything in SwiftUI, it’s not really important, it’s good enough, the bases are here to stay, and it will give Apple time to mature the API and the tools. Maybe it will never replace UIKit, and we will have to learn how to live with both.

To sum up, I feel that SwiftUI will allow iOS dev to produce better UI by being straightforward to “test” and verify the implementation. Is it ready yet for developers to start to learn? No, the API it’s still changing, and it’s a good thing, that’s what Beta is for. When will it be ready for prime time? If Apple keep is consistency, around XCode 11.2 or 11.3.

In the meanwhile you can check our example App, EndZone in github.

Continue Reading Interface Builder Is Dead Long Live SwiftUI

From Java Builders to Kotlin DSLs

From Java Builders to Kotlin DSLs

Introduction

DSLs – Domain Specific Languages – are an ever trending topic in Kotlin circles. They allow us to flex some of the most exciting language features while accomplishing more readable and maintainable solutions in our code.

Today I’d like to show you how to implement a certain kind of DSL – we’re going to be wrapping an existing Java Builder in Kotlin. No doubt you’ve come across the builder pattern in Java before, for example if you’re an Android developer, you must’ve used an

AlertDialog.Builder

, an

OkHttpClient.Builder

, or a

Retrofit.Builder

at some point. Wrapping a builder like this is a good exercise in just pure DSL design. All you have to worry about is designing the API you provide with your wrapper, since all the implementation for whatever your DSL provides its users is already done inside the builder!

Our example case

It just so happens that I’m the creator and maintainer of a library that does this very thing, and a small part of its implementation is what we’re going to be using as our example. The original library is the wonderful and hugely popular MaterialDrawer by Mike Penz, which allows you to create complex, good looking, and customized navigation drawers in your application, all via various

Builder

objects in Java, with no XML writing involved on your part. This is what my library, MaterialDrawerKt provides a convenient Kotlin DSL wrapper for.

The Builder API

Let’s take a look at the drawer we’re going to be creating in our example.

android_drawer

Here’s the code, using the builders in the original API:


DrawerBuilder()
        .withActivity(this)
        .withTranslucentStatusBar(false)
        .withDrawerLayout(R.layout.material_drawer_fits_not)
        .addDrawerItems(
                PrimaryDrawerItem()
                        .withName("Home")
                        .withDescription("Get started here!"),
                PrimaryDrawerItem()
                        .withName("Settings")
                        .withDescription("Tinker around")
        )
        .withOnDrawerItemClickListener { view, position, drawerItem ->
            when(position) {
                0 -> toast("Home clicked")
                1 -> toast("Settings clicked")
            }
            true
        }
        .withOnDrawerListener(object: Drawer.OnDrawerListener {
            override fun onDrawerSlide(drawerView: View?, slideOffset: Float) {
                // Empty
            }

            override fun onDrawerClosed(drawerView: View?) {
                toast("Drawer closed")
            }

            override fun onDrawerOpened(drawerView: View?) {
                toast("Drawer opened")
            }
        })
        .build()

In this code, we’ve…
– set the

Activity

we want the drawer to appear in,
– made some layout adjustments so that the drawer is below the

ActionBar

,
– created just two menu items with names and descriptions,
– set a listener where we can handle item selections by position, using a SAM conversion to implement a single method interface,
– added a listener to detect drawer movement, using an object expression, because the necessary interface has multiple methods.

We saw two different builders used here, namely, the DrawerBuilder and PrimaryDrawerItem classes. While their builder syntax actually looks pretty decent and readable, we’ll see that a DSL can do even better.

Note that you can check out the entire working demo project for this article on GitHub here. See the commit history to follow the article step by step as we build our DSL.

Creating instances

Let’s start small. We’ll want to create a drawer with a

drawer {}

call, let’s implement just that.


fun Activity.drawer(dummy: () -> Unit) {
    DrawerBuilder()
            .withActivity(this)
            .build()
}

We’ve defined our first function as an extension on

Activity

, so that it’s available when we’re in one, and it can access the

Activity

instance as

this

without us having to pass it in explicitly.

Now, we should add our

PrimaryDrawerItem

instances. This syntax would be nice for a start:


drawer {
    primaryItem()
    primaryItem()
}

To get this, we’ll need a

primaryItem

function that’s only available within the

drawer

block, and that somehow adds an item to the

DrawerBuilder

we’ve already created.

To be able to call methods on our

DrawerBuilder

instance before we

build()

it, we’ll introduce a new wrapper class that holds it:


class DrawerBuilderKt(activity: Activity) {
    val builder = DrawerBuilder().withActivity(activity)

    internal fun build() {
        builder.build()
    }
}

We’ll update our original

drawer

function to create an instance of this wrapper class. We’ll also modify its parameter – by making the

setup

function an extension on our own class, the client of the DSL will be placed in a new scope inside the lambda passed to the

drawer

function, where the methods of

DrawerBuilderKt

become available, as we’ll see in a moment.


fun Activity.drawer(setup: DrawerBuilderKt.() -> Unit) {
    val builder = DrawerBuilderKt(this)
    builder.setup()
    builder.build()
}

You might have spotted that we’ve marked our own

build

method

internal

– this is because the only call to it will be the one inside the

drawer

function. Hence, there’s no need to expose it to the clients of our library.

The visibility of

builder

is another story – we’ll have to keep this

public

so that our library stays extensible. We could make it

internal

for the purpose of us implementing wrappers around the built-in drawer items, but that would mean that nobody else could add their own custom drawer items to the DSL – something you could do with the original library. This is functionality we don’t want to strip from our clients.

Now we can finally add the

primaryItem

function we were planning earlier. To make this available inside the lambda passed to

drawer

, we could make it a member of the

DrawerBuilderKt

class. Modifying this class for every new drawer item type we add, however, seems like an odd thing to do. The various types of drawer items should in no way affect how the drawer itself works.

We can instead use the same mechanics as clients would use to create custom drawer items. We’ll get a neat, decoupled design by adding

primaryItem

as an extension function:


fun DrawerBuilderKt.primaryItem() {
    builder.addDrawerItems(PrimaryDrawerItem())
}

We can now add blank drawer items to our drawer!

Setting properties

Before we get to setting the name and description of our drawer items with the DSL, we have the properties of

DrawerBuilder

to take care of, as we’ve seen in the very first code snippet. This is the syntax we’ll create for these:


drawer {
    drawerLayout = R.layout.material_drawer_fits_not
    translucentStatusBar = false
}

These, of course, will be properties on the

DrawerBuilderKt

class so that they’re available in the right scope.

Since the original builder doesn’t let us access the values we’ve set, what we’ll need to create are essentially write-only properties. These properties won’t have backing fields to store values, all they’ll do is forward the calls to the appropriate builder methods.

Unfortunately, Kotlin only has properties that can be both read and written (

var

) and read-only ones (

val

). We’ll solve this by using a

var

, and throwing an exception when someone tries to read these properties. We’ll also include Kotlin’s powerful

@Deprecated

annotation that lets us mark using the getters an error, so that clients are stopped from doing so at edit/compile time, rather than just getting the runtime exception:


class DrawerBuilderKt(activity: Activity) {
    ...

    var drawerLayout: Int
        @Deprecated(message = "Non readable property.", level = DeprecationLevel.ERROR)
        get() = throw UnsupportedOperationException("")
        set(value) {
            builder.withDrawerLayout(value)
        }
}

Alternatives for setting properties

Now, we can move on to customizing the drawer items themselves. The obvious solution here is to continue with the same syntax style as before:


drawer {
    primaryItem {
        name = "Home"
        description = "Get started here!"
    }
}

We know how to do this by creating a wrapper class around

PrimaryDrawerItem

, and then adding a couple non-readable properties, just like we did before. But let’s do something more interesting, and create this alternative syntax:


drawer {
    primaryItem(name = "Home", description = "Get started here!")
}

This is also pretty straightforward, we’re just calling a function that has two parameters, and using named parameters for readability. Let’s add these parameters to

primaryItem

then. We’ll also throw in default values so that they’re each optional:


fun DrawerBuilderKt.primaryItem(name: String = "", description: String = "") {
    val item = PrimaryDrawerItem()
            .withName(name)
            .withDescription(description)
    builder.addDrawerItems(item)
}

This, of course, isn’t a feasible method for adding dozens of properties to an item we’re constructing with our DSL, adding a wrapper around the

PrimaryItemClass

with separate write-only properties that can be set in a

setup

lambda is still the way to go for most things.

However, this is a nice way to lift some very basic or commonly set properties to a more prominent position in the code. Here’s what the DSL could look like with some more properties implemented:


primaryItem(name = "Games", description = "Ready, player one?") {
    iicon = FontAwesome.Icon.faw_gamepad
    identifier = 3
    selectable = false
}

Listeners

We know how to add properties to our DSL, now let’s see how we can go about listeners. We’ll start with the easy one, setting the

OnDrawerItemClickListener

to handle item clicks for a given position in the drawer. Here’s our goal:


drawer {
    onItemClick { position ->
        when (position) {
            0 -> toast("Home clicked")
            1 -> toast("Settings clicked")
        }
        true
    }
}

onItemClick

will be a method in

DrawerBuilderKt

, and it will take a lambda parameter that can be called when the original listener fires:


class DrawerBuilderKt(activity: Activity) {
    ...

    fun onItemClick(handler: (view: View?, position: Int, drawerItem: IDrawerItem<*, *>) -> Boolean) {
        builder.withOnDrawerItemClickListener(handler)
    }
}

We’re making use of SAM conversion with the call to the

withOnDrawerItemClickListener

method of the builder here. The usual SAM conversion syntax would have us passing in a lambda that gets transformed to the

OnDrawerItemClickListener

interface, but instead, we’re going just a small step further, and we’re passing in the

handler

parameter which has the appropriate function type for a conversion.

We’ll simplify the above method a bit, by taking a lambda which only gets the position passed to it, as clients will usually only care about that parameter. We’re using SAM conversion again, this time with a regular lambda, because we want to ignore some parameters when calling our simpler

handler

parameter.


class DrawerBuilderKt(activity: Activity) {
    ...

    fun onItemClick(handler: (position: Int) -> Boolean) {
        builder.withOnDrawerItemClickListener { _, position, _ -> handler(position) }
    }
}

Of course, having only this version of the method would hide functionality of the original library, so in the real wrapper library, I’ve included both of these.

Complex listeners

Last but not least, let’s see what we can do about the

OnDrawerListener

in our example. This interface has three methods, so the previous, simple solution won’t work here. As always, let’s start with the syntax we want to achieve. Not that we’re only setting two of the three methods that the interface defines.


drawer {
    onClosed {
        toast("Drawer closed")
    }
    onOpened {
        toast("Drawer opened")
    }  
}

As you can see, it would be nice to be able to specify either one or multiple of the methods of the interface independently of each other. We know that we’ll want to define three methods to take the appropriate

handler

lambdas, very similarly to what we did before:


class DrawerBuilderKt(activity: Activity) {
    ...

    fun onOpened(handler: (drawerView: View) -> Unit) {
        // TODO implement
    }

    fun onClosed(handler: (drawerView: View) -> Unit) {
        // TODO implement
    }

    fun onSlide(handler: (drawerView: View, slideOffset: Float) -> Unit) {
        // TODO implement
    }
}

The question is how to pass these handlers to the builder we’re holding. We can’t make a

withOnDrawerListener

call in each of them and create an

object

that wraps just that one handler, as the

object

created there would always implement just one of the three methods.

What I came up with for this is an

object

property in our

DrawerBuilderKt

wrapper class that implements the

OnDrawerListener

interface, and delegates each of these calls to one of its properties.


class DrawerBuilderKt(activity: Activity) {
    ...

    private val onDrawerListener = object : Drawer.OnDrawerListener {
        var onSlide: ((View, Float) -> Unit)? = null

        override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
            onSlide?.invoke(drawerView, slideOffset)
        }

        var onClosed: ((View) -> Unit)? = null

        override fun onDrawerClosed(drawerView: View) {
            onClosed?.invoke(drawerView)
        }

        var onOpened: ((View) -> Unit)? = null

        override fun onDrawerOpened(drawerView: View) {
            onOpened?.invoke(drawerView)
        }
    }
}

All our previous methods have to do then is to set these properties when they’re called:


class DrawerBuilderKt(activity: Activity) {
    ...

    fun onOpened(handler: (drawerView: View) -> Unit) {
        onDrawerListener.onOpened = handler
    }

    fun onClosed(handler: (drawerView: View) -> Unit) {
        onDrawerListener.onClosed = handler
    }

    fun onSlide(handler: (drawerView: View, slideOffset: Float) -> Unit) {
        onDrawerListener.onSlide = handler
    }
}

Of course, we’ll still have to pass this

object

to the builder at some point – we’ll do that after the

setup

lambda passed to

drawer

has already done its work, and it calls our

DrawerBuidlerKt.build()

method:


class DrawerBuilderKt(activity: Activity) {
    val builder = DrawerBuilder().withActivity(activity)

    internal fun build() {
        builder.withOnDrawerListener(onDrawerListener)
        builder.build()
    }

    ...
}

Result

As a reminder, here’s the builder code we started the article with:


DrawerBuilder()
        .withActivity(this)
        .withTranslucentStatusBar(false)
        .withDrawerLayout(R.layout.material_drawer_fits_not)
        .addDrawerItems(
                PrimaryDrawerItem()
                        .withName("Home")
                        .withDescription("Get started here!"),
                PrimaryDrawerItem()
                        .withName("Settings")
                        .withDescription("Tinker around")
        )
        .withOnDrawerItemClickListener { view, position, drawerItem ->
            when(position) {
                0 -> toast("Home clicked")
                1 -> toast("Settings clicked")
            }
            true
        }
        .withOnDrawerListener(object: Drawer.OnDrawerListener {
            override fun onDrawerSlide(drawerView: View?, slideOffset: Float) {
                // Empty
            }

            override fun onDrawerClosed(drawerView: View?) {
                toast("Drawer closed")
            }

            override fun onDrawerOpened(drawerView: View?) {
                toast("Drawer opened")
            }
        })
        .build()

And then here’s the final DSL we’ve put together:


drawer {
    drawerLayout = R.layout.material_drawer_fits_not
    translucentStatusBar = false

    primaryItem(name = "Home", description = "Get started here!")
    primaryItem(name = "Settings", description = "Tinker around")

    onItemClick { position ->
        when (position) {
            0 -> toast("Home clicked")
            1 -> toast("Settings clicked")
        }
        true
    }
    onClosed {
        toast("Drawer closed")
    }
    onOpened {
        toast("Drawer opened")
    }
}

I hope this serves as a good example of how much better looking and more expressive a DSL can be than builder style solutions. Another benefit is how much easier making changes in the DSL is, which of course becomes apparent when you’re actually making them (but worrying about placing commas in the right places, for example, is in the past).

Conclusion

Thank you for sticking with me for this entire journey. I hope you got a good feel for how to design your own DSLs (always write down your desired syntax first!), and how to structure your DSL implementations.

If you’re interested in more, I recommend checking out the source of the actual MaterialDrawerKt library, as it deals with many more advanced concepts that I couldn’t fit in this post. Among other things, it uses generics and inheritance heavily to deal with MaterialDrawer‘s complex hierarchy of built-in drawer item types, restricts function calls and property accesses to the appropriate levels of the DSL hierarchy, and much more. I might cover these in a future article.

Additionally, you can find another article covering the process of writing a DSL here, and another article about more general DSL design here.

But this is it for now, and with that, it’s now your time to go and start creating your own DSLs. Good luck!

The post From Java Builders to Kotlin DSLs appeared first on Kotlin Expertise Blog.

Continue Reading From Java Builders to Kotlin DSLs

From Java Builders to Kotlin DSLs

From Java Builders to Kotlin DSLs

Introduction

DSLs – Domain Specific Languages – are an ever trending topic in Kotlin circles. They allow us to flex some of the most exciting language features while accomplishing more readable and maintainable solutions in our code.

Today I’d like to show you how to implement a certain kind of DSL – we’re going to be wrapping an existing Java Builder in Kotlin. No doubt you’ve come across the builder pattern in Java before, for example if you’re an Android developer, you must’ve used an

AlertDialog.Builder

, an

OkHttpClient.Builder

, or a

Retrofit.Builder

at some point. Wrapping a builder like this is a good exercise in just pure DSL design. All you have to worry about is designing the API you provide with your wrapper, since all the implementation for whatever your DSL provides its users is already done inside the builder!

Our example case

It just so happens that I’m the creator and maintainer of a library that does this very thing, and a small part of its implementation is what we’re going to be using as our example. The original library is the wonderful and hugely popular MaterialDrawer by Mike Penz, which allows you to create complex, good looking, and customized navigation drawers in your application, all via various

Builder

objects in Java, with no XML writing involved on your part. This is what my library, MaterialDrawerKt provides a convenient Kotlin DSL wrapper for.

The Builder API

Let’s take a look at the drawer we’re going to be creating in our example.

android_drawer

Here’s the code, using the builders in the original API:


DrawerBuilder()
        .withActivity(this)
        .withTranslucentStatusBar(false)
        .withDrawerLayout(R.layout.material_drawer_fits_not)
        .addDrawerItems(
                PrimaryDrawerItem()
                        .withName("Home")
                        .withDescription("Get started here!"),
                PrimaryDrawerItem()
                        .withName("Settings")
                        .withDescription("Tinker around")
        )
        .withOnDrawerItemClickListener { view, position, drawerItem ->
            when(position) {
                0 -> toast("Home clicked")
                1 -> toast("Settings clicked")
            }
            true
        }
        .withOnDrawerListener(object: Drawer.OnDrawerListener {
            override fun onDrawerSlide(drawerView: View?, slideOffset: Float) {
                // Empty
            }

            override fun onDrawerClosed(drawerView: View?) {
                toast("Drawer closed")
            }

            override fun onDrawerOpened(drawerView: View?) {
                toast("Drawer opened")
            }
        })
        .build()

In this code, we’ve…
– set the

Activity

we want the drawer to appear in,
– made some layout adjustments so that the drawer is below the

ActionBar

,
– created just two menu items with names and descriptions,
– set a listener where we can handle item selections by position, using a SAM conversion to implement a single method interface,
– added a listener to detect drawer movement, using an object expression, because the necessary interface has multiple methods.

We saw two different builders used here, namely, the DrawerBuilder and PrimaryDrawerItem classes. While their builder syntax actually looks pretty decent and readable, we’ll see that a DSL can do even better.

Note that you can check out the entire working demo project for this article on GitHub here. See the commit history to follow the article step by step as we build our DSL.

Creating instances

Let’s start small. We’ll want to create a drawer with a

drawer {}

call, let’s implement just that.


fun Activity.drawer(dummy: () -> Unit) {
    DrawerBuilder()
            .withActivity(this)
            .build()
}

We’ve defined our first function as an extension on

Activity

, so that it’s available when we’re in one, and it can access the

Activity

instance as

this

without us having to pass it in explicitly.

Now, we should add our

PrimaryDrawerItem

instances. This syntax would be nice for a start:


drawer {
    primaryItem()
    primaryItem()
}

To get this, we’ll need a

primaryItem

function that’s only available within the

drawer

block, and that somehow adds an item to the

DrawerBuilder

we’ve already created.

To be able to call methods on our

DrawerBuilder

instance before we

build()

it, we’ll introduce a new wrapper class that holds it:


class DrawerBuilderKt(activity: Activity) {
    val builder = DrawerBuilder().withActivity(activity)

    internal fun build() {
        builder.build()
    }
}

We’ll update our original

drawer

function to create an instance of this wrapper class. We’ll also modify its parameter – by making the

setup

function an extension on our own class, the client of the DSL will be placed in a new scope inside the lambda passed to the

drawer

function, where the methods of

DrawerBuilderKt

become available, as we’ll see in a moment.


fun Activity.drawer(setup: DrawerBuilderKt.() -> Unit) {
    val builder = DrawerBuilderKt(this)
    builder.setup()
    builder.build()
}

You might have spotted that we’ve marked our own

build

method

internal

– this is because the only call to it will be the one inside the

drawer

function. Hence, there’s no need to expose it to the clients of our library.

The visibility of

builder

is another story – we’ll have to keep this

public

so that our library stays extensible. We could make it

internal

for the purpose of us implementing wrappers around the built-in drawer items, but that would mean that nobody else could add their own custom drawer items to the DSL – something you could do with the original library. This is functionality we don’t want to strip from our clients.

Now we can finally add the

primaryItem

function we were planning earlier. To make this available inside the lambda passed to

drawer

, we could make it a member of the

DrawerBuilderKt

class. Modifying this class for every new drawer item type we add, however, seems like an odd thing to do. The various types of drawer items should in no way affect how the drawer itself works.

We can instead use the same mechanics as clients would use to create custom drawer items. We’ll get a neat, decoupled design by adding

primaryItem

as an extension function:


fun DrawerBuilderKt.primaryItem() {
    builder.addDrawerItems(PrimaryDrawerItem())
}

We can now add blank drawer items to our drawer!

Setting properties

Before we get to setting the name and description of our drawer items with the DSL, we have the properties of

DrawerBuilder

to take care of, as we’ve seen in the very first code snippet. This is the syntax we’ll create for these:


drawer {
    drawerLayout = R.layout.material_drawer_fits_not
    translucentStatusBar = false
}

These, of course, will be properties on the

DrawerBuilderKt

class so that they’re available in the right scope.

Since the original builder doesn’t let us access the values we’ve set, what we’ll need to create are essentially write-only properties. These properties won’t have backing fields to store values, all they’ll do is forward the calls to the appropriate builder methods.

Unfortunately, Kotlin only has properties that can be both read and written (

var

) and read-only ones (

val

). We’ll solve this by using a

var

, and throwing an exception when someone tries to read these properties. We’ll also include Kotlin’s powerful

@Deprecated

annotation that lets us mark using the getters an error, so that clients are stopped from doing so at edit/compile time, rather than just getting the runtime exception:


class DrawerBuilderKt(activity: Activity) {
    ...

    var drawerLayout: Int
        @Deprecated(message = "Non readable property.", level = DeprecationLevel.ERROR)
        get() = throw UnsupportedOperationException("")
        set(value) {
            builder.withDrawerLayout(value)
        }
}

Alternatives for setting properties

Now, we can move on to customizing the drawer items themselves. The obvious solution here is to continue with the same syntax style as before:


drawer {
    primaryItem {
        name = "Home"
        description = "Get started here!"
    }
}

We know how to do this by creating a wrapper class around

PrimaryDrawerItem

, and then adding a couple non-readable properties, just like we did before. But let’s do something more interesting, and create this alternative syntax:


drawer {
    primaryItem(name = "Home", description = "Get started here!")
}

This is also pretty straightforward, we’re just calling a function that has two parameters, and using named parameters for readability. Let’s add these parameters to

primaryItem

then. We’ll also throw in default values so that they’re each optional:


fun DrawerBuilderKt.primaryItem(name: String = "", description: String = "") {
    val item = PrimaryDrawerItem()
            .withName(name)
            .withDescription(description)
    builder.addDrawerItems(item)
}

This, of course, isn’t a feasible method for adding dozens of properties to an item we’re constructing with our DSL, adding a wrapper around the

PrimaryItemClass

with separate write-only properties that can be set in a

setup

lambda is still the way to go for most things.

However, this is a nice way to lift some very basic or commonly set properties to a more prominent position in the code. Here’s what the DSL could look like with some more properties implemented:


primaryItem(name = "Games", description = "Ready, player one?") {
    iicon = FontAwesome.Icon.faw_gamepad
    identifier = 3
    selectable = false
}

Listeners

We know how to add properties to our DSL, now let’s see how we can go about listeners. We’ll start with the easy one, setting the

OnDrawerItemClickListener

to handle item clicks for a given position in the drawer. Here’s our goal:


drawer {
    onItemClick { position ->
        when (position) {
            0 -> toast("Home clicked")
            1 -> toast("Settings clicked")
        }
        true
    }
}

onItemClick

will be a method in

DrawerBuilderKt

, and it will take a lambda parameter that can be called when the original listener fires:


class DrawerBuilderKt(activity: Activity) {
    ...

    fun onItemClick(handler: (view: View?, position: Int, drawerItem: IDrawerItem<*, *>) -> Boolean) {
        builder.withOnDrawerItemClickListener(handler)
    }
}

We’re making use of SAM conversion with the call to the

withOnDrawerItemClickListener

method of the builder here. The usual SAM conversion syntax would have us passing in a lambda that gets transformed to the

OnDrawerItemClickListener

interface, but instead, we’re going just a small step further, and we’re passing in the

handler

parameter which has the appropriate function type for a conversion.

We’ll simplify the above method a bit, by taking a lambda which only gets the position passed to it, as clients will usually only care about that parameter. We’re using SAM conversion again, this time with a regular lambda, because we want to ignore some parameters when calling our simpler

handler

parameter.


class DrawerBuilderKt(activity: Activity) {
    ...

    fun onItemClick(handler: (position: Int) -> Boolean) {
        builder.withOnDrawerItemClickListener { _, position, _ -> handler(position) }
    }
}

Of course, having only this version of the method would hide functionality of the original library, so in the real wrapper library, I’ve included both of these.

Complex listeners

Last but not least, let’s see what we can do about the

OnDrawerListener

in our example. This interface has three methods, so the previous, simple solution won’t work here. As always, let’s start with the syntax we want to achieve. Not that we’re only setting two of the three methods that the interface defines.


drawer {
    onClosed {
        toast("Drawer closed")
    }
    onOpened {
        toast("Drawer opened")
    }  
}

As you can see, it would be nice to be able to specify either one or multiple of the methods of the interface independently of each other. We know that we’ll want to define three methods to take the appropriate

handler

lambdas, very similarly to what we did before:


class DrawerBuilderKt(activity: Activity) {
    ...

    fun onOpened(handler: (drawerView: View) -> Unit) {
        // TODO implement
    }

    fun onClosed(handler: (drawerView: View) -> Unit) {
        // TODO implement
    }

    fun onSlide(handler: (drawerView: View, slideOffset: Float) -> Unit) {
        // TODO implement
    }
}

The question is how to pass these handlers to the builder we’re holding. We can’t make a

withOnDrawerListener

call in each of them and create an

object

that wraps just that one handler, as the

object

created there would always implement just one of the three methods.

What I came up with for this is an

object

property in our

DrawerBuilderKt

wrapper class that implements the

OnDrawerListener

interface, and delegates each of these calls to one of its properties.


class DrawerBuilderKt(activity: Activity) {
    ...

    private val onDrawerListener = object : Drawer.OnDrawerListener {
        var onSlide: ((View, Float) -> Unit)? = null

        override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
            onSlide?.invoke(drawerView, slideOffset)
        }

        var onClosed: ((View) -> Unit)? = null

        override fun onDrawerClosed(drawerView: View) {
            onClosed?.invoke(drawerView)
        }

        var onOpened: ((View) -> Unit)? = null

        override fun onDrawerOpened(drawerView: View) {
            onOpened?.invoke(drawerView)
        }
    }
}

All our previous methods have to do then is to set these properties when they’re called:


class DrawerBuilderKt(activity: Activity) {
    ...

    fun onOpened(handler: (drawerView: View) -> Unit) {
        onDrawerListener.onOpened = handler
    }

    fun onClosed(handler: (drawerView: View) -> Unit) {
        onDrawerListener.onClosed = handler
    }

    fun onSlide(handler: (drawerView: View, slideOffset: Float) -> Unit) {
        onDrawerListener.onSlide = handler
    }
}

Of course, we’ll still have to pass this

object

to the builder at some point – we’ll do that after the

setup

lambda passed to

drawer

has already done its work, and it calls our

DrawerBuidlerKt.build()

method:


class DrawerBuilderKt(activity: Activity) {
    val builder = DrawerBuilder().withActivity(activity)

    internal fun build() {
        builder.withOnDrawerListener(onDrawerListener)
        builder.build()
    }

    ...
}

Result

As a reminder, here’s the builder code we started the article with:


DrawerBuilder()
        .withActivity(this)
        .withTranslucentStatusBar(false)
        .withDrawerLayout(R.layout.material_drawer_fits_not)
        .addDrawerItems(
                PrimaryDrawerItem()
                        .withName("Home")
                        .withDescription("Get started here!"),
                PrimaryDrawerItem()
                        .withName("Settings")
                        .withDescription("Tinker around")
        )
        .withOnDrawerItemClickListener { view, position, drawerItem ->
            when(position) {
                0 -> toast("Home clicked")
                1 -> toast("Settings clicked")
            }
            true
        }
        .withOnDrawerListener(object: Drawer.OnDrawerListener {
            override fun onDrawerSlide(drawerView: View?, slideOffset: Float) {
                // Empty
            }

            override fun onDrawerClosed(drawerView: View?) {
                toast("Drawer closed")
            }

            override fun onDrawerOpened(drawerView: View?) {
                toast("Drawer opened")
            }
        })
        .build()

And then here’s the final DSL we’ve put together:


drawer {
    drawerLayout = R.layout.material_drawer_fits_not
    translucentStatusBar = false

    primaryItem(name = "Home", description = "Get started here!")
    primaryItem(name = "Settings", description = "Tinker around")

    onItemClick { position ->
        when (position) {
            0 -> toast("Home clicked")
            1 -> toast("Settings clicked")
        }
        true
    }
    onClosed {
        toast("Drawer closed")
    }
    onOpened {
        toast("Drawer opened")
    }
}

I hope this serves as a good example of how much better looking and more expressive a DSL can be than builder style solutions. Another benefit is how much easier making changes in the DSL is, which of course becomes apparent when you’re actually making them (but worrying about placing commas in the right places, for example, is in the past).

Conclusion

Thank you for sticking with me for this entire journey. I hope you got a good feel for how to design your own DSLs (always write down your desired syntax first!), and how to structure your DSL implementations.

If you’re interested in more, I recommend checking out the source of the actual MaterialDrawerKt library, as it deals with many more advanced concepts that I couldn’t fit in this post. Among other things, it uses generics and inheritance heavily to deal with MaterialDrawer‘s complex hierarchy of built-in drawer item types, restricts function calls and property accesses to the appropriate levels of the DSL hierarchy, and much more. I might cover these in a future article.

Additionally, you can find another article covering the process of writing a DSL here, and another article about more general DSL design here.

But this is it for now, and with that, it’s now your time to go and start creating your own DSLs. Good luck!

The post From Java Builders to Kotlin DSLs appeared first on Kotlin Expertise Blog.

Continue Reading From Java Builders to Kotlin DSLs

End of content

No more pages to load