# Stories by Paul Woitaschek on Medium

Stories by Paul Woitaschek on Medium

This is one page of public article previews, not the complete archive. Follow Next page to continue. Summaries are not the original full articles.

## Building AI Features in Kotlin Multiplatform Apps with Local Prompt Iteration

DevFeed: [Building AI Features in Kotlin Multiplatform Apps with Local Prompt Iteration](<https://devfeed.tech/articles/building-ai-features-isn-t-scary-26004.md>)

Original publisher: [Read original article](<https://medium.com/yazio-engineering/building-ai-features-isnt-scary-92817564e364?source=rss-fbf9b6d94e65------2>)

Author: Paul Woitaschek

Published: 2026-07-17T11:49:08Z

Content type: tutorial

Language: en

Sources: [Stories by Paul Woitaschek on Medium](<https://devfeed.tech/sources/stories-by-paul-woitaschek-on-medium.md>)

Topics: [Kotlin Multiplatform](<https://devfeed.tech/topics/kotlin-multiplatform.md>), [LLMs](<https://devfeed.tech/topics/llms.md>), [Mobile](<https://devfeed.tech/topics/mobile.md>), [API](<https://devfeed.tech/topics/api.md>), [Back end](<https://devfeed.tech/topics/backend.md>), [voice ai](<https://devfeed.tech/topics/voice-ai.md>), [anthropic](<https://devfeed.tech/topics/anthropic.md>), [OpenAI](<https://devfeed.tech/topics/openai.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [anthropic](<https://devfeed.tech/tags/anthropic.md>), [api](<https://devfeed.tech/tags/api.md>), [backend](<https://devfeed.tech/tags/backend.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [kotlin-multiplatform](<https://devfeed.tech/tags/kotlin-multiplatform.md>), [llm](<https://devfeed.tech/tags/llm.md>), [llms](<https://devfeed.tech/tags/llms.md>), [mobile](<https://devfeed.tech/tags/mobile.md>), [mobile-app-development](<https://devfeed.tech/tags/mobile-app-development.md>), [openai](<https://devfeed.tech/tags/openai.md>), [speech](<https://devfeed.tech/tags/speech.md>), [voice](<https://devfeed.tech/tags/voice.md>)

### AI overview

This tutorial presents a workflow for building AI features in Kotlin Multiplatform mobile apps. It recommends defining the app interface first, implementing the feature locally with Koog to iterate quickly on prompts, and moving the implementation to a backend once the output is reliable. The example uses natural-language and voice-based nutrition tracking, with LLM output converted into proposed structured actions.

### Source excerpt

TLDR: Define the interface in the app, iterate locally with Koog, move the logic to the backend when it's done. AI features have a weird reputation among mobile developers. The model sits behind an API, the prompt lives on a server someone else owns, and every change means a ticket, a review, a deploy. So the whole topic feels like backend territory. But an AI feature is just a feature. Text in, structured data out, some logic in between. The only unusual part: the core of that logic is a prompt. And prompts are not designed, they are iterated. You try an input, look at the output, adjust, try again. Dozens of times. So the real problem is not building the feature. It's making that loop fast. The pattern I would like to show: define an interface in the app, build the real UI against it, implement it locally with Koog, iterate until the output is solid, then move the implementation to the backend. In Kotlin that last step is mostly copy paste. The example: track everything in one sentence We build Yazio, a nutrition tracking app, as Kotlin Multiplatform: one shared codebase for iOS and Android. That matters here, because everything below is written once and runs on both platforms. Tracking today is many small interactions: search a food, pick a portion, add water, log a workout. Now imagine the user just says what happened: "I had two scrambled eggs and a coffee for breakfast, drank half a liter of water, went for a 30 minute run, and for lunch I ate the same as yesterday."Users can track using voice The app answers with a list of proposed actions. The user deletes the wrong ones, speaks again to add what's missing, and confirms everything at once. Speech to text is solved on both platforms, so from our side the input is just a string. Messy natural language in, exact typed data out. That's what LLMs are good at. It's also the kind of logic you won't get right on the first try. The constraint: the key can't live in the app You can't ship an OpenAI or Anthropic API ke

## Speeding up Kotlin Multiplatform

DevFeed: [Speeding up Kotlin Multiplatform](<https://devfeed.tech/articles/speeding-up-kotlin-multiplatform-26008.md>)

Original publisher: [Read original article](<https://medium.com/yazio-engineering/speeding-up-kotlin-multiplatform-61ebf8dae560?source=rss-fbf9b6d94e65------2>)

Author: Paul Woitaschek

Published: 2022-05-18T09:10:34Z

Content type: tutorial

Language: en

Sources: [Stories by Paul Woitaschek on Medium](<https://devfeed.tech/sources/stories-by-paul-woitaschek-on-medium.md>)

Topics: [Kotlin Multiplatform](<https://devfeed.tech/topics/kotlin-multiplatform.md>), [build times](<https://devfeed.tech/topics/build-times.md>), [Gradle](<https://devfeed.tech/topics/gradle.md>), [iOS](<https://devfeed.tech/topics/ios.md>), [watchOS](<https://devfeed.tech/topics/watchos.md>), [Android](<https://devfeed.tech/topics/android.md>), [ide](<https://devfeed.tech/topics/ide.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-studio](<https://devfeed.tech/tags/android-studio.md>), [build-times](<https://devfeed.tech/tags/build-times.md>), [gradle](<https://devfeed.tech/tags/gradle.md>), [ide](<https://devfeed.tech/tags/ide.md>), [ios](<https://devfeed.tech/tags/ios.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [kotlin-multiplatform](<https://devfeed.tech/tags/kotlin-multiplatform.md>), [kotlin-native](<https://devfeed.tech/tags/kotlin-native.md>), [watchos](<https://devfeed.tech/tags/watchos.md>)

### AI overview

This tutorial describes how YAZIO reduces Kotlin Multiplatform development overhead by selectively removing Apple targets during common development. It discusses Gradle module growth, long synchronization and IDE indexing times, and using Gradle Configuration Cache when iOS targets are absent.

### Source excerpt

At YAZIO we are heavily investing into Kotlin Multiplatform and use Kotlin to share all business logic between the iOS and the Android App. Naturally this also leads to a lot of code. Since we modularize our software properly to achieve encapsulation and fast incremental build times, it also means a lot of Gradle modules. 70 modules and counting, to be more precise. And for Kotlin Multiplatform, a lot of Gradle modules means a very long Gradle Sync and IDE indexing time. We tried to mitigate some of that by switching to the latest Macbook Pros, which helped quite a bit. But with all the targets we have (1x Android, 3 x iOS, 4 x watchOS), it still takes 6 minutes for Gradle to sync after adding a new Gradle module. This is a very long time, and worse: It encourages developers to not change any build logic at all and leads to them not adding any more modules. Is moving these files into their own module really worth the six-minute wait?Selectively removing targets The idea here is simple: Fewer targets -- less sync time. We rarely need to touch the platform-specific source sets anyway and 99% of the time, we are developing solely in commonMain . This means that while developing, we can just remove the Apple targets. And it turns out: Apple targets have the most effect on sync times. This is due to the Commonizer, large native distributions and a complicated mechanism that allows you to see what's in your classpath from within the IDE. This is pretty simple to implement. First, add a new property to your gradle.properties file: https://medium.com/media/389dc8cfc03487a54eaeb6f2c62a81f9/href Now, in your build.gradle.kts file(s), simply check for that property before creating the native targets. https://medium.com/media/330ad55c6d767fbd4bfcb96822dc4639/href Et voilà -- it's not blazingly fast, but at least we can work with it. Bonus: Configuration Cache 🎉 The other big benefit that we can now make use of is Gradles Configuration Cache. With the iOS targets, Kotlin Multiplat

## Kotlin Native -- Using swift, not Objective-C

DevFeed: [Kotlin Native -- Using swift, not Objective-C](<https://devfeed.tech/articles/kotlin-native-using-swift-not-objective-c-26007.md>)

Original publisher: [Read original article](<https://medium.com/yazio-engineering/kotlin-native-using-swift-not-objective-c-d7742c040539?source=rss-fbf9b6d94e65------2>)

Author: Paul Woitaschek

Published: 2021-11-19T07:21:13Z

Content type: tutorial

Language: en

Sources: [Stories by Paul Woitaschek on Medium](<https://devfeed.tech/sources/stories-by-paul-woitaschek-on-medium.md>)

Topics: [kotlin-native](<https://devfeed.tech/topics/kotlin-native.md>), [Kotlin](<https://devfeed.tech/topics/kotlin.md>), [Swift](<https://devfeed.tech/topics/swift.md>), [Code generation](<https://devfeed.tech/topics/code-generation.md>), [Objective-C](<https://devfeed.tech/topics/objective-c.md>), [Compiler](<https://devfeed.tech/topics/compiler.md>), [Library](<https://devfeed.tech/topics/library.md>), [Tutorial](<https://devfeed.tech/topics/tutorial.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [code-generation](<https://devfeed.tech/tags/code-generation.md>), [compiler](<https://devfeed.tech/tags/compiler.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [kotlin-multiplatform](<https://devfeed.tech/tags/kotlin-multiplatform.md>), [kotlin-native](<https://devfeed.tech/tags/kotlin-native.md>), [ksp](<https://devfeed.tech/tags/ksp.md>), [library](<https://devfeed.tech/tags/library.md>), [objective-c](<https://devfeed.tech/tags/objective-c.md>), [swift](<https://devfeed.tech/tags/swift.md>), [tutorial](<https://devfeed.tech/tags/tutorial.md>)

### AI overview

This developer article explains how YAZIO uses Kotlin Native for shared business logic while addressing the limitations of exposing Kotlin types through Objective-C to Swift. It describes manually mapping the generated API to Swift and introduces KSP and SwiftPoet as tools for generating Swift mappings and reducing boilerplate.

### Source excerpt

Kotlin Native -- Using Swift, not Objective-C At YAZIO we use Kotlin Native Extensively. The business logic of all new app features is exclusively written in Kotlin. In its core, every screen has one ViewModel which exposes a single Flow<ViewState> that the Android App and the iOS can consume to render the state. On Android, rendering the view state is no burden because it's the language we use in the Android App anyways. However this is different for iOS. While Swift does interop with Objective-C, the public API as seen from Swift is bad. A simple data class is exported as a regular class. The properties are not native Swift types. What should be a Bool becomes a KotlinBoolean. What should be come a Double becomes a KotlinDouble. But what we want is most of the times a struct with Swift types. These are the lesser evils, the greater ones are enums and sealed classes. In Objective-C, they both become regular classes. And therefore lose all the advantages they have when used from Swift. But what we want is a Swift enum. To overcome these restrictions for quite some time our iOS team has created mappings. There is a whole target, dedicated to mapping the Objective-C classes to Swift. Basically when developing a new feature, the whole public API is manually mapped to Swift. This is good because the result is a clean Swift-native API. But that's bad because it's extreme boilerplate and a real productivity killer. What if we could directly generate these mappings and use code generation to handle that for us? Turns out: There are two tools available that make it possible. The first one is KSP. It is basically a library that lets you implement a Kotlin compiler plugin and it gives you hooks to analyze the Kotlin Syntax Tree and generate code based on it. The second one is SwiftPoet. SwiftPoet is a library you can use to generate Swift code from Kotlin, similar to what KotlinPoet and JavaPoet do. Okay, let's get our handy dirty. For the basic setup, just follow the tutorial

## How an R8 ProGuard rule can break Android application synchronization

DevFeed: [How an R8 ProGuard rule can break Android application synchronization](<https://devfeed.tech/articles/how-to-break-your-android-app-with-proguard-r8-26006.md>)

Original publisher: [Read original article](<https://medium.com/yazio-engineering/how-to-break-your-android-app-with-proguard-r8-6566bc387b63?source=rss-fbf9b6d94e65------2>)

Author: Paul Woitaschek

Published: 2020-06-08T05:52:07Z

Content type: tutorial

Language: en

Sources: [Stories by Paul Woitaschek on Medium](<https://devfeed.tech/sources/stories-by-paul-woitaschek-on-medium.md>)

Topics: [R8](<https://devfeed.tech/topics/r8.md>), [Android Gradle Plugin](<https://devfeed.tech/topics/android-gradle-plugin.md>), [Android](<https://devfeed.tech/topics/android.md>), [Testing](<https://devfeed.tech/topics/testing.md>), [Concurrent Programming](<https://devfeed.tech/topics/concurrent-programming.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-app-development](<https://devfeed.tech/tags/android-app-development.md>), [android-gradle-plugin](<https://devfeed.tech/tags/android-gradle-plugin.md>), [androiddev](<https://devfeed.tech/tags/androiddev.md>), [bug](<https://devfeed.tech/tags/bug.md>), [ci](<https://devfeed.tech/tags/ci.md>), [configuration](<https://devfeed.tech/tags/configuration.md>), [proguard](<https://devfeed.tech/tags/proguard.md>), [r8](<https://devfeed.tech/tags/r8.md>), [synchronization](<https://devfeed.tech/tags/synchronization.md>), [testing](<https://devfeed.tech/tags/testing.md>)

### AI overview

The article investigates Android instrumentation tests and network calls hanging after an Android Gradle Plugin update. It traces the behavior to the R8 rule `-assumenosideeffects class android.util.Log { public * ; }`, which can treat synchronization methods inherited from `Object` as having no side effects. The author recommends avoiding wildcards with `-assumenosideeffects` and specifying exact signatures.

### Source excerpt

I recently updated the android gradle plugin to 4.0.0. While developing, everything went smoothly and at some point I was ready to cut a release. The very last manual testing of the release app bundle was also fine so no Proguard / R8 issues on the first sight either. Then I thought: Let me check the CI results again. Everything was fine, but: The instrumentation tests. Almost all of them were timing out. After inspecting the logs, they all hang at the end of the on-boarding. Strangely the loading spinner was loading for way longer than the timeout should be. So I tested it on my phone and faced the same results. At that time I had no idea it was caused by updating AGP, so I did a 2 hours long git bisect. Okay, now lets create a minimal self reproducible bug report because I found a huge bug in AGP, everyone should know about immediately. I created a Hello World project that was making a single network call and printed it results into a TextView. Strangely here it did not reproduce. After experimenting with the OkHttp configuration and testing if it was related to some gradle plugin, I finally had the idea that it might be related to R8. So I added my proguard configuration and now the network call hang! Let's locate the issue further. I was step by step removing lines from my proguard configuration until only a single line was left: -assumenosideeffects class android.util.Log { public * ; } Now what is this? How is that even slightly related to anything? It should remove the log spam of some third party libraries we're using. So I was asking Mads Ager. (he works on D8/R8) you have to be very careful with -assumenosideeffects The problem is that in order for -assumenosideeffects to have the effect of actually removing calls, it matches up the class hierarchy. Therefore, this rule says that anything public in android.util.Log and its superclasses has no side-effects. That include the synchronization methods defined on Object. So, please don't use * wildcards in conne

## Data Binding: BindingAdapter

DevFeed: [Data Binding: BindingAdapter](<https://devfeed.tech/articles/data-binding-bindingadapter-26005.md>)

Original publisher: [Read original article](<https://medium.com/yazio-engineering/data-binding-bindingadapter-7d0824b1efff?source=rss-fbf9b6d94e65------2>)

Author: Paul Woitaschek

Published: 2016-01-31T22:16:59Z

Content type: tutorial

Language: en

Sources: [Stories by Paul Woitaschek on Medium](<https://devfeed.tech/sources/stories-by-paul-woitaschek-on-medium.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [recyclerview](<https://devfeed.tech/topics/recyclerview.md>), [XML](<https://devfeed.tech/topics/xml.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-app-development](<https://devfeed.tech/tags/android-app-development.md>), [code](<https://devfeed.tech/tags/code.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [recyclerview](<https://devfeed.tech/tags/recyclerview.md>), [xml](<https://devfeed.tech/tags/xml.md>)

### AI overview

This tutorial explains how to use Android Data Binding and BindingAdapter to manipulate views without repeated findViewById calls. It demonstrates defining a binding adapter that applies or removes strike-through text based on a model value.

### Source excerpt

For a long time I did not investigate further into https://developer.android.com/tools/data-binding/guide.html but recently it just got my attention. Just like Kotlin or RxJava this was something that was constantly trying to get my attention so I decided to give it a try and to see how it works while writing my new notes app. And I must say: I'm impressed. I applied it on the ViewHolder of my RecyclerView and reduced the boilerplate code by about 50%. But there were some issues. Issues The biggest I had was manipulating the view. The grey text could be simply archieved by setting the view enabled based on the view model: android:enabled="@{!model.done()}" As the text was set to @color/abc_primary_text_material_light from the support library this automatically works. But how to set the strike through? I did not wanted to call all these `findViewById` any more as that would defeat the purpose of DataBinding. Solution It turned out there was a simple solution, called BindingAdapter> https://developer.android.com/reference/android/databinding/BindingAdapter.html Basically you just define a static method and annotate it as a binding adapter. So for strikeThrough it is: @BindingAdapter("text:strike") public static void setStrike(TextView text, boolean strike) { if (strike) { text.setPaintFlags(text.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); } else { text.setPaintFlags(text.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); } } So now you can set it just as a regular xml attribute: android:strike="@{model.done()}" Data Binding: BindingAdapter was originally published in Yazio Engineering on Medium, where people are continuing the conversation by highlighting and responding to this story.