<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="https://benasher.co/feed.xml" rel="self" type="application/atom+xml" /><link href="https://benasher.co/" rel="alternate" type="text/html" /><updated>2026-05-23T07:42:32-07:00</updated><id>https://benasher.co/feed.xml</id><title type="html">Ben Asher</title><subtitle>Ben Asher&apos;s personal website and blog</subtitle><author><name>Ben Asher</name></author><entry><title type="html">Leaving Mobile for Web</title><link href="https://benasher.co/leaving-mobile-for-web/" rel="alternate" type="text/html" title="Leaving Mobile for Web" /><published>2024-05-04T00:00:00-07:00</published><updated>2024-05-04T00:00:00-07:00</updated><id>https://benasher.co/leaving-mobile-for-web</id><content type="html" xml:base="https://benasher.co/leaving-mobile-for-web/"><![CDATA[<p>I wrote my last post about a month before I left Autodesk (what may be my last iOS development role). After 10 years of doing iOS development in some form, I got an opportunity to switch to web at another company, and I took it.</p>

<h2 id="how-i-got-here">How I got here</h2>

<p>I started in iOS development because I was in college (2009-2013) getting my CS degree during the heyday of the App Store. I joined <a href="http://mobiata.com/">Mobiata</a> as one of their first interns, where I worked on mobile travel apps like FlightTrack and FlightBoard. It was an exciting time! While I was in class learning how to write algorithms in C, I was also building an app that I could CARRY AROUND ON MY PHONE that told me when the next University of Michigan bus would arrive (worked on a public XML feed at the time). This was nothing short of amazing.</p>

<p>Then there were the WWDCs. If I was ever feeling any kind of boredom around iOS development, attending WWDC (and/or the alt confs) would usually provide the shot of adrenaline I needed to carry me through to the next one. Meeting folks in the community and discussing all of the “new things you could do now” with the latest updates was intoxicating. Each time, it almost revived the original novelty of when I first launched BenBus (what my friends lovingly called my original bus app).</p>

<p>Toward the end of my iOS career, I stumbled upon Kotlin. I had heard about it plenty, having always worked alongside Android developers. They loved Kotlin, but I got to use it myself in an iOS context. I had been tasked with coming up with a way to not have us rewrite parts of the sync engine (mostly just logic + marshaling JSON + sqlite) across the 3 platforms that PlanGrid supported: iOS, Android, and Windows. After a bit of research, I found Kotlin Multiplatform, which I have written about a fair bit.</p>

<h2 id="community-tooling">Community tooling</h2>

<p>As I was building a multi-platform sync engine at PlanGrid in Kotlin, I kept having this thought of “wow I like writing Kotlin way more than Swift.” It wasn’t the language itself though — more the experience of the work. I began to realize it was having open tooling and an ecosystem with community support. If I got stuck, there was no need to decompile a binary or read a blog post about somebody who had decompiled the binary first. The tooling and SDKs were (for the most part) open source, down to the build system.</p>

<p>The build system, gradle, has you write code for configuration, which means the build system had its own SDK, with documentation. This was such a huge contrast to Xcode (in mid-2021 — am not up-to-date on latest Xcode capabilities), which had you insert shell scripts at various build phases (as the mode of customization). While one could argue that shell scripting is the ultimate flexibility, after trying gradle (also an imperfect system people complain about!), I was sold on the idea that build phases could be programmable with their own API and documented way of telling the system whether they needed to be re-run, could be skipped, etc.</p>

<p>The other amazing part about all of this (again coming from the Xcode world) was that if I ever had a problem whose root cause was in some library the build system relied upon, I could almost always file a bug somewhere in the open, or open a PR to fix it myself. Someone in the open would almost always fix it, or there would be a workaround that required a small snippet of build system code that I could drop into my configuration, until the issue was fixed.</p>

<h2 id="more-than-the-build-system">More than the build system</h2>

<p>The build system parts that make Kotlin Multiplatform builds work are a metaphor unto themselves. There’s gradle, which is the core build system. Companies and the community build plugins that work on top of gradle to make the various build phases work. In the end, you have a happy (albeit sometimes messy!) result. And at no step of the way is anyone hiding the ball (for the next annual release); everyone has the same goal, which is to work together on a tool that lets everyone get work done.</p>

<p>I was enjoying working with Kotlin because I was enjoying working in an ecosystem that didn’t get in my way. Best of all, I didn’t have to wait for the annual update to see my problems fixed or not fixed. I came to work 8 hours a day, and Kotlin was right there with me. There was no App Store to “protect,” no IP to hide, and no annual release to wait for. I just got to code (for the most part).</p>

<h2 id="time-to-go">Time to go</h2>

<p>After seeing how quickly the Kotlin ecosystem had moved (in the ~2 years I had been a part of it), it became excruciating to look back at the iOS world. After experiencing the pace and flow outside of Apple’s walled garden, I felt I might be dead by the time I saw any of my radars fixed.</p>

<h2 id="today">Today</h2>

<p>In my new job, I do a lot of backend work in TypeScript (JavaScript with a type checker) with node as the runtime. JavaScript-based web development has more similarities than you might think to iOS development. In both worlds, you have to think about the different runtimes your code supports. In web land for example, I have to think about whether my JS will run on node vs in a browser. I might also have to consider whether the supported browser variants support the language features I want to use. As mobile developers supporting different devices and OS versions, we’ve seen this movie before.</p>

<p>Despite the JavaScript ecosystem’s popularity and relative openness, I once saw a job posting that highlighted how you “don’t have to deal with node” there. I wish I had saved a screenshot of it. It’s kind of funny, but it’s also interesting because it’s part of an ad (for a job). It hits on the emotional connection you have with your work and the way developer tooling impacts your day-to-day experience.</p>

