<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Evgeny Khokhlov</title>
    <description>The latest articles on Forem by Evgeny Khokhlov (@ttypic).</description>
    <link>https://forem.com/ttypic</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F988939%2Fdc3cbf24-20b7-4164-bb40-220253c02570.jpeg</url>
      <title>Forem: Evgeny Khokhlov</title>
      <link>https://forem.com/ttypic</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ttypic"/>
    <language>en</language>
    <item>
      <title>Measuring and minimizing latency in a Kafka Sink Connector</title>
      <dc:creator>Evgeny Khokhlov</dc:creator>
      <pubDate>Wed, 17 Jul 2024 07:51:44 +0000</pubDate>
      <link>https://forem.com/ably/measuring-and-minimizing-latency-in-a-kafka-sink-connector-3op6</link>
      <guid>https://forem.com/ably/measuring-and-minimizing-latency-in-a-kafka-sink-connector-3op6</guid>
      <description>&lt;p&gt;Kafka is often chosen as a solution for realtime data streaming because it is highly scalable, fault-tolerant, and can operate with low latency even under high load. This has made it popular for companies in the fan engagement space who need to transmit transactional with low latency (e.g. betting) to ensure that actions and responses happen quickly, maintaining the fluidity and immediacy of the experience. One of the easiest ways for companies to deliver data from Kafka to client devices is by using a Connector.&lt;/p&gt;

&lt;p&gt;Kafka Connectors act as a bridge between event-driven and non-event-driven technologies and enable the streaming of data between systems - with ‘Sink Connectors’ taking on the responsibility of streaming data from topics in the Kafka Cluster to external systems. Connectors operate within Kafka Connect, which is a tool designed for the scalable and reliable streaming of data between Apache Kafka and other data systems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnug7v2on8ipmlxrz5o2r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnug7v2on8ipmlxrz5o2r.png" alt="An image showing the relationship between Kafka Connect and Connectors." width="800" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, traditional approaches to scaling Kafka Connectors and managing extensive loads often prioritize throughput over latency. Considering the distributed nature of Kafka Connect, and a Connector’s dependency on external services, it’s a challenge to optimize end-to-end latency whilst maximizing throughput.&lt;/p&gt;

&lt;p&gt;This means that where Kafka has been chosen for its low latency, benefits can be lost when Connectors are introduced. This is particularly problematic in fan engagement applications, and those using transactional data, because latency is critical to their success.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solving for low latency with the Ably Kafka Connector
&lt;/h2&gt;

&lt;p&gt;Having worked with businesses like &lt;a href="https://hubs.la/Q02LpCRf0" rel="noopener noreferrer"&gt;NASCAR&lt;/a&gt;, &lt;a href="https://hubs.la/Q02LpCSb0" rel="noopener noreferrer"&gt;Genius Sports&lt;/a&gt;, and &lt;a href="https://hubs.la/Q02LpCSt0" rel="noopener noreferrer"&gt;Tennis Australia&lt;/a&gt;, the engineering teams at Ably understand the importance of low latency. So, to support companies looking to stream data between Kafka and end-user devices &lt;a href="https://hubs.la/Q02LpCSj0" rel="noopener noreferrer"&gt;Ably developed its own Kafka Connector&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Recently, we conducted research to determine the optimal configuration for achieving minimal latency for a Kafka Connector, specifically under a moderate load scenario of between 1,000 and 2,500 messages per second. Let’s look at how we achieved this, and took on the challenge of measuring latency and finding bottlenecks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring latency and finding bottlenecks
&lt;/h2&gt;

&lt;p&gt;Balancing latency and throughput is a complex task, as improving one often means sacrificing the other. The distributed nature of Kafka Connect and Connector’s dependency on external services make it challenging to understand the impact of your optimizations on end-to-end latency.&lt;br&gt;
To address these challenges, we adopted a comprehensive approach using distributed tracing. Distributed tracing provides a detailed view of a request's journey through various services and components. This end-to-end visibility helps identify where latency is introduced and which components are contributing the most to the overall processing time.&lt;br&gt;
We decided to use OpenTelemetry for distributed tracing. OpenTelemetry is an open-source observability framework that supports over 40 different observability and monitoring tools. It integrates smoothly with various languages and frameworks, both on the frontend and backend, making it an ideal choice for gaining visibility into the end-to-end flow of messages in our Kafka Connect environment.&lt;/p&gt;
&lt;h2&gt;
  
  
  How did we trace messages inside the Kafka Connector?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Instrumenting the load-testing tool
&lt;/h3&gt;

&lt;p&gt;We began by patching our load-testing tool to include OpenTelemetry context in Kafka message headers. This modification allowed us to embed tracing information directly into each message, ensuring that the trace context was carried along with the message throughout its lifecycle.&lt;/p&gt;

&lt;p&gt;Distributed tracing relies on context to correlate signals between different services. This context contains information that allows the sending and receiving services to associate one signal with another.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Enhancing the Kafka Connector
&lt;/h3&gt;

&lt;p&gt;Next, we patched the Kafka connector to extract the OpenTelemetry context from the message headers. The connector was modified to send traces at key points: when a message was queued and when it was published. By instrumenting these stages, we could monitor and measure the time spent within the Kafka connector itself.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Measuring end-to-end latency
&lt;/h3&gt;

&lt;p&gt;Finally, we extended our client application, which listens to the final Ably messages, to include tracing. By doing so, we could capture the complete end-to-end latency from the moment a message was produced until it was consumed. This comprehensive tracing setup allowed us to pinpoint latency bottlenecks and understand the impact of various optimizations on the overall performance.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Visualization
&lt;/h3&gt;

