<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://www.chimehq.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.chimehq.com/" rel="alternate" type="text/html" /><updated>2025-05-29T10:57:30+00:00</updated><id>https://www.chimehq.com/feed.xml</id><title type="html">Chime</title><subtitle>Chime is an editor for macOS.</subtitle><entry><title type="html">Chime 2.0</title><link href="https://www.chimehq.com/blog/version-two" rel="alternate" type="text/html" title="Chime 2.0" /><published>2022-10-24T00:00:00+00:00</published><updated>2022-10-24T00:00:00+00:00</updated><id>https://www.chimehq.com/blog/version-two</id><content type="html" xml:base="https://www.chimehq.com/blog/version-two"><![CDATA[<h1 id="native-extensions-new-languages-chime-20">Native Extensions, New Languages: Chime 2.0</h1>

<p>This is a big one for us: Chime 2.0 is now <a href="/download">available</a>! It includes an <strong>extension system</strong> and support for <strong>23 new languages</strong>. On top of that, we snuck in a command-line tool and quick open with fuzzy matching. In so many ways, it feels like an entirely new app.</p>

<figure class="image version-two">
  <img src="/assets/images/blog/version_two.png" alt="Chime 2.0 icon with programming language special characters floating around" />
  <figcaption></figcaption>
</figure>

<h2 id="extensions">Extensions</h2>

<p>By far, the thing we’re most exited about is <a href="/extensions">extensions</a>. We’re using <a href="/blog/extensionkit-intro">ExtensionKit</a>, which is new to macOS Ventura. Extensions are built with our <a href="https://github.com/ChimeHQ/ChimeKit">Swift SDK</a>, which even supports views using SwiftUI! Yes, this is an <strong>entirely native</strong> system. The <a href="https://github.com/ChimeHQ/ChimeKit">SDK</a>, as well as our extensions for <a href="https://github.com/ChimeHQ/chime-swift">Swift</a>, <a href="https://github.com/ChimeHQ/chime-rust">Rust</a>, <a href="https://github.com/ChimeHQ/chime-ruby">Ruby</a> and <a href="https://github.com/ChimeHQ/chime-go">Go</a> are all open source.</p>

<p>We’re also interested in expanding the system. Please <a href="https://github.com/ChimeHQ/ChimeKit">open an issue</a> if you’d like to see some new APIs!</p>

<h2 id="languages">Languages</h2>

<p>Chime 2.0 includes support for 23 <strong>new</strong> languages and formats. Here’s the list:</p>

<p>Bash, C, C++, C#, CSS, Elixir, Elm, Go workspaces, Haskell, HTML, Java, JavaScript, JSON, Julia, Lua, Markdown, Perl, PHP, Python, Rust, SQL, YAML, Zig.</p>

<p>Not all languages support all of Chime’s syntactic features today, but that’s coming. And, <a href="/contact">let us know</a> if there’s one missing you’d like.</p>

<h2 id="more-open-source">More Open Source</h2>

<p>Large portions of Chime were already open source, but this release definitely resulted in the most open source work we’ve ever done. Over the course of building 2.0, we created <strong>five</strong> new projects, and made significant updates to <strong>nine</strong> others. Open source has become a huge part of what we do, and we really enjoy it.</p>

<p>If you want to see what we’ve been up to, take a look at our <a href="https://github.com/ChimeHQ">GitHub profile</a>.</p>

<h2 id="check-it-out">Check it Out</h2>

<p>So, yes. This is a very significant update. It can do so much more, and we’re pumped. Chime 2.0 is available for <a href="/download">download</a> now.</p>]]></content><author><name>Matt Massicotte</name></author><summary type="html"><![CDATA[Native Extensions, New Languages: Chime 2.0]]></summary></entry><entry><title type="html">ExtensionKit Views</title><link href="https://www.chimehq.com/blog/extensionkit-views" rel="alternate" type="text/html" title="ExtensionKit Views" /><published>2022-09-03T00:00:00+00:00</published><updated>2022-09-03T00:00:00+00:00</updated><id>https://www.chimehq.com/blog/extensionkit-views</id><content type="html" xml:base="https://www.chimehq.com/blog/extensionkit-views"><![CDATA[<h1 id="extensionkit-views">ExtensionKit Views</h1>

<p>If you’re looking into ExtensionKit, it’s probably because of remote views. Hosting a view, managed entirely from a separate extension process, is a pretty incredible feature. Building and integrating view-based extensions is definitely more complex than the <a href="end-to-end">non-view</a> case. But, the payoff is a very powerful and unique capability. So, let’s get to it!</p>

<p>This is part four of our series on ExtensionKit.</p>

<ul>
  <li>Part One: <a href="/blog/extensionkit-intro">Introduction to ExtensionKit</a></li>
  <li>Part Two: <a href="/blog/extensionkit-xpc">ExtensionKit and XPC</a></li>
  <li>Part Three: <a href="/blog/extensionkit-end-to-end">ExtensionKit End-to-End</a></li>
  <li>Part Four: ExtensionKit Views</li>
</ul>

<h2 id="host-side-support">Host-Side Support</h2>

<p>View-based extensions work the same as the non-view type. You discover them with <a href="https://developer.apple.com/documentation/extensionfoundation/appextensionidentity"><code class="language-plaintext highlighter-rouge">AppExtensionIdentity</code></a>. They still can have a global, per-extension XPC connection. You can still start them up with <a href="https://developer.apple.com/documentation/extensionfoundation/appextensionprocess"><code class="language-plaintext highlighter-rouge">AppExtensionProcess</code></a>. But, that part is optional.</p>

<p>Hosting the view can be done all on its own, with <a href="https://developer.apple.com/documentation/extensionkit/exhostviewcontroller"><code class="language-plaintext highlighter-rouge">EXHostViewController</code></a>. This is a simple <code class="language-plaintext highlighter-rouge">NSViewController</code> subclass. You can connect it up to an extension via its <a href="https://developer.apple.com/documentation/extensionkit/exhostviewcontroller/3991625-configuration"><code class="language-plaintext highlighter-rouge">configuration</code></a> property. That property takes two values: an <code class="language-plaintext highlighter-rouge">AppExtensionIdentity</code> and a scene name.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">hostViewController</span> <span class="o">=</span> <span class="kt">EXHostViewController</span><span class="p">()</span>

<span class="n">hostViewController</span><span class="o">.</span><span class="n">configuration</span> <span class="o">=</span> <span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">appExtension</span><span class="p">:</span> <span class="n">identity</span><span class="p">,</span> <span class="nv">sceneID</span><span class="p">:</span> <span class="s">"default"</span><span class="p">)</span>
</code></pre></div></div>

<p>Right now, all the extension-hosting capabilities are macOS-only. But, every other part of ExtensionKit is supported by all Apple platforms.</p>

<h2 id="per-view-connections">Per-View Connections</h2>

<p>In addition to the optional global connection, you can also make a connection to each view. Initially, I found this pretty confusing. But, it does make sense and is really convenient. Remember that an extension can be asked to instantiate any number of its views. Your app could have 10 windows, each of which contains its own remote view with its own separate state. A per-view connection gives you a way to interact with these individually.</p>

<p>To use view connections, you must make use of the <code class="language-plaintext highlighter-rouge">EXHostViewController</code> delegate.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">MyViewController</span><span class="p">:</span> <span class="kt">EXHostViewControllerDelegate</span> <span class="p">{</span>
	<span class="kd">func</span> <span class="nf">shouldAccept</span><span class="p">(</span><span class="n">_</span> <span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
		<span class="k">return</span> <span class="kc">true</span>
	<span class="p">}</span>

	<span class="kd">func</span> <span class="nf">hostViewControllerDidActivate</span><span class="p">(</span><span class="n">_</span> <span class="nv">viewController</span><span class="p">:</span> <span class="kt">EXHostViewController</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">let</span> <span class="nv">connection</span> <span class="o">=</span> <span class="k">try</span> <span class="n">viewController</span><span class="o">.</span><span class="nf">makeXPCConnection</span><span class="p">()</span>

		<span class="c1">// ...</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This looks similar to <code class="language-plaintext highlighter-rouge">AppExtensionProcess</code>, but it’s important that you wait for <code class="language-plaintext highlighter-rouge">hostViewControllerDidActivate</code> to be invoked.</p>

<h2 id="ui-extension-template">UI Extension Template</h2>

<p>Xcode 14’s “Generic Extension” template can also be used to support UIs. The resulting code has a lot going on. We’re not going to get into every part of it, but I’m including it here for reference. It’s ok to just skip it.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">/// The AppExtensionScene protocol to which this extension's scenes will conform.</span>
<span class="c1">/// This is typically defined by the extension host in a framework.</span>
<span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">ExampleAppExtensionScene</span><span class="p">:</span> <span class="kt">AppExtensionScene</span> <span class="p">{}</span>

<span class="c1">/// An AppExtensionScene that this extension can provide.</span>
<span class="c1">/// This is typically defined by the extension host in a framework.</span>
<span class="kd">struct</span> <span class="kt">ExampleScene</span><span class="o">&lt;</span><span class="kt">Content</span><span class="p">:</span> <span class="kt">View</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">ExampleAppExtensionScene</span> <span class="p">{</span>

	<span class="k">let</span> <span class="nv">sceneID</span> <span class="o">=</span> <span class="s">"example-scene"</span>

	<span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">content</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">()</span> <span class="o">-&gt;</span>  <span class="kt">Content</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">self</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="n">content</span>
	<span class="p">}</span>

	<span class="kd">private</span> <span class="k">let</span> <span class="nv">content</span><span class="p">:</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Content</span>

	<span class="kd">public</span> <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">AppExtensionScene</span> <span class="p">{</span>
		<span class="kt">PrimitiveAppExtensionScene</span><span class="p">(</span><span class="nv">id</span><span class="p">:</span> <span class="n">sceneID</span><span class="p">)</span> <span class="p">{</span>
			<span class="nf">content</span><span class="p">()</span>
			<span class="p">}</span> <span class="nv">onConnection</span><span class="p">:</span> <span class="p">{</span> <span class="n">connection</span> <span class="k">in</span>
				<span class="c1">// TODO: Configure the XPC connection and return true</span>
				<span class="k">return</span> <span class="kc">false</span>
			<span class="p">}</span>
		<span class="p">}</span>
	<span class="p">}</span>

	<span class="c1">/// The AppExtension protocol to which this extension will conform.</span>
	<span class="c1">/// This is typically defined by the extension host in a framework.</span>
	<span class="kd">protocol</span> <span class="kt">ExampleExtension</span> <span class="p">:</span> <span class="kt">AppExtension</span> <span class="p">{</span>
		<span class="kd">associatedtype</span> <span class="kt">Body</span><span class="p">:</span> <span class="kt">ExampleAppExtensionScene</span>
		<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kt">Body</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
	<span class="p">}</span>

	<span class="c1">/// The AppExtensionConfiguration that will be provided by this extension.</span>
	<span class="c1">/// This is typically defined by the extension host in a framework.</span>
	<span class="kd">struct</span> <span class="kt">ExampleConfiguration</span><span class="o">&lt;</span><span class="kt">E</span><span class="p">:</span> <span class="kt">ExampleExtension</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">AppExtensionConfiguration</span> <span class="p">{</span>

		<span class="k">let</span> <span class="nv">appExtension</span><span class="p">:</span> <span class="kt">E</span>

		<span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">appExtension</span><span class="p">:</span> <span class="kt">E</span><span class="p">)</span> <span class="p">{</span>
			<span class="k">self</span><span class="o">.</span><span class="n">appExtension</span> <span class="o">=</span> <span class="n">appExtension</span>
		<span class="p">}</span>

		<span class="c1">/// Determine whether to accept the XPC connection from the host.</span>
		<span class="kd">func</span> <span class="nf">accept</span><span class="p">(</span><span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
			<span class="c1">// TODO: Configure the XPC connection and return true</span>
			<span class="k">return</span> <span class="kc">false</span>
		<span class="p">}</span>
	<span class="p">}</span>

	<span class="kd">extension</span> <span class="kt">ExampleExtension</span> <span class="p">{</span>
		<span class="k">var</span> <span class="nv">configuration</span><span class="p">:</span> <span class="kt">AppExtensionSceneConfiguration</span> <span class="p">{</span>
			<span class="c1">// Return your extension's configuration upon request.</span>
			<span class="k">return</span> <span class="kt">AppExtensionSceneConfiguration</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">body</span><span class="p">,</span> <span class="nv">configuration</span><span class="p">:</span> <span class="kt">ExampleConfiguration</span><span class="p">(</span><span class="k">self</span><span class="p">))</span>
		<span class="p">}</span>
	<span class="p">}</span>

	<span class="kd">@main</span>
	<span class="kd">class</span> <span class="kt">ViewExtension</span><span class="p">:</span> <span class="kt">ExampleExtension</span> <span class="p">{</span>
		<span class="kd">required</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
			<span class="c1">// Initialize your extension here.</span>
		<span class="p">}</span>
    
		<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">ExampleAppExtensionScene</span> <span class="p">{</span>
			<span class="kt">ExampleScene</span> <span class="p">{</span>
				<span class="kt">Text</span><span class="p">(</span><span class="s">"Hello, app extension!"</span><span class="p">)</span>
			<span class="p">}</span>
		<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>That’s a lot of code! It’s hard to see, but there is really just one core component. View-based extensions are all built around scenes, defined by conforming to <code class="language-plaintext highlighter-rouge">AppExtensionScene</code>. These are very similar to a SwiftUI <a href="https://developer.apple.com/documentation/swiftui/scene"><code class="language-plaintext highlighter-rouge">Scene</code></a>.</p>