<p>In the years since I left Autodesk, I’ve been working at <a href="https://ashbyhq.com">Ashby</a> (with a lot of my former iOS teammates from PlanGrid/Autodesk!) building a relationship with a new stack. Working in a stack based on TypeScript end-to-end has been great, and I think it’ll hold me over another 10 years.</p>]]></content><author><name>Ben Asher</name></author><category term="software" /><category term="kotlin" /><category term="multiplatform" /><category term="mobile" /><category term="web" /><category term="node" /><category term="javascript" /><category term="typescript" /><category term="iOS" /><category term="Xcode" /><summary type="html"><![CDATA[A reflection on leaving iOS development for web]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://benasher.co/img/logo.png" /><media:content medium="image" url="https://benasher.co/img/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">An Interface for Multiplatform Networking</title><link href="https://benasher.co/kotlin-multiplatform-networking/" rel="alternate" type="text/html" title="An Interface for Multiplatform Networking" /><published>2021-04-04T00:00:00-07:00</published><updated>2021-04-04T00:00:00-07:00</updated><id>https://benasher.co/kotlin-multiplatform-networking</id><content type="html" xml:base="https://benasher.co/kotlin-multiplatform-networking/"><![CDATA[<p>If you’re starting a new Kotlin multiplatform mobile project, I think starting with ktor for your networking layer is a reasonable choice. If you’re planning to add Kotlin multiplatform to an existing project, this may not be desirable for a couple of reasons:</p>

<ol>
  <li>Your application may already have a working networking layer, which itself may contain logic related to your API (e.g. authentication). Going with ktor means repeating or refactoring that logic in the shared multiplatform layer.</li>
  <li>ktor will manage its own platform-specific networking client (e.g. <code class="language-plaintext highlighter-rouge">URLSession</code> on iOS). If your ktor-based networking stack and your application’s “legacy” stack are making connections to the same hosts, you may end up with kept-alive connections to the same hosts in the connection pools managed by each stack (doubling your connections, depending on your platform).</li>
</ol>

<p>Depending on your project, these issues may or may not matter to you, but with these ideas in mind, we (my team at Autodesk) took a stack-agnostic approach to multiplatform networking. In our multiplatform layer, we have an <code class="language-plaintext highlighter-rouge">interface</code> called <code class="language-plaintext highlighter-rouge">Network</code>, and it allows us to make network calls to our backend API. Here’s a look at the <code class="language-plaintext highlighter-rouge">interface</code> itself. It started as this core method and a collection of supporting classes, though it has grown over time:</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="cm">/** Defines the interaction between the library and the host app for making API requests */</span>
<span class="k">public</span> <span class="kd">interface</span> <span class="nc">Network</span> <span class="p">{</span>

    <span class="cm">/**
     * Makes an API request
     *
     * @param host The host, to which to make the request
     * @param request The request details
     * @param completion: The closure to call when the request is complete with the response
     */</span>
    <span class="k">public</span> <span class="k">fun</span> <span class="nf">makeRequest</span><span class="p">(</span>
        <span class="n">host</span><span class="p">:</span> <span class="nc">APIHost</span><span class="p">,</span>
        <span class="n">request</span><span class="p">:</span> <span class="nc">Request</span><span class="p">,</span>
        <span class="n">completion</span><span class="p">:</span> <span class="p">(</span><span class="nc">Request</span><span class="p">.</span><span class="nc">DataResponse</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span>
    <span class="p">):</span> <span class="nc">NetworkDisposable</span>
<span class="p">}</span>

<span class="cm">/** Helper for making coroutine-driven network calls */</span>
<span class="k">internal</span> <span class="k">suspend</span> <span class="k">fun</span> <span class="nc">Network</span><span class="p">.</span><span class="nf">makeRequest</span><span class="p">(</span><span class="n">host</span><span class="p">:</span> <span class="nc">APIHost</span><span class="p">,</span> <span class="n">request</span><span class="p">:</span> <span class="nc">Request</span><span class="p">):</span> <span class="nc">Request</span><span class="p">.</span><span class="nc">DataResponse</span> <span class="p">{</span>
    <span class="c1">// Use deferred, so that we can capture coroutine cancellation</span>
    <span class="c1">// and use that to cancel our network request, if needed</span>
    <span class="k">return</span> <span class="nf">threadSafeSuspendCallback</span> <span class="p">{</span> <span class="n">completion</span> <span class="p">-&gt;</span>
        <span class="kd">val</span> <span class="py">requestCompletion</span><span class="p">:</span> <span class="p">(</span><span class="nc">Request</span><span class="p">.</span><span class="nc">DataResponse</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="p">{</span>
            <span class="nf">completion</span><span class="p">(</span><span class="nc">Result</span><span class="p">.</span><span class="nf">success</span><span class="p">(</span><span class="n">it</span><span class="p">))</span>
        <span class="p">}</span>
        <span class="kd">val</span> <span class="py">cancellable</span> <span class="p">=</span> <span class="nf">makeRequest</span><span class="p">(</span><span class="n">host</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">requestCompletion</span><span class="p">.</span><span class="nf">freeze</span><span class="p">())</span>
        <span class="k">return</span><span class="nd">@threadSafeSuspendCallback</span> <span class="p">{</span> <span class="n">cancellable</span><span class="p">.</span><span class="nf">cancel</span><span class="p">()</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>


<span class="cm">/**
 * Details of the network request
 */</span>
<span class="k">public</span> <span class="kd">data class</span> <span class="nc">Request</span><span class="p">(</span>

    <span class="cm">/**
     * The request's path, starting with "/"
     */</span>
    <span class="k">public</span> <span class="kd">val</span> <span class="py">path</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span>

    <span class="cm">/**
     * A [Map] of the request's query parameters
     */</span>
    <span class="k">public</span> <span class="kd">val</span> <span class="py">queryParameters</span><span class="p">:</span> <span class="nc">Map</span><span class="p">&lt;</span><span class="nc">String</span><span class="p">,</span> <span class="nc">String</span><span class="p">&gt;?,</span>

    <span class="cm">/**
     * The request's body, if any
     */</span>
    <span class="k">public</span> <span class="kd">val</span> <span class="py">body</span><span class="p">:</span> <span class="nc">String</span><span class="p">?,</span>

    <span class="cm">/**
     * The request's method
     */</span>
    <span class="k">public</span> <span class="kd">val</span> <span class="py">method</span><span class="p">:</span> <span class="nc">Method</span>
<span class="p">)</span> <span class="p">{</span>
    <span class="nf">init</span> <span class="p">{</span> <span class="nf">freeze</span><span class="p">()</span> <span class="p">}</span>

    <span class="k">public</span> <span class="k">enum</span> <span class="kd">class</span> <span class="nc">Method</span> <span class="p">{</span> <span class="nc">GET</span><span class="p">,</span> <span class="nc">POST</span><span class="p">,</span> <span class="nc">PUT</span><span class="p">,</span> <span class="nc">DELETE</span><span class="p">,</span> <span class="nc">PATCH</span> <span class="p">}</span>

    <span class="cm">/** The kinds of responses that can be returned by the host app */</span>
    <span class="k">public</span> <span class="k">sealed</span> <span class="kd">class</span> <span class="nc">DataResponse</span> <span class="p">{</span>

        <span class="cm">/**
         * An HTTP response of in-memory data
         *
         * @param data The wrapped data
         * @param statusCode The status code of the network request. Expected to be in the success range.
         */</span>
        <span class="k">public</span> <span class="kd">class</span> <span class="nc">Success</span><span class="p">(</span><span class="k">public</span> <span class="kd">val</span> <span class="py">data</span><span class="p">:</span> <span class="nc">HTTPResponseData</span><span class="p">,</span> <span class="k">public</span> <span class="kd">val</span> <span class="py">statusCode</span><span class="p">:</span> <span class="nc">HttpStatusCode</span><span class="p">)</span> <span class="p">:</span>
            <span class="nc">DataResponse</span><span class="p">()</span> <span class="p">{</span>
            <span class="nf">init</span> <span class="p">{</span> <span class="nf">freeze</span><span class="p">()</span> <span class="p">}</span>
        <span class="p">}</span>

        <span class="cm">/**
         * An error during the request
         */</span>
        <span class="k">public</span> <span class="kd">class</span> <span class="nc">Failure</span><span class="p">(</span><span class="k">public</span> <span class="kd">val</span> <span class="py">error</span><span class="p">:</span> <span class="nc">NetworkError</span><span class="p">)</span> <span class="p">:</span> <span class="nc">DataResponse</span><span class="p">()</span> <span class="p">{</span>
            <span class="nf">init</span> <span class="p">{</span> <span class="nf">freeze</span><span class="p">()</span> <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>Starting at the top, we have <code class="language-plaintext highlighter-rouge">interface Network</code>, which defines a method for making a request to one of our API hosts, which are represented by an enum with cases for each of our internal API hosts (not shown here—each case maps to a host URI, which can change based on staging/production environment). The non-host parts of the request are represented by the <code class="language-plaintext highlighter-rouge">Request</code> class, and then the completion lambda is called with the response, when the request is finished. Making this call returns a handle to the request in the form of <code class="language-plaintext highlighter-rouge">NetworkDisposable</code> (definition omitted), which is an <code class="language-plaintext highlighter-rouge">interface</code> with a single method that can be called to cancel the request. Below that, you can see a sampling of the request and response classes. I left some definitions out for brevity, but hopefully you get the idea: a simple collection of classes to represent a basic HTTP request and response.</p>

<p>With these, you have each client application, who wants to use functionality of the multiplatform layer requiring network access, provide an implementation of <code class="language-plaintext highlighter-rouge">Network</code>. So instead of ktor, you just bring along a class that implements this <code class="language-plaintext highlighter-rouge">interface</code> and negotiates calls to your existing networking stack. In the PlanGrid app, we got started with this interface about two years ago, and it continues to serve us well, though we have added to it over time to support more use cases (e.g. downloading files). Here is a short list of things I like about it, which we take advantage of in our code:</p>

<ol>
  <li>With all of our multiplatform business logic that needs network access using <code class="language-plaintext highlighter-rouge">Network</code>, it’s easy to write <code class="language-plaintext highlighter-rouge">Network</code> fakes in our tests that work across platforms.</li>
  <li>We have a helper function that allows making a suspend-y network call. It’s an internal extension function on <code class="language-plaintext highlighter-rouge">Network</code> (using <a href="https://github.com/autodesk/coroutineworker#waiting-on-asynchronous-callback-based-work">this</a>) that allows internal code to use <code class="language-plaintext highlighter-rouge">Network</code> with coroutines, while on the outside (back on your native iOS/Android/Windows platform), all clients get an easy-to-support lambda-based API.</li>
  <li>It doesn’t make our decision on ktor final. With <code class="language-plaintext highlighter-rouge">Network</code> as this seam we have throughout our code, we can one day decide the time is right to swap out all of our platform-specific <code class="language-plaintext highlighter-rouge">Network</code> implementations for a single one based on ktor.</li>
</ol>

<p>Like I said earlier, ktor may be the right choice for your project, but our choice to take this <code class="language-plaintext highlighter-rouge">interface Network</code> approach allowed us to get going on building the meat of our multiplatform business logic, fast. We continue to reap its benefits years later.</p>]]></content><author><name>Ben Asher</name></author><category term="software" /><category term="kotlin" /><category term="multiplatform" /><category term="kotlin/native" /><category term="mobile" /><category term="networking" /><category term="ktor" /><summary type="html"><![CDATA[An approach to networking with Kotlin multiplatform]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://benasher.co/img/logo.png" /><media:content medium="image" url="https://benasher.co/img/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Debugging Binary Kotlin Frameworks</title><link href="https://benasher.co/kotlin-binary-debugging/" rel="alternate" type="text/html" title="Debugging Binary Kotlin Frameworks" /><published>2020-11-24T00:00:00-08:00</published><updated>2020-11-24T00:00:00-08:00</updated><id>https://benasher.co/kotlin-binary-debugging</id><content type="html" xml:base="https://benasher.co/kotlin-binary-debugging/"><![CDATA[<p>When getting Kotlin integrated into your iOS and Android teams’ workflows, something you will need to tackle is figuring out how to debug your iOS-ready Kotlin from Xcode. This quest may lead you to some of the following resources:</p>

<ul>
  <li>Kevin Galligan’s post: <a href="https://dev.to/touchlab/debugging-kotlin-on-ios-with-xcode-37fd">Debugging Kotlin on iOS with Xcode</a></li>
  <li>Touchlab’s GitHub resources for doing this, such as <a href="https://github.com/touchlab/xcode-kotlin/">xcode-kotlin</a></li>
</ul>

<p>At Autodesk, engineers aren’t always working alongside a Kotlin framework that was built from local sources. Often, many of us are testing our latest Swift code, which links against the pre-built binary version of the framework that was built by CI and downloaded and installed from a remote location via CocoaPods (podspec checked into a private specs repo). We do this for a few reasons:</p>

<ol>
  <li>Early on, this meant we could test out Kotlin multiplatform without requiring that other iOS engineers have a certain Java setup to run a local build. To the unaware iOS engineer, it just looked like we added another CocoaPod that installs a binary framework— one of many.</li>
  <li>Even if you are someone on the team that often does work in our shared Kotlin code, using a pre-built binary means you can skip a whole build step (building Kotlin into an iOS framework).</li>
</ol>

<p>If you follow all of the advice from Kevin and Touchlab’s available documentation and use their plugins, then you should find yourself with a working setup that allows you to debug Kotlin built from source on your machine. This is exciting! You now have a basic working “dev setup.”</p>

<p>Now, let’s say you’re working with the pre-built version of your Kotlin framework. All of a sudden, you find a bug! You do some initial debugging, and you come to the conclusion that, yes, this bug is in the Kotlin code somewhere. To get this sorted out, you will need to get your new “dev setup” back into place:</p>

<ol>
  <li>Figure out what version of the Kotlin code you were using, and check that out.</li>
  <li>Follow the above-linked guidance, and get Xcode building your Kotlin from source.</li>
  <li>Wait anywhere from 1 to 5 minutes for your Kotlin build to finish.</li>
  <li>Wait some more, while Xcode finishes its rebuild.</li>
  <li>Come back 20 minutes later because you answered a Slack message and forgot you were doing all of the above.</li>
</ol>

<p>Now that it has been about 30 minutes, you resume debugging, fix the bug, rebuild to verify it, (answer more Slack messages in between), verify the fix, and then PR and push a patch. Now that you’re done, you can go back to what you were working on before when you encountered the bug, which was what again? Oh shoot it’s the end of the day— try again tomorrow.</p>

<h2 id="debugging-from-the-binary">Debugging from the Binary</h2>

<p>If this sounds miserable, you are right! Slack relationship issues aside, there is a better way. You can debug a binary pre-built from CI. The whole reason we have to debug using Kotlin that was built from local sources in the first place is because dSYM bundles (which enable Xcode to map binary function addresses to your sources and therefore enable your breakpoints) contain references to your sources that contain absolute paths from the machine that built the framework and dSYM. You can set all of the break points you want, but Xcode won’t be able to figure out that <code class="language-plaintext highlighter-rouge">/var/lib/jenkins/YourLibrary/src/commonMain/kotlin/MyClass.kt</code> referenced in the dSYM built by your CI machine is <code class="language-plaintext highlighter-rouge">~/Code/YourLibrary/src/commonMain/kotlin/MyClass.kt</code> on your local machine.</p>

<p>The good news though is that there is a way to make Xcode and LLDB understand just that. Apple has a page <a href="https://opensource.apple.com/source/lldb/lldb-179.1/www/symbols.html">here</a> that explains that you can embed a plist file in your dSYM bundle that tells LLDB (run by Xcode) how to map absolute paths in your dSYM to those on your machine. Here’s their example plist:</p>

<figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="cp">&lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;</span>
<span class="nt">&lt;plist</span> <span class="na">version=</span><span class="s">"1.0"</span><span class="nt">&gt;</span>
<span class="nt">&lt;dict&gt;</span>
   <span class="nt">&lt;key&gt;</span>DBGArchitecture<span class="nt">&lt;/key&gt;</span>
   <span class="nt">&lt;string&gt;</span>i386<span class="nt">&lt;/string&gt;</span>
   <span class="nt">&lt;key&gt;</span>DBGBuildSourcePath<span class="nt">&lt;/key&gt;</span>
   <span class="nt">&lt;string&gt;</span>/path/to/build/sources<span class="nt">&lt;/string&gt;</span>
   <span class="nt">&lt;key&gt;</span>DBGSourcePath<span class="nt">&lt;/key&gt;</span>
   <span class="nt">&lt;string&gt;</span>/path/to/actual/sources<span class="nt">&lt;/string&gt;</span>
   <span class="nt">&lt;key&gt;</span>DBGDSYMPath<span class="nt">&lt;/key&gt;</span>
   <span class="nt">&lt;string&gt;</span>/path/to/foo.dSYM/Contents/Resources/DWARF/foo<span class="nt">&lt;/string&gt;</span>
   <span class="nt">&lt;key&gt;</span>DBGSymbolRichExecutable<span class="nt">&lt;/key&gt;</span>
   <span class="nt">&lt;string&gt;</span>/path/to/unstripped/exectuable<span class="nt">&lt;/string&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span></code></pre></figure>

<p>I won’t spend too much time in here, but we can eyeball the plist and see that there’s a key called <code class="language-plaintext highlighter-rouge">DBGBuildSourcePath</code> and <code class="language-plaintext highlighter-rouge">DBGSourcePath</code>, which together create the mapping we want (from CI sources to local sources). If you read through the Apple page a bit, you’ll find that once such plist per architecture included in your binary is needed to make this work. Each file is named <code class="language-plaintext highlighter-rouge">&lt;UUID&gt;.plist</code> with the UUID that identifies the architecture slice in your binary (<code class="language-plaintext highlighter-rouge">dwarfdump --uuid &lt;path_to_framework_binary&gt;</code> to see these).</p>

<p>Stepping back a bit, our goal is to be able to debug our pre-built Kotlin framework from Xcode, which we can’t do (without making some changes) because the dSYM we have with our framework contains absolute paths on a machine (the CI machine that built the framework) that isn’t our local machine. To make this work we need to:</p>

<ol>
  <li>Generate a plist that maps absolute paths from the CI machine to ones that work on our local machine.</li>
  <li>Repeat this once per architecture slice included in our framework.</li>
  <li>Name those based on the UUID (ick) of said architecture slices.</li>
  <li>Place them in the dSYM bundle for our framework.</li>
</ol>

<h2 id="automating-the-uuid-plists">Automating the UUID Plists</h2>

<p>When I was first digging into this problem, I came to realize that any of this was solvable at all by stumbling upon Max Raskin’s <a href="https://medium.com/@maxraskin/background-1b4b6a9c65be">post</a> on the subject. Max (who now happens to be one of my colleagues) wrote this post addressing the same issue but for a C++ library. As it turns out, this problem has nothing to do with Kotlin. It’s a general problem in this (Apple?) ecosystem for binary libraries. <a href="https://bugs.swift.org/browse/SR-11661">Here’s a bug</a> discussing these plists in the Swift bug tracker, for example (another non-Kotlin context). In Max’s case, it’s used to help debug a binary C++ library. In our case, we’re using it to debug a Kotlin binary framework.</p>

<p>If you read Max’s article, you’ll see that Max put together a nice python script that, when given paths to your framework, dSYM, and sources, will generate and output the plists you need, and put them in the right place. Max was also kind enough to include a reference to <a href="https://gist.github.com/benasher44/fcf92fc12ff8b539bee7cc50fb52ed32">my ruby port</a> of the same script. On my team, everyone has a dependable ruby setup for CocoaPods, but that’s not always the case for their python setups. So, I made this port.</p>

<p>To use either script and get this working:</p>

<ol>
  <li>Run the script and pass the paths to the binary framework, the dSYM, and a local checkout of the sources <em>that is the same version used to build the framework</em> (some digging in git and cross-referencing with CI may be required to get this right). This will generate the plists and put them in the right place in your dSYM bundle.</li>
  <li><a href="https://github.com/touchlab/xcode-kotlin/issues/16">Create a folder reference in Xcode to your sources</a>.</li>
  <li>Build and run your application.</li>
</ol>

<p>If all goes well, Xcode should be able to stop on your Kotlin breakpoints, even though the binary was built on your CI machine. At Autodesk, we have this ruby script run in a <a href="https://guides.cocoapods.org/syntax/podfile.html#post_install"><code class="language-plaintext highlighter-rouge">post_install</code></a> hook during <code class="language-plaintext highlighter-rouge">pod install</code>, if you set an environment variable that contains the path to your local checkout of the Kotlin sources. Then, all you need to do when you want to debug is create that folder reference in Xcode. This saves us a lot of time and hassle, if we encounter a bug in our Kotlin while testing our applications. We no longer need to stop to reconfigure our setup to build from source, just to debug our Kotlin.</p>

<p>Give the ruby script a try, and leave a comment <a href="https://gist.github.com/benasher44/fcf92fc12ff8b539bee7cc50fb52ed32">in the gist</a>, if you run into issues.</p>

<p>Happy Kotlin debugging!</p>]]></content><author><name>Ben Asher</name></author><category term="software" /><category term="kotlin" /><category term="multiplatform" /><category term="kotlin/native" /><category term="ios" /><category term="swift" /><category term="debugging" /><category term="Xcode" /><summary type="html"><![CDATA[How to post-process a binary Kotlin/Native iOS framework, so you can debug it in Xcode]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://benasher.co/img/logo.png" /><media:content medium="image" url="https://benasher.co/img/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">An Ergonomics Review of Using Kotlin from Swift</title><link href="https://benasher.co/kotlin-ios-ergonomics/" rel="alternate" type="text/html" title="An Ergonomics Review of Using Kotlin from Swift" /><published>2020-08-16T00:00:00-07:00</published><updated>2020-08-16T00:00:00-07:00</updated><id>https://benasher.co/kotlin-ios-ergonomics</id><content type="html" xml:base="https://benasher.co/kotlin-ios-ergonomics/"><![CDATA[<p>At Autodesk, my colleagues and I are more than a year and a half into our Kotlin multiplatform (KMP) shared library journey. That’s one Kotlin shared library, shared among our three mobile platforms that we support for the PlanGrid app (iOS, Android, and Windows).</p>

<p>Most new feature development for the PlanGrid app starts in the shared library now. It has become so much an extension of our main application that we’re planning a move to a mono-repo (iOS, Android, Windows, and the KMP shared library all in one place) later this year, which will help solve some of our scale issues (great problem to have all things considered).</p>

<h2 id="making-it-work">Making it Work</h2>

<p>Looking back, there were a few things that made Kotlin work well for us that have to do with a combination of developer experience and our team. Early on, the few of us working on the proof-of-concept and pitching KMP to the rest of the team focused on developer experience. We ensured that integrating our experiment wouldn’t get in the way of day-to-day work by distributing it to iOS as a binary framework via CocoaPods (already integrate other binary frameworks this way without issue).</p>

<p>While testing out KMP-based functionality alongside all of the other work going on, we made use of feature flags in case shit hit the fan in production. Once we had our foot in the door, had proved things worked well, and had come up with a plan for how the library was going to evolve, the next step was getting people to adopt the shared library for their teams’ features.</p>

<p>This again takes us back to developer experience. Lucky for us, KMP comes with iOS interop out-of-the-box. It’s so good that one of my colleagues thought they were using some kind of bridging layer that we must have written to make Kotlin feel Swift-y. When they command-clicked through to source, they were surprised to find the KMP-generated shared library header.</p>

<h3 id="having-a-good-team-helps">Having a Good Team Helps</h3>

<p>Did I mention we have a Windows team? For that side of things, we got lucky. The level of support from KMP you get for Windows does not measure up to what you get for iOS. On the Windows side, my colleagues would like to have a C# library in the style of the KMP-generated Obj-C library with the nice, generated headers that we get on iOS. Instead, they get a library that uses C-interop— quite a different experience. Fortunate for us, a few folks on our Windows team were experienced and interested enough to write and maintain their own C# code generation on top of that. We hope to open source it later this year.</p>

<h2 id="looking-at-interop">Looking at Interop</h2>

<p>To recap, these elements made it possible for KMP to work well for us:</p>

<ol>
  <li>Ease of integration on iOS and Android</li>
  <li>Ergonomic interop out-of-the-box for iOS</li>
  <li>Colleagues on our Windows team willing to take on the challenge of solving the interop issues there</li>
</ol>

<p>If I could only pick one, it would be the interop on iOS that allowed this to become as successful as it has for us. It would be a much tougher sell, for example, had both the iOS and Windows teams needed to spend loads of effort on interop.</p>

<p>My favorite example of this great interop is one from <a href="/kotlin-ios-getting-started-interop/">my earlier post</a>, where Phill and I wrote about the iOS interop:</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="c1">// Kotlin</span>
<span class="k">enum</span> <span class="kd">class</span> <span class="nc">LogLevel</span> <span class="p">{</span>
    <span class="nc">ERROR</span><span class="p">,</span>
    <span class="nc">WARNING</span><span class="p">,</span>
    <span class="nc">INFO</span><span class="p">,</span>
    <span class="nc">DEBUG</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nc">Logger</span> <span class="p">{</span>
    <span class="k">companion</span> <span class="k">object</span> <span class="nf">default</span> <span class="p">{</span>
        <span class="k">fun</span> <span class="nf">log</span><span class="p">(</span><span class="n">level</span><span class="p">:</span> <span class="nc">LogLevel</span> <span class="p">=</span> <span class="nc">LogLevel</span><span class="p">.</span><span class="nc">ERROR</span><span class="p">,</span> <span class="n">message</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="n">completion</span><span class="p">:</span> <span class="p">(</span><span class="nc">Boolean</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">// Swift</span>
<span class="kt">Logger</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">log</span><span class="p">(</span><span class="o">.</span><span class="n">error</span><span class="p">,</span> <span class="s">"An error ocurred"</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Closure</span>
<span class="p">}</span></code></pre></figure>

<p>This example highlights some idiomatic Kotlin that allows you to write idiomatic-looking Swift. It’s impressive. However, this example has a secret. It also highlights many of the areas where Kotlin/Native interop with iOS has room for improvement. With Kotlin 1.4.0 out the door, I hope now is a good time to raise these issues. Fixing them would take the sell to iOS teams to the next level, at least in terms of having excellent interop. I’ll discuss the issues in increasing in order of how long it took our team to notice and bump into them.</p>

<h3 id="exhaustive-enums">Exhaustive Enums</h3>

<p>This is one of the earliest areas, for which I filed <a href="https://github.com/JetBrains/kotlin-native/issues/2521">an issue</a> in the Kotlin/Native GitHub. Enums are a great way to describe an input for an API, such as the above log method. However, we ran into problems with Kotlin enums as soon as we wanted to do an exhaustive <code class="language-plaintext highlighter-rouge">switch</code> (equivalent to exhaustive <code class="language-plaintext highlighter-rouge">when</code> in Kotlin) over them in Swift.</p>

<p>The problem lies in how enums are represented in Kotlin compared to Obj-C (remember Swift is irrelevant for comparison— interop is via Swift-y feeling Obj-C). Enums in Kotlin are reference types (a special <code class="language-plaintext highlighter-rouge">class</code>). In Obj-C, they’re integers. Attempting to <code class="language-plaintext highlighter-rouge">switch</code> over them from Swift would be like trying to <code class="language-plaintext highlighter-rouge">switch</code> over any other class instance that you defined in Swift, for example. The Swift compiler doesn’t know that there happens to be a finite number of instances of that <code class="language-plaintext highlighter-rouge">enum class</code> like Kotlin does.</p>

<p>There is a workaround though that can improve the ergonomics a bit. You can define a matching C-style enum (with a matching name), and <a href="https://github.com/JetBrains/kotlin-native/issues/2521#issuecomment-453009890">redefine the ordinal property</a> as having that type. At Autodesk, we do this with a bit of fragile (albeit has worked in our codebase with only one minor tweak since its creation) code generation that adds these C-style enums to the Obj-C framework header as Obj-C extensions on all of our enum types. With that, we get exhaustive <code class="language-plaintext highlighter-rouge">switch</code> for all of our Kotlin enums, by doing a <code class="language-plaintext highlighter-rouge">switch</code> over the ordinal property.</p>

<p>In the above sample code, you can see there’s no problem passing enums around. You wouldn’t have any idea about how Kotlin enums are represented, until you go to <code class="language-plaintext highlighter-rouge">switch</code> on one.</p>

<h3 id="companion-objects">Companion Objects</h3>

<p>In <a href="/kotlin-ios-getting-started/">my first article</a>, I mentioned how, as an iOS developer, I found the <code class="language-plaintext highlighter-rouge">companion object</code> a bit strange at first, but I get it now. As my iOS colleagues have become better acquainted with them, we have begun to see them more often in our shared library. The problem comes if you don’t know that you can name a <code class="language-plaintext highlighter-rouge">companion object</code>, like in the above example. Unnamed ones are more common, in my experience. Here is what the Swift code would look like, if we hadn’t named the above <code class="language-plaintext highlighter-rouge">companion object</code> “default”:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">// Swift</span>
<span class="kt">Logger</span><span class="o">.</span><span class="kt">Companion</span><span class="p">()</span><span class="o">.</span><span class="nf">log</span><span class="p">(</span><span class="o">.</span><span class="n">error</span><span class="p">,</span> <span class="s">"An error ocurred"</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Closure</span>
<span class="p">}</span></code></pre></figure>

<p>As someone who writes a lot of Swift, this looks funny at first. Are we creating a new <code class="language-plaintext highlighter-rouge">Logger.Companion</code>? If so, where can I see what this does in Kotlin? The ergonomics of the unnamed <code class="language-plaintext highlighter-rouge">companion object</code> is another issue that <a href="https://github.com/JetBrains/kotlin-native/issues/2757">I filed early on</a>. To answer the question, <a href="https://github.com/JetBrains/kotlin-native/issues/2757#issuecomment-472866293">you aren’t creating a new</a> <code class="language-plaintext highlighter-rouge">Logger.Companion</code>.</p>

<p>The fix here isn’t straightforward. You can convince your team to prefer naming a <code class="language-plaintext highlighter-rouge">companion object</code>, but that doesn’t always make sense, if say your goal is to use a <code class="language-plaintext highlighter-rouge">companion object</code> to namespace a public constant. Solutions discussed in the ticket would be breaking changes. That said, I think improving the ergonomics here would be an easy way to prevent less enthusiastic iOS developers from having this easy (and small) thing to point at. At Autodesk, we just acknowledge this quirk of the Obj-C export and move on. But, that’s easy for us to do now, as we have a critical mass.</p>

<h3 id="default-arguments">Default Arguments</h3>

<p>Although we bumped into this one later on, a fix for this would make the biggest difference for our team right now because our dependence on KMP has grown. If you notice in the above Kotlin, the log function has a default argument. However, you cannot use that default argument from Swift. As a log parameter, this might not be that important. In other contexts though, this could be a default integer parameter, for example. In that case, it can be near impossible to know what argument you should pass in the “default” case. To avoid bugs, your best bet is to go back to the Kotlin source and find the answer.</p>

<p>Though, I’ve heard folks say: but Swift supports default arguments, why shouldn’t this work? This is often the first place folks begin to internalize that this doesn’t matter. The interop is via Obj-C, so all that matters is what Obj-C supports. You can repeat this same answer for a bevy of similar complaints. <code class="language-plaintext highlighter-rouge">Interface</code> extensions? Swift supports the equivalent <code class="language-plaintext highlighter-rouge">protocol</code> extensions. Obj-C does not. Optional primitive types? Obj-C doesn’t have those either. None of this is a knock on Kotlin/Native’s interop, which again is great! Obj-C interop was the correct and stable choice at the time, as that was before Swift had a stable ABI. I made comments about this in my previous <a href="/kotlin-ios-getting-started-interop/">interop article</a>, so let’s go back to the issue at hand.</p>

<p>Default arguments have a solution in Kotlin/JVM interop, which is used in hand-written Obj-C as well: <a href="https://youtrack.jetbrains.com/issue/KT-38685">generated overloads</a>. Generating overloads for C and Obj-C KMP libraries would be a huge improvement to Kotlin/Native’s export facilities. I hope with Kotlin 1.4.0 out the door, time can be made for this one.</p>

<h3 id="enums---missing-exports">Enums - Missing Exports</h3>

<p><del>I want to go back to enums for a moment. I mentioned that I would review the issues in the order that my team ran into them. After getting more comfortable using Kotlin enums in our code, we began to come up with cases where we wanted to enumerate Kotlin enums. However, the needed <code class="language-plaintext highlighter-rouge">values()</code> function <a href="https://youtrack.jetbrains.com/issue/KT-38530">is not exported to Obj-C by default</a>. If you need this, you’re left to define it yourself in your library and have it call the equivalent function. The workaround is fine, but it gets in the way, when the library you’re using is one that’s already packaged up and distributed. A fix requires another PR and waiting on another CI deploy.</del></p>

<h4 id="new-in-1430-enumtvalues">New in 1.4.30: <code class="language-plaintext highlighter-rouge">Enum&lt;T&gt;.values()</code></h4>

<p>As of Kotlin 1.4.30, your Kotlin enums now export a <code class="language-plaintext highlighter-rouge">values()</code> <code class="language-plaintext highlighter-rouge">class</code> method, which maps directly to the <code class="language-plaintext highlighter-rouge">Enum&lt;T&gt;.values()</code> Kotlin method. One thing to note though is that this direct mapping exposes the unfortunate (for Obj-C/Swift) <code class="language-plaintext highlighter-rouge">Kotlin.Array</code>. If you’re using Kotlin/Native, you may know that <code class="language-plaintext highlighter-rouge">Kotlin.List</code> is the type that Kotlin/Native exposes as a native <code class="language-plaintext highlighter-rouge">NSArray</code>/<code class="language-plaintext highlighter-rouge">Swift.Array</code>. With <code class="language-plaintext highlighter-rouge">Kotlin.Array</code>, you end up with a special <code class="language-plaintext highlighter-rouge">KotlinArray</code> type, which exposes an iterator. To make of use of this in your Swift code, you’ll want to write something like this:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">extension</span> <span class="kt">Array</span> <span class="k">where</span> <span class="kt">Element</span><span class="p">:</span> <span class="kt">AnyObject</span> <span class="p">{</span>
    <span class="c1">/// Allows conversion from `KotlinArray` to `Array`</span>
    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">array</span><span class="p">:</span> <span class="kt">KotlinArray</span><span class="o">&lt;</span><span class="kt">Element</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>
        <span class="k">self</span><span class="o">.</span><span class="nf">reserveCapacity</span><span class="p">(</span><span class="kt">Int</span><span class="p">(</span><span class="n">array</span><span class="o">.</span><span class="n">size</span><span class="p">))</span>
        <span class="k">let</span> <span class="nv">iterator</span> <span class="o">=</span> <span class="n">array</span><span class="o">.</span><span class="nf">iterator</span><span class="p">()</span>
        <span class="k">while</span> <span class="n">iterator</span><span class="o">.</span><span class="nf">hasNext</span><span class="p">()</span> <span class="p">{</span>
            <span class="k">self</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">iterator</span><span class="o">.</span><span class="nf">next</span><span class="p">()</span> <span class="k">as!</span> <span class="kt">Element</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>This will let you bridge <code class="language-plaintext highlighter-rouge">KotlinArray</code> to <code class="language-plaintext highlighter-rouge">Swift.Array</code> like so: <code class="language-plaintext highlighter-rouge">Array(MyEnum.values())</code>. Not bad! I also tried adding some kind of Swift extension to enable bridging like <code class="language-plaintext highlighter-rouge">MyEnum.values().toArray()</code>, but I ran into Obj-C to Swift generics compatibility issues. If you find a way to make that work though, please let me know on mastodon!</p>

<h3 id="translation-to-obj-c-primitives">Translation to Obj-C Primitives</h3>

<p>The final one I want to talk about is the most minor, but we do come across it on occasion. In the above example, we are hiding the fact that the completion closure has one parameter. It’s a Kotlin <code class="language-plaintext highlighter-rouge">boolean</code>, but it is translated to Obj-C as a <code class="language-plaintext highlighter-rouge">KotlinBoolean</code> <code class="language-plaintext highlighter-rouge">class</code>. This is bound to happen if the type is optional. Again, Obj-C doesn’t support optional primitives. In this case however, it’s not. It should be a <code class="language-plaintext highlighter-rouge">BOOL</code> instead, which will translate to a <code class="language-plaintext highlighter-rouge">Swift</code> <code class="language-plaintext highlighter-rouge">Bool</code>.</p>

<p>In most cases, Kotlin/Native does the right thing, but there are occasions like <a href="https://youtrack.jetbrains.com/issue/KT-41106">this one</a> that come across a bit clunky. I admit though, I don’t have the expertise to understand why this happened in this case. Again, the interop you get for iOS is great. Fixing polish-type issues like this would take the interop to the next level.</p>

<h3 id="interface-method-collisions">Interface Method Collisions</h3>

<p>Okay I lied. There’s one more quirk, but I felt that it didn’t fit as well with some of the above issues. It is also maybe a bit niche, and it depends on how you architect your code, as to whether or not you’ll bump into this one. In our codebase, we create an <code class="language-plaintext highlighter-rouge">interface</code> for each of our repository classes. Let’s say you have a repository like this one:</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="c1">// Swift</span>
<span class="kd">interface</span> <span class="nc">FishRepository</span> <span class="p">{</span>
   <span class="k">fun</span> <span class="nf">fetchById</span><span class="p">(</span><span class="n">id</span><span class="p">:</span> <span class="nc">String</span><span class="p">):</span> <span class="nc">Fish</span><span class="p">?</span>
<span class="p">}</span></code></pre></figure>

<p>Over time, other people on my team will also add repositories with <code class="language-plaintext highlighter-rouge">fetchById</code> methods with the same name. Why not? <code class="language-plaintext highlighter-rouge">fetchById</code> is a reasonable way to name that method. However, every time you add such a method, methods with the same name generated for Obj-C will get an underscore added to the end to disambiguate them from others. But why you ask? Again, the explanation has to do with Kotlin vs. Obj-C. Read <a href="https://github.com/JetBrains/kotlin-native/issues/3293">this issue</a> for the full details. At the time of this writing, one of our <code class="language-plaintext highlighter-rouge">fetchById</code> methods is up to seven underscores in Obj-C 😂.</p>

<p>If you have a look at the GitHub issue, there doesn’t appear to be an easy solution from the Kotlin/Native side. What we’ve started doing is just naming our methods better. In this case, renaming the method to <code class="language-plaintext highlighter-rouge">fetchFishById</code> should prevent this from happening (until we add another fishy repository). That said, it would be great if Kotlin/Native could emit a warning about such issues. Then, we can catch these during development before the Obj-C export happens, and a random API gets an extra underscore (making the overall change breaking). Kotlin 1.4.0 has new native-specific frontend checkers. A new one that helps us out here would be most welcome.</p>

<h2 id="️-kotlinnative">❤️ Kotlin/Native</h2>

<p>Kotlin/Native’s interop for iOS is great, and it has allowed us to scale a shared codebase with little effort on the iOS side. That’s a huge deal, when you consider that most of the mobile engineers that work on the PlanGrid app had to learn a new language for this to work. Again, I hope folks (including those at JB) don’t read this as a knock on Kotlin/Native. Now that Kotlin 1.4.0 is out the door, I hope these issues can be addressed to take Kotlin/Native’s interop to the next level 🚀.</p>

<p>For those evaluating Kotlin/Native, of course you’ll want to know what issues lie ahead as you scale up. This lays out all of the source-level issues that we’ve run into. Despite these issues, it has been well worth it.</p>]]></content><author><name>Ben Asher</name></author><category term="software" /><category term="kotlin" /><category term="multiplatform" /><category term="kotlin/native" /><category term="ios" /><category term="swift" /><summary type="html"><![CDATA[A review of using Kotlin from Swift— good parts and those that could use improvement.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://benasher.co/img/logo.png" /><media:content medium="image" url="https://benasher.co/img/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Getting Started with Kotlin on iOS, Part 3: The Build</title><link href="https://benasher.co/kotlin-ios-getting-started-build/" rel="alternate" type="text/html" title="Getting Started with Kotlin on iOS, Part 3: The Build" /><published>2020-07-05T00:00:00-07:00</published><updated>2020-07-05T00:00:00-07:00</updated><id>https://benasher.co/kotlin-ios-getting-started-build</id><content type="html" xml:base="https://benasher.co/kotlin-ios-getting-started-build/"><![CDATA[<p>Once you get your Kotlin multiplatform proof-of-concept in a working state, you’ll want to get your build ready for integration into your team’s workflow. This means CI that builds your library and runs the tests on every platform you support. This also means a build setup that allows playing with changes to your multiplatform library in a local debug build of your app. And finally, you’ll want to distribute the library for consumption in your application. Now, each of those pieces could be its own article. My goal here is to help you understand the basics that, when put together, can be used to accomplish each of those goals.</p>

<p>Fundamental to getting this working is understanding what the various Gradle tasks do, which make your multiplatform build work. It’s also important to dig into how Gradle tasks work in general, so you can better understand how to debug Gradle build issues, when they arise — a useful skill upgrading to a new version of the Kotlin Gradle plugin.</p>

<p>On top of that, I think you’ll also start to understand how powerful Gradle is and begin to see how you can use it to automate all sorts of other tasks that are part of your build. And while you can get away with not diving into details about how Gradle tasks work for awhile, I promise that if you spend a bit of time on some of the recommended reading below, you’ll wish you hadn’t put it off.</p>

<h2 id="gradle">Gradle</h2>

<p>For Kotlin multiplatform, the build works via Gradle and the <a href="https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#setting-up-a-multiplatform-project">Kotlin multiplatform Gradle plugin</a>. For iOS developers coming to Gradle for the first time, Gradle is like <code class="language-plaintext highlighter-rouge">xcodebuild</code>. But, instead of lots of build variables and a project file that defines your targets and configuration, in Gradle it’s all defined in code. I found this a bit confusing and unfamiliar at first, but now that I understand it, I find that I enjoy Gradle’s build tooling far more than what I get with Xcode.</p>

<p>The best advice I can give about learning Gradle is to read some of their documentation. Early on, my coworkers and I found we were able to make the most progress on debugging build issues after we had done some reading on <a href="https://gradle.org">gradle.org</a> and <a href="https://kotlinlang.org">kotlinlang.org</a> to understand some core Gradle and Kotlin multiplatform plugin concepts. To help with that, I’ve made a recommend reading list:</p>

<ol>
  <li><a href="https://docs.gradle.org/current/userguide/gradle_wrapper.html">Gradle Wrapper</a>: While IDEs like IntelliJ and Android Studio know how to interact with Gradle, it’s helpful to know how to run tasks from the CLI, and you do that via the wrapper.</li>
  <li><a href="https://docs.gradle.org/current/userguide/tutorial_using_tasks.html">Build Script Basics</a>: Read up through “Task dependencies.”</li>
  <li><a href="https://docs.gradle.org/current/userguide/more_about_tasks.html">Authoring Tasks</a>: Tasks are the basic building blocks of a Gradle build. Even if you never write a task, you’ll understand how they work after this guide.</li>
  <li><a href="https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html">Building Multiplatform Projects with Gradle</a>: Even if your project is already setup, your goal with this guide should be to understand targets, source sets, and maybe a bit about the Kotlin/Native-specific target DSLs and shortcuts.</li>
</ol>

<h3 id="gradle-tasks-in-a-kotlin-multiplatform-project">Gradle Tasks in a Kotlin Multiplatform Project</h3>

<p>In your project, Gradle tasks are what make it go. They run your tests, build your debug builds, and it’s what your CI will run to verify your build. Kotlin multiplatform projects turn the Gradle task complexity up to 11. So again, if you can spare the time, do as much of the recommended reading above as you can stand. From this point on, I’ll assume some base knowledge of tasks.</p>

<p>Something I often encounter on my team is folks, that are newer to multiplatform, running the “wrong” Gradle tasks. For example, someone will come to me with a build issue, and I’ll find that this whole time they’ve been running the <a href="https://docs.gradle.org/current/userguide/base_plugin.html"><code class="language-plaintext highlighter-rouge">build</code>, <code class="language-plaintext highlighter-rouge">check</code>, or <code class="language-plaintext highlighter-rouge">assemble</code></a> tasks as part of their development workflow. On a project with fewer targets (i.e. only a JVM target), this is fine. In a multiplatform project, this is going to run the build and/or test tasks for all of the targets in your project that are supported by your machine. That can take a <em>lot</em> of time.</p>

<p>Instead, find a target that works for you — maybe one that’s for your preferred development platform. Then, let CI handle the final <code class="language-plaintext highlighter-rouge">check</code> across all of your targets. To see what any of these does on your machine, run (for example for <code class="language-plaintext highlighter-rouge">check</code>) <code class="language-plaintext highlighter-rouge">./gradlew check --dry-run</code>. You’ll get a list of all of the tasks that <code class="language-plaintext highlighter-rouge">check</code> would invoke, and you can pick one from the list that makes sense for you. Read on for more info on decoding some of these task names.</p>

<h2 id="building-out-ci">Building out CI</h2>

<p>Alright so at this point, I’m going to assume you’ve done some of the recommended reading above. You understand what tasks are, how you use them to build your project, and that there are a lot of them in a Kotlin multiplatform project. If you don’t have a project setup, you can clone <a href="https://github.com/benasher44/KotlinMobileBootstrap">my bootstrap project</a> and run <code class="language-plaintext highlighter-rouge">./gradlew tasks</code> to see for yourself.</p>

<p>When getting started on CI work, I like to start by listing the high level steps that need to happen during the build. For PRs, there are two things we need to do:</p>

<ol>
  <li>Build the project</li>
  <li>Run tests for the project</li>
</ol>

<p>If those two things run without error, the build is passing. With multiplatform, this becomes complicated by the number of platforms you support. This might mean setting up some kind of build <a href="https://docs.travis-ci.com/user/build-matrix/">matrix</a>-<a href="https://help.github.com/en/actions/configuring-and-managing-workflows/configuring-a-workflow#configuring-a-build-matrix">type</a> <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started-multiplatform?view=azure-devops">setup</a> where these steps run on multiple operating systems for each platform.</p>

<p>In many cases, it’s easy enough to just run <code class="language-plaintext highlighter-rouge">build</code>. From <a href="https://docs.gradle.org/current/userguide/base_plugin.html#sec:base_tasks">the docs</a>:</p>

<blockquote>
  <p>Intended to build everything, including running all tests, producing the production artifacts and generating documentation…</p>
</blockquote>

<p>If you’re in a hurry, <code class="language-plaintext highlighter-rouge">build</code> will get the job done, and for small projects, this <a href="https://github.com/benasher44/uuid/blob/297d2f038d93cae6fce976b15ed922429c4cab62/.github/workflows/pr.yml#L61">is enough</a>.</p>

<h3 id="too-much-build">Too Much <code class="language-plaintext highlighter-rouge">build</code></h3>

<p>However, be aware that in cases where you’re running your build on multiple operating systems, the <code class="language-plaintext highlighter-rouge">build</code> task on its own can be wasteful. Let’s say your multiplatform project has a JVM target. Well, if your macOS and linux environments both support JVM, then running <code class="language-plaintext highlighter-rouge">build</code> in all environments during CI is going to run your JVM build twice. If you don’t need that, consider running the <code class="language-plaintext highlighter-rouge">macosTest</code> and <code class="language-plaintext highlighter-rouge">jvmTest</code> tasks explicitly in separate environments to reduce your build times. Again, use <code class="language-plaintext highlighter-rouge">--dry-run</code> to dig into what your <code class="language-plaintext highlighter-rouge">build</code> actually does, so you can figure out a separation of environment-specific tasks that works for you.</p>

<h2 id="kotlin-multiplatform-task-cheat-sheet">Kotlin Multiplatform Task Cheat Sheet</h2>

<p>Figuring out the right tasks to run in the right environments takes some tinkering and opinions on what you want your build to validate. This is true for both CI and local development. There are a lot of tasks to choose from, but you can boil them down to just a few “task templates,” if you will, once you understand how they work.</p>

<p>First, there are two variables to consider: configuration (sometimes called variant I think?) and target. Configuration can be either “Debug” or “Release”. Target is the name of one of your targets (e.g. JVM). These variables are mixed with verbs to create the task names that make up your multiplatform project. For example, <code class="language-plaintext highlighter-rouge">&lt;target&gt;Test</code> is the format of all of the test tasks, so if you know the names of the targets in your project, you now also know the names of all of the test tasks. With that in mind, here is a bit of a cheat sheet that maps out the kinds of tasks you can run (follow along by running <code class="language-plaintext highlighter-rouge">./gradlew tasks</code> in your project):</p>

<ul>
  <li>Run tests for a target: <code class="language-plaintext highlighter-rouge">&lt;target&gt;Test</code></li>
  <li>Compile Kotlin for a target: <code class="language-plaintext highlighter-rouge">compileKotlin&lt;Target&gt;</code></li>
  <li>Links (and produces) a Kotlin/Native framework (from your binaries DSL): <code class="language-plaintext highlighter-rouge">link&lt;Configuration&gt;Framework&lt;target&gt;</code> (replace <code class="language-plaintext highlighter-rouge">Framework</code> with other binary types)</li>
</ul>

<p>With that, you should be able to figure out which Gradle tasks make sense for your build. Be warned, the Android Gradle Plugin (if that’s part of your project) adds loads more tasks that look kind of like these, but they may not always fit into the above formulas.</p>

<h2 id="ios-fat-framework">iOS Fat Framework</h2>

<p>When you’re sharing your multiplatform build with an iOS team, you may end up coming across this “fat” framework concept. In <a href="https://github.com/benasher44/KotlinMobileBootstrap">KotlinMobileBootstrap</a>, I have a special task that I created that inherits from <a href="https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#building-universal-frameworks">FatFrameworkTask</a>. Back over in Xcode-land, we’re expecting a framework that will work with our build on both iOS simulator <em>and</em> device. Those link tasks I mentioned above will only produce frameworks for one or the other. To get a framework that works for both, which is called a “fat” or “universal” framework, we have the <code class="language-plaintext highlighter-rouge">debugFatFramework</code> task (another could also be created for the release configuration).</p>

<p>You can tell which kind of framework you have and which architectures are embedded by running the <code class="language-plaintext highlighter-rouge">file</code> command on the binary in the framework. For example, running <code class="language-plaintext highlighter-rouge">./gradlew linkDebugFrameworkIosX64</code> in and then <code class="language-plaintext highlighter-rouge">file ./build/bin/iosX64/debugFramework/KotlinMobileBootstrap.framework/KotlinMobileBootstrap</code> (on the output) yields</p>

<blockquote>
  <p>./build/bin/iosX64/debugFramework/KotlinMobileBootstrap.framework/KotlinMobileBootstrap: Mach-O 64-bit dynamically linked shared library x86_64</p>
</blockquote>

<p>You can see that it only has the slice for the iOS simulator. But, if we do <code class="language-plaintext highlighter-rouge">./gradlew debugFatFramework</code> and then <code class="language-plaintext highlighter-rouge">file build/fat-framework/debug/KotlinMobileBootstrap.framework/KotlinMobileBootstrap</code>, you’ll see that it has the slices for both iOS simulator and device.</p>

<p>Okay so getting back to CI, this fat framework thing is something we want to think about. If your team is expecting a working fat framework, it’s a good idea to run these tasks during your build to add to what “passing” means for you and your team.</p>

<h2 id="putting-it-all-together">Putting it all Together</h2>

<p>The hardest part of getting all of this working is that there is no one best way to setup your build tooling to accomplish the goals of CI and a local development workflow. You may not find the answer by doing a quick web search. Instead, you have to figure out how best to use the tools you have for the different parts of the build that are a part of your team’s build setup.</p>

<p>At Autodesk, we use a homegrown CocoaPods setup that works for local development and distribution. We distribute our library as a <a href="https://guides.cocoapods.org/making/private-cocoapods.html">private pod</a>, which is configured to present the multiplatform framework as a <a href="https://guides.cocoapods.org/syntax/podspec.html#vendored_frameworks">vendored framework</a>. I may do another post all about that. However, I hope you can now use some of the information here about Gradle tasks to produce a framework that will work as a CocoaPods vendored framework, if that’s something you’re interested in. For the local setup, the Kotlin/Native <a href="https://github.com/JetBrains/kotlin-native/blob/master/COCOAPODS.md">CocoaPods plugin</a> is worth a look. Happy building!</p>]]></content><author><name>Ben Asher</name></author><category term="software" /><category term="kotlin" /><category term="multiplatform" /><category term="kotlin/native" /><category term="ios" /><category term="swift" /><summary type="html"><![CDATA[An introduction to setting up your Kotlin multiplatform build for iOS.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://benasher.co/img/logo.png" /><media:content medium="image" url="https://benasher.co/img/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Getting Started with Kotlin on iOS, Part 2: Interop</title><link href="https://benasher.co/kotlin-ios-getting-started-interop/" rel="alternate" type="text/html" title="Getting Started with Kotlin on iOS, Part 2: Interop" /><published>2020-03-22T00:00:00-07:00</published><updated>2020-03-22T00:00:00-07:00</updated><id>https://benasher.co/kotlin-ios-getting-started-interop</id><content type="html" xml:base="https://benasher.co/kotlin-ios-getting-started-interop/"><![CDATA[<h6 id="co-authored-by-phill-farrugia">Co-authored by <a href="https://github.com/phillfarrugia">Phill Farrugia</a></h6>

<p>After you get a <a href="https://benasher.co/kotlin-ios-getting-started/">feel for the language</a> and do some <a href="https://play.kotlinlang.org/koans/">Koans</a>, the next step in your journey to writing Kotlin for iOS is understanding what that Kotlin is going to look like from the Swift in your iOS app. The way Kotlin reverse interop (Swift talking to Kotlin) works is via Objective-C. For some, discovering that they get an Obj-C header from their Kotlin library, instead of a Swift one, is disappointing. That’s an understandable reaction. In a all (or majority) Swift code base, you and your team may have spent a lot of time building out your project using all that Swift has to offer — even the stuff that’s not compatible with Obj-C.
<!--more--></p>
<h2 id="but-my-swift">But my Swift!</h2>

<p>The first thing to understand here is that Kotlin/Native — the member of the Kotlin multiplatform family responsible for this part — predates the Swift language features (Swift ABI, and module ABI, stability) that would make Swift-only reverse interop for Kotlin possible. We know, and JetBrains knows, that Apple has (some) Swift-only system frameworks now, and Apple’s ecosystem is moving in that direction. If JetBrains wants developers to <a href="https://blog.jetbrains.com/kotlin/2019/12/what-to-expect-in-kotlin-1-4-and-beyond/">build iOS (and Android) apps in Android Studio</a> this year, I think we can expect Swift reverse interop in the future 🙏.</p>

<p>For now though, what you get when you build a Kotlin library into an Obj-C framework is one with a header that is well-annotated for Swift. Phill and others on our team using our Kotlin-based framework for the first time didn’t realize — until they looked under the hood — that the classes they were interacting with were Obj-C. And for what it’s worth, this is the status quo with the majority of Apple’s system frameworks that you interact with in Swift right now. You get an Obj-C framework that is annotated to feel “Swifty”.</p>

<h2 id="kotlin-visibility">Kotlin Visibility</h2>

<p>The next step to writing your own Kotlin library is understanding visibility. Like Swift, Kotlin has <a href="https://kotlinlang.org/docs/reference/visibility-modifiers.html">visibility modifiers</a> for top-level declarations: <code class="language-plaintext highlighter-rouge">public</code>, <code class="language-plaintext highlighter-rouge">internal</code>, and <code class="language-plaintext highlighter-rouge">private</code>. By default, everything is <code class="language-plaintext highlighter-rouge">public</code> 😱. It’s therefore good to get into the habit of marking your classes as <code class="language-plaintext highlighter-rouge">internal</code> until you’re sure you want your API to be used by your downstream application. There’s no cost to keeping an API hidden. But, I assure you that your colleagues in other timezones (👋 Tel Aviv) will not be happy when they wake up, pull, and find breaking changes because you changed your mind about an API that you didn’t mark as <code class="language-plaintext highlighter-rouge">internal</code> 😇.</p>

<p>For reasons like that, JetBrains recommends in <a href="https://kotlinlang.org/docs/reference/coding-conventions.html#coding-conventions-for-libraries">their Coding Conventions</a> that library authors “always explicitly specify member visibility.” Be on the lookout in a future Kotlin release for “<a href="https://youtrack.jetbrains.com/issue/KT-36016">API mode</a>”, which will allow you to run the compiler in a mode that generates warnings when visibility isn’t explicit 🙌.</p>

<h2 id="kotlin-to-swift-obj-c">Kotlin to <del>Swift</del> Obj-C</h2>

<p>A good starting place to understand what you’re going to get when you export your library to an Obj-C framework is <a href="https://kotlinlang.org/docs/reference/native/objc_interop.html">this one-pager on Kotlin and Obj-C/Swift interop</a>. We’re starting from a good place: <code class="language-plaintext highlighter-rouge">List&lt;String&gt;</code> in Kotlin gets you <code class="language-plaintext highlighter-rouge">NSArray&lt;NSString&gt;</code> in Obj-C and <code class="language-plaintext highlighter-rouge">[String]</code> in Swift, for example. <code class="language-plaintext highlighter-rouge">class</code> in Kotlin is <code class="language-plaintext highlighter-rouge">class</code> in Swift. <code class="language-plaintext highlighter-rouge">interface</code> is <code class="language-plaintext highlighter-rouge">protocol</code>. If you don’t read the whole guide, scan over the table at the top to get a quick understanding of what you can expect to get when exporting your Kotlin.</p>

<p>To make the interop work, Kotlin/Native generates some base-layer (for lack of a better term) code in your framework to make this translation work well between Swift and Kotlin. Let’s take a look at <a href="https://github.com/benasher44/KotlinMobileBootstrap/">KotlinMobileBootstrap</a>’s framework output. Clone the repository, and run <code class="language-plaintext highlighter-rouge">./gradlew debugFatFramework</code>. If all goes well, you should end up with a framework at <code class="language-plaintext highlighter-rouge">build/fat-framework/debug/KotlinMobileBootstrap.framework</code> (relative to the repository root). If you then crack open the framework’s header, you’ll see a healthy list of base classes. All of your types will inherit from these, and you may see more depending on what all you export in your framework (e.g. an added base class for Kotlin’s enum type). Here are some of the types in KotlinMobileBootstrap:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">@interface KMBBase : NSObject</code></li>
  <li><code class="language-plaintext highlighter-rouge">@interface KMBMutableSet&lt;ObjectType&gt; : NSMutableSet&lt;ObjectType&gt;</code></li>
  <li><code class="language-plaintext highlighter-rouge">@interface KMBMutableDictionary&lt;KeyType, ObjectType&gt; : NSMutableDictionary&lt;KeyType, ObjectType&gt;</code></li>
  <li><code class="language-plaintext highlighter-rouge">@interface KMBNumber : NSNumber</code></li>
  <li><code class="language-plaintext highlighter-rouge">@interface KMBBoolean : KMBNumber</code></li>
</ul>

<p>You won’t find these in your actual Kotlin code. They’re generated when creating the Obj-C framework. The overarching reason we need all of this is to ensure smooth interop between the Swift/Obj-C world and the Kotlin one. To pick an example, one of Kotlin/Native’s promises is Kotlin working in your application without the help of the JVM, yet Kotlin/Native has to have working <a href="https://github.com/JetBrains/kotlin-native/blob/master/FAQ.md#q-what-is-kotlinnative-memory-management-model">automatic memory management</a>. Among other things, <code class="language-plaintext highlighter-rouge">KMBBase</code> allows the memory management environments of Kotlin/Native and Swift to work together without help from me and you. I won’t get into how their automated memory management works in detail (could be a post in itself if I had the expertise), but it’s one of the concerns <a href="https://androiddev.social/@alec">Alec Strong</a> addresses in <a href="https://bit.ly/basher_kotlinconf_2019">our talk from KotlinConf 2019</a>.</p>

<p>You also might notice lots of <code class="language-plaintext highlighter-rouge">__attribute__((swift_name("SwiftyNameHere")))</code> sprinkled throughout the header. If you haven’t worked with making Obj-C APIs feel “Swifty” before, this is the mechanism for making a Obj-C-sounding declarations look and feel “Swifty” when interacting with them in Swift. Obj-C will see the classes and methods as is, and Swift will see the string inside the <code class="language-plaintext highlighter-rouge">swift_name</code> part.</p>

<p>Now that we have some of those basics covered, it’s time to write some Kotlin. Phill is going to walk through how some of the basic Kotlin constructs come out on the other end and appear to Swift.</p>

<p>From here on out, you can try out the below samples from <a href="https://github.com/benasher44/KotlinIos2">this sample project</a>.</p>

<h2 id="classes">Classes</h2>

<p>One of the most common features of the Kotlin language you’ll work with is a class, which for the most part works exactly as you would expect it to in Swift and Obj-C. Define a <code class="language-plaintext highlighter-rouge">class</code> (or a <code class="language-plaintext highlighter-rouge">data class</code>) <code class="language-plaintext highlighter-rouge">Sample</code> in Kotlin and a corresponding class will be defined in Obj-C.</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="c1">// Kotlin</span>
<span class="kd">class</span> <span class="nc">Sample</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="c1">// Obj-C</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_subclassing_restricted</span><span class="p">))</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"Sample"</span><span class="p">)))</span>
<span class="k">@interface</span> <span class="nc">KotlinIos2Sample</span> <span class="p">:</span> <span class="nc">KotlinIos2Base</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="n">init</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"init()"</span><span class="p">)))</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_designated_initializer</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="n">new</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">availability</span><span class="p">(</span><span class="n">swift</span><span class="p">,</span> <span class="n">unavailable</span><span class="p">,</span> <span class="n">message</span><span class="o">=</span><span class="s">"use object initializers instead"</span><span class="p">)));</span>
<span class="k">@end</span><span class="p">;</span></code></pre></figure>

<p>Notice that generated Obj-C classes inherit from the <code class="language-plaintext highlighter-rouge">KotlinIos2Base</code> super class, which itself inherits from <code class="language-plaintext highlighter-rouge">NSObject</code>. Generated classes are prefixed with a prefix that is derived from the framework name, in this case “KotlinIos2”. <a href="https://clang.llvm.org/docs/AttributeReference.html">Attributes</a> are used to ensure this prefix is omitted from Swift, and that methods and initializers look and behave natively to Swift language conventions.</p>

<h3 id="inheritance">Inheritance</h3>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="c1">// Kotlin</span>
<span class="k">open</span> <span class="kd">class</span> <span class="nc">Sample</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="c1">// Obj-C</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"Sample"</span><span class="p">)))</span>
<span class="k">@interface</span> <span class="nc">KotlinIos2Sample</span> <span class="p">:</span> <span class="nc">KotlinIos2Base</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="n">init</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"init()"</span><span class="p">)))</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_designated_initializer</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="n">new</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">availability</span><span class="p">(</span><span class="n">swift</span><span class="p">,</span> <span class="n">unavailable</span><span class="p">,</span> <span class="n">message</span><span class="o">=</span><span class="s">"use object initializers instead"</span><span class="p">)));</span>
<span class="k">@end</span><span class="p">;</span></code></pre></figure>

<p>Since classes in Kotlin are final by default, their native counterpart is annotated to restrict subclassing with the <code class="language-plaintext highlighter-rouge">__attribute__((objc_subclassing_restricted))</code> attribute. By specifying <code class="language-plaintext highlighter-rouge">open</code> on your Kotlin class, the generated Obj-C class will also support subclassing.</p>

<h3 id="protocol-conformance">Protocol Conformance</h3>

<p>Conforming your class to an interface in Kotlin defines the interface as a protocol and adds conformance in the public header of the generated Obj-C class.</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="c1">// Kotlin</span>
<span class="kd">interface</span> <span class="nc">SampleInterface</span>

<span class="kd">class</span> <span class="nc">SampleClass</span><span class="p">:</span> <span class="nc">SampleInterface</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="c1">// Obj-C</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"SampleInterface"</span><span class="p">)))</span>
<span class="k">@protocol</span> <span class="nc">KotlinIos2SampleInterface</span>
<span class="err">@required</span>
<span class="k">@end</span><span class="p">;</span>

<span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_subclassing_restricted</span><span class="p">))</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"SampleClass"</span><span class="p">)))</span>
<span class="k">@interface</span> <span class="nc">KotlinIos2SampleClass</span> <span class="p">:</span> <span class="nc">KotlinBase</span> <span class="o">&lt;</span><span class="n">KotlinIos2SampleInterface</span><span class="o">&gt;</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="n">init</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"init()"</span><span class="p">)))</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_designated_initializer</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="n">new</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">availability</span><span class="p">(</span><span class="n">swift</span><span class="p">,</span> <span class="n">unavailable</span><span class="p">,</span> <span class="n">message</span><span class="o">=</span><span class="s">"use object initializers instead"</span><span class="p">)));</span>
<span class="k">@end</span><span class="p">;</span></code></pre></figure>