&lt;p&gt;To visualize the telemetry data, we used Amazon CloudWatch, which integrates seamlessly with OpenTelemetry. This integration allowed us to collect, visualize, and analyze the traces and metrics with ease:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzopwxqtvi04ka244tqzj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzopwxqtvi04ka244tqzj.png" alt="Visualizing the telemetry data with Amazon CloudWatch and OpenTelemetry" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although we couldn't find an existing OpenTelemetry library to inject and extract context directly into and from Kafka messages, it was easy to implement this functionality ourselves. We achieved this by implementing simple &lt;code&gt;TextMapGetter&lt;/code&gt; and &lt;code&gt;TextMapSetter&lt;/code&gt; interfaces in Java. This custom implementation allowed us to embed and retrieve the tracing context within the Kafka message headers, ensuring that the trace information was properly propagated through our system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Implement TextMapGetter to extract telemetry context from Kafka message &lt;/span&gt;
&lt;span class="c1"&gt;// headers &lt;/span&gt;

&lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;final&lt;/span&gt; &lt;span class="nx"&gt;TextMapGetter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SinkRecord&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;textMapGetter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;TextMapGetter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Override&lt;/span&gt; 
    &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SinkRecord&lt;/span&gt; &lt;span class="nx"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nx"&gt;Header&lt;/span&gt; &lt;span class="nx"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;lastWithName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valueOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Override&lt;/span&gt; 
    &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;Iterable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SinkRecord&lt;/span&gt; &lt;span class="nx"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;StreamSupport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;spliterator&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Collectors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;};&lt;/span&gt; 
 &lt;span class="c1"&gt;// Implement TextMapSetter to inject telemetry context into Kafka message &lt;/span&gt;
&lt;span class="c1"&gt;// headers &lt;/span&gt;

&lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;final&lt;/span&gt; &lt;span class="nx"&gt;TextMapSetter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SinkRecord&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;textMapSetter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;TextMapSetter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Override&lt;/span&gt; 
    &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SinkRecord&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fine-tuning with built-in Kafka Connector configuration
&lt;/h2&gt;

&lt;p&gt;With distributed tracing up and running, we were then ready to explore various methods to improve latency in Kafka Connectors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Partitioning
&lt;/h3&gt;

&lt;p&gt;One of the initial approaches we considered for reducing latency in Kafka was to increase the number of partitions. A topic partition is a fundamental unit of parallelism in Kafka. By distributing the load more evenly across multiple partitions, we anticipated that we could significantly reduce message processing times, leading to lower overall latency. However, during our research, we found out that our clients are not always able to increase the number of partitions due to their application logic constraints. Given these limitations, we decided to shift our focus to other optimization options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Number of tasks
&lt;/h3&gt;

&lt;p&gt;After deciding against increasing the number of partitions, we next focused on optimizing the &lt;a href="https://docs.confluent.io/platform/current/installation/configuration/connect/sink-connect-configs.html#tasks-max" rel="noopener noreferrer"&gt;tasks.max&lt;/a&gt; option in our Kafka Connector configuration. The &lt;code&gt;tasks.max&lt;/code&gt; setting controls the maximum number of tasks that the connector can run concurrently. The &lt;a href="https://docs.confluent.io/platform/current/connect/index.html#connect-tasks" rel="noopener noreferrer"&gt;tasks&lt;/a&gt; are essentially consumer threads that receive partitions to read from. Our hypothesis was that adjusting this parameter could help us achieve lower latency by running several tasks concurrently.&lt;/p&gt;

&lt;p&gt;During our tests, we varied the &lt;code&gt;tasks.max&lt;/code&gt; value and monitored the resulting latency. Interestingly, we found that the lowest latency was consistently achieved when using a single task. Running multiple tasks did not significantly improve, and in some cases even increased, the latency due to the overhead of managing concurrent processes and potential contention for resources. This outcome suggested that the process of sending data into the Ably was the primary factor influencing latency. &lt;/p&gt;

&lt;h3&gt;
  
  
  Message converters
&lt;/h3&gt;

&lt;p&gt;In our pursuit of reducing latency, we decided to avoid using complicated converters with schema validation. While these converters ensure data consistency and integrity, they introduce significant overhead due to serialization and deserialization. Instead, we opted for the built-in string converter, which transfers data as text.&lt;/p&gt;

&lt;p&gt;By using the string converter, we sent messages in JSON format directly to Ably. This approach proved to be highly efficient, since Ably natively supports JSON, and that minimized the overhead associated with serialization and deserialization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Improving latency with Ably Connector solutions
&lt;/h3&gt;

&lt;p&gt;After thoroughly exploring built-in Kafka Connector configurations to reduce latency, we turned our attention to optimizing the Kafka Connector itself. First we focused on the internal batching mechanism of the Ably Kafka Connector. Our goal was to reduce the number of requests sent to Ably, thereby improving performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Experimenting with batching intervals
&lt;/h3&gt;

&lt;p&gt;We conducted experiments with different batching intervals, ranging from 0ms (no batching) to 100ms, using the &lt;code&gt;batchExecutionMaxBufferSizeMs&lt;/code&gt; option. The objective was to find an optimal batching interval that could potentially reduce the request frequency without adversely affecting latency.&lt;/p&gt;

&lt;p&gt;Our tests revealed that even small batching intervals, such as 20ms, increased latency in both the p50 and p99 percentiles across our scenarios. Specifically, we observed that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;0ms (no batching):&lt;/strong&gt; This configuration yielded the lowest latency, as messages were sent individually without any delay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;20ms batching:&lt;/strong&gt; Despite the minimal delay, there was a noticeable increase in latency, which impacted both the median (p50) and the higher percentile (p99) latencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;100ms batching:&lt;/strong&gt; The latency continued to increase significantly, reinforcing that batching was not beneficial for our use case.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These results indicated that for our specific requirements and testing scenarios, avoiding batching altogether was the most effective approach to maintaining low latency. By sending messages immediately without batching, we minimized the delay introduced by waiting for additional messages to accumulate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Internal Connector parallelism
&lt;/h3&gt;