<p>You use an <code class="language-plaintext highlighter-rouge">AppExtensionConfiguration</code> type to wire everything up, just like with a non-ui extension. But here we use <code class="language-plaintext highlighter-rouge">AppExtensionSceneConfiguration</code>, which takes your scene definitions, along with an optional <code class="language-plaintext highlighter-rouge">AppExtensionConfiguration</code> for the global connection.</p>

<h2 id="a-minimal-ui-extension">A Minimal UI Extension</h2>

<p>Now, this stock template includes a lot of protocols and generic types to help guide your structure. Unfortunately, I’ve found it incredibly difficult to keep that all straight. Before I suggest an alternative, let’s start by stripping it all down.</p>

<p>Here is a truly minimal implementation:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@main</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">ViewExtension</span><span class="p">:</span> <span class="kt">AppExtension</span> <span class="p">{</span>
	<span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
	<span class="p">}</span>

	<span class="k">var</span> <span class="nv">configuration</span><span class="p">:</span> <span class="kt">AppExtensionSceneConfiguration</span> <span class="p">{</span>
		<span class="k">let</span> <span class="nv">scene</span> <span class="o">=</span> <span class="kt">PrimitiveAppExtensionScene</span><span class="p">(</span><span class="nv">id</span><span class="p">:</span> <span class="s">"example-scene"</span><span class="p">)</span> <span class="p">{</span>
			<span class="kt">Text</span><span class="p">(</span><span class="s">"Hello, app extension!"</span><span class="p">)</span>
			<span class="p">}</span> <span class="nv">onConnection</span><span class="p">:</span> <span class="p">{</span> <span class="n">connection</span> <span class="k">in</span>
				<span class="k">return</span> <span class="kc">false</span>
			<span class="p">}</span>
		<span class="p">}</span>

		<span class="k">return</span> <span class="kt">AppExtensionSceneConfiguration</span><span class="p">(</span><span class="n">scene</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>To enable view support, you just need to use <code class="language-plaintext highlighter-rouge">AppExtensionSceneConfiguration</code> and configure a scene. The <a href="https://developer.apple.com/documentation/extensionkit/primitiveappextensionscene"><code class="language-plaintext highlighter-rouge">PrimitiveAppExtensionScene</code></a> type is the basic building-block for an extension scene. And that <code class="language-plaintext highlighter-rouge">onConnection</code> callback is how you react to the host connecting.</p>

<h2 id="multiple-scenes">Multiple Scenes</h2>

<p>It took me a surprisingly long time to figure out how to configure more than one scene. You must use <a href="https://developer.apple.com/documentation/extensionkit/appextensionscenebuilder"><code class="language-plaintext highlighter-rouge">AppExtensionSceneBuilder</code></a>. Again, this is very similar to regular SwiftUI Scenes.</p>

<p>Perhaps there is some API missing in this current beta, but I had to make a custom type using this builder to get multiple scenes to work.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">AppExtensionSceneGroup</span><span class="o">&lt;</span><span class="kt">Content</span><span class="p">:</span> <span class="kt">AppExtensionScene</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">AppExtensionScene</span> <span class="p">{</span>
	<span class="kd">private</span> <span class="k">let</span> <span class="nv">content</span><span class="p">:</span> <span class="kt">Content</span>

	<span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="kd">@AppExtensionSceneBuilder</span> <span class="nv">content</span><span class="p">:</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Content</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">self</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="nf">content</span><span class="p">()</span>
	<span class="p">}</span>

	<span class="kd">public</span> <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">AppExtensionScene</span> <span class="p">{</span>
		<span class="k">return</span> <span class="n">content</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With this type, you can build up multiple scenes like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">scene</span> <span class="o">=</span> <span class="kt">AppExtensionSceneGroup</span> <span class="p">{</span>
	<span class="kt">PrimitiveAppExtensionScene</span><span class="p">(</span><span class="nv">id</span><span class="p">:</span> <span class="s">"one"</span><span class="p">)</span>
	<span class="kt">PrimitiveAppExtensionScene</span><span class="p">(</span><span class="nv">id</span><span class="p">:</span> <span class="s">"two"</span><span class="p">)</span>
	<span class="kt">PrimitiveAppExtensionScene</span><span class="p">(</span><span class="nv">id</span><span class="p">:</span> <span class="s">"three"</span><span class="p">)</span>
<span class="p">}</span>

<span class="kt">AppExtensionSceneConfiguration</span><span class="p">(</span><span class="n">scene</span><span class="p">)</span>
</code></pre></div></div>

<p>You can find <code class="language-plaintext highlighter-rouge">AppExtensionSceneGroup</code> in <a href="https://github.com/ChimeHQ/Extendable">Extendable</a>.</p>

<h2 id="managing-the-connection">Managing the Connection</h2>

<p>The biggest challenge I’ve faced when working with view-based extensions is getting access to the connection in my views. Once the connection object is accessible to the view, you get it into the environment, manage it with ViewModels, or do whatever else you might want in a typical SwiftUI application. But, the structure of <code class="language-plaintext highlighter-rouge">PrimitiveAppExtensionScene</code> makes it tricky!</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">PrimitiveAppExtensionScene</span><span class="p">(</span><span class="nv">id</span><span class="p">:</span> <span class="s">"one"</span><span class="p">)</span> <span class="p">{</span>
	<span class="c1">// I need the connection here in the view!</span>