<h3 id="constructors-and-properties">Constructors and Properties</h3>

<p>Using a primary constructor in Kotlin to define a set of parameters directly after the class name will generate a designated initializer in Obj-C. It will also generate <code class="language-plaintext highlighter-rouge">@property</code> members on the public interface of the class, marked with <code class="language-plaintext highlighter-rouge">readonly</code> if defined as a <code class="language-plaintext highlighter-rouge">val</code>, or not if the property is a <code class="language-plaintext highlighter-rouge">var</code>.</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="c1">// Kotlin</span>
<span class="kd">class</span> <span class="nc">MyClass</span><span class="p">(</span><span class="kd">val</span> <span class="py">str1</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="kd">var</span> <span class="py">str2</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="c1">// Obj-C</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_subclassing_restricted</span><span class="p">))</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"MyClass"</span><span class="p">)))</span>
<span class="k">@interface</span> <span class="nc">KotlinIos2MyClass</span> <span class="p">:</span> <span class="nc">KotlinBase</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithStr1</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">str1</span> <span class="nf">str2</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">str2</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"init(str1:str2:)"</span><span class="p">)))</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_designated_initializer</span><span class="p">));</span>
<span class="k">@property</span> <span class="p">(</span><span class="n">readonly</span><span class="p">)</span> <span class="n">NSString</span> <span class="o">*</span><span class="n">str1</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"str1"</span><span class="p">)));</span>
<span class="k">@property</span> <span class="n">NSString</span> <span class="o">*</span><span class="n">str2</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"str2"</span><span class="p">)));</span>
<span class="k">@end</span><span class="p">;</span></code></pre></figure>