&lt;p&gt;Next, we examined the internal thread pool of the Ably Kafka Connector. We observed that messages were often blocked, waiting for previous messages or batches to be sent to Ably. The Ably Kafka Connector has a special option to control thread pool size called &lt;code&gt;batchExecutionThreadPoolSize&lt;/code&gt;. To address this, we dramatically increased the number of threads from 1 to 1,000 in our tests. This change significantly decreased latency, since it allowed more messages to be processed in parallel.&lt;/p&gt;

&lt;h4&gt;
  
  
  Trade-offs and challenges
&lt;/h4&gt;

&lt;p&gt;However, this approach came with a trade-off: we could no longer guarantee message ordering when publish requests to Ably were executed in parallel. At Ably, we recognize the critical importance of maintaining message order in realtime data processing. (Many applications rely on messages being processed in the correct sequence to function properly. Therefore, though it would increase latency,  &lt;code&gt;batchExecutionThreadPoolSize&lt;/code&gt; can be set to &lt;code&gt;1&lt;/code&gt; to guarantee message ordering if absolutely required.)&lt;/p&gt;

&lt;h4&gt;
  
  
  Future directions
&lt;/h4&gt;

&lt;p&gt;Looking ahead, our focus is on developing solutions that increase parallelism without disrupting message order. We understand that maintaining the correct sequence of messages is crucial for various applications. We are actively exploring several strategies to overcome this limitation and will share our findings soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  How our Ably Kafka Connector insights apply elsewhere
&lt;/h2&gt;

&lt;p&gt;The insights and optimizations we explored are not limited to the Ably Kafka Connector; they can be applied broadly to any Kafka Connector to improve performance and reduce latency. Here are some general principles and strategies that can be universally beneficial:&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding and optimizing built-in Kafka configuration
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Partitions and tasks management:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Partitions:&lt;/strong&gt; Carefully consider the number of partitions. While increasing partitions can enhance parallel processing, it can also introduce complexity and overhead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tasks:&lt;/strong&gt; Adjusting the tasks.max  setting can help balance concurrency and resource utilization. Our research showed that using a single task minimized latency in our scenario, but this might vary depending on the specific use case.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Simple converters:&lt;/strong&gt; Using simpler converters, such as the built-in String converter, can reduce the overhead associated with serialization and deserialization. This approach is particularly effective when the data format, like JSON, is natively supported by the target system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimizing connector-specific settings
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Batching mechanisms:&lt;/strong&gt; While batching can reduce the number of requests sent to external systems, our findings indicate that even small batching intervals can increase latency. Evaluate the impact of batching on latency and throughput carefully.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Thread pool configuration:&lt;/strong&gt; Increasing the number of threads in the connector’s internal thread pool can significantly reduce latency by allowing more messages to be processed in parallel. However, be mindful of the trade-offs, such as potential issues with message ordering.&lt;/p&gt;

&lt;p&gt;Although these settings are specific to the Ably Kafka Connector, similar options often exist in other Kafka Sink Connectors. Adjusting batch sizes, thread pools, and other configuration parameters can be effective ways to optimize performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Based on our experiments with the Ably Kafka Connector, we have achieved remarkable processing latency metrics. With a moderate load of approximately 2,400 messages per second, the connector demonstrated a median (p50) processing latency of under 1.5 milliseconds and a 99th percentile (p99) latency of approximately 10 milliseconds. These figures specifically represent the processing time within the connector, from the moment a message is received to its publication to the Ably service.&lt;/p&gt;

</description>
      <category>latency</category>
      <category>kafka</category>
      <category>webdev</category>
      <category>news</category>
    </item>
    <item>
      <title>Going Swiftly: Using a Swift-only libraries in your Kotlin Multiplatform App</title>
      <dc:creator>Evgeny Khokhlov</dc:creator>
      <pubDate>Sun, 19 Mar 2023 19:10:53 +0000</pubDate>
      <link>https://forem.com/ttypic/going-swiftly-using-a-swift-only-libraries-in-your-kotlin-multiplatform-app-1ml9</link>
      <guid>https://forem.com/ttypic/going-swiftly-using-a-swift-only-libraries-in-your-kotlin-multiplatform-app-1ml9</guid>
      <description>&lt;p&gt;This article demonstrates how to use the &lt;em&gt;Apple CryptoKit&lt;/em&gt; in KMM shared module. You'll learn how to create a Kotlin-compatible wrapper in Swift, build it using &lt;a href="https://github.com/ttypic/swift-klib-plugin"&gt;Swift Klib Gradle Plugin&lt;/a&gt;, and provide bindings in &lt;strong&gt;KMM&lt;/strong&gt; shared module through Kotlin/Native &lt;code&gt;cinterop&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By using this technique, developers can access cryptographic algorithms (such as &lt;em&gt;SHA&lt;/em&gt;, &lt;em&gt;MD5&lt;/em&gt;, &lt;em&gt;AES&lt;/em&gt;, &lt;em&gt;ChaChaPoly&lt;/em&gt;, and more) in &lt;strong&gt;Kotlin Multiplatform&lt;/strong&gt; without needing to rely on additional libraries. This is particularly beneficial since there are currently no widely-used and reliable cryptographic libraries for &lt;strong&gt;Kotlin Multiplatform&lt;/strong&gt; from verified and respected companies.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Kotlin Multiplatform Mobile&lt;/strong&gt; (KMM) is an SDK designed to simplify the development of cross-platform mobile applications. You can share common code between iOS and Android apps and write platform-specific code only where it's necessary&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Kotlin Multiplatform&lt;/strong&gt; brings a lot of benefits to the project, including increased code reuse, consistent business logic, and faster time-to-market. Additionally, one of its key advantages is the ability to access native APIs for each platform, enabling developers to tap into platform-specific functionality while still maintaining a shared codebase. Unfortunately, this powerful feature has certain limitations for the iOS platform. Kotlin does not have direct interoperability with the Swift language; instead, it relies on interoperability with Objective-C. As a result, there is no built-in support in Kotlin for Swift-only libraries that do not have an Objective-C API. For example, the &lt;em&gt;Apple CryptoKit&lt;/em&gt; library cannot be directly accessed from Kotlin. Fortunately, a special &lt;strong&gt;Swift Klib&lt;/strong&gt; Gradle Plugin  exists to help overcome this limitation.&lt;/p&gt;

