-
Notifications
You must be signed in to change notification settings - Fork 43
Description
I'm using PBandK to generate the networking layer in a Kotlin Multiplatform Mobile project (iOS/Android apps) with the help of a service-gen plugin I open-sourced at https://github.com/collectiveidea/twirp-kmm.
Due to the lack of packages in Objective-C, Kotlin/Native renames classes that share the same name by appending underscores to avoid name collisions. This presents issues when protobuf message classes share the same class name as client-side models. For example, if protobuf defines an example.api.Item message but there also exists an example.model.Item model class, Kotlin/Native renames one of them to Item_ so that both classes can be used in Swift code. Working with Item_ and Item is both confusing and unpleasant. There is a feature request at https://youtrack.jetbrains.com/issue/KT-50767/Kotlin-Native-Objective-C-Stable-rename-algorithm-for-classes-with-same-name to improve Kotlin/Native support for this scenario, which suggests a new @SwiftName annotation to improve interop, but no timeframe is given on implementing it.
Ideally, PBandK would allow an option to append a suffix to protobuf messages during the generation process. Something like message_suffix (commonly DTO in this context):
protoc --pbandk_out=message_suffix=DTO,kotlin_package=com.example.api:shared/src/commonMain/kotlin sample.protoThis would cause the protobuf message Example to be generated as @pbandk.Export public data class ExampleDTO. The suffix would not apply to messages ending in Request or Response. In the future, should Kotlin/Native introduce @SwiftName per the linked Youtrack, then the suffix option might instead generate @pbandk.Export @SwiftName("ExampleDTO") public data class Example. EDIT: Kotlin/Native 1.8.0 adds a @ObjCName annotation that the generated code can leverage.
This is a problem that I needed to solve in the short-term. As a workaround, I've created a small bash script that pre-processes my project's protobuf file to append the DTO suffix to messages before running the PBandK code-gen. The net result is the that the generated code appends DTO to the message class names. My (macOS) script looks roughly like this (for anyone else running into the same issue):
#
# Pre-process the `example.proto` file to change the message names to have a DTO suffix.
#
# First, copy the example.proto file to example_processed.proto, where we'll make our edits.
cp shared/src/commonMain/proto/example.proto example_processed.proto
# Process the file extract a list of message class names. Use `pcre2grep` to easily capture the group
# within the matching regex, to isolate the type name
pcre2grep -o1 "^message ([A-Za-z0-9]+) {" example_processed.proto | \
# From that this list, strip out type names ending in either `Request` or `Response` since
# we don't want to append the `DTO` suffix to those.
grep -v "Response$" | grep -v "Request$" | \
# At this point, our list looks something like:
# Item
# Message
# Category
# (etc.)
# Replace the message type names with the type name followed by the DTO suffix. We do this by
# reading all of the types from our stdin list and replacing them in the target file.
while read -r type; do
# The type names are either surrounded by parens or spaces; replace both.
sed -i '' -e "s/($type)/(${type}DTO)/g" -e "s/ $type / ${type}DTO /g" example_processed.proto
done
# Generate the protobuf code from the processed file
protoc --pbandk_out=kotlin_package=com.example.api:shared/src/commonMain/kotlin example_processed.proto
# The generated code is placed in `example_processed.kt`. We need to move that to the correct
# location, overwriting the `ExampleProtobufs.kt` file if it already exists.
mv -f shared/src/commonMain/kotlin/com/example/api/example_processed.kt shared/src/commonMain/kotlin/com/example/api/ExampleProtobufs.kt
# Delete the processed proto file now that we're done with it.
rm example_processed.proto