<p>Initializers and functions will concatenate parameter names into an Obj-C-friendly camel case method name, such as <code class="language-plaintext highlighter-rouge">initWithStr1:str2:</code> and provide a more concise Swift-friendly function name annotation.</p>

<h2 id="value-types">Value Types</h2>

<p>Coming from the world of Swift, you’re probably used to defining structs but you’ll quickly notice that the Kotlin language doesn’t have value types. Instead, the next best thing is a <code class="language-plaintext highlighter-rouge">data class</code>, which is simply a class that allows the compiler to derive some out-of-the-box members such as <code class="language-plaintext highlighter-rouge">equals()</code>, <code class="language-plaintext highlighter-rouge">hashCode()</code>, <code class="language-plaintext highlighter-rouge">toString()</code> and <code class="language-plaintext highlighter-rouge">copy()</code> from the properties defined on the object.</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="c1">// Kotlin</span>
<span class="kd">data class</span> <span class="nc">MyDataClass</span><span class="p">(</span><span class="kd">val</span> <span class="py">str1</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="kd">var</span> <span class="py">str2</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="c1">// Obj-C</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_subclassing_restricted</span><span class="p">))</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"MyDataClass"</span><span class="p">)))</span>
<span class="k">@interface</span> <span class="nc">KotlinIos2MyDataClass</span> <span class="p">:</span> <span class="nc">KotlinBase</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithStr1</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">str1</span> <span class="nf">str2</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">str2</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"init(str1:str2:)"</span><span class="p">)))</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_designated_initializer</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="n">component1</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"component1()"</span><span class="p">)));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="n">component2</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"component2()"</span><span class="p">)));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">KotlinIos2MyDataClass</span> <span class="o">*</span><span class="p">)</span><span class="nf">doCopyStr1</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">str1</span> <span class="nf">str2</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">str2</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"doCopy(str1:str2:)"</span><span class="p">)));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">BOOL</span><span class="p">)</span><span class="nf">isEqual</span><span class="p">:(</span><span class="n">id</span> <span class="n">_Nullable</span><span class="p">)</span><span class="nv">other</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"isEqual(_:)"</span><span class="p">)));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">NSUInteger</span><span class="p">)</span><span class="n">hash</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"hash()"</span><span class="p">)));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="n">description</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"description()"</span><span class="p">)));</span>
<span class="k">@property</span> <span class="p">(</span><span class="n">readonly</span><span class="p">)</span> <span class="n">NSString</span> <span class="o">*</span><span class="n">str1</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"str1"</span><span class="p">)));</span>
<span class="k">@property</span> <span class="n">NSString</span> <span class="o">*</span><span class="n">str2</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"str2"</span><span class="p">)));</span>
<span class="k">@end</span><span class="p">;</span></code></pre></figure>