&lt;p&gt;In this article, we'll explore how to use this plugin to build a simple Kotlin Multiplatform app that showcases the MD5 hash of a selected file.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The final code of the app can be found in the &lt;code&gt;/examples&lt;/code&gt; folder of &lt;a href="https://github.com/ttypic/swift-klib-plugin/tree/main/examples/file-hasher"&gt;Swift Klib repository&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Getting Started&lt;/li&gt;
&lt;li&gt;Swift Klib&lt;/li&gt;
&lt;li&gt;KMM Shared Module&lt;/li&gt;
&lt;li&gt;Using In SwiftUI&lt;/li&gt;
&lt;li&gt;Resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To get started, we'll create a Kotlin Multiplatform app using the KMM plugin in Android Studio.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you run into any issues while creating your Kotlin Multiplatform app, be sure to check out the &lt;a href="https://kotlinlang.org/docs/multiplatform-mobile-getting-started.html"&gt;official KMM tutorial&lt;/a&gt; for additional guidance and support.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While this article will focus on the iOS implementation, if you're interested in exploring the Android implementation, you can check out our &lt;a href="https://github.com/ttypic/swift-klib-plugin/tree/main/examples/file-hasher"&gt;GitHub repository&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;Our first step will be to create a wrapper for the CryptoKit library that provides an Objective-C API for the MD5 hash function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;CryptoKit&lt;/span&gt;
&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Foundation&lt;/span&gt;

&lt;span class="kd"&gt;@objc&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;KCrypto&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@objc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;hashed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Insecure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;MD5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hashed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compactMap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"%02x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;joined&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By utilizing the &lt;code&gt;@objc&lt;/code&gt; attribute, we can control which Objective-C API is generated from Swift code and how it will appear in Kotlin. You can find more information about this in &lt;a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/attributes/#objc"&gt;the documentation&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s important that our class inherited from &lt;code&gt;NSObject&lt;/code&gt; and has &lt;code&gt;@objc&lt;/code&gt; declarations, that make our Swift code compatible with Objective-C. More about Swift → Objective-C compatibility you can read in &lt;a href="https://lazarevzubov.medium.com/compatible-with-objective-c-swift-code-e7c3239d949"&gt;the article&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Swift Klib &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;With the code for our wrapper in hand, we can now proceed to add it to the shared module, which is located in the &lt;code&gt;native/KCrypto&lt;/code&gt; directory. Once we've done that, we're ready to set up the &lt;strong&gt;Swift Klib&lt;/strong&gt; Gradle plugin. To add the plugin, simply include it in the &lt;code&gt;plugins&lt;/code&gt; section of the shared module's &lt;code&gt;build.gradle.kts&lt;/code&gt; file. Here's what the section should look like after adding the plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"multiplatform"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.github.ttypic.swiftklib"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"0.2.1"&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After adding the &lt;strong&gt;Swift Klib&lt;/strong&gt; plugin, the next step is to add &lt;code&gt;cinterop&lt;/code&gt; for the target platforms in the &lt;strong&gt;Kotlin Multiplatform Plugin&lt;/strong&gt;. The good news is that there's no need to configure it or add a &lt;code&gt;.def&lt;/code&gt; file, as all the necessary configuration will be handled automatically by the &lt;strong&gt;Swift Klib&lt;/strong&gt; plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;kotlin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;iosX64&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nf"&gt;iosArm64&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nf"&gt;iosSimulatorArm64&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compilations&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;main&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;getting&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;cinterops&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"KCrypto"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to provide the necessary settings for the Swift Klib plugin in the &lt;code&gt;swiftklib&lt;/code&gt; extension. This involves specifying the &lt;code&gt;path&lt;/code&gt; and &lt;code&gt;packageName&lt;/code&gt; parameters, which will be used by the plugin to generate the necessary bindings.&lt;/p&gt;

&lt;p&gt;Here's an example of what the extension configuration might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;swiftklib&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"KCrypto"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"native/KCrypto"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;packageName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.ttypic.objclibs.kcrypto"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, we've specified that the Swift library files are located in the &lt;code&gt;native/KCrypto&lt;/code&gt; directory and that the generated package name should be &lt;code&gt;com.ttypic.objclibs.kcrypto&lt;/code&gt;. Be sure to adjust these settings to match the specifics of your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  KMM Shared Module &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;With the &lt;strong&gt;Swift Klib&lt;/strong&gt; plugin properly configured, we can now access our wrapper for the &lt;em&gt;CryptoKit&lt;/em&gt; library in the &lt;strong&gt;KMM&lt;/strong&gt; shared module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.ttypic.objclibs.kcrypto.KCrypto&lt;/span&gt;