<span class="p">}</span> <span class="nv">onConnection</span><span class="p">:</span> <span class="p">{</span> <span class="n">connection</span> <span class="k">in</span>
	<span class="c1">// ... but how?</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now, I understand why <code class="language-plaintext highlighter-rouge">PrimitiveAppExtensionScene</code> was set up as it was. This connection isn’t just optional, it also might be established after the view is displayed. But, I found myself always wanting something more like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">MyAppExtensionScene</span><span class="p">(</span><span class="nv">sceneID</span><span class="p">:</span> <span class="s">"one"</span><span class="p">)</span> <span class="p">{</span> <span class="p">(</span><span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span><span class="p">?)</span> <span class="k">in</span>
	<span class="c1">// The view can now be constructed with access to the optional connection.</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It took quite a bit of experimentation to get this working. But, I’m happy to report that it was possible, is open source, and is packaged up in <a href="https://github.com/ChimeHQ/Extendable">Extendable</a>.</p>

<h2 id="a-less-minimal-example">A Less-Minimal Example</h2>

<p>Armed with this infrastructure, let’s look at a little more full-featured implementation. We’ll get back to our <code class="language-plaintext highlighter-rouge">GreetingProtocol</code> and glue it all together.</p>

<p>First, we define a ViewModel that will manage the connection and implement our interface.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@MainActor</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">GreetingModel</span><span class="p">:</span> <span class="kt">ObservableObject</span> <span class="p">{</span>
	<span class="kd">@Published</span> <span class="k">var</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">String</span>

	<span class="kd">private</span> <span class="k">let</span> <span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span><span class="p">?</span>

	<span class="nf">init</span><span class="p">(</span><span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span><span class="p">?)</span> <span class="k">throws</span> <span class="p">{</span>
		<span class="k">self</span><span class="o">.</span><span class="n">connection</span> <span class="o">=</span> <span class="n">connection</span>
		<span class="k">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="s">"model"</span>

		<span class="k">if</span> <span class="k">let</span> <span class="nv">connection</span> <span class="p">{</span>
			<span class="k">self</span><span class="o">.</span><span class="nf">export</span><span class="p">(</span><span class="nv">over</span><span class="p">:</span> <span class="n">connection</span><span class="p">)</span>
			<span class="n">connection</span><span class="o">.</span><span class="n">exportedObject</span> <span class="o">=</span> <span class="k">self</span>
		<span class="p">}</span>
	<span class="p">}</span>
	
	<span class="k">var</span> <span class="nv">greeting</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
		<span class="k">return</span> <span class="s">"Hello </span><span class="se">\(</span><span class="n">value</span><span class="se">)</span><span class="s">!"</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="kd">extension</span> <span class="kt">GreetingModel</span><span class="p">:</span> <span class="kt">ExtensionProtocol</span> <span class="p">{</span>
	<span class="kd">func</span> <span class="nf">greet</span><span class="p">(</span><span class="n">_</span> <span class="nv">greetingParam</span><span class="p">:</span> <span class="kt">GreetingParameters</span><span class="p">)</span> <span class="k">async</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">Greeting</span> <span class="p">{</span>
		<span class="k">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">greetingParam</span><span class="o">.</span><span class="n">name</span>

		<span class="k">return</span> <span class="kt">Greeting</span><span class="p">(</span><span class="n">greeting</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If you are wondering why its <code class="language-plaintext highlighter-rouge">init</code> is marked <code class="language-plaintext highlighter-rouge">throws</code>, it’s to handle the situation where we want to reject a requested connection. This is unusual, so making the common case simpler makes sense.</p>

<p>Now, we can use this model in a simple view at the root of the scene.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">GreetingView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
	<span class="kd">@ObservedObject</span> <span class="k">var</span> <span class="nv">model</span><span class="p">:</span> <span class="kt">GreetingModel</span>

	<span class="nf">init</span><span class="p">(</span><span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span><span class="p">?)</span> <span class="k">throws</span> <span class="p">{</span>
		<span class="k">self</span><span class="o">.</span><span class="n">model</span> <span class="o">=</span> <span class="k">try</span> <span class="kt">ConnectionModel</span><span class="p">(</span><span class="nv">connection</span><span class="p">:</span> <span class="n">connection</span><span class="p">)</span>
	<span class="p">}</span>

	<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
		<span class="kt">Text</span><span class="p">(</span><span class="s">"Greeting: </span><span class="se">\(</span><span class="n">model</span><span class="o">.</span><span class="n">greeting</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Finally, we’ll use that view in our <code class="language-plaintext highlighter-rouge">AppExtension</code>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@main</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">ViewExtension</span><span class="p">:</span> <span class="kt">AppExtension</span> <span class="p">{</span>
	<span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
	<span class="p">}</span>

	<span class="k">var</span> <span class="nv">configuration</span><span class="p">:</span> <span class="kt">AppExtensionSceneConfiguration</span> <span class="p">{</span>
		<span class="k">let</span> <span class="nv">scene</span> <span class="o">=</span> <span class="kt">ConnectingAppExtensionScene</span><span class="p">(</span><span class="nv">id</span><span class="p">:</span> <span class="s">"one"</span><span class="p">)</span> <span class="p">{</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">connection</span><span class="p">)</span> <span class="k">in</span>
			<span class="k">try</span> <span class="kt">GreetingView</span><span class="p">(</span><span class="nv">connection</span><span class="p">:</span> <span class="n">connection</span><span class="p">)</span>
		<span class="p">}</span>

		<span class="k">return</span> <span class="kt">AppExtensionSceneConfiguration</span><span class="p">(</span><span class="n">scene</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>There’s very little ExtensionKit you need to work with. <code class="language-plaintext highlighter-rouge">ConnectingAppExtensionScene</code> helps to get you right into the more-familiar SwiftUI world quickly.</p>

<h2 id="going-further">Going Further</h2>

<p><a href="https://github.com/ChimeHQ/Extendable">Extendable</a> includes another type that helps to slim down the boilerplate even further. Check out this version using <code class="language-plaintext highlighter-rouge">ConnectableSceneExtension</code>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@main</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">ViewExtension</span><span class="p">:</span> <span class="kt">ConnectableSceneExtension</span> <span class="p">{</span>
	<span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
	<span class="p">}</span>

	<span class="kd">func</span> <span class="nf">acceptConnection</span><span class="p">(</span><span class="n">_</span> <span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span><span class="p">)</span> <span class="k">throws</span> <span class="p">{</span>
		<span class="c1">// handle global connection</span>
	<span class="p">}</span>

	<span class="k">var</span> <span class="nv">scene</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">AppExtensionScene</span> <span class="p">{</span>
		<span class="kt">AppExtensionSceneGroup</span> <span class="p">{</span>
			<span class="kt">ConnectingAppExtensionScene</span><span class="p">(</span><span class="nv">id</span><span class="p">:</span> <span class="s">"one"</span><span class="p">)</span> <span class="p">{</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">connection</span><span class="p">)</span> <span class="k">in</span>
				<span class="k">try</span> <span class="kt">GreetingView</span><span class="p">(</span><span class="nv">connection</span><span class="p">:</span> <span class="n">connection</span><span class="p">)</span>
			<span class="p">}</span>
		<span class="p">}</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We’ve removed the need to manage the configuration. We’ve set ourselves up to define additional named scenes. And, it’s also now easier to access the global connection.</p>

<h2 id="check-out-that-view">Check out that View!</h2>

<p>The remote view capabilities of ExtensionKit are pretty cool. As far as I can tell, all of SwiftUI and AppKit is usable from them. They do behave a little differently from in-process views though. For one, it looks like as of Ventura Beta 6, ExtensionKit does not integrate with any of the accessibility systems. Remote views can also have an impact on window resizing performance, but from my experimenting it’s a little unpredictable.</p>

<p>I have definitely struggled to understand and use ExtensionKit’s view APIs and suggested structure. But, it’s also been really fun to take the things I’ve learned about what has worked for Chime’s remote view integrations and put them into <a href="https://github.com/ChimeHQ/Extendable">Extendable</a>. The extension-side stuff is even usable from iOS! Of course, it could very well be that this all changes before Ventura ships. But, for now I’ve found it makes things much easier, especially for views.</p>

<p>When I started writing about ExtensionKit, I just assumed it would be one post. But, between XPC, managing APIs, and working with views, it’s a really big topic. I think there’s a whole new chapter opening up for third-party app integrations, and I’m excited to see what everyone builds. 
I hope you’ve enjoyed the series so far. And, if you have questions or comments, I’d love to hear them!</p>

<p>Oh, and we’ve also got a bonus post coming. We’re going to use ExtensionKit in a real-world example, by adding support for a new language to Chime using <a href="https://github.com/ChimeHQ/ChimeKit">ChimeKit</a>. 😎</p>

<p>It’s now available! We’ve made a <a href="https://swiftpackageindex.com/ChimeHQ/ChimeKit/main/documentation/chimeextensioninterface">DocC tutorial</a> on how we built <a href="https://github.com/ChimeHQ/chime-swift">chime-swift</a>, to add Swift support to Chime.</p>]]></content><author><name>Matt Massicotte</name></author><summary type="html"><![CDATA[Learn about the ExtensionKit view system.]]></summary></entry><entry><title type="html">ExtensionKit End-to-End</title><link href="https://www.chimehq.com/blog/extensionkit-end-to-end" rel="alternate" type="text/html" title="ExtensionKit End-to-End" /><published>2022-08-26T00:00:00+00:00</published><updated>2022-08-26T00:00:00+00:00</updated><id>https://www.chimehq.com/blog/extensionkit-end-to-end</id><content type="html" xml:base="https://www.chimehq.com/blog/extensionkit-end-to-end"><![CDATA[<h1 id="extensionkit-end-to-end">ExtensionKit End-to-End</h1>

<p>Working with ExtensionKit means you’ll need to think about APIs more than you might be used to. Of course, ExtensionKit has an <a href="https://developer.apple.com/documentation/extensionkit">API</a> that you use in your app and extensions. Then there’s all the XPC stuff you have to deal with. And, on top of all that, you’ll probably also want a higher-level API that your extension developers will use. It’s a lot! In this post, we’re going to look at using all these APIs to build an end-to-end example.</p>

<p>This is part three of our series on ExtensionKit.</p>

<ul>
  <li>Part One: <a href="/blog/extensionkit-intro">Introduction to ExtensionKit</a></li>
  <li>Part Two: <a href="/blog/extensionkit-xpc">ExtensionKit and XPC</a></li>
  <li>Part Three: <a href="/blog/extensionkit-end-to-end">ExtensionKit End-to-End</a></li>
  <li>Part Four: <a href="/blog/extensionkit-views">ExtensionKit Views</a></li>
</ul>

<h2 id="viewing-available-extensions">Viewing Available Extensions</h2>

<p>Before we get into creating extensions, there’s a little bit of administration we have to deal with. Extension-hosting applications must include a bit of UI. This is the browser that shows what extensions are available to your app and gives the user the ability to enable/disable them. This comes in the form of a standalone view controller: <a href="https://developer.apple.com/documentation/extensionkit/exappextensionbrowserviewcontroller"><code class="language-plaintext highlighter-rouge">EXAppExtensionBrowserViewController</code></a>. There’s no configuration required. You just have to find an appropriate spot in your app for its view.</p>

<p>We also have to publish the extension point identifiers our app supports. This is done with a plist file that has an <code class="language-plaintext highlighter-rouge">appextensionpoint</code> extension. And, that file must be put into the <code class="language-plaintext highlighter-rouge">Extensions</code> directory of the app bundle. Xcode 14 includes a pre-made destination for that location in its Copy Files Phase UI.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><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>com.yourcompany.Extension<span class="nt">&lt;/key&gt;</span>
	<span class="nt">&lt;dict&gt;</span>
		<span class="nt">&lt;key&gt;</span>EXPresentsUserInterface<span class="nt">&lt;/key&gt;</span>
		<span class="nt">&lt;false</span> <span class="nt">/&gt;</span>
	<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<p>As far as I can tell, this process is all totally undocumented at the moment. I was fortunate enough to find an Apple engineer at WWDC that shared the information with me. But, I’m sure this is a temporary problem.</p>

<h2 id="a-minimal-extension">A Minimal Extension</h2>

<p>Xcode 14 comes with a new macOS target template called “Generic Extension”. From the code that spits out, you can see there are two core protocols involved in making an extension: <a href="https://developer.apple.com/documentation/extensionfoundation/appextension"><code class="language-plaintext highlighter-rouge">AppExtension</code></a> and <a href="https://developer.apple.com/documentation/extensionfoundation/appextensionconfiguration"><code class="language-plaintext highlighter-rouge">AppExtensionConfiguration</code></a>. They must be used together to correctly set things up, and I found the structure pretty confusing.</p>

<p>Let’s define a few types that conform to these protocols to see how it all fits together. We’re going to continue with our <code class="language-plaintext highlighter-rouge">Greeting</code> design from the <a href="/blog/extensionkit-xpc">previous post</a>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">ExtensionProtocol</span> <span class="p">{</span>
	<span class="kd">func</span> <span class="nf">greet</span><span class="p">(</span><span class="n">_</span> <span class="nv">greeting</span><span class="p">:</span> <span class="kt">GreetingParameters</span><span class="p">)</span> <span class="k">async</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">Greeting</span>
<span class="p">}</span>

<span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">MyAppExtension</span><span class="p">:</span> <span class="kt">ExtensionProtocol</span><span class="p">,</span> <span class="kt">AppExtension</span> <span class="p">{</span>
	<span class="k">var</span> <span class="nv">hostApp</span><span class="p">:</span> <span class="kt">HostProtocol</span><span class="p">?</span> <span class="p">{</span> <span class="k">get</span> <span class="k">set</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I’ve chosen to separate the <code class="language-plaintext highlighter-rouge">ExtensionProtocol</code> API from the <code class="language-plaintext highlighter-rouge">AppExtension</code> type. This extra level of abstraction makes it possible for a type to conform to <code class="language-plaintext highlighter-rouge">ExtensionProtocol</code> but not actually be an extension. I’ve found this pattern useful for testing, and when wrangling all of the interface objects involved in communication. But, it certainly isn’t required and you might find another arrangement suits you better.</p>

<p>Did you notice that <code class="language-plaintext highlighter-rouge">hostApp</code> property? Don’t worry, we’ll get to that soon.</p>

<h2 id="extension-configuration">Extension Configuration</h2>

<p>The next important bit of setting up an extension is managing a type that conforms to <code class="language-plaintext highlighter-rouge">AppExtensionConfiguration</code>. Here’s an implementation that fits in with our <code class="language-plaintext highlighter-rouge">Greeting</code> protocols.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">MyAppExtensionConfiguration</span><span class="o">&lt;</span><span class="kt">Extension</span><span class="p">:</span> <span class="kt">MyAppExtension</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">AppExtensionConfiguration</span> <span class="p">{</span>
	<span class="k">let</span> <span class="nv">appExtension</span><span class="p">:</span> <span class="kt">Extension</span>

	<span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">appExtension</span><span class="p">:</span> <span class="kt">Extension</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">self</span><span class="o">.</span><span class="n">appExtension</span> <span class="o">=</span> <span class="n">appExtension</span>
	<span class="p">}</span>

	<span class="kd">func</span> <span class="nf">accept</span><span class="p">(</span><span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
		<span class="n">appExtension</span><span class="o">.</span><span class="nf">export</span><span class="p">(</span><span class="nv">over</span><span class="p">:</span> <span class="n">connection</span><span class="p">)</span>
		<span class="n">appExtension</span><span class="o">.</span><span class="n">hostApp</span> <span class="o">=</span> <span class="kt">RemoteHost</span><span class="p">(</span><span class="n">connection</span><span class="p">)</span>

		<span class="n">connection</span><span class="o">.</span><span class="nf">activate</span><span class="p">()</span>

		<span class="k">return</span> <span class="kc">true</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The important part here is the <code class="language-plaintext highlighter-rouge">accept(connection:)</code> method. This is where you configure the XPC connection to the host. We’re using the helpers we defined in the <a href="/blog/extensionkit-xpc">last post</a> to keep this pretty slim. And, you can see where we set the extension’s <code class="language-plaintext highlighter-rouge">hostApp</code> property. This is used for the extension-to-host communication path.</p>

<p>With all this, we can finally set up a real extension:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@main</span>
<span class="kd">class</span> <span class="kt">GreetingExtension</span><span class="p">:</span> <span class="kt">MyAppExtension</span> <span class="p">{</span>
	<span class="k">var</span> <span class="nv">hostApp</span><span class="p">:</span> <span class="kt">HostProtocol</span><span class="p">?</span> <span class="p">{</span>
		<span class="k">didSet</span> <span class="p">{</span>
			<span class="c1">// connection has been established (or removed?) here</span>
		<span class="p">}</span>
	<span class="p">}</span>

	<span class="kd">required</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="kd">extension</span> <span class="kt">MyAppExtension</span> <span class="p">{</span>
	<span class="k">var</span> <span class="nv">configuration</span><span class="p">:</span> <span class="kt">MyAppExtensionConfiguration</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span> <span class="p">{</span>
		<span class="k">return</span> <span class="kt">MyAppExtensionConfiguration</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="configuration-complexity">Configuration Complexity</h2>

<p>I’ll be honest - I don’t love this pattern. I find it very complex, and I can see only downsides from that additional complexity. It’s extremely awkward that extension creation is separated from connection configuration like this. As far as I can tell, the design enforces this strange in-between state where an extension exists, but doesn’t have a connection. And, an extension has to handle the situation where a host connection is unset.</p>

<p>I would much prefer if this was all gone and the required initializer for an extension was just:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">AppExtension</span> <span class="p">{</span>
	<span class="nf">init</span><span class="p">(</span><span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span><span class="p">)</span> <span class="k">throws</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I’ve provided feedback asking for this interface, but I suspect this is the API we’ll have to live with when Ventura ships. If anyone out there comes up with a nicer way to handle initialization/connection-configuration, or at least a use-case for the current design, please share it with me!</p>

<h2 id="a-simpler-extension-side-api">A Simpler Extension-Side API</h2>

<p>I was annoyed enough with the existing API that I looked into ways to improve the situation. Here’s an equivalent implementation to the above.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">GreetingExtension</span><span class="p">:</span> <span class="kt">ExtensionProtocol</span><span class="p">,</span> <span class="kt">ConnectableExtension</span> <span class="p">{</span>
	<span class="k">var</span> <span class="nv">hostApp</span><span class="p">:</span> <span class="kt">HostProtocol</span><span class="p">?</span>

	<span class="kd">func</span> <span class="nf">acceptConnection</span><span class="p">(</span><span class="n">_</span> <span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span><span class="p">)</span> <span class="k">throws</span> <span class="p">{</span>
		<span class="k">self</span><span class="o">.</span><span class="n">hostApp</span> <span class="o">=</span> <span class="kt">RemoteHost</span><span class="p">(</span><span class="n">connection</span><span class="p">)</span>
		
		<span class="k">self</span><span class="o">.</span><span class="nf">export</span><span class="p">(</span><span class="nv">over</span><span class="p">:</span> <span class="n">connection</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>That <code class="language-plaintext highlighter-rouge">ConnectableExtension</code> protocol removes the need for separate configuration and provides more convenient access to the connection. I’ve put this implementation into a Swift package called <a href="https://github.com/ChimeHQ/Extendable">Extendable</a>, in case you want to check it out.</p>

<p>An earlier version of this blog post proposed adding a new <code class="language-plaintext highlighter-rouge">init</code> method that accepted the connection. That approach helped to eliminate the in-between state where an extension is been created, but not connected. I still don’t like that <code class="language-plaintext highlighter-rouge">hostApp</code> is optional. But, you may very well want to run some setup on extension process start. And, a host isn’t required to make a global connection. So, I think this version strikes a better balance, given how ExtensionKit actually can be used.</p>

<h2 id="discovering-extensions">Discovering Extensions</h2>

<p>Oh, whew. That was a lot of work to get our extension all set up. But, we’re now finally ready to make use of them in the host application.</p>

<p>We now need to actually find extensions that match our advertised extension point identifiers. This is done using the <a href="https://developer.apple.com/documentation/extensionfoundation/appextensionidentity"><code class="language-plaintext highlighter-rouge">AppExtensionIdentity</code></a> APIs. All extensions that have been enabled by the user (via that <code class="language-plaintext highlighter-rouge">EXAppExtensionBrowserViewController</code> view) can be discovered using its <a href="https://developer.apple.com/documentation/extensionfoundation/appextensionidentity/4043633-matching"><code class="language-plaintext highlighter-rouge">matching(appExtensionPointIDs:)</code></a> method.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Task</span> <span class="p">{</span>
	<span class="k">let</span> <span class="nv">identitiesSeq</span> <span class="o">=</span> <span class="k">try!</span> <span class="kt">AppExtensionIdentity</span><span class="o">.</span><span class="nf">matching</span><span class="p">(</span><span class="nv">appExtensionPointIDs</span><span class="p">:</span> <span class="s">"com.yourcompany.Extension"</span><span class="p">)</span>

	<span class="k">for</span> <span class="k">await</span> <span class="n">identities</span> <span class="k">in</span> <span class="n">identitiesSeq</span> <span class="p">{</span>
		<span class="k">for</span> <span class="n">identity</span> <span class="k">in</span> <span class="n">identities</span> <span class="p">{</span>
			<span class="nf">installIdentity</span><span class="p">(</span><span class="n">identity</span><span class="p">)</span>
		<span class="p">}</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Interestingly, this API requires the use of an <code class="language-plaintext highlighter-rouge">AsyncSequence</code>, and was my first real exposure to that system. No ExtensionKit for you Objective-C developers!</p>

<h2 id="connecting-to-an-extension">Connecting to an Extension</h2>

<p>Armed with our <code class="language-plaintext highlighter-rouge">AppExtensionIdentity</code>, we can actually start the extension and connect to it. This is done using <a href="https://developer.apple.com/documentation/extensionfoundation/appextensionprocess"><code class="language-plaintext highlighter-rouge">AppExtensionProcess</code></a>, which models the process the extension runs in. This gives the hosting process control over the lifecycle of extensions, as well as access to the per-extension <code class="language-plaintext highlighter-rouge">NSXPCConnection</code>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">installIdentity</span><span class="p">(</span><span class="n">_</span> <span class="nv">identity</span><span class="p">:</span> <span class="kt">AppExtensionIdentity</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">let</span> <span class="nv">config</span> <span class="o">=</span> <span class="kt">AppExtensionProcess</span><span class="o">.</span><span class="kt">Configuration</span><span class="p">(</span><span class="nv">appExtensionIdentity</span><span class="p">:</span> <span class="n">identity</span><span class="p">)</span>
	<span class="k">let</span> <span class="nv">process</span> <span class="o">=</span> <span class="k">try</span> <span class="k">await</span> <span class="kt">AppExtensionProcess</span><span class="p">(</span><span class="nv">configuration</span><span class="p">:</span> <span class="n">config</span><span class="p">)</span>
	<span class="k">let</span> <span class="nv">connection</span> <span class="o">=</span> <span class="k">try</span> <span class="n">process</span><span class="o">.</span><span class="nf">makeXPCConnection</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If you’re following along with the patterns setup from the <a href="/blog/extensionkit-xpc">previous post</a>, configuring the connection is straightforward.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">exportedHost</span><span class="o">.</span><span class="nf">export</span><span class="p">(</span><span class="nv">over</span><span class="p">:</span> <span class="n">connection</span><span class="p">)</span>

<span class="k">let</span> <span class="nv">remoteExtension</span> <span class="o">=</span> <span class="kt">RemoteExtension</span><span class="p">(</span><span class="nv">connection</span><span class="p">:</span> <span class="n">connection</span><span class="p">)</span>

<span class="kt">Task</span> <span class="p">{</span>
	<span class="k">let</span> <span class="nv">value</span> <span class="o">=</span> <span class="k">try</span> <span class="k">await</span> <span class="n">remoteExtension</span><span class="o">.</span><span class="nf">greet</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>
	
	<span class="nf">print</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="we-built-something">We Built Something!</h2>

<p>We’ve finally gotten somewhere, and it only took three posts! We covered how <a href="/blog/extensionkit-intro">ExtensionKit works</a> and how we can design a <a href="/blog/extensionkit-xpc">system with XPC</a>. Now we’ve put it all together to build an extension and integrate it into an app. And after a little ranting, also presented an <a href="https://github.com/ChimeHQ/Extendable">open source library</a> that helps to simplify building an extension.</p>

<p>But, believe it or not, we’re not done. In our next installment, we’re going to get into one of the most interesting features of ExtensionKit - supporting remote views!</p>]]></content><author><name>Matt Massicotte</name></author><summary type="html"><![CDATA[Using the ExtensionKit APIs to build an end-to-end example.]]></summary></entry><entry><title type="html">ExtensionKit and XPC</title><link href="https://www.chimehq.com/blog/extensionkit-xpc" rel="alternate" type="text/html" title="ExtensionKit and XPC" /><published>2022-08-22T00:00:00+00:00</published><updated>2022-08-22T00:00:00+00:00</updated><id>https://www.chimehq.com/blog/extensionkit-xpc</id><content type="html" xml:base="https://www.chimehq.com/blog/extensionkit-xpc"><![CDATA[<h1 id="extensionkit-and-xpc">ExtensionKit and XPC</h1>

<p>ExtensionKit makes extensive use of <a href="https://developer.apple.com/documentation/xpc">XPC</a>. There are XPC connections all over the place!
There’s a per-extension XPC interface. But, there’s also a per-view XPC interface. This can make for a lot of APIs to manage. It’s all critical too, as this will make up the foundation of an ExtensionKit system. So, to be successful with ExtensionKit, you have to work really closely with XPC. Let’s take a look at how we can do that.</p>

<p>This is part two of our series on ExtensionKit.</p>

<ul>
  <li>Part One: <a href="/blog/extensionkit-intro">Introduction to ExtensionKit</a></li>
  <li>Part Two: ExtensionKit and XPC</li>
  <li>Part Three: <a href="/blog/extensionkit-end-to-end">ExtensionKit End-to-End</a></li>
  <li>Part Four: <a href="/blog/extensionkit-views">ExtensionKit Views</a></li>
</ul>

<h2 id="minimal-xpc-interface">Minimal XPC Interface</h2>

<p>ExtensionKit uses XPC for all communication, via <a href="https://developer.apple.com/documentation/foundation/nsxpcconnection"><code class="language-plaintext highlighter-rouge">NSXPCConnection</code></a>. Let’s start out by looking at an XPC interface that we’ll use as a basis for all our examples.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">typealias</span> <span class="kt">XPCGreetingParameters</span> <span class="o">=</span> <span class="kt">Data</span>
<span class="kd">typealias</span> <span class="kt">XPCGreeting</span> <span class="o">=</span> <span class="kt">Data</span>

<span class="kd">@objc</span> <span class="kd">protocol</span> <span class="kt">ExtensionXPCProtocol</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">greet</span><span class="p">(</span><span class="n">_</span> <span class="nv">greeting</span><span class="p">:</span> <span class="kt">XPCGreetingParameters</span><span class="p">,</span> <span class="nv">reply</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">XPCGreeting</span><span class="p">?,</span> <span class="kt">Error</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Hmm, well this is <em>technically</em> Swift code, but it looks terrible. It turns out, <code class="language-plaintext highlighter-rouge">NSXPCConnection</code> doesn’t work nicely with Swift. Before we address this, let’s get into why it looks the way it does.</p>

<p><code class="language-plaintext highlighter-rouge">NSXPCConnection</code> imposes two big limitations on our interface protocol. First, it can only use types that are representable in Objective-C. This means no enums, no structs, and optionals only for classes, to name some of the most problematic cases. At least these are compiler-enforced, so we’ll know what’s allowed right up front.</p>

<p>The second constraint is that all types must conform to <code class="language-plaintext highlighter-rouge">NSSecureCoding</code>. XPC must serialize all the parameters and return values for a method call, and it uses <code class="language-plaintext highlighter-rouge">NSSecureCoding</code> to do that. You absolutely can make a Swift class conform to <code class="language-plaintext highlighter-rouge">NSSecureCoding</code>. However, it requires a lot of boilerplate, it’s error-prone, and honestly, it’s just a pain.</p>

<p>It would be much nicer if we could just use Swift’s <code class="language-plaintext highlighter-rouge">Codable</code> instead. It makes for much easier seralization. It also means we don’t have to be constrained to ObjC-compatible types, because we’re just passing data over the wire. But, because we’ve lost our types, I’ve used typealiases. You could just use <code class="language-plaintext highlighter-rouge">Data</code>, but I like some clue as to what is actually expected on the other end.</p>

<p>Oh, and just in case you were wondering, I did do some testing, and <code class="language-plaintext highlighter-rouge">NSSecureCoding</code> offered no significant performance benefits over this approach.</p>

<h2 id="pitfall-automatic-async-translation">Pitfall: Automatic Async Translation</h2>

<p>You might not realize this, but Swift can automatically translate certain closure-based APIs into async-compatible versions. This works for XPC too, and at first, seems like it could be a huge win. Take a look - this version will work with <code class="language-plaintext highlighter-rouge">NSXPCConnection</code>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@objc</span> <span class="kd">protocol</span> <span class="kt">ExtensionXPCProtocol</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">greet</span><span class="p">(</span><span class="n">_</span> <span class="nv">greeting</span><span class="p">:</span> <span class="kt">XPCGreetingParameters</span><span class="p">)</span> <span class="k">async</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">XPCGreeting</span>
<span class="p">}</span>
</code></pre></div></div>

<p>When I first discovered this, I was pretty amazed. It looks like it gets us a lot closer to the Swift API we’d like. However, there is a serious problem with this approach.</p>

<p>Being a communication system, all XPC calls can fail. They can fail even if the method does not return an error. And, because of how they can fail, XPC methods do not guarantee that their reply callback will be called. This is extremely important, because that behavior violates the Swift concurrency runtime requirements. XPC calls <strong>will</strong> hang your tasks when they fail. Because of this, it is <strong>unsafe</strong> to use this technique in your XPC interfaces.</p>

<p>Let’s now try to address all of this so we can have a nice Swift API.</p>

<h2 id="swift-wrapper">Swift Wrapper</h2>

<p>This poor fit between XPC and Swift has bothered many others. There are two libraries that look pretty nice for dealing with all this nonsense: <a href="https://github.com/CharlesJS/SwiftyXPC">SwiftyXPC</a> and <a href="https://github.com/trilemma-dev/SecureXPC">SecureXPC</a>. They both offer async/await support, and use <code class="language-plaintext highlighter-rouge">Codable</code> for serializing data. Unfortunately, they also both use their own custom communication primitives. That doesn’t work well for us - ExtensionKit requires <code class="language-plaintext highlighter-rouge">NSXPCConnection</code> instances.</p>

<p>I have a feeling that it would be possible to adapt these libraries for use with ExtensionKit, but I didn’t try. I did play around with making a custom <code class="language-plaintext highlighter-rouge">NSProxy</code> to ease some of the burden here, but I didn’t get too far. In the end, I just accepted that some boilerplate is necessary. I very much look forward to a third- or even first-party solution. And, given how prominently XPC is used here, I expect something from Apple has to be forthcoming.</p>

<p>Ok, rant over. What does a Swift solution look like? Here’s a much more desirable version:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">GreetingParameters</span><span class="p">:</span> <span class="kt">Codable</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>

<span class="kd">struct</span> <span class="kt">Greeting</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
	<span class="k">let</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">String</span>
<span class="p">}</span>

<span class="kd">protocol</span> <span class="kt">ExtensionProtocol</span> <span class="p">{</span>
	<span class="kd">func</span> <span class="nf">greet</span><span class="p">(</span><span class="n">_</span> <span class="nv">greeting</span><span class="p">:</span> <span class="kt">GreetingParameters</span><span class="p">)</span> <span class="k">async</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">Greeting</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Getting from our <code class="language-plaintext highlighter-rouge">ExtensionXPCProtocol</code> to this is going to take a bit of code, so let’s jump in.</p>

<h2 id="remoteextension">RemoteExtension</h2>

<p>Here’s a first pass of a wrapper for the host-end interface. There’s a lot of code here, and we’ve only got one method! But, it highlights the important problems we have to solve. Namely:</p>

<ul>
  <li>Encoding and decoding our types so they are compatible with XPC serialization</li>
  <li>Using continuations to enable async support</li>
  <li>Using of <code class="language-plaintext highlighter-rouge">remoteObjectProxyWithErrorHandler</code> to handle failures</li>
</ul>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">RemoteExtension</span> <span class="p">{</span>
	<span class="kd">private</span> <span class="k">let</span> <span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span>
	<span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">self</span><span class="o">.</span><span class="n">connection</span> <span class="o">=</span> <span class="n">connection</span>

		<span class="nf">precondition</span><span class="p">(</span><span class="n">connection</span><span class="o">.</span><span class="n">remoteObjectInterface</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>
		<span class="n">connection</span><span class="o">.</span><span class="n">remoteObjectInterface</span> <span class="o">=</span> <span class="kt">NSXPCInterface</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="kt">ExtensionXPCProtocol</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="kd">extension</span> <span class="kt">RemoteExtension</span><span class="p">:</span> <span class="kt">ExtensionProtocol</span> <span class="p">{</span>
	<span class="kd">func</span> <span class="nf">greet</span><span class="p">(</span><span class="n">_</span> <span class="nv">greeting</span><span class="p">:</span> <span class="kt">GreetingParameters</span><span class="p">)</span> <span class="k">async</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">Greeting</span> <span class="p">{</span>
		<span class="k">let</span> <span class="nv">xpcGreeting</span> <span class="o">=</span> <span class="k">try</span> <span class="kt">JSONEncoder</span><span class="p">()</span><span class="o">.</span><span class="nf">encode</span><span class="p">(</span><span class="n">greeting</span><span class="p">)</span>

		<span class="k">return</span> <span class="k">try</span> <span class="k">await</span> <span class="nf">withCheckedThrowingContinuation</span><span class="p">(</span><span class="nv">function</span><span class="p">:</span> <span class="n">function</span><span class="p">)</span> <span class="p">{</span> <span class="n">continuation</span> <span class="k">in</span>
			<span class="k">let</span> <span class="nv">proxy</span> <span class="o">=</span> <span class="n">connection</span><span class="o">.</span><span class="n">remoteObjectProxyWithErrorHandler</span> <span class="p">{</span> <span class="n">error</span> <span class="k">in</span>
				<span class="n">continuation</span><span class="o">.</span><span class="nf">resume</span><span class="p">(</span><span class="nv">throwing</span><span class="p">:</span> <span class="n">error</span><span class="p">)</span>
			<span class="p">}</span>

			<span class="k">guard</span> <span class="k">let</span> <span class="nv">service</span> <span class="o">=</span> <span class="n">proxy</span> <span class="k">as?</span> <span class="kt">ExtensionXPCProtocol</span> <span class="k">else</span> <span class="p">{</span>
				<span class="n">continuation</span><span class="o">.</span><span class="nf">resume</span><span class="p">(</span><span class="nv">throwing</span><span class="p">:</span> <span class="kt">ConnectionContinuationError</span><span class="o">.</span><span class="n">serviceTypeMismatch</span><span class="p">)</span>
				<span class="k">return</span>
			<span class="p">}</span>

			<span class="n">service</span><span class="o">.</span><span class="nf">greet</span><span class="p">(</span><span class="n">xpcGreeting</span><span class="p">,</span> <span class="nv">reply</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span> <span class="k">in</span>
				<span class="k">switch</span> <span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span> <span class="p">{</span>
					<span class="k">case</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="k">let</span> <span class="nv">error</span><span class="p">?):</span>
						<span class="nf">resume</span><span class="p">(</span><span class="nv">throwing</span><span class="p">:</span> <span class="n">error</span><span class="p">)</span>
					<span class="k">case</span> <span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="kc">nil</span><span class="p">):</span>
						<span class="nf">resume</span><span class="p">(</span><span class="nv">throwing</span><span class="p">:</span> <span class="kt">ConnectionContinuationError</span><span class="o">.</span><span class="n">missingBothValueAndError</span><span class="p">)</span>
					<span class="k">case</span> <span class="p">(</span><span class="k">let</span> <span class="nv">encodedValue</span><span class="p">?,</span> <span class="kc">nil</span><span class="p">):</span>
						<span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="kt">Result</span><span class="p">(</span><span class="nv">catching</span><span class="p">:</span> <span class="p">{</span> <span class="k">try</span> <span class="kt">JSONDecoder</span><span class="p">()</span><span class="o">.</span><span class="nf">decode</span><span class="p">(</span><span class="kt">Greeting</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="nv">from</span><span class="p">:</span> <span class="n">encodedValue</span><span class="p">)</span> <span class="p">})</span>

						<span class="nf">resume</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">result</span><span class="p">)</span>
				<span class="p">}</span>
				<span class="p">})</span>
			<span class="p">}</span>
		<span class="p">}</span>
	<span class="p">}</span>
</code></pre></div></div>

<h2 id="improving-on-our-wrapper">Improving on our Wrapper</h2>

<p>I think you can now really appreciate why there have been efforts to help improve XPC-Swift compatibility. This is just awful.</p>

<p>Fortunately, it is possible to factor out quite a lot of the common code. I did this, and put them into a very small package that is fully compatible with <code class="language-plaintext highlighter-rouge">NSXPCConnection</code>. Check out this version using <a href="https://github.com/ChimeHQ/ConcurrencyPlus">ConcurrencyPlus</a>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">ConcurrencyPlus</span>

<span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">RemoteExtension</span> <span class="p">{</span>
	<span class="kd">private</span> <span class="k">let</span> <span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span>
	<span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">self</span><span class="o">.</span><span class="n">connection</span> <span class="o">=</span> <span class="n">connection</span>

		<span class="nf">precondition</span><span class="p">(</span><span class="n">connection</span><span class="o">.</span><span class="n">remoteObjectInterface</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>
		<span class="n">connection</span><span class="o">.</span><span class="n">remoteObjectInterface</span> <span class="o">=</span> <span class="kt">NSXPCInterface</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="kt">ExtensionXPCProtocol</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="kd">extension</span> <span class="kt">RemoteExtension</span><span class="p">:</span> <span class="kt">ExtensionProtocol</span> <span class="p">{</span>
	<span class="kd">func</span> <span class="nf">greet</span><span class="p">(</span><span class="n">_</span> <span class="nv">greeting</span><span class="p">:</span> <span class="kt">GreetingParameters</span><span class="p">)</span> <span class="k">async</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">Greeting</span> <span class="p">{</span>
		<span class="k">let</span> <span class="nv">xpcGreeting</span> <span class="o">=</span> <span class="k">try</span> <span class="kt">JSONEncoder</span><span class="p">()</span><span class="o">.</span><span class="nf">encode</span><span class="p">(</span><span class="n">greeting</span><span class="p">)</span>

		<span class="k">return</span> <span class="n">connection</span><span class="o">.</span><span class="n">withContinuation</span> <span class="p">{</span> <span class="p">(</span><span class="nv">service</span><span class="p">:</span> <span class="kt">ExtensionXPCProtocol</span><span class="p">,</span> <span class="n">continuation</span><span class="p">)</span> <span class="k">in</span>
			<span class="n">service</span><span class="o">.</span><span class="nf">greet</span><span class="p">(</span><span class="n">xpcGreeting</span><span class="p">,</span> <span class="nv">reply</span><span class="p">:</span> <span class="n">continuation</span><span class="o">.</span><span class="n">resumingHandler</span><span class="p">)</span>
		<span class="p">}</span>
	<span class="p">}</span>
</code></pre></div></div>

<p>This is a lot more tolerable. I admit, it’s still a huge pain. But, at least we now have a pattern we can follow that gives us the error behavior we need and the Swift API we want.</p>

<h2 id="exporting-wrapper">Exporting Wrapper</h2>

<p>This <code class="language-plaintext highlighter-rouge">RemoteExtension</code> class would be used by the host talking to an extension. But, it’s likely that we’ll also need to go in the opposite direction. To do that, we can use a similar approach. First, we need a protocol used for XPC:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@objc</span> <span class="kd">protocol</span> <span class="kt">HostXPCProtocol</span> <span class="p">{</span>
	<span class="kd">func</span> <span class="nf">acknowledgeGreeting</span><span class="p">(</span><span class="nv">reply</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">Error</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Next, we’ll define a protocol for the actual API we’d like to use:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">HostProtocol</span> <span class="p">{</span>
	<span class="kd">func</span> <span class="nf">acknowledgeGreeting</span><span class="p">()</span> <span class="k">async</span> <span class="k">throws</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And then, we’ll make a wrapper class that handles adapting the two:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">ExportedHost</span><span class="o">&lt;</span><span class="kt">Host</span><span class="p">:</span> <span class="kt">HostProtocol</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">HostXPCProtocol</span> <span class="p">{</span>
	<span class="k">let</span> <span class="nv">host</span><span class="p">:</span> <span class="kt">Host</span>

	<span class="kd">func</span> <span class="nf">acknowledgeGreeting</span><span class="p">(</span><span class="nv">reply</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">Error</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
		<span class="kt">Task</span> <span class="p">{</span>
			<span class="k">do</span> <span class="p">{</span>
				<span class="k">try</span> <span class="n">host</span><span class="o">.</span><span class="nf">acknowledgeGreeting</span><span class="p">()</span>
			<span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
				<span class="nf">reply</span><span class="p">(</span><span class="n">error</span><span class="p">)</span>
			<span class="p">}</span>
		<span class="p">}</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Again, lots of boilerplate. In this case, I’ve omitted the need to deal with coding and decoding. But, you may still have to do it, and it will definitely still be annoying.</p>

<h2 id="aside-tasks">Aside: Tasks</h2>

<p>One thing that I do want to point out is the use of a standalone <code class="language-plaintext highlighter-rouge">Task</code>. This is a very typical way of getting an async context within a non-async function. It works. But, the <strong>ordering</strong> of <code class="language-plaintext highlighter-rouge">Task</code> execution is out of your control. If that <code class="language-plaintext highlighter-rouge">host</code> object was stateful, this could matter a lot.</p>

<p>In fact, I struggled terribly with this exact thing while introducing async APIs into my codebase. I was coming from the expectation that I could transparently move from closure-based functions to async. That <strong>only</strong> works when the underlying system isn’t stateful. And, I would imagine that is a special-case. The more I use Swift Concurrency, the more I view a standalone <code class="language-plaintext highlighter-rouge">Task</code> as a red flag. When you see it, it demands very careful thought about the ordering requirements.</p>

<p>To help address this, I ended up building a simple queue for Tasks. It works nearly the same as the standalone Task invocation, but guarantees ordering. I’ve also put that into <a href="https://github.com/ChimeHQ/ConcurrencyPlus">ConcurrencyPlus</a>. Check it out, if such a thing sounds like it could be useful.</p>

<h2 id="using-nsxpcconnection">Using NSXPCConnection</h2>

<p>Here, we have the building blocks we need to actually communicate over an <code class="language-plaintext highlighter-rouge">NSXPCConnection</code> with reasonable APIs. But, we haven’t yet actually used these with a real <code class="language-plaintext highlighter-rouge">NSXPCConnection</code>. To do that, we need to work out two parts - what local object we’ll <strong>export</strong> and the remote object <strong>proxy</strong> we’ll interact with.</p>

<p>To export our local <code class="language-plaintext highlighter-rouge">Host</code> object, I like to add a little extension to encapsulate the details. Here’s how that looks:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">HostProtocol</span> <span class="p">{</span>
	<span class="kd">public</span> <span class="kd">func</span> <span class="nf">export</span><span class="p">(</span><span class="n">over</span> <span class="nv">connection</span><span class="p">:</span> <span class="kt">NSXPCConnection</span><span class="p">)</span> <span class="p">{</span>
		<span class="nf">precondition</span><span class="p">(</span><span class="n">connection</span><span class="o">.</span><span class="n">exportedInterface</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>

		<span class="n">connection</span><span class="o">.</span><span class="n">exportedInterface</span> <span class="o">=</span> <span class="kt">NSXPCInterface</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="kt">HostXPCProtocol</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
		<span class="n">connection</span><span class="o">.</span><span class="n">exportedObject</span> <span class="o">=</span> <span class="kt">ExportedHost</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With this, and our <code class="language-plaintext highlighter-rouge">RemoteExtension</code>, we can configure our connection with just a few lines.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// given host and connection objects</span>
<span class="n">host</span><span class="o">.</span><span class="nf">export</span><span class="p">(</span><span class="nv">over</span><span class="p">:</span> <span class="n">connection</span><span class="p">)</span>

<span class="c1">// and here's the remote we can interact with</span>
<span class="k">let</span> <span class="nv">remote</span> <span class="o">=</span> <span class="kt">RemoteExtension</span><span class="p">(</span><span class="nv">connection</span><span class="p">:</span> <span class="n">connection</span><span class="p">)</span>
</code></pre></div></div>

<p>Note that <code class="language-plaintext highlighter-rouge">NSXPCConnection</code> will retain its <code class="language-plaintext highlighter-rouge">exportedObject</code>. But, it will be up to you to manage the lifecycle of the connection itself.</p>

<h2 id="recap">Recap</h2>

<p>Don’t forget, what we’ve covered here is just <strong>one side</strong> of the communication. Both the host and the extensions will need these wrappers. So, a finished version would contain:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">HostXPCProtocol</code></li>
  <li><code class="language-plaintext highlighter-rouge">HostProtocol</code></li>
  <li><code class="language-plaintext highlighter-rouge">ExportedHost</code> for the host side</li>
  <li><code class="language-plaintext highlighter-rouge">RemoteHost</code> for the extension side</li>
  <li><code class="language-plaintext highlighter-rouge">ExtensionXPCProtocol</code></li>
  <li><code class="language-plaintext highlighter-rouge">ExtensionProtocol</code></li>
  <li><code class="language-plaintext highlighter-rouge">ExportedExtension</code> for the extension side</li>
  <li><code class="language-plaintext highlighter-rouge">RemoteExtension</code> for the host side</li>
</ul>

<p>That’s a total of four protocols and four concrete types! And, all this is just for <strong>one</strong> connection! Remember that ExtensionKit can use more than one, depending on how your views are managed. I went through quite a lot of iterations trying to come up with a reasonable way to manage communication APIs. The patterns I’ve settled on here work ok. But, I really feel like this situation is untenable. We need better first-party Swift support for XPC, and we need it yesterday.</p>

<p>The things we’ve covered here aren’t actually that ExtensionKit-specific. But, XPC is a critical aspect of building a system with ExtensionKit, and it isn’t a simple thing to work with. Hopefully I’ve either presented some useful techniques, or horrified you and you are now inspired to build a library that makes this nicer. And if you do, please get in touch with me.</p>

<p>In the next part of this series, we’ll cover how to use these XPC primitives with ExtensionKit APIs.</p>]]></content><author><name>Matt Massicotte</name></author><summary type="html"><![CDATA[See how to deal with XPC and make your own APIs for ExtensionKit]]></summary></entry><entry><title type="html">An Introduction to ExtensionKit</title><link href="https://www.chimehq.com/blog/extensionkit-intro" rel="alternate" type="text/html" title="An Introduction to ExtensionKit" /><published>2022-08-17T00:00:00+00:00</published><updated>2022-08-17T00:00:00+00:00</updated><id>https://www.chimehq.com/blog/extensionkit-intro</id><content type="html" xml:base="https://www.chimehq.com/blog/extensionkit-intro"><![CDATA[<h1 id="an-introduction-to-extensionkit">An Introduction to ExtensionKit</h1>

<p><a href="https://developer.apple.com/documentation/extensionkit">ExtensionKit</a> is a pretty significant new feature of macOS Ventura. But, I wouldn’t be surprised if you didn’t know, as it had a conspicuously quiet introduction. There were no sessions or labs about it during WWDC 2022. I only discovered it because a friend stumbled across the beta documentation and sent it to me.</p>

<p>But whew, did it arrive at the <strong>right</strong> time. We’d just finished up adding <a href="/blog/hello-ruby">Ruby</a> support. It was a big project for us, and really highlighted how much we needed a better system for expanding Chime. We had just begun investigating how we could build a real <a href="/extensions">extension system</a> when ExtensionKit came along. We decided pretty quickly to move to adopt it, and we now use it as the basis for <a href="https://github.com/ChimeHQ/ChimeKit">ChimeKit</a>.</p>

<p>And, now that ChimeKit’s out, we thought we’d take a moment to write a little about our experiences with ExtensionKit. But, it turned out to be a big topic so we’re doing a series. This is the first post, really just focusing on getting familar with what ExtensionKit is all about.</p>

<ul>
  <li>Part One: Introduction to ExtensionKit</li>
  <li>Part Two: <a href="/blog/extensionkit-xpc">ExtensionKit and XPC</a></li>
  <li>Part Three: <a href="/blog/extensionkit-end-to-end">ExtensionKit End-to-End</a></li>
  <li>Part Four: <a href="/blog/extensionkit-views">ExtensionKit Views</a></li>
</ul>

<h2 id="capabilities">Capabilities</h2>

<p>It appears that ExtensionKit, and the lower-level <a href="https://developer.apple.com/documentation/extensionfoundation">ExtensionFoundation</a> have been around for quite a while. All of the iOS and macOS extension features are based on it. But, it also looks like Apple has been using this system internally as well. This bodes well for any framework, and ExtensionKit has indeed been pretty solid.</p>

<p>At a high-level, you can define extension points in your app, either with or without a UI component. All communication between extension and host goes over XPC, and there’s a bit of infrastructure provided by Apple for discovering available extensions and establishing a connection.</p>

<p>One of the most exciting things ExtensionKit can do is remote views. This is a view that is constructed with SwiftUI and managed within the extension, but displayed within the hosting application. As far as I can tell, this arrangement is totally transparent and supports virtually everything that SwiftUI can do, even animation. Perhaps the only real downside is window/view resizing can sometimes have a little lag.</p>

<h2 id="communication">Communication</h2>

<p>Communication between extension and host is all done using <a href="https://developer.apple.com/documentation/xpc">XPC</a>. There’s one global XPC connection per extension, but there’s also an optional connection per UI scene. At first, this was surprising to me. But, once I started actually experimenting with UIs, I realized this arrangement makes a lot of sense. While there will be only one extension instance, an extension can be asked to render multiple instances of its views. Of course, your design may not support this. But, if you do, this is a good means of coordination.</p>

<p>Extension hosts (ie app developers) will almost certainly want to provide some kind of framework that defines a higher-level API for extension-host interaction. This is an area that demands some <a href="/blog/extensionkit-xpc">careful work</a>. First, you want to provide a reasonable API for extension developers. But, you also need to consider forwards- and backwards-compatibility. There’s nothing provided by ExtensionKit to keep these APIs in sync across the versions of extensions and app that your users have installed.</p>

<h2 id="sandboxing">Sandboxing</h2>

<p>ExtensionKit will refuse to load any extensions that do not have the sandbox entitlement set. How much of a problem this is really depends on what your extension developers need to do. Fortunately, if it is a problem, you have some options.</p>

<p>It is possible to pass URL bookmark data across the XPC connection to share access. I’ve found it pretty confusing to understand how to use the security features of bookmarks, but in this case, it’s proven to be easy.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// create the data on the side that has access</span>
<span class="k">let</span> <span class="nv">data</span> <span class="o">=</span> <span class="k">try</span> <span class="n">url</span><span class="o">.</span><span class="nf">bookmarkData</span><span class="p">()</span>

<span class="c1">// a side-effect of resolving it on the other end will grant the process access</span>
<span class="k">var</span> <span class="nv">stale</span><span class="p">:</span> <span class="kt">Bool</span> <span class="o">=</span> <span class="kc">false</span>

<span class="k">let</span> <span class="nv">_</span> <span class="o">=</span> <span class="k">try</span> <span class="kt">URL</span><span class="p">(</span><span class="nv">resolvingBookmarkData</span><span class="p">:</span> <span class="n">data</span><span class="p">,</span>
                <span class="nv">options</span><span class="p">:</span> <span class="p">[</span><span class="o">.</span><span class="n">withoutUI</span><span class="p">],</span>
                <span class="nv">relativeTo</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span>
                <span class="nv">bookmarkDataIsStale</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">stale</span><span class="p">)</span>
</code></pre></div></div>

<p>If sharing file access isn’t sufficient, and it definitely is not for Chime, you’ll need to turn to other methods. One option is a library we’ve open sourced called <a href="https://github.com/ChimeHQ/ProcessService">ProcessService</a>. It is a system for executing processes outside of a sandbox via an XPC service.</p>

<h2 id="distribution">Distribution</h2>

<p>An interesting quirk of ExtensionKit is that extensions <strong>must</strong> be bundled within a <code class="language-plaintext highlighter-rouge">.app</code>. They cannot be distributed as standalone artifacts. I think this makes a lot of sense when you are attempting to expose functionality of an existing application in the form of an extension. But, there are lots of reasons why you might want to just make the extension itself. This even makes sense for Apple’s own supported use-cases, like Safari and Xcode editor extensions. And, Chime definitely falls into a similar category.</p>

<p>Now, you totally can just make a little wrapper app that does nothing but contain your extension. You can even get these minimal, placeholder apps onto the App Store. But, it’s just a pain. So, we’re planning on putting together a simple first-party “Extension Gallery” application. We’ll handle the details. All an extension developer will have to do is structure the extension as an open-source library.</p>

<p>This is probably more work than most extension-hosting applications will do. But, <a href="/extensions">extensions</a> are going to be a core part of Chime going forward, so it makes sense for us.</p>

<h2 id="approval">Approval</h2>

<p>Regardless of how you get an extension onto a user’s machine, there’s one more required step before they can be used. Extensions have to be user-approved via <a href="https://developer.apple.com/documentation/extensionkit/exappextensionbrowserviewcontroller"><code class="language-plaintext highlighter-rouge">EXAppExtensionBrowserViewController</code></a>. This controller presents a UI that displays all available extensions, organized by containing application. You’ll have to find a place to put this view within your hosting app. And, perhaps adding to the integration complexity, users can approve and revoke that approval at any time.</p>

<p>Requiring user approval is reasonable. And, I’m really happy to say that extensions bundled within the hosting app itself are auto-approved. This could be an atypical arrangement, but something that’s worth knowing just in case.</p>

<h2 id="ios-support">iOS Support?</h2>

<p>Right now, <strong>hosting</strong> ExtensionKit extensions can only be done from macOS. But, as far as I can tell, all of the extension-side APIs are available to iOS, tvOS and watchOS as well. I think this, combined with the extremely conspicuous lack of WWDC sessions means ExtensionKit was supposed to ship with iOS 16, but was pulled at the last minute.</p>

<p>I’ll also submit as evidence <code class="language-plaintext highlighter-rouge">EXAppExtensionBrowserViewController.h</code> as found in Xcode 14.0 beta 6:</p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#if !TARGET_OS_WATCH
</span>
<span class="cp">#if TARGET_OS_OSX
#import &lt;AppKit/AppKit.h&gt;
#else
#import &lt;UIKit/UIKit.h&gt;
#endif // TARGET_OS_OSX
</span>
<span class="n">NS_ASSUME_NONNULL_BEGIN</span>

<span class="n">API_AVAILABLE</span><span class="p">(</span><span class="n">macos</span><span class="p">(</span><span class="mi">13</span><span class="p">.</span><span class="mi">0</span><span class="p">))</span>
<span class="n">API_UNAVAILABLE</span><span class="p">(</span><span class="n">ios</span><span class="p">,</span> <span class="n">watchos</span><span class="p">,</span> <span class="n">tvos</span><span class="p">)</span>
<span class="n">EXTENSIONKIT_EXPORT</span>
<span class="cp">#if TARGET_OS_OSX
</span><span class="k">@interface</span> <span class="nc">EXAppExtensionBrowserViewController</span> <span class="p">:</span> <span class="nc">NSViewController</span>
<span class="k">@end</span>
<span class="cp">#else
</span><span class="k">@interface</span> <span class="nc">EXAppExtensionBrowserViewController</span> <span class="p">:</span> <span class="nc">UIViewController</span>
<span class="k">@end</span>
<span class="cp">#endif // TARGET_OS_OSX
</span>
<span class="n">NS_ASSUME_NONNULL_END</span>

<span class="cp">#endif // !TARGET_OS_WATCH
</span></code></pre></div></div>

<p>Today, I’m not sure you can do too much with ExtensionKit on non-macOS platforms. But, when you consider just how many entirely new types of apps could be built with it, just the <strong>idea</strong> is exciting.</p>

<h2 id="alternatives">Alternatives</h2>

<p>Lots of apps offer some kind of plugin system. I would guess that nearly all use JavaScript to do it. There are many reasons why this makes sense. First, JavaScript is a very well-known and well-used language, with an enormous number of available libraries. Second, there are many (<a href="https://nodejs.org/">node</a>, <a href="https://deno.land">deno</a>, <a href="https://bun.sh">bun</a>) JavaScript runtimes available, including Apple’s own <a href="https://developer.apple.com/documentation/javascriptcore">JavaScriptCore</a>.</p>

<p>But, another big advantage is control over installation and execution. Any language that requires static compilation will have to deal with code-signing. ExtensionKit makes things even more complex with sandboxing, app-bundled delivery, and approval. Things are just much simpler, and potentially much more end-user-friendly with something like JavaScript.</p>

<p>Now, of course ExtensionKit does have some pretty compelling features. It’s all Swift, providing easy access to all of Apple’s first-party APIs. Its remote view capabilities are really amazing. And, it offers a way for 3rd-party apps to integrate with each other in a way that is straightforward and well-supported. I’m sure all of these things are <em>possible</em> with another approach, but ExtensionKit makes them all easy.</p>

<h2 id="next-up">Next Up</h2>

<p>I certainly wasn’t expecting ExtensionKit, but I’m really happy it’s here. It’s made working on <a href="https://github.com/ChimeHQ/ChimeKit">ChimeKit</a> really fun, and we’re excited about what it makes possible.</p>

<p>ExtensionKit, and our use of it, turned out to be a pretty big topic. This is just an introduction, but hopefully its gotten you interested in learning more. Next up, we’ll be getting into details on the <a href="/blog/extensionkit-xpc">communication infrastructure</a> we used to build an ExtensionKit-based framework.</p>]]></content><author><name>Matt Massicotte</name></author><summary type="html"><![CDATA[High-level details on macOS Ventura's ExtensionKit and how we use it for ChimeKit]]></summary></entry><entry><title type="html">Extension are coming to Chime</title><link href="https://www.chimehq.com/blog/extensions" rel="alternate" type="text/html" title="Extension are coming to Chime" /><published>2022-08-09T00:00:00+00:00</published><updated>2022-08-09T00:00:00+00:00</updated><id>https://www.chimehq.com/blog/extensions</id><content type="html" xml:base="https://www.chimehq.com/blog/extensions"><![CDATA[<h1 id="extension-support">Extension Support</h1>

<p>Chime 2.0 is getting closer to a public beta. It will have a bunch of new features, but the really big news is support for <strong>extensions</strong>! The extension system is built with <a href="https://developer.apple.com/documentation/extensionkit">ExtensionKit</a>, a new API coming with macOS Ventura. Extensions can add language-level functionality, as well as in-app user interfaces, all built with Swift and SwiftUI.</p>

<figure class="image extensions">
  <img src="/assets/images/blog/extensions.svg" alt="Three puzzle pieces" />
  <figcaption></figcaption>
</figure>

<p>This is <strong>big</strong> - 3rd parties will be able to add capabilities to Chime!</p>

<h2 id="language-support">Language Support</h2>

<p>A few months ago, we added support for <a href="https://www.chimehq.com/blog/hello-ruby">Ruby</a>. That project was the big motivator for an internal extension system, especially because we’re planning to bring more languages to Chime. By chance, we were just really getting going with an official extension system when ExtensionKit was released during WWDC 2022.</p>

<p>Our base extension API can be used to provide all the language-level features that Chime supports today. It offers full integration with Chime’s project and document management model, sandboxing support, as well as a <a href="https://microsoft.github.io/language-server-protocol/">Language Server Protocol</a> abstraction to make wrapping an LSP server straightforward.</p>

<h2 id="app-hosted-views">App-Hosted Views</h2>

<p>One of the amazing capabilities of ExtensionKit is that is allows extensions to display a view within the hosting application. While Chime’s extension system was originally setup for languages, we just had to support this too because it’s cool. Chime 2.0 will support two different UI extension types.</p>

<h2 id="fixed-sidebars">Fixed Sidebars</h2>

<p>The first is a fixed trailing-edge sidebar. This view can be active for projects and documents, independently. This is a fairly standard approach among IDEs. But, what isn’t standard is this view can be constructed using SwiftUI, and supports all of its features - even animation.</p>

<h2 id="document-synced-views">Document-Synced Views</h2>

<p>The second type of UI extension is also a trailing-edge sidebar. But its height and scroll position are kept in sync with the text contents of an open text document. A generalized view that can coordinate with the source code enables a lot of powerful features.</p>

<h2 id="api-feedback">API Feedback</h2>

<p>Our core extension API is set, and we’ve now got lots of experience building with ExtensionKit. But, we still do have work to do for document-synced views. In particular, we’ve been having a hard time coming up with a reasonable API for determining text element positions and measurements. Chime makes extensive use of text layout information for its own UI, but extensions present some challenges, not the least of which is the inherent asynchronous interface.</p>

<p>We thought it might make more sense to open up our extension system early, to get some feedback and ideas on how these APIs should work. <a href="https://github.com/ChimeHQ/ChimeKit">ChimeKit</a> is available now, and should have enough information to get you going today.</p>

<p>We’re also posting a very early beta of Chime 2.0 (download link available on <a href="https://github.com/ChimeHQ/ChimeKit">this page</a>). It still has a number of issues, and isn’t quite ready for a general beta release. But, it’s definitely enough to try our extension support.</p>

<h2 id="what-do-you-think">What do you think?</h2>

<p>Chime 2.0 is an absolutely enormous release. It features a CLI tool and fuzzy-matching quick open, but extensions are what we are most excited about.</p>

<p>Our APIs are still evolving, so your input either as an extension developer or Chime user, would be incredibly helpful. If you are interested in Chime’s <a href="https://github.com/ChimeHQ/ChimeKit">extension system</a>, we’d love to <a href="https://www.chimehq.com/contact">hear</a> from you.</p>]]></content><author><name>Matt Massicotte</name></author><summary type="html"><![CDATA[Extension Support]]></summary></entry><entry><title type="html">Xcode Project Organization</title><link href="https://www.chimehq.com/blog/xcode-project-organization" rel="alternate" type="text/html" title="Xcode Project Organization" /><published>2022-06-16T00:00:00+00:00</published><updated>2022-06-16T00:00:00+00:00</updated><id>https://www.chimehq.com/blog/xcode-project-organization</id><content type="html" xml:base="https://www.chimehq.com/blog/xcode-project-organization"><![CDATA[<h1 id="xcode-project-organization">Xcode Project Organization</h1>

<p>I have a bad habit of under-investing in my project’s organization. I use Xcode groups a lot, but when it comes to local code, that’s usually as far as I go. I’ve been having great luck with using Swift packages for project-external dependencies, so I decided to try using them for local modules as well. I ended up going on quite a journey. Come along, let’s talk about organizing local code in Xcode!</p>

<figure class="image xcode_project">
  <img src="/assets/images/blog/xcode_project.svg" alt="Xcode project structure" />
  <figcaption></figcaption>
</figure>

<p>TL;DR: Local packages can work. But static/dynamic libraries give you nearly all the benefits with more control and should be seriously considered.</p>

<h2 id="targets">Targets</h2>

<p>We’re going to be talking about Xcode targets a lot. They represent the artifacts we produce, and are the most important parts. You might have a straight-forward setup with just one single app target. But, things can quickly get much more complex with extensions and services. And all of these can have complex relationships at both build- and link-time.</p>

<p>You cannot really get around Xcode targets. Apps, extensions, and many other components can only be created with them. But, pretty much everything else is up to you. You can even share source files across targets, so you can definitely get by with a very flat project structure. This might seem like the simplest approach, but will result in larger binaries, slower build times, and low modularization. You can definitely do better, and you’ll probably want to.</p>

<h2 id="benefits-of-modularization">Benefits of Modularization</h2>

<p>I think everyone can get behind smaller binaries and faster build times. But, modularization may not be something you’ve used a lot in your project. I think it’s very worthwhile, and probably one of the biggest benefits of more intentional project organization. Converting your app project into distinct modules really helps to think about and define the boundaries of your subsystems.  Swift’s <a href="https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html">access control</a> features give the tools to enforce those boundaries.</p>

<p>As I began modularizing my app, I immediately started finding surprising dependencies. I found a number of systems that were tangled up in ways that made no sense. It wasn’t always straightforward to fix, but I always found it incredibly satisfying to do. In all cases, I found the systems much better factored afterwards.</p>

<h2 id="local-packages">Local Packages</h2>

<p>I had always thought about Swift packages as exclusively external to a project. But, they actually can be used inside a project as well. For the most part, they behave exactly like an external package. You can add them as dependencies to your Xcode targets, and can link against them. All that really changes is their contents become editable.</p>

<p>One really interesting thing about working with local packages is the Xcode project file itself references just the top-level package directory. This means any file operations within the package do not produce modifications to the containing <code class="language-plaintext highlighter-rouge">.xcodeproj</code> file. This makes for less chance for merge conflicts. Xcode also gives them a cool icon in the project navigator 📦.</p>

<h2 id="dependencies">Dependencies</h2>

<p>Local packages can define dependencies to both external and other local packages. And, Xcode targets can depend on 
packages. However, it is really important to note that the reverse is not true. A package <em>cannot</em> depend on an Xcode target. This might be a non-issue for you, especially if you are exclusively using local packages. But, if you have any existing static/dynamic library targets, this could be a show-stopper.</p>

<h2 id="linking">Linking</h2>

<p>By default, SPM packages do not specify what kind of library they should build. Without any configuration, this is left up to the build system. In many cases, this works just fine. However, this can result in some really surprising behavior. When you link more than one Xcode target against the same package, local or remote, Xcode will <em>automatically</em> produce a dynamic framework.</p>

<p>While this works, it isn’t always what you actually want. Dynamic libraries have a non-trivial impact on application launch time. One or two isn’t too much to worry about. But as you start adding more packages, you can easily end up with a very serious launch time penalty. Worse, because it’s totally automatic, you really have to go look in the final .app bundle to get an idea of what is going on.</p>

<p>I really like to avoid implicit behavior like this. It is possible to explicitly set the library type in a Package.swift, and I thought this would help me control for this situation. While it can help, it can also cause more problems. Xcode does not allow you to link the same static library into multiple targets. Despite being done intentionally, it won’t just produce a warning - it is a build error. I get that it is a potentially sub-optimal configuration, but disallowing it entirely seems too severe.</p>

<p><strong>Update</strong>: The <code class="language-plaintext highlighter-rouge">DISABLE_DIAMOND_PROBLEM_DIAGNOSTIC</code> build setting can be used to influence Xcode’s SPM linking and artifact generation behavior. Check this out if packages aren’t working as you’d like.</p>

<h2 id="libraries-as-an-alternative">Libraries as an Alternative</h2>

<p>At this point, I was getting deeply frustrated with local packages. They offered me the modularization I was looking for, but they took away too much control over the build process. I really didn’t want to go back to a flat project, so I decided to try Xcode library targets.</p>

<p>In many respects, library targets and local packages are equivalent. But libraries, being first-class Xcode targets, have many benefits. They can participate in the Xcode dependency system. They support all of Xcode’s build settings, and those settings can be defined, and shared, in xcconfig files. And, by their very nature, they offer explicit control over the final artifact generation. No more static/dynamic ambiguity.</p>

<p>I was really loving using static libraries to define modules! There are, as far as I can tell, only three downsides to explicit targets. They are marginally more difficult to open source as packages down the road. Their structure lives entirely within the xcodeproj file. But the third issue was a heartbreaker: they have a critical interoperability issue with <em>certain</em> SPM modules.</p>

<h2 id="non-swift-package-dependencies">Non-Swift Package Dependencies</h2>

<p>Here’s the situation: you have an Xcode target that depends on a package, but does <em>not</em> link against it. If that package itself has non-Swift package dependencies, it will fail to build in Xcode. This bug will only happen with all three of these conditions. But, if you are unlucky enough to be in this situation, you are stuck.</p>

<p>Or, so I thought!</p>

<p>By total luck, I found <a href="https://pfandrade.me/blog/mixing-swift-objective-c-spm-and-static-frameworks/">this article</a> by <a href="https://twitter.com/pfandrade_">Paulo</a>. In it, he outlines a nearly identical issue and also shares a workaround. It works in this case too!</p>

<p>For any target that happens to be hit by this issue, it can be fixed with this <code class="language-plaintext highlighter-rouge">OTHER_SWIFT_FLAGS</code> build setting:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-Xcc -fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/Target_That_Fails_To_Build.modulemap
</code></pre></div></div>

<p>This is really, really annoying. But, it’s also probably a relatively rare situation. And, in my case, getting back the ability to control the linking and framework generation behavior was absolutely essential. (Filed as FB10032666)</p>

<h2 id="the-verdict">The Verdict</h2>

<p>Ok, so let’s get right down to it. Should you go with local packages? If you have a simple project, with just one single app target, it can definitely work. But, if you are embedding other binaries in your app, like extensions or services, I think you may want to avoid them. Bug notwithstanding, static libraries offer nearly all of the benefits, without giving up control that can become essential for more complex projects.</p>

<p>As I discovered during my journey, going back and forth actually isn’t too painful. So, you can always start with packages if that feels best, and then change when/if needed. Even a hybrid approach is possible. Regardless which option you decide on, project modularization is really worth the effort. The faster build times are just icing on the cake. Having APIs with clear and intentional boundaries is such an enormous win, even for small projects. This is definitely something you should look at if you aren’t doing it today.</p>]]></content><author><name>Matt Massicotte</name></author><summary type="html"><![CDATA[Xcode Project Organization]]></summary></entry><entry><title type="html">Hello Ruby!</title><link href="https://www.chimehq.com/blog/hello-ruby" rel="alternate" type="text/html" title="Hello Ruby!" /><published>2022-05-12T00:00:00+00:00</published><updated>2022-05-12T00:00:00+00:00</updated><id>https://www.chimehq.com/blog/hello-ruby</id><content type="html" xml:base="https://www.chimehq.com/blog/hello-ruby"><![CDATA[<h1 id="hello-ruby">Hello Ruby</h1>

<p>When we first started planning work for Chime, the word “focus” came up a lot. We wanted to build something that blended modern IDE features with a minimalist user interface. Our goal was something you could just open and use, with no configuration required. To make this feasible, we had to restrict ourselves to just one language. Go was an obvious choice – we were already using it for service-side work.</p>

<figure class="image hello_ruby">
  <img src="/assets/images/blog/hello_ruby.png" alt="Highlighting, Diagnostics and Type-Based Completions" />
  <figcaption></figcaption>
</figure>

<p>But, Chime has evolved quite a bit since then. We’ve spent a lot of time iterating on the app, both in ways that are user-facing and internal. That work has had the nice side-effect of making it easier to expand Chime to support other languages. And, once we realized we could it, we started to feel like it was silly not to. So, we added support for Ruby!</p>

<h2 id="how-it-works">How it Works</h2>

<p>As it turns out, Ruby still ended up requiring a lot of work to support well. Everything was just different enough. Even something that might seem straightforward, like indentation, was a big technical challenge. Ruby projects can also have complex dependencies on configuration and the environment. Chime does its best to integrate with <a href="https://bundler.io">Bundler</a> and your shell setup.</p>

<p>To drive some features, Chime uses the <a href="https://solargraph.org">Solargraph</a> language server and <a href="https://rubocop.org">RuboCop</a>. If you have used these tools with your project in the past, Chime should be able to just pick up your existing configuration. If not, we’ve tried to get things working for most projects transparently. We aren’t including support for <a href="https://sorbet.org">Sorbet</a> yet, but please let us know if this is something you’d be interested in.</p>

<h2 id="more-open-source">More Open Source</h2>

<p>Bringing a new language to Chime is a big deal. But, I’m especially happy with how much of the work here has been open sourced. We’ve done lots of work on our Language Server Protocol <a href="https://github.com/ChimeHQ/LanguageClient">client</a>. And, our tree-sitter <a href="https://github.com/ChimeHQ/SwiftTreeSitter">client</a> has seen a bunch of enhancements.</p>

<p>We’ve also released two entirely new projects! <a href="https://github.com/ChimeHQ/TextFormation">TextFormation</a> is a rule-based system for typing completions and whitespace control. And, <a href="https://github.com/ChimeHQ/Neon">Neon</a> is a system for working with language syntax built for efficiency.</p>

<p>I’ve being using Ruby for a very long time, so as part of testing I also worked on a little Rake <a href="https://github.com/mattmassicotte/rake-file-content">file task extension</a>.</p>

<h2 id="try-it-out">Try it Out!</h2>

<p>Adding a new language to Chime is a big thing for us, and we’re pretty excited. And it’s been a lot of fun to use Chime to actually write some Ruby! Chime 1.7 is available to <a href="/download">download</a> today and includes beta support for Ruby. Of course, our commitment to Go isn’t changing at all. We’ll still be building Go-specific features, we’ll now just be doing Ruby-specific stuff too.</p>

<p>So, give it a shot, and please let us know what you think!</p>]]></content><author><name>Matt Massicotte</name></author><summary type="html"><![CDATA[Hello Ruby]]></summary></entry><entry><title type="html">MeterReporter: Lightweight MetricKit Diagnostics</title><link href="https://www.chimehq.com/blog/meterreporter" rel="alternate" type="text/html" title="MeterReporter: Lightweight MetricKit Diagnostics" /><published>2022-03-29T00:00:00+00:00</published><updated>2022-03-29T00:00:00+00:00</updated><id>https://www.chimehq.com/blog/meterreporter</id><content type="html" xml:base="https://www.chimehq.com/blog/meterreporter"><![CDATA[<h1 id="meterreporter-lightweight-metrickit-diagnostics">MeterReporter: Lightweight MetricKit Diagnostics</h1>

<p>MetricKit is an incredible source of diagnostic information for your app. Not only can it get you some information that is inaccesible otherwise, but it does so in a way that respects the user’s privacy decisions. Unfortunately, it’s actually quite hard to use in practice. At a minimum, you need to capture the information, send it out to some server, and then process it into a human-readable form. But, each of this steps turns out to be a real pain.</p>

<p><a href="https://github.com/ChimeHQ/MeterReporter">MeterReporter</a> is a open source library that makes getting at MetricKit data easier.</p>

<h2 id="uncaught-exceptions">Uncaught Exceptions</h2>

<p>One of the first big shortcomings I discovered about MetricKit-based crash reporting was a lack of uncaught exception information. My suspicion is MetricKit has a strict policy against capturing application-generated data. And, I also believe this won’t change in the future, for two reasons. First, any system that sends back in-app data could leak potentially private user information. Second, exceptions are playing less and less of an important role in application development on Apple platforms, so the need to address this is decreasing.</p>

<p>While their exception use might be on the decline, they are still quite common and the extra information can be helpful. The exception name and reason can provide invaluable context for understanding why the exception occurred. And, the throw-site stack trace, which is not necessarily the same as the crash site, can be critical. In particular, because of generally poor exception handling <a href="sad-state-of-exceptions">behavior in AppKit</a> (and some other macOS frameworks), it’s vital for macOS developers.</p>

<p><a href="https://github.com/ChimeHQ/Meter">Meter</a> includes a simple facility for including runtime exception information in a MetricKit payload. This is a fully automated process for iOS apps. MeterReporter also includes a way to capture uncaught exceptions in an AppKit app. It isn’t entirely automatic, but at least it’s possible.</p>

<h2 id="symbolication">Symbolication</h2>

<p>File/line information for your apps’ frames are usually extremely useful, but even function names go a very long way. Without either, you probably aren’t going to get very far. The process of adding this information to a report is called symbolication. MetricKit leaves symbolication completely up to you. You’ll typically use a dSYM from your app to symbolicate your app’s frames. This is annoying and laborious, but at least it’s possible.</p>

<p>There will also be a bunch of OS frames in a report. Those are technically possible to symbolicate, but it’s much more involved. You can only symbolicate against the exact version of a binary that was loaded at the time of the crash. This changes for every OS release, and often also across devices. This means, even for just iOS 15 releases, you’re looking at <strong>thousands</strong> of possible variants. And, even if you do have these binaries, actually performing the symbolication is quite challenging. There’s a <a href="https://github.com/ChimeHQ/SwiftCoreSymbolication">Swift library</a> available that can do it, but it’s really not something most people are going to be willing to deal with.</p>

<p>This is another place where Meter can help. It includes a system for symbolicating both app and OS frames on the device before the data is submitted. This system makes use of the binaries on the affected device to look up symbol information. It is not perfect, and the current implementation doesn’t always produce a result on iOS. But, it is <strong>far</strong> simpler than the alternative.</p>

<h2 id="transmission">Transmission</h2>

<p>The last piece of the puzzle may seem like the simplest - just getting the data back to a server you control. A simple <code class="language-plaintext highlighter-rouge">NSURLSession</code> setup can certainly work here. But, MeterReporter uses a much more robust system, backed by a library called <a href="https://github.com/ChimeHQ/Wells">Wells</a>. Wells supports background uploads and a robust retry mechanism. These are both surprisingly difficult because they have to work across application launches and cannot persist state in memory. But, the payoff is reliable and power-efficient diagnostics reporting.</p>

<p>By default, MeterReporter will issue an HTTP <code class="language-plaintext highlighter-rouge">PUT</code> with the data. You can easily configure the endpoint that it will point the request to. And, it will also include a few headers with some metadata about the report. You’ll get the platform, a unique report identifier, and your application’s bundle identifier. Everything your server should need to process the data.</p>

<h2 id="conclusion">Conclusion</h2>

<p>I get that most developers are going to opt for a full-featured in-process diagnostics system. There are many out there, they are high-quality, group related crashes, and take care of lots of details for you. Or, perhaps you just want to go with Apple’s own diagnostics, which has gotten consistently better each year. You get Apple’s system for free, so you could easily just use both. But, there are still some compelling reasons for going the MetricKit route.</p>

<p>First and foremost, Apple’s system only works for App Store-delivered apps. So, if you have a macOS app that lives outside of the App Store (ahem), Apple’s system is just not an option. MetrickKit also has a strong focus on respecting the user’s privacy. It can capture data, including many types of crashes that are impossible to detect using an in-process system. And, it is an incredibly lightweight solution that imposes basically no overhead in your app.</p>

<p>Yes, you do need to run a server to accept the reports. But, it’s very doable, and at very low cost. And, if MetricKit sounds like something you might want for your application, <a href="https://github.com/ChimeHQ/MeterReporter">MeterReporter</a> might be just the ticket.</p>]]></content><author><name>Matt Massicotte</name></author><summary type="html"><![CDATA[MeterReporter: Lightweight MetricKit Diagnostics]]></summary></entry><entry><title type="html">Supporting older SDK versions with Swift</title><link href="https://www.chimehq.com/blog/swift-and-old-sdks" rel="alternate" type="text/html" title="Supporting older SDK versions with Swift" /><published>2022-02-28T00:00:00+00:00</published><updated>2022-02-28T00:00:00+00:00</updated><id>https://www.chimehq.com/blog/swift-and-old-sdks</id><content type="html" xml:base="https://www.chimehq.com/blog/swift-and-old-sdks"><![CDATA[<h1 id="supporting-older-sdk-versions-with-swift">Supporting Older SDK Versions with Swift</h1>

<h2 id="its-possible-but-its-a-pain">It’s possible, but it’s a pain.</h2>

<p>The Swift Package Manager has made it easier than ever to make a library with wide compatibility. By default, SPM packages even support Linux! However, it can be quite tricky to support different major iOS and macOS SDK versions. Swift provides a number of conditional compilation conditions that can be used to help. But, there is currently no way to determine what SDK you are building against.</p>

<p>Or is there?</p>

<h2 id="the-initial-problem">The Initial Problem</h2>

<p>The type <code class="language-plaintext highlighter-rouge">NSTextLocation</code> was introduced in iOS 15/macOS 12. Let’s say you want to add an extension on this type. Your first pass might look something like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#if os(macOS)</span>
<span class="kd">import</span> <span class="kt">AppKit</span>
<span class="cp">#elseif os(iOS) || os(tvOS)</span>
<span class="kd">import</span> <span class="kt">UIKit</span>
<span class="cp">#endif</span>

<span class="kd">@available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">15.0</span><span class="p">,</span> <span class="n">macOS</span> <span class="mf">12.0</span><span class="p">,</span> <span class="n">tvOS</span> <span class="mf">15.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
<span class="kd">public</span> <span class="kd">extension</span> <span class="kt">NSTextLocation</span> <span class="p">{</span>
    <span class="o">...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This is a pretty good start. You’ve got the conditional <code class="language-plaintext highlighter-rouge">import</code> statements, and you’ve used the <code class="language-plaintext highlighter-rouge">@available</code> directive to indicate the runtime availability. However, this code still has some compatibility issues.</p>

<h2 id="using-available">Using @available</h2>

<p>As written, this code will fail to build for watchOS. And, perhaps less obviously, will also fail to build on Linux. I think maintaining broad compatibility is a good general goal, so let’s try to do better.</p>

<p>You might be tempted, as I was at first, to try something like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">15.0</span><span class="p">,</span> <span class="n">macOS</span> <span class="mf">12.0</span><span class="p">,</span> <span class="n">tvOS</span> <span class="mf">15.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
<span class="kd">@available</span><span class="p">(</span><span class="n">watchOS</span><span class="p">,</span> <span class="n">unavailable</span><span class="p">)</span>
<span class="kd">@available</span><span class="p">(</span><span class="kt">Linux</span><span class="p">,</span> <span class="n">unavailable</span><span class="p">)</span>
<span class="kd">public</span> <span class="kd">extension</span> <span class="kt">NSTextLocation</span> <span class="p">{</span>
    <span class="o">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It is true that this extension should be marked unavailable in these conditions. However, the thing to keep in mind is <code class="language-plaintext highlighter-rouge">@available</code> controls <strong>runtime</strong> availability. So, while these directives are appropriate, the reference to <code class="language-plaintext highlighter-rouge">NSTextLocation</code> still won’t be visibile at <strong>compile</strong> time on watchOS or Linux.</p>

<h2 id="more-conditional-compilation">More Conditional Compilation</h2>

<p>Just like with the <code class="language-plaintext highlighter-rouge">import</code>, we need to control what code the compiler sees, and we have to do that with conditional compilation statements. Luckily, this is a pretty straightforward thing to do.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#if os(macOS)</span>
<span class="kd">import</span> <span class="kt">AppKit</span>
<span class="cp">#elseif os(iOS) || os(tvOS)</span>
<span class="kd">import</span> <span class="kt">UIKit</span>
<span class="cp">#endif</span>

<span class="cp">#if os(macOS) || os(iOS) || os(tvOS)</span>

<span class="kd">@available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">15.0</span><span class="p">,</span> <span class="n">macOS</span> <span class="mf">12.0</span><span class="p">,</span> <span class="n">tvOS</span> <span class="mf">15.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
<span class="kd">public</span> <span class="kd">extension</span> <span class="kt">NSTextLocation</span> <span class="p">{</span>
    <span class="o">...</span>
<span class="p">}</span>

<span class="cp">#endif</span>
</code></pre></div></div>

<p>The fix is a little unsightly, perhaps, but it works. We’re now using conditional compilation to control the visibility of the extension itself. This is critical to prevent the compiler from running into the <code class="language-plaintext highlighter-rouge">NSTextLocation</code> type when it hasn’t been defined. You can always assume that conditional compilation around <code class="language-plaintext highlighter-rouge">import</code> statements means you need it other places too.</p>

<h2 id="older-xcodesdk">Older Xcode/SDK</h2>

<p>At this point, I wouldn’t blame you for calling it a day. This code will compile on all platforms that Swift supports, even Windows! But, there’s a very subtle issue still lurking here - older SDKs.</p>

<p>This code will <strong>only</strong> compile with a version of Xcode that ships with the iOS 15/mac 12.0 SDK. As written, this will fail to build for Xcode 12. Typically, this isn’t a huge problem. Developers adopt new Xcode versions quickly…</p>

<p>…except when new SDKs are released in beta!</p>

<p>This phenomenon strikes around WWDC, and while rare, has also come up a few other times. If you maintain a library and want to start working with new types in the SDK, it will affect you. As far as I know, there is no nice way of handling this situation. Some might opt for a special branch of the code, and that may be the most sensible option. But, there is another tool we can use.</p>

<h2 id="sdk-conditionals">SDK conditionals</h2>

<p>What we really want is to conditionalize our code not just on platform, but <strong>SDK version</strong> as well. The problem is, as of writing, Swift does not support that. Swift offers exactly five conditional compilation conditions: <code class="language-plaintext highlighter-rouge">os()</code>, <code class="language-plaintext highlighter-rouge">arch()</code>, <code class="language-plaintext highlighter-rouge">swift()</code>, <code class="language-plaintext highlighter-rouge">compiler()</code>, <code class="language-plaintext highlighter-rouge">canImport()</code>, and <code class="language-plaintext highlighter-rouge">targetEnvironment()</code>.</p>

<p>We can use <code class="language-plaintext highlighter-rouge">compiler()</code> as a proxy for SDK version. It’s not pretty, but gets the job done. Here’s how:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#if (os(macOS) || os(iOS) || os(tvOS)) &amp;&amp; compiler(&gt;=5.5)</span>
</code></pre></div></div>

<p>We’re making use of the fact that we know Xcode 13, the one with the SDK we need, shipped with Swift 5.5. The last version of Xcode 12 shipped with Swift 5.4.2. For reference, Xcode 14 uses Swift 5.7.</p>

<p>Note that we don’t want to use <code class="language-plaintext highlighter-rouge">swift()</code>, as that value represents the <strong>language</strong> version. We need the <strong>compiler</strong> version here.</p>

<p>Also, don’t forget about that <code class="language-plaintext highlighter-rouge">canImport()</code> condition. It wasn’t helpful for us in this particular example, but it’s very handy when a new framework is introduced.</p>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>It’s surprisingly tricky to keep Swift packages compatible with all the platforms that the language actually supports. And, while I think it’s a nice thing to do, I’m not advocating for huge efforts. Just a little understanding of conditional compilation and some care can go a long way. Plus, it just feels good to see all those green checkboxes over at the <a href="https://swiftpackageindex.com">Swift Package Index</a>.</p>

<p>I won’t lie, I don’t love this <code class="language-plaintext highlighter-rouge">compiler()</code> trick. I’m always happy when I get to remove it. But, I think it’s a handy thing to keep in your toolbox, especially around WWDC season.</p>]]></content><author><name>Matt Massicotte</name></author><summary type="html"><![CDATA[Supporting Older SDK Versions with Swift]]></summary></entry></feed>