<p>If you inspect the Obj-C output of a Kotlin data class, you’ll see that the compiler has mapped these derived methods into their Obj-C equivalents on <code class="language-plaintext highlighter-rouge">NSObject</code> such as <code class="language-plaintext highlighter-rouge">isEqual</code>, <code class="language-plaintext highlighter-rouge">hash</code> and <code class="language-plaintext highlighter-rouge">description</code>.</p>

<h2 id="enums">Enums</h2>

<p>Enums are actually a special type of class in Kotlin, but they work in much the same way as they do in Swift. When you define an enum in Kotlin and peek at the Obj-C generated header, you may not see what you thought you’d see.</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="c1">// Kotlin</span>
<span class="k">enum</span> <span class="kd">class</span> <span class="nc">MyEnum</span> <span class="p">{</span> <span class="nc">CASE1</span><span class="p">,</span> <span class="nc">CASE2</span> <span class="p">}</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="c1">// Obj-C</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_subclassing_restricted</span><span class="p">))</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"MyEnum"</span><span class="p">)))</span>
<span class="k">@interface</span> <span class="nc">KotlinIos2MyEnum</span> <span class="p">:</span> <span class="nc">KotlinIos2KotlinEnum</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="n">alloc</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">allocWithZone</span><span class="p">:(</span><span class="k">struct</span> <span class="n">_NSZone</span> <span class="o">*</span><span class="p">)</span><span class="nv">zone</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithName</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">name</span> <span class="nf">ordinal</span><span class="p">:(</span><span class="kt">int32_t</span><span class="p">)</span><span class="nv">ordinal</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"init(name:ordinal:)"</span><span class="p">)))</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_designated_initializer</span><span class="p">))</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">@property</span> <span class="p">(</span><span class="n">class</span><span class="p">,</span> <span class="n">readonly</span><span class="p">)</span> <span class="n">KotlinIos2MyEnum</span> <span class="o">*</span><span class="n">case1</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"case1"</span><span class="p">)));</span>
<span class="k">@property</span> <span class="p">(</span><span class="n">class</span><span class="p">,</span> <span class="n">readonly</span><span class="p">)</span> <span class="n">KotlinIos2MyEnum</span> <span class="o">*</span><span class="n">case2</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"case2"</span><span class="p">)));</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">int32_t</span><span class="p">)</span><span class="nf">compareToOther</span><span class="p">:(</span><span class="n">KotlinIos2MyEnum</span> <span class="o">*</span><span class="p">)</span><span class="nv">other</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"compareTo(other:)"</span><span class="p">)));</span>
<span class="k">@end</span><span class="p">;</span></code></pre></figure>