&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;FileMd5Hasher&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nsdata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;NSData&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;KCrypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nsdata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can use this &lt;code&gt;FileMd5Hasher&lt;/code&gt; both in &lt;strong&gt;KMM&lt;/strong&gt; shared module and our iOS app module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using in SwiftUI &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The final step is to use our  &lt;code&gt;FileMd5Hasher&lt;/code&gt; to calculate the MD5 hash of a selected file in a SwiftUI app. Here's an example of how we might use &lt;code&gt;FileMd5Hasher&lt;/code&gt; to achieve this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ContentView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@State&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;fileMd5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
    &lt;span class="kd"&gt;@State&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;fileImporterVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;VStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fileMd5&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File's MD5 hash:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fileMd5&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileMd5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="kt"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;fileImporterVisible&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Choose File"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fileImporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
             &lt;span class="nv"&gt;isPresented&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;$fileImporterVisible&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="nv"&gt;allowedContentTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
             &lt;span class="nv"&gt;allowsMultipleSelection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
         &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
             &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                 &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;selectedFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                 &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;contentsOf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;selectedFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                 &lt;span class="n"&gt;fileMd5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;FileMd5Hasher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;nsdata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                 &lt;span class="c1"&gt;// Handle errors&lt;/span&gt;
             &lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While Kotlin has no direct interoperability with Swift, the &lt;strong&gt;Swift Klib&lt;/strong&gt; Gradle plugin provides a solution for accessing Swift-only APIs in &lt;strong&gt;Kotlin Multiplatform&lt;/strong&gt; shared modules. In this article, we explored how to use the &lt;strong&gt;Swift Klib&lt;/strong&gt; plugin to create a wrapper for the &lt;em&gt;CryptoKit&lt;/em&gt; library and calculate the MD5 hash of a selected file in a &lt;strong&gt;KMM&lt;/strong&gt; app.&lt;/p&gt;