<p>Since enums are just classes in Kotlin, the compiler generates an Obj-C class with each enum case defined as a <code class="language-plaintext highlighter-rouge">readonly</code> class property on the type. This generated class inherits from a <code class="language-plaintext highlighter-rouge">KotlinIos2KotlinEnum</code> superclass, which is another generated class that utilizes Obj-C lightweight generics to provide base-layer enum functionality including case comparison, equality and initialization.</p>

<h2 id="method-calls">Method Calls</h2>

<p>Functions defined in Kotlin along with their named arguments, default arguments, and return types map to Obj-C and Swift almost seamlessly. As an API consumer, interacting with these methods feels exactly the same as if they were originally defined in Swift. <a href="https://clang.llvm.org/docs/AttributeReference.html">Attributes</a> maintain a Swift-y method signature, parameter types are mapped to native types and even Lambda’s defined in Kotlin are generated as closures with the same parameters and return types.</p>

<p>Since Obj-C doesn’t support default arguments, these are unfortunately not mapped through to Swift. If you’re familiar with static and class methods in Swift, Kotlin’s companion objects provide similar functionality. Companion objects defined in Kotlin will actually generate separate Obj-C classes but use the <code class="language-plaintext highlighter-rouge">swift_name</code> annotation to maintain a consistent API interface, so while the generated Obj-C looks different you can interact with the method in the same way you would a static method.</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="c1">// Kotlin</span>
<span class="k">enum</span> <span class="kd">class</span> <span class="nc">LogLevel</span> <span class="p">{</span>
    <span class="nc">ERROR</span><span class="p">,</span>
    <span class="nc">WARNING</span><span class="p">,</span>
    <span class="nc">INFO</span><span class="p">,</span>
    <span class="nc">DEBUG</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nc">Logger</span> <span class="p">{</span>
    <span class="k">companion</span> <span class="k">object</span> <span class="nf">default</span> <span class="p">{</span>
        <span class="k">fun</span> <span class="nf">log</span><span class="p">(</span><span class="n">level</span><span class="p">:</span> <span class="nc">LogLevel</span> <span class="p">=</span> <span class="nc">LogLevel</span><span class="p">.</span><span class="nc">ERROR</span><span class="p">,</span> <span class="n">message</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="n">completion</span><span class="p">:</span> <span class="p">(</span><span class="nc">Boolean</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="c1">// Obj-C</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_subclassing_restricted</span><span class="p">))</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"LogLevel"</span><span class="p">)))</span>
<span class="k">@interface</span> <span class="nc">KotlinIos2LogLevel</span> <span class="p">:</span> <span class="nc">KotlinIos2KotlinEnum</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="n">alloc</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">allocWithZone</span><span class="p">:(</span><span class="k">struct</span> <span class="n">_NSZone</span> <span class="o">*</span><span class="p">)</span><span class="nv">zone</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithName</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">name</span> <span class="nf">ordinal</span><span class="p">:(</span><span class="kt">int32_t</span><span class="p">)</span><span class="nv">ordinal</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"init(name:ordinal:)"</span><span class="p">)))</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_designated_initializer</span><span class="p">))</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">@property</span> <span class="p">(</span><span class="n">class</span><span class="p">,</span> <span class="n">readonly</span><span class="p">)</span> <span class="n">KotlinIos2LogLevel</span> <span class="o">*</span><span class="n">error</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"error"</span><span class="p">)));</span>
<span class="k">@property</span> <span class="p">(</span><span class="n">class</span><span class="p">,</span> <span class="n">readonly</span><span class="p">)</span> <span class="n">KotlinIos2LogLevel</span> <span class="o">*</span><span class="n">warning</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"warning"</span><span class="p">)));</span>
<span class="k">@property</span> <span class="p">(</span><span class="n">class</span><span class="p">,</span> <span class="n">readonly</span><span class="p">)</span> <span class="n">KotlinIos2LogLevel</span> <span class="o">*</span><span class="n">info</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"info"</span><span class="p">)));</span>
<span class="k">@property</span> <span class="p">(</span><span class="n">class</span><span class="p">,</span> <span class="n">readonly</span><span class="p">)</span> <span class="n">KotlinIos2LogLevel</span> <span class="o">*</span><span class="n">debug</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"debug"</span><span class="p">)));</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">int32_t</span><span class="p">)</span><span class="nf">compareToOther</span><span class="p">:(</span><span class="n">KotlinIos2LogLevel</span> <span class="o">*</span><span class="p">)</span><span class="nv">other</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"compareTo(other:)"</span><span class="p">)));</span>
<span class="k">@end</span><span class="p">;</span>

<span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_subclassing_restricted</span><span class="p">))</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"Logger"</span><span class="p">)))</span>
<span class="k">@interface</span> <span class="nc">KotlinIos2Logger</span> <span class="p">:</span> <span class="nc">KotlinIos2Base</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="n">init</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"init()"</span><span class="p">)))</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_designated_initializer</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="n">new</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">availability</span><span class="p">(</span><span class="n">swift</span><span class="p">,</span> <span class="n">unavailable</span><span class="p">,</span> <span class="n">message</span><span class="o">=</span><span class="s">"use object initializers instead"</span><span class="p">)));</span>
<span class="k">@end</span><span class="p">;</span>

<span class="n">__attribute__</span><span class="p">((</span><span class="n">objc_subclassing_restricted</span><span class="p">))</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"Logger.default"</span><span class="p">)))</span>
<span class="k">@interface</span> <span class="nc">KotlinIos2LoggerDefault</span> <span class="p">:</span> <span class="nc">KotlinIos2Base</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="n">alloc</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">allocWithZone</span><span class="p">:(</span><span class="k">struct</span> <span class="n">_NSZone</span> <span class="o">*</span><span class="p">)</span><span class="nv">zone</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="n">default_</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"init()"</span><span class="p">)));</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">logLevel</span><span class="p">:(</span><span class="n">KotlinIos2LogLevel</span> <span class="o">*</span><span class="p">)</span><span class="nv">level</span> <span class="nf">message</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">message</span> <span class="nf">completion</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">KotlinIos2Boolean</span> <span class="o">*</span><span class="p">))</span><span class="nv">completion</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"log(level:message:completion:)"</span><span class="p">)));</span>
<span class="k">@end</span><span class="p">;</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">// Swift</span>
<span class="kt">Logger</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">log</span><span class="p">(</span><span class="o">.</span><span class="n">error</span><span class="p">,</span> <span class="s">"An error ocurred"</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Closure</span>
<span class="p">}</span></code></pre></figure>