&lt;p&gt;By following the steps outlined in this article, developers can easily integrate Swift-only APIs into their &lt;strong&gt;Kotlin Multiplatform&lt;/strong&gt; projects, and take full advantage of the unique features and functionality provided by both languages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/ttypic/swift-klib-plugin"&gt;Swift Klib GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lazarevzubov.medium.com/compatible-with-objective-c-swift-code-e7c3239d949"&gt;Compatible with Objective-C Swift Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kotlinlang.org/docs/multiplatform-mobile-getting-started.html#next-step"&gt;Official KMM tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/swlh/cryptokit-tutorial-how-to-use-cryptokit-on-ios13-apps-5961019752f5"&gt;How to use CryptoKit&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>kotlin</category>
      <category>kotlinmultiplatform</category>
      <category>swift</category>
      <category>gradle</category>
    </item>
    <item>
      <title>Swift Klib v0.2.0 is out</title>
      <dc:creator>Evgeny Khokhlov</dc:creator>
      <pubDate>Sun, 26 Feb 2023 17:07:10 +0000</pubDate>
      <link>https://forem.com/ttypic/swift-klib-v020-is-out-57jk</link>
      <guid>https://forem.com/ttypic/swift-klib-v020-is-out-57jk</guid>
      <description>&lt;p&gt;&lt;strong&gt;Swift Klib&lt;/strong&gt; Gradle Plugin v0.2.0 is released.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ttypic" rel="noopener noreferrer"&gt;
        ttypic
      &lt;/a&gt; / &lt;a href="https://github.com/ttypic/swift-klib-plugin" rel="noopener noreferrer"&gt;
        swift-klib-plugin
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Gradle Plugin for injecting Swift code into Kotlin Multiplatform Mobile shared module
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/ttypic/swift-klib-plugin/raw/main/docs/media/swiftklib-light.svg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fttypic%2Fswift-klib-plugin%2Fraw%2Fmain%2Fdocs%2Fmedia%2Fswiftklib-light.svg" alt="Swift Klib library logo" width="300"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Swift Klib Gradle Plugin&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/3b3752f46069b60d8dadeb32afad97bdf522114f1a67df4e8f8a44ece8886b75/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d696f732d6c69676874"&gt;&lt;img src="https://camo.githubusercontent.com/3b3752f46069b60d8dadeb32afad97bdf522114f1a67df4e8f8a44ece8886b75/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d696f732d6c69676874" alt="badge-ios"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/0140f03ccd6c792f720148100a483123f5509908e47b13fe5c09fb579b30643c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61636f732d6c69676874"&gt;&lt;img src="https://camo.githubusercontent.com/0140f03ccd6c792f720148100a483123f5509908e47b13fe5c09fb579b30643c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61636f732d6c69676874" alt="badge-mac"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/8d97793270f6d47d7c6a135481da76d8eaa8c876a4b7f01524c5168d1844cd80/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d74766f732d6c69676874"&gt;&lt;img src="https://camo.githubusercontent.com/8d97793270f6d47d7c6a135481da76d8eaa8c876a4b7f01524c5168d1844cd80/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d74766f732d6c69676874" alt="badge-tvos"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/0e79c5f408f0b5fb3b7fe05cc7696d421b9068574ad785e0ce88891fa1d9399d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d77617463686f732d6c69676874"&gt;&lt;img src="https://camo.githubusercontent.com/0e79c5f408f0b5fb3b7fe05cc7696d421b9068574ad785e0ce88891fa1d9399d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d77617463686f732d6c69676874" alt="badge-watchos"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This gradle plugin provides easy way to include your Swift source files in your &lt;strong&gt;Kotlin Multiplatform Mobile&lt;/strong&gt;
shared module and access them in Kotlin via &lt;code&gt;cinterop&lt;/code&gt; for iOS targets. It is useful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Accessing Swift-only libraries &lt;em&gt;(e.g. CryptoKit)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Creating a Kotlin-Friendly Swift API&lt;/li&gt;
&lt;li&gt;Learning how Swift &amp;lt;-&amp;gt; Kotlin interoperability works&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;Plugin has been tested on Gradle 7.5+, Xcode 15+&lt;/em&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Using the &lt;a href="https://docs.gradle.org/current/userguide/plugins.html#sec:plugins_block" rel="nofollow noopener noreferrer"&gt;plugins DSL&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-kotlin notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;plugins {
    id(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;io.github.ttypic.swiftklib&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;) version &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;0.6.4&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
}&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Using &lt;a href="https://docs.gradle.org/current/userguide/plugins.html#sec:old_plugin_application" rel="nofollow noopener noreferrer"&gt;legacy plugin application&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-kotlin notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;buildscript {
  repositories {
    maven {
      url &lt;span class="pl-k"&gt;=&lt;/span&gt; uri(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;https://plugins.gradle.org/m2/&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;)
    }
  }
  dependencies {
    classpath(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;io.github.ttypic:plugin:0.6.4&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;)
  }
}

&lt;span class="pl-c1"&gt;apply&lt;/span&gt;(plugin &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;io.github.ttypic.swiftklib&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;)&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Add this to the project-level gradle.properties file (if it’s not already included).&lt;/p&gt;
&lt;div class="highlight highlight-source-ini notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt;Kotlin Multiplatform&lt;/span&gt;
&lt;span class="pl-k"&gt;kotlin.mpp.enableCInteropCommonization&lt;/span&gt;=true
&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Plugin works together with &lt;a href="https://plugins.gradle.org/plugin/org.jetbrains.kotlin.multiplatform" rel="nofollow noopener noreferrer"&gt;Kotlin Multiplatform plugin&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Prepare Swift code&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Place your Swift…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ttypic/swift-klib-plugin" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This gradle plugin provides easy way to include your Swift source files in your &lt;strong&gt;Kotlin Multiplatform Mobile&lt;/strong&gt;&lt;br&gt;
shared module and access them in Kotlin via &lt;code&gt;cinterop&lt;/code&gt; for iOS targets. It is useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accessing Swift-only libraries &lt;em&gt;(e.g. CryptoKit)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Creating a Kotlin-Friendly Swift API&lt;/li&gt;
&lt;li&gt;Learning how Swift &amp;lt;-&amp;gt; Kotlin interoperability works&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This release contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fix:&lt;/strong&gt; add a step to clean old build files in the build directory&lt;/li&gt;
&lt;li&gt;Light refactoring of source code&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Swift Klib Gradle Plugin is out</title>
      <dc:creator>Evgeny Khokhlov</dc:creator>
      <pubDate>Wed, 08 Feb 2023 22:40:02 +0000</pubDate>
      <link>https://forem.com/ttypic/swift-klib-gradle-plugin-is-out-4k2n</link>
      <guid>https://forem.com/ttypic/swift-klib-gradle-plugin-is-out-4k2n</guid>
      <description>&lt;p&gt;I am thrilled to announce the release of new Gradle plugin for &lt;strong&gt;Kotlin Multiplatform Mobile&lt;/strong&gt; development - &lt;a href="https://github.com/ttypic/swift-klib-plugin" rel="noopener noreferrer"&gt;Swift Klib&lt;/a&gt;! It designed to make it easier for you to integrate and manage Swift sources in your KMM shared module. With &lt;a href="https://github.com/ttypic/swift-klib-plugin" rel="noopener noreferrer"&gt;Swift Klib&lt;/a&gt;, you can now easily include your Swift source files in your KMM shared module and access them in Kotlin via  &lt;code&gt;cinterop&lt;/code&gt; for iOS targets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use cases
&lt;/h2&gt;

&lt;p&gt;Although &lt;a href="https://github.com/ttypic/swift-klib-plugin" rel="noopener noreferrer"&gt;Swift Klib&lt;/a&gt; was originally created to access Swift-only Libraries in Kotlin, it is also ideal for several other things. Here are a few:&lt;/p&gt;

&lt;h3&gt;
  
  
  Learning how Swift &amp;lt;-&amp;gt; Kotlin interoperability works
&lt;/h3&gt;

&lt;p&gt;You can get hands-on experience with the interoperability of Swift and Kotlin. You can start with simple examples, such as the Hello World example in the plugin's &lt;a href="https://github.com/ttypic/swift-klib-plugin" rel="noopener noreferrer"&gt;repo&lt;/a&gt;, and observe how bindings are created in Kotlin. Then you can make changes, add more code, and observe the results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessing Swift-only libraries
&lt;/h3&gt;

&lt;p&gt;There is &lt;a href="https://kotlinlang.org/docs/native-objc-interop.html#usage" rel="noopener noreferrer"&gt;no direct interoperability&lt;/a&gt; between Kotlin and modern Swift libraries without an Objective-C API, such as ActivityKit, CryptoKit, Combine, etc. &lt;strong&gt;Swift Klib&lt;/strong&gt; fills this gap by allowing you to easily create Kotlin-friendly wrappers in Swift and access them in your Kotlin code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Kotlin-Friendly Swift API
&lt;/h3&gt;

&lt;p&gt;Kotlin uses Objective-C APIs for libraries, that's why the automatic bindings for iOS libraries can produce an API that may be unfamiliar to those familiar with the Swift API. For example, the Swift &lt;code&gt;UserDefaults.standard&lt;/code&gt; translates to &lt;code&gt;NSUserDefaults.standardUserDefaults&lt;/code&gt; in Kotlin. With &lt;strong&gt;Swift Klib&lt;/strong&gt;, you can create wrappers for the API you want to use and have full control over it. &lt;/p&gt;

&lt;p&gt;I hope you enjoy using Swift Klib and can't wait to see what you'll create with it. If you have any questions or feedback, please don't hesitate to create an issue on &lt;a href="https://github.com/ttypic/swift-klib-plugin" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Happy coding!    &lt;/p&gt;

</description>
      <category>privacy</category>
      <category>webdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>KMM: writing Kotlin API for Swift - 7 things you need to know</title>
      <dc:creator>Evgeny Khokhlov</dc:creator>
      <pubDate>Sun, 05 Feb 2023 12:22:23 +0000</pubDate>
      <link>https://forem.com/ttypic/kmm-writing-kotlin-api-for-swift-7-things-you-need-to-know-4io6</link>
      <guid>https://forem.com/ttypic/kmm-writing-kotlin-api-for-swift-7-things-you-need-to-know-4io6</guid>
      <description>&lt;p&gt;&lt;em&gt;This article is true for Kotlin 1.8&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kotlin Multiplatform Mobile&lt;/strong&gt; (KMM) has been in beta for a while, first stable release is coming, API is safe to use and that's a perfect time to give it a try!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KMM&lt;/strong&gt; has powerful bidirectional interoperability with Objective-C/Swift (kudos to &lt;strong&gt;Kotlin/Native&lt;/strong&gt;), but it also has some limitations and tricky parts. It's not easy to write Kotlin API for your shared module that works nicely in Swift. I've tried to summarize all the things I've come across so far and learned the hard way. Where applicable I also provided links to documentation to help give you extra context. &lt;/p&gt;

&lt;h2&gt;
  
  
  Consider &lt;code&gt;abstract class&lt;/code&gt; instead of &lt;code&gt;interface&lt;/code&gt; in Kotlin API for Swift
&lt;/h2&gt;

&lt;p&gt;Swift Protocols generated from Kotlin Interfaces don't have methods from &lt;code&gt;kotlin.Any&lt;/code&gt;. When you are writing code in Kotlin you are often working with interfaces and implicitly using methods from &lt;code&gt;Any&lt;/code&gt;. You can compare instances, print them or for example collect them in &lt;code&gt;HashMap&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;This code works pretty well in Kotlin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Vehicle&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Garage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;parkedVehicle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;park&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vehicle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parkedVehicle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;parkedVehicle&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vehicle&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;vehicle&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"$vehicle already parked"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"garage is occupied"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But in Swift part this &lt;code&gt;interface Vehicle&lt;/code&gt; is translated to &lt;code&gt;protocol Vehicle&lt;/code&gt; and can't be treated that way, it doesn't have any kind of &lt;code&gt;equals&lt;/code&gt; or &lt;code&gt;hashCode&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;SwiftGarage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;parkedVehicle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;park&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;vehicle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parkedVehicle&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;vehicle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// Compile error: 'Vehicle' cannot be used as a type conforming to protocol 'Equatable' because 'Equatable' has static requirements&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;vehicle&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt; already parked"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course you could cast &lt;code&gt;Vehicle&lt;/code&gt; to &lt;code&gt;NSObject&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;SwiftGarage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;parkedVehicle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;park&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;vehicle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parkedVehicle&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;parkedVehicle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vehicle&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;parkedVehicle&lt;/span&gt; &lt;span class="k"&gt;as!&lt;/span&gt; &lt;span class="kt"&gt;NSObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vehicle&lt;/span&gt; &lt;span class="k"&gt;as!&lt;/span&gt; &lt;span class="kt"&gt;NSObject&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;vehicle&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt; parked"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"garage is occupied"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will work, but it doesn't look nice.&lt;/p&gt;

&lt;p&gt;On the other hand Kotlin classes: regular and abstract maps methods of &lt;code&gt;kotlin.Any&lt;/code&gt; (&lt;code&gt;equals()&lt;/code&gt;, &lt;code&gt;hashCode()&lt;/code&gt; and &lt;code&gt;toString()&lt;/code&gt;) to the methods &lt;code&gt;isEquals:&lt;/code&gt;, &lt;code&gt;hash&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; in Objective-C, and to the method &lt;code&gt;isEquals(_:)&lt;/code&gt; and the properties &lt;code&gt;hash&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt; in Swift. Also they conform &lt;code&gt;Equatable&lt;/code&gt;, &lt;code&gt;Hashable&lt;/code&gt; protocols in Swift.&lt;/p&gt;

&lt;p&gt;In addition you can extend them to adopt and conform to any new protocol in Swift.&lt;/p&gt;

&lt;p&gt;That's why it better to provide &lt;code&gt;abstract class&lt;/code&gt; instead of &lt;code&gt;interface&lt;/code&gt; where it's possible, when you are creating Kotlin API for shared module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't rely on generics interface in your Kotlin API for Swift
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Generics&lt;/strong&gt; interoperability &lt;a href="https://kotlinlang.org/docs/native-objc-interop.html#generics" rel="noopener noreferrer"&gt;can only be defined on classes&lt;/a&gt;, not on interfaces (protocols in Objective-C and Swift) or methods. So ideally your Kotlin API shouldn't include generic interfaces. &lt;/p&gt;

&lt;h2&gt;
  
  
  Swift Generic generated from Kotlin Generics has limitations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Kotlin/Native&lt;/strong&gt; has &lt;a href="https://kotlinlang.org/docs/native-objc-interop.html#usage" rel="noopener noreferrer"&gt;no direct Kotlin &amp;lt;-&amp;gt; Swift interoperability&lt;/a&gt;, it uses Kotlin &amp;lt;-&amp;gt; Objective-C and Objective-C &amp;lt;-&amp;gt; Swift interops to make it works. That's create some limitation.&lt;/p&gt;

&lt;p&gt;For example Kotlin and Swift define nullability as part of the type specification, while Objective-C defines nullability on methods and properties.&lt;/p&gt;

&lt;p&gt;That's why this class in Kotlin&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;transforms to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;Container&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;KotlinBase&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AnyObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt;
    &lt;span class="kd"&gt;open&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in Swift.&lt;/p&gt;

&lt;p&gt;Fortunately there is an easy trick to make our &lt;code&gt;get&lt;/code&gt; function non-nullable. You need to provide non-null type constraint (e.g. &lt;code&gt;kotlin.Any&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now it will translate as expected.&lt;/p&gt;

&lt;p&gt;Another interesting thing with &lt;strong&gt;generics&lt;/strong&gt; is that in Swift part they have &lt;code&gt;AnyObject&lt;/code&gt; constraint (maybe you've noticed it in previous example).&lt;/p&gt;

&lt;p&gt;That means that we can't use Swift &lt;code&gt;struct&lt;/code&gt; with them. It also means if we want to initialize our &lt;code&gt;Container&lt;/code&gt; class with &lt;code&gt;String&lt;/code&gt; or &lt;code&gt;Int&lt;/code&gt; we got compiler error&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;containerInt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Generic class 'Container' requires that 'Int' be a class type &lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;containerString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"one"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Generic class 'Container' requires that 'String' be a class type &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a solution for this too. You need to cast Swift values to Objective-C one's:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;containerInt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;NSNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;containerString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"one"&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;NSString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code will compile just fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid name collision even in different packages
&lt;/h2&gt;

&lt;p&gt;All Kotlin classes and interfaces from shared module get into one Swift module. That's why having two (or more) classes or interfaces with the same name even if they are located in different packages can cause problems. &lt;strong&gt;Kotlin/Native&lt;/strong&gt; deals with it by adding &lt;code&gt;_&lt;/code&gt; to the name in Swift module. For example if you have &lt;code&gt;Utils&lt;/code&gt; class defined in 3 different packages in your shared module in Kotlin, you get &lt;code&gt;Utils&lt;/code&gt;, &lt;code&gt;Utils_&lt;/code&gt;, &lt;code&gt;Utils__&lt;/code&gt; in Swift. &lt;/p&gt;

&lt;p&gt;It is not very obvious how to match these Swift classes with Kotlin's. That's why it's better to create unique names in Kotlin part.&lt;/p&gt;

&lt;p&gt;Another option is to use experimental &lt;code&gt;@ObjCName()&lt;/code&gt; &lt;a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native/-obj-c-name/" rel="noopener noreferrer"&gt;annotation&lt;/a&gt; which tells &lt;strong&gt;Kotlin/Native&lt;/strong&gt; how to translate names.&lt;/p&gt;

&lt;h2&gt;
  
  
  Include 3rd party libraries into shared module
&lt;/h2&gt;

&lt;p&gt;When you are building your app in KMM, sometimes you need to pack some libraries along with project files in the shared module. For example if you are using &lt;a href="https://arkivanov.github.io/Decompose/" rel="noopener noreferrer"&gt;Decompose&lt;/a&gt; you need to access it's &lt;code&gt;Value&lt;/code&gt; class in Swift.&lt;/p&gt;

&lt;p&gt;In order to do this, there is an &lt;code&gt;export&lt;/code&gt; method in your &lt;code&gt;Framework DSL&lt;/code&gt; in gradle build. If you are using regular framework setup  you need to find &lt;code&gt;binaries.framework&lt;/code&gt; block in your shared module &lt;code&gt;build.gradle.kts&lt;/code&gt;. And if you are using &lt;code&gt;cocoapods&lt;/code&gt; look at &lt;code&gt;cocoapods.framework&lt;/code&gt; block. In this block you need to add &lt;code&gt;export&lt;/code&gt; methods with all libraries you want to include.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;binaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;framework&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;baseName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"shared"&lt;/span&gt;
    &lt;span class="nf"&gt;export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.example:exported-library1:1.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.example:exported-library2:1.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And also make sure that libraries decelerated in &lt;code&gt;export&lt;/code&gt; are &lt;code&gt;api&lt;/code&gt; dependencies. &lt;code&gt;Export&lt;/code&gt; only works with &lt;code&gt;api&lt;/code&gt; dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;iosMain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.example:exported-library1:1.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.example:exported-library2:1.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This very well explained in Kotlin &lt;a href="https://kotlinlang.org/docs/multiplatform-build-native-binaries.html#export-dependencies-to-binaries" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Provide Kotlin API that doesn't make you invoke coroutine inside coroutine
&lt;/h2&gt;

&lt;p&gt;The Kotlin/Native memory manager has a &lt;a href="https://kotlinlang.org/docs/native-ios-integration.html#calling-kotlin-suspending-functions" rel="noopener noreferrer"&gt;restriction&lt;/a&gt; on calling Kotlin suspending functions from Swift and Objective-C from threads other than the main one. &lt;/p&gt;

&lt;p&gt;For example if you have Kotlin class&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;invokeSmth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And planning to use it in swift like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;handleButtonClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getToken&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
       &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invokeSmth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
           &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invoked!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It won't work, app will crash while executing nested coroutine. To prevent this you could wrap calling of second coroutine in &lt;code&gt;DispatchQueue.main.sync&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;handleButtonClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getToken&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
       &lt;span class="kt"&gt;DispatchQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invokeSmth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
               &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invoked!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or else you could turn on experimental flag &lt;code&gt;kotlin.native.binary.objcExportSuspendFunctionLaunchThreadRestriction=none&lt;/code&gt; in your &lt;code&gt;gradle.properties&lt;/code&gt;. But it's better to avoid this situations by creating additional method in Kotlin part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;invokeSmth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getTokenThenInvokeSmth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;invokeSmth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When you explore KMM articles, check article date before you read it
&lt;/h2&gt;

&lt;p&gt;There are a lot of articles already has been written about &lt;strong&gt;Kotlin Multiplatform Mobile&lt;/strong&gt; for the last 5 years. KMM api has been changed and improved since then. Inevitably some of very popular articles are outdated. &lt;/p&gt;

&lt;p&gt;Good examples are articles about &lt;strong&gt;Kotlin/Native&lt;/strong&gt; memory models. If there are advices about freezes and a lot of limitation, they are about old memory model which is deprecated now.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>workflow</category>
      <category>management</category>
    </item>
  </channel>
</rss>