<h2 id="integrating-kotlin-on-ios">Integrating Kotlin on iOS</h2>

<p>Kotlin’s Kotlin/Native backend does a good job of setting you up with the basics you need to integrate Kotlin on iOS. There are some rough edges, and I’ll dig into some of those in a future post. For now, I wanted to focus on the basics of what you can expect from Kotlin/Native, so that you can get started. And with that, my next and final post in this “getting started” series will focus on the build and integrating your Kotlin-based library, which I think is one of the more intimidating parts of making Kotlin a part of your team’s workflow.</p>]]></content><author><name>Ben Asher</name></author><category term="software" /><category term="kotlin" /><category term="multiplatform" /><category term="kotlin/native" /><category term="ios" /><category term="swift" /><summary type="html"><![CDATA[An introduction to interop between Kotlin and Swift in Kotlin multiplatform.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://benasher.co/img/logo.png" /><media:content medium="image" url="https://benasher.co/img/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Getting Started with Kotlin on iOS, Part 1</title><link href="https://benasher.co/kotlin-ios-getting-started/" rel="alternate" type="text/html" title="Getting Started with Kotlin on iOS, Part 1" /><published>2020-02-09T00:00:00-08:00</published><updated>2020-02-09T00:00:00-08:00</updated><id>https://benasher.co/kotlin-ios-getting-started</id><content type="html" xml:base="https://benasher.co/kotlin-ios-getting-started/"><![CDATA[<p>Hello! I’m Ben. I’m an iOS engineer at Autodesk where I work on the PlanGrid app. PlanGrid is a construction productivity tool that works on iOS, Android, and even Windows 😱. The iOS version is written in Swift (some Obj-C too because it’s older), but now most of the code I write day-to-day for the iOS app is in Kotlin.</p>

<p>We started exploring how to share code between our native mobile platforms at the end of 2018, and we wrote a bit about that in a recent post <a href="https://medium.com/plangrid-technology/cross-platform-with-kotlin-native-at-plangrid-3e84b9cfe39c">here</a>. After a year of learning how all of the various parts of Kotlin multiplatform fit together to make that project work, I felt motivated to make this blog to write about it.</p>

<p>To get started, I’m going to write a bit about Kotlin and Kotlin multiplatform from the perspective of someone (me) who came at this from iOS development. I’ll start with Kotlin and work my way through multiplatform.</p>

<h2 id="from-swift-to-kotlin">From Swift to Kotlin</h2>

<p>If I were starting an app from scratch today, it’s hard to say what language I would write it in. Swift is an impressive language with lots of useful modern features. But, if I also want to write this app for Android <em>and</em> am limited in the number of engineers (just me in this scenario), why would I set myself up to write the same app in two languages? JetBrains <a href="https://blog.jetbrains.com/kotlin/2019/12/what-to-expect-in-kotlin-1-4-and-beyond">announced</a> at KotlinConf 2019 that you’ll be able to build and iterate on an iOS and Android app simultaneously in Kotlin using Android Studio. If nothing else, it highlights what you can build on a foundation of great tooling. In this case, that foundation is <a href="https://www.jetbrains.com/lp/mobilecrossplatform">Kotlin multiplatform</a>.</p>

<p>A year ago, I hadn’t written any Kotlin. When Kotlin multiplatform became a possibility for us, I got my start with <a href="https://play.kotlinlang.org/koans/">Kotlin Koans</a>. This got me enough Kotlin knowledge to where I could write working code and test things out with iOS. Let’s walk through a quick comparative example between Kotlin and Swift.</p>

<p>Let’s say I want to represent a dog. In Swift, you might represent it with a <code class="language-plaintext highlighter-rouge">struct</code> like this:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">Dog</span><span class="p">:</span> <span class="kt">Equatable</span> <span class="p">{</span>
  <span class="k">let</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span>
<span class="p">}</span></code></pre></figure>

<p>In Kotlin, the tool we reach for here is a <code class="language-plaintext highlighter-rouge">data class</code>, which looks like this:</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="kd">data class</span> <span class="nc">Dog</span><span class="p">(</span><span class="kd">val</span> <span class="py">name</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span></code></pre></figure>

<p>In one line, we’ve explicitly defined the type, its properties, and its constructor. With a <code class="language-plaintext highlighter-rouge">struct</code>, you get this too (though such a one-liner is discouraged in Swift), but the constructor is implicit (generated by the compiler). With a <code class="language-plaintext highlighter-rouge">data class</code>, <code class="language-plaintext highlighter-rouge">equals</code> and <code class="language-plaintext highlighter-rouge">hashCode</code> are generated for you as well. To get this with a <code class="language-plaintext highlighter-rouge">struct</code>, you make sure to declare it as <code class="language-plaintext highlighter-rouge">Equatable</code> (also gets you <code class="language-plaintext highlighter-rouge">Hashable</code>).</p>

<p>Let’s expand on this with a <code class="language-plaintext highlighter-rouge">protocol</code>:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">protocol</span> <span class="kt">Pet</span> <span class="p">{</span>
  <span class="k">var</span> <span class="nv">ownerName</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>In Kotlin, this is an <code class="language-plaintext highlighter-rouge">interface</code>:</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="kd">interface</span> <span class="nc">Pet</span> <span class="p">{</span>
  <span class="kd">val</span> <span class="py">ownerName</span><span class="p">:</span> <span class="nc">String</span>
<span class="p">}</span></code></pre></figure>

<p>Let’s implement it. First, in Swift:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">Dog</span><span class="p">:</span> <span class="kt">Equatable</span><span class="p">,</span> <span class="kt">Pet</span> <span class="p">{</span>
  <span class="k">let</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span>
  <span class="k">let</span> <span class="nv">ownerName</span><span class="p">:</span> <span class="kt">String</span>
<span class="p">}</span></code></pre></figure>

<p>And in Kotlin:</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="kd">data class</span> <span class="nc">Dog</span><span class="p">(</span><span class="kd">val</span> <span class="py">name</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="k">override</span> <span class="kd">val</span> <span class="py">ownerName</span><span class="p">:</span> <span class="nc">String</span><span class="p">):</span> <span class="nc">Pet</span></code></pre></figure>

<p>In Kotlin, we’re still at one line. I don’t know how others feel about ability to do this in one line, but as I’ve been writing more Kotlin, I’ve found it to be practical and concise. It’s one of the “Kotlin-y” things I’ve come to enjoy most, and I’ve found more and more of these “little things” as I’ve explored the language.</p>

<h2 id="coming-from-swift">Coming from Swift</h2>

<p>At this point, you may have noticed some similarities between the two languages, and there are many that you can lean on to help get started writing some Kotlin:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">val</code>/<code class="language-plaintext highlighter-rouge">var</code> and <code class="language-plaintext highlighter-rouge">let</code>/<code class="language-plaintext highlighter-rouge">var</code> for declaring variables</li>
  <li><code class="language-plaintext highlighter-rouge">interface</code> and <code class="language-plaintext highlighter-rouge">protocol</code> for defining methods and properties that a type must implement</li>
  <li><code class="language-plaintext highlighter-rouge">fun foo(arg: String)</code> and <code class="language-plaintext highlighter-rouge">func foo(arg: String)</code> for defining a function called “foo”</li>
  <li><code class="language-plaintext highlighter-rouge">class</code> and <code class="language-plaintext highlighter-rouge">class</code></li>
</ul>

<p>Coming from Swift, there were also tools I wanted to reach for when writing Kotlin that didn’t have such an exact match in Kotlin. For me, these all fell into the category of working with statics. The first thing to know is that there is no <code class="language-plaintext highlighter-rouge">static</code> keyword like in Swift. Think about all of the places you might use <code class="language-plaintext highlighter-rouge">static </code>in Swift. How do we do those things in Kotlin?</p>

<h3 id="singletons">Singletons</h3>

<p>In Kotlin, singletons are declared using the <code class="language-plaintext highlighter-rouge">object</code> keyword, and all members declared within are accessed on the type directly.</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="kd">object</span> <span class="nc">Platform</span> <span class="p">{</span>
   <span class="kd">val</span> <span class="py">name</span> <span class="p">=</span> <span class="s">"iOS"</span>

   <span class="k">fun</span> <span class="nf">printName</span><span class="p">()</span> <span class="p">=</span> <span class="nf">println</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="p">}</span></code></pre></figure>

<p>In this example, I’ve declared an <code class="language-plaintext highlighter-rouge">object</code> called <code class="language-plaintext highlighter-rouge">Platform</code>. This acts as a singleton, and we access its sole property via <code class="language-plaintext highlighter-rouge">Platform.name</code> and call its sole function via <code class="language-plaintext highlighter-rouge">Platform.printName()</code>.</p>

<h3 id="static-class-members">Static Class Members</h3>

<p>In Swift, we’re used to using <code class="language-plaintext highlighter-rouge">static func</code> and <code class="language-plaintext highlighter-rouge">static let</code> for type-scoped members. In Kotlin, we do this by extending the <code class="language-plaintext highlighter-rouge">object</code> concept and declaring one within the <code class="language-plaintext highlighter-rouge">class</code> using the <code class="language-plaintext highlighter-rouge">companion</code> 🤝 keyword.</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="kd">data class</span> <span class="nc">Dog</span><span class="p">(</span><span class="kd">val</span> <span class="py">name</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">companion</span> <span class="k">object</span> <span class="p">{</span>
        <span class="c1">// they're all good dogs</span>
        <span class="kd">val</span> <span class="py">isGood</span> <span class="p">=</span> <span class="k">true</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>When I first saw this, my mind went to the C++ <code class="language-plaintext highlighter-rouge">friend</code> keyword 🙈, but all it does is provide us an area to define <code class="language-plaintext highlighter-rouge">static</code>-level members for the <code class="language-plaintext highlighter-rouge">Dog</code> class. In this case, we can now access <code class="language-plaintext highlighter-rouge">Dog.isGood</code>, which will always return <code class="language-plaintext highlighter-rouge">true</code> 🐶.</p>

<h3 id="kotlinnative-and-objects">Kotlin/Native and Objects</h3>

<p><del>One thing to note about <code class="language-plaintext highlighter-rouge">object</code>s when working with them in multiplatform contexts is that they behave in slightly different ways on native platforms like iOS (i.e. built using Kotlin/Native) compared to the JVM. On native platforms, globals (including <code class="language-plaintext highlighter-rouge">object</code>s) are initialized during Kotlin/Native runtime initialization, which is typically when you launch your application. On the JVM, they’re initialized on access (lazily).</del></p>

<p>Their behavior here turns out to be the same 🎉. I made a mistake understanding the <a href="https://github.com/JetBrains/kotlin-native/blob/95ee655fbe8a5d346aea00c42deeb7ca493cb757/CONCURRENCY.md#global-variables-and-singletons">concurrency rules</a> and reading too much into this line in the <a href="https://github.com/JetBrains/kotlin-native/blob/985385e2fac037a1e9d8f2253139fced195c7421/IMMUTABILITY.md">immutability docs</a>:</p>

<blockquote>
  <p>Top level/global variables of non-primitive types are by default accessible in the main thread (i.e., the thread which initialized Kotlin/Native runtime first) only.</p>
</blockquote>

<p>I’ll discuss this a bit more in a future post.</p>

<h2 id="multiplatform">Multiplatform</h2>

<p>I hope that if you’re coming from Swift, this gives you a sense of Kotlin as you’re getting started. Next, I’ll get into Kotlin multiplatform and how these Kotlin concepts bridge back to Swift/Obj-C.</p>]]></content><author><name>Ben Asher</name></author><category term="software" /><category term="kotlin" /><category term="multiplatform" /><category term="kotlin/native" /><category term="ios" /><category term="swift" /><summary type="html"><![CDATA[A brief introduction to Kotlin for Swift/iOS developers before diving into Kotlin multiplatform.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://benasher.co/img/logo.png" /><media:content medium="image" url="https://benasher.co/img/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>