<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>angelo on write.as</title>
    <link>https://write.as/angelo/</link>
    <description>Building Swift things against the Write.as API</description>
    <pubDate>Tue, 23 Jun 2026 07:35:14 +0000</pubDate>
    <item>
      <title>WriteFreely for iOS 1.0.18</title>
      <link>https://write.as/angelo/writefreely-for-ios-1-0-18?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[Launch policy fixes and iOS 18 readiness!&#xA;!--more--&#xA;&#xA;WriteFreely for iOS has been updated with a couple of fixes:&#xA;&#xA;Fixed&#xA;&#xA;Fixed a bug that would close the active post when you’d leave and come back to the app.&#xA;Improved compatibility with the upcoming iOS 18 launch.&#xA;&#xA;Download it on the App Store now, and leave a review if you’re enjoying the app!&#xA;&#xA;WriteFreely for iOS is open-source. Want to contribute? Check it out on GitHub!&#xA;&#xA;---&#xD;&#xA;&#xD;&#xA;Enter your email to subscribe to updates:&#xD;&#xA;&#xD;&#xA;!--emailsub--&#xD;&#xA;&#xD;&#xA;You can also subscribe via RSS or follow @angelo@write.as on Mastodon.]]&gt;</description>
      <content:encoded><![CDATA[<p>Launch policy fixes and iOS 18 readiness!
</p>

<p>WriteFreely for iOS has been updated with a couple of fixes:</p>

<h2 id="fixed" id="fixed">Fixed</h2>
<ul><li>Fixed a bug that would close the active post when you’d leave and come back to the app.</li>
<li>Improved compatibility with the upcoming iOS 18 launch.</li></ul>

<p><a href="https://apps.apple.com/app/writefreely/id1531530896">Download it on the App Store now</a>, and leave a review if you’re enjoying the app!</p>

<p><strong>WriteFreely for iOS is open-source.</strong> Want to contribute? <a href="https://github.com/writefreely/writefreely-swiftui-multiplatform">Check it out on GitHub</a>!</p>

<hr/>

<p>Enter your email to subscribe to updates:</p>



<p>You can also subscribe <a href="https://write.as/angelo/feed/">via RSS</a> or follow <code><a href="/@/angelo@write.as" class="u-url mention">@<span>angelo@write.as</span></a></code> on Mastodon.</p>
]]></content:encoded>
      <guid>https://write.as/angelo/writefreely-for-ios-1-0-18</guid>
      <pubDate>Mon, 19 Aug 2024 10:14:22 +0000</pubDate>
    </item>
    <item>
      <title>WriteFreely for iOS 1.0.17</title>
      <link>https://write.as/angelo/writefreely-for-ios-1-0-17?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[Refined offline mode and better compatibility with iOS 17!&#xA;!--more--&#xA;&#xA;WriteFreely for iOS has been updated with a couple of fixes:&#xA;&#xA;Fixed&#xA;&#xA;Improved the offline experience to let you know when you&#39;re not connected to a network (no more inscrutable WFError -2 messages)!&#xA;Fixed the navigation experience with iOS 17.&#xA;&#xA;Download it on the App Store now, and leave a review if you’re enjoying the app!&#xA;&#xA;---&#xD;&#xA;&#xD;&#xA;Enter your email to subscribe to updates:&#xD;&#xA;&#xD;&#xA;!--emailsub--&#xD;&#xA;&#xD;&#xA;You can also subscribe via RSS or follow @angelo@write.as on Mastodon.]]&gt;</description>
      <content:encoded><![CDATA[<p>Refined offline mode and better compatibility with iOS 17!
</p>

<p>WriteFreely for iOS has been updated with a couple of fixes:</p>

<h2 id="fixed" id="fixed">Fixed</h2>
<ul><li>Improved the offline experience to let you know when you&#39;re not connected to a network (no more inscrutable <code>WFError -2</code> messages)!</li>
<li>Fixed the navigation experience with iOS 17.</li></ul>

<p><a href="https://apps.apple.com/app/writefreely/id1531530896">Download it on the App Store now</a>, and leave a review if you’re enjoying the app!</p>

<hr/>

<p>Enter your email to subscribe to updates:</p>



<p>You can also subscribe <a href="https://write.as/angelo/feed/">via RSS</a> or follow <code><a href="/@/angelo@write.as" class="u-url mention">@<span>angelo@write.as</span></a></code> on Mastodon.</p>
]]></content:encoded>
      <guid>https://write.as/angelo/writefreely-for-ios-1-0-17</guid>
      <pubDate>Mon, 22 Jan 2024 12:38:37 +0000</pubDate>
    </item>
    <item>
      <title>WriteFreely ❤️ Hacktoberfest 2023</title>
      <link>https://write.as/angelo/writefreely-hacktoberfest-2023?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[We&#39;re participating in Hacktoberfest X!&#xA;&#xA;During the month of October this year, the WriteFreely Swift repositories will be accepting pull requests that count towards your contributions for the month-long event.&#xA;&#xA;What&#39;s WriteFreely?&#xA;&#xA;WriteFreely is the open source platform for building a writing space on the web. It powers over 500,000 blogs on Write.as, requires few resources to run, and publishes to the Fediverse.&#xA;&#xA;What Are The WriteFreely Swift Repositories?&#xA;&#xA;There are two repositories that make up the WriteFreely iOS and macOS app:&#xA;&#xA;The writefreely-swift repository is the source for the Swift package that wraps the WriteFreely API for fetching and publishing posts to your WriteFreely instance (including Write.as).&#xA;The writefreely-swiftui-multiplatform repository is the source for the iOS/iPadOS, and macOS client app. It&#39;s a SwiftUI multiplatform app, and so there&#39;s a lot of code shared between the various platforms, but we dip into UIKit and AppKit where necessary.&#xA;&#xA;How to Participate&#xA;&#xA;Register between September 26 and October 31 on the Hacktoberfest website.&#xA;Check out the writefreely-swift and writefreely-swiftui-multiplatform repositories and review their Code of Conduct and Contributing docs in the root folder.&#xA;Look for issues tagged #hacktoberfest and claim one by assigning it to yourself.&#xA;Open a PR and request a review from @writefreely/swift-maintainers.&#xA;&#xA;If your PR meets the criteria set out by the organizers, it&#39;ll be merged in to the repo.&#xA;&#xA;We&#39;ve set up pen.writefree.ly as a demo site for testing. Happy coding!&#xA;&#xA;!--references--&#xA;[1]: https://hacktoberfest.com/&#xA;[2]: https://writefreely.org/&#xA;[3]: https://write.as/&#xA;[4]: https://github.com/writefreely/writefreely-swift/&#xA;[5]: https://github.com/writefreely/writefreely-swiftui-multiplatform/&#xA;[6]: https://hacktoberfest.com/participation/#pr-mr-details&#xA;[7]: https://pen.writefree.ly/&#xA;&#xA;---&#xD;&#xA;&#xD;&#xA;Enter your email to subscribe to updates:&#xD;&#xA;&#xD;&#xA;!--emailsub--&#xD;&#xA;&#xD;&#xA;You can also subscribe via RSS or follow @angelo@write.as on Mastodon.]]&gt;</description>
      <content:encoded><![CDATA[<p>We&#39;re participating in <a href="https://hacktoberfest.com/">Hacktoberfest X</a>!</p>

<p>During the month of October this year, the WriteFreely Swift repositories will be accepting pull requests that count towards your contributions for the month-long event.</p>

<h2 id="what-s-writefreely" id="what-s-writefreely">What&#39;s WriteFreely?</h2>

<p><a href="https://writefreely.org/">WriteFreely</a> is the open source platform for building a writing space on the web. It powers over 500,000 blogs on <a href="https://write.as/">Write.as</a>, requires few resources to run, and publishes to the Fediverse.</p>

<h2 id="what-are-the-writefreely-swift-repositories" id="what-are-the-writefreely-swift-repositories">What Are The WriteFreely Swift Repositories?</h2>

<p>There are two repositories that make up the WriteFreely iOS and macOS app:</p>
<ul><li>The <a href="https://write.as/">writefreely-swift</a> repository is the source for the Swift package that wraps the WriteFreely API for fetching and publishing posts to your WriteFreely instance (including Write.as).</li>
<li>The <a href="https://github.com/writefreely/writefreely-swift/">writefreely-swiftui-multiplatform</a> repository is the source for the iOS/iPadOS, and macOS client app. It&#39;s a SwiftUI multiplatform app, and so there&#39;s a lot of code shared between the various platforms, but we dip into UIKit and AppKit where necessary.</li></ul>

<h2 id="how-to-participate" id="how-to-participate">How to Participate</h2>
<ol><li>Register between September 26 and October 31 <a href="https://hacktoberfest.com/">on the Hacktoberfest website</a>.</li>
<li>Check out the <a href="https://github.com/writefreely/writefreely-swift/">writefreely-swift</a> and <a href="https://github.com/writefreely/writefreely-swiftui-multiplatform/">writefreely-swiftui-multiplatform</a> repositories and review their Code of Conduct and Contributing docs in the root folder.</li>
<li>Look for issues tagged <code>#hacktoberfest</code> and claim one by assigning it to yourself.</li>
<li>Open a PR and request a review from <code>@writefreely/swift-maintainers</code>.</li></ol>

<p>If your PR <a href="https://hacktoberfest.com/participation/#pr-mr-details">meets the criteria set out by the organizers</a>, it&#39;ll be merged in to the repo.</p>

<p>We&#39;ve set up <a href="https://pen.writefree.ly/">pen.writefree.ly</a> as a demo site for testing. Happy coding!</p>



<hr/>

<p>Enter your email to subscribe to updates:</p>



<p>You can also subscribe <a href="https://write.as/angelo/feed/">via RSS</a> or follow <code><a href="/@/angelo@write.as" class="u-url mention">@<span>angelo@write.as</span></a></code> on Mastodon.</p>
]]></content:encoded>
      <guid>https://write.as/angelo/writefreely-hacktoberfest-2023</guid>
      <pubDate>Fri, 06 Oct 2023 20:31:07 +0000</pubDate>
    </item>
    <item>
      <title>WriteFreely for iOS 1.0.16</title>
      <link>https://write.as/angelo/writefreely-for-ios-1-0-16?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[Full-text search, refresh from server, and more!&#xA;!--more--&#xA;&#xA;WriteFreely for iOS has been updated with a couple of new features and a bugfix:&#xA;&#xA;Added&#xA;&#xA;Added a way to search for text across all posts.&#xA;Added a way to refresh an edited post from the server copy.&#xA;&#xA;Changed&#xA;&#xA;The app now reverts a post from edited to published status if you undo your changes.&#xA;&#xA;Fixed&#xA;&#xA;Fixed a bug where posts moved from one blog to another on a different client didn&#39;t update in the app.&#xA;&#xA;Download it on the App Store now, and leave a review if you’re enjoying the app!&#xA;&#xA;---&#xD;&#xA;&#xD;&#xA;Enter your email to subscribe to updates:&#xD;&#xA;&#xD;&#xA;!--emailsub--&#xD;&#xA;&#xD;&#xA;You can also subscribe via RSS or follow @angelo@write.as on Mastodon.]]&gt;</description>
      <content:encoded><![CDATA[<p>Full-text search, refresh from server, and more!
</p>

<p>WriteFreely for iOS has been updated with a couple of new features and a bugfix:</p>

<h2 id="added" id="added">Added</h2>
<ul><li>Added a way to search for text across all posts.</li>
<li>Added a way to refresh an edited post from the server copy.</li></ul>

<h2 id="changed" id="changed">Changed</h2>
<ul><li>The app now reverts a post from edited to published status if you undo your changes.</li></ul>

<h2 id="fixed" id="fixed">Fixed</h2>
<ul><li>Fixed a bug where posts moved from one blog to another on a different client didn&#39;t update in the app.</li></ul>

<p><a href="https://apps.apple.com/app/writefreely/id1531530896">Download it on the App Store now</a>, and leave a review if you’re enjoying the app!</p>

<hr/>

<p>Enter your email to subscribe to updates:</p>



<p>You can also subscribe <a href="https://write.as/angelo/feed/">via RSS</a> or follow <code><a href="/@/angelo@write.as" class="u-url mention">@<span>angelo@write.as</span></a></code> on Mastodon.</p>
]]></content:encoded>
      <guid>https://write.as/angelo/writefreely-for-ios-1-0-16</guid>
      <pubDate>Mon, 24 Jul 2023 01:33:06 +0000</pubDate>
    </item>
    <item>
      <title>WriteFreely for iOS v1.0.15</title>
      <link>https://write.as/angelo/writefreely-for-ios-v1-0-15?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[WriteFreely for iOS 1.0.15 has been approved for release on the App Store —in record time (ten minutes!)— and fixes a crash-on-launch bug when there were no existing posts in the app. Get it now!&#xA;&#xA;---&#xD;&#xA;&#xD;&#xA;Enter your email to subscribe to updates:&#xD;&#xA;&#xD;&#xA;!--emailsub--&#xD;&#xA;&#xD;&#xA;You can also subscribe via RSS or follow @angelo@write.as on Mastodon.]]&gt;</description>
      <content:encoded><![CDATA[<p>WriteFreely for iOS 1.0.15 has been approved for release on the App Store —in record time (ten minutes!)— and fixes a crash-on-launch bug when there were no existing posts in the app. <a href="https://apps.apple.com/app/writefreely/id1531530896">Get it now</a>!</p>

<hr/>

<p>Enter your email to subscribe to updates:</p>



<p>You can also subscribe <a href="https://write.as/angelo/feed/">via RSS</a> or follow <code><a href="/@/angelo@write.as" class="u-url mention">@<span>angelo@write.as</span></a></code> on Mastodon.</p>
]]></content:encoded>
      <guid>https://write.as/angelo/writefreely-for-ios-v1-0-15</guid>
      <pubDate>Sun, 15 Jan 2023 21:35:52 +0000</pubDate>
    </item>
    <item>
      <title>WriteFreely for iOS v1.0.14 now available</title>
      <link>https://write.as/angelo/writefreely-for-ios-v1-0-14-now-available?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[For reasons I&#39;m still investigating, I started getting crash-on-launch reports a couple of weeks back on v1.0.13, specifically if you didn&#39;t have any posts stored within the app.&#xA;&#xA;We pushed an emergency update to the App Store to work around this, while I work on the fix.&#xA;&#xA;---&#xD;&#xA;&#xD;&#xA;Enter your email to subscribe to updates:&#xD;&#xA;&#xD;&#xA;!--emailsub--&#xD;&#xA;&#xD;&#xA;You can also subscribe via RSS or follow @angelo@write.as on Mastodon.]]&gt;</description>
      <content:encoded><![CDATA[<p>For reasons I&#39;m still investigating, I started getting <a href="https://github.com/writefreely/writefreely-swiftui-multiplatform/issues/235">crash-on-launch reports</a> a couple of weeks back on v1.0.13, specifically if you didn&#39;t have any posts stored within the app.</p>

<p>We pushed an emergency update to the App Store to work around this, while I work on the fix.</p>

<hr/>

<p>Enter your email to subscribe to updates:</p>



<p>You can also subscribe <a href="https://write.as/angelo/feed/">via RSS</a> or follow <code><a href="/@/angelo@write.as" class="u-url mention">@<span>angelo@write.as</span></a></code> on Mastodon.</p>
]]></content:encoded>
      <guid>https://write.as/angelo/writefreely-for-ios-v1-0-14-now-available</guid>
      <pubDate>Sun, 18 Dec 2022 23:00:19 +0000</pubDate>
    </item>
    <item>
      <title>WriteFreely v1.0.11: Ready For iOS 16</title>
      <link>https://write.as/angelo/writefreely-v1-0-11-ready-for-ios-16?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[I’m very happy to note that we got the latest update for WriteFreely ready for the App Store just in time for today’s launch of iOS 16.&#xA;!--more--&#xA;As WriteFreely for iOS is a SwiftUI app, and there are changes to the way navigation works as of iOS 16, I was a bit concerned that I’d try the app on the developer beta and everything would be broken.&#xA;&#xA;Turns out, things were… mostly fine! One bug involved a missing new-post button, which has been resolved. With that, WriteFreely v1.0.11 is ready for iOS 16!&#xA;&#xA;Download the update now on the App Store.&#xA;&#xA;---&#xD;&#xA;&#xD;&#xA;Enter your email to subscribe to updates:&#xD;&#xA;&#xD;&#xA;!--emailsub--&#xD;&#xA;&#xD;&#xA;You can also subscribe via RSS or follow @angelo@write.as on Mastodon.]]&gt;</description>
      <content:encoded><![CDATA[<p>I’m very happy to note that we got the latest update for WriteFreely ready for the App Store just in time for today’s launch of iOS 16.

As WriteFreely for iOS is a SwiftUI app, and there are changes to the way navigation works as of iOS 16, I was a bit concerned that I’d try the app on the developer beta and everything would be broken.</p>

<p>Turns out, things were… mostly fine! One bug involved a missing new-post button, which has been resolved. With that, WriteFreely v1.0.11 is ready for iOS 16!</p>

<p>Download the update now <a href="https://apps.apple.com/app/writefreely/id1531530896">on the App Store</a>.</p>

<hr/>

<p>Enter your email to subscribe to updates:</p>



<p>You can also subscribe <a href="https://write.as/angelo/feed/">via RSS</a> or follow <code><a href="/@/angelo@write.as" class="u-url mention">@<span>angelo@write.as</span></a></code> on Mastodon.</p>
]]></content:encoded>
      <guid>https://write.as/angelo/writefreely-v1-0-11-ready-for-ios-16</guid>
      <pubDate>Mon, 12 Sep 2022 14:17:44 +0000</pubDate>
    </item>
    <item>
      <title>Building the WriteFreely Action Extension in SwiftUI</title>
      <link>https://write.as/angelo/building-the-writefreely-action-extension-in-swiftui?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[Today, I want to talk a little bit about a new feature in WriteFreely for iOS: sharing from Safari.&#xA;!--more--&#xA;&#xA;One thing I love to do with the app is write up quick little notes on my iPhone as &#34;titleless&#34; micro-blog posts. These get posted to one of my Write.as blogs, and are then syndicated to various social networks. Posts like this are great for sharing a quick thought, or a snippet from a web page.&#xA;&#xA;Doing the latter, though, is a bit annoying — it involves several round trips between WriteFreely and Safari, copying and pasting bits of information (the URL, the page title, and any selection of text) between the two apps, one at a time. Like an animal.&#xA;&#xA;With version 1.0.8 —now available on the App Store— we&#39;ve added an action extension for Safari that makes this task much easier.&#xA;&#xA;How It Works&#xA;&#xA;Essentially, iOS exposes an extension system that developers can use to extend their apps: this includes things like home-screen widgets, Siri shortcuts, and action extensions.&#xA;&#xA;The action extension is a bit special, in that it lets users interact with one app from another app (typically via the Share button). In the case of the WriteFreely for iOS extension, Safari&#39;s Share menu gets a new entry:&#xA;&#xA;img alt=&#34;The action extension adds a &#39;Create WriteFreely draft&#39; entry to Safari&#39;s Share menu.&#34; src=&#34;https://i.snap.as/C638z0qA.png&#34; style=&#34;display:block; margin:2em auto; width:300px&#34; /&#xA;&#xA;Tapping the &#34;Create WriteFreely draft&#34; option in the menu brings up the extension&#39;s user interface, which lets you create a note and save it as a local draft for a particular blog in your WriteFreely/Write.as account:&#xA;&#xA;img alt=&#34;The action extension&#39;s user interface in, um, action.&#34; src=&#34;https://i.snap.as/qoMoUIvH.png&#34; style=&#34;display:block; margin:2em auto; width:300px&#34; /&#xA;&#xA;The Title field is optional; if you want to create a new draft with a title, enter it here. If not, leave it blank.&#xA;&#xA;The Content field gets pre-populated with a Markdown link using the title and URL of the current webpage. If you&#39;ve selected any text on the page, that&#39;s included too.&#xA;&#xA;The formatting of the content is a little bit different, depending on whether there&#39;s selected text.&#xA;&#xA;Without selected text, you just get the link. Add anything else you want to the Content field, before or after the link:&#xA;&#xA;Page Title&#xA;&#xA;If you have selected text on the webpage, then it&#39;s included as a blockquote before the link:&#xA;&#xA;  This text was selected on the webpage.&#xA;&#xA;Via: Page Title&#xA;&#xA;Below that, the Save To menu lets you choose the blog to which you&#39;d like to save the post. This is only saved to your iOS device, so you can always go back to the app and continue editing it before publishing.&#xA;&#xA;How It Was Built&#xA;&#xA;Building this functionality was done in a couple of steps (see the related GitHub issue). First, some data and preferences needed to be moved to an App Group so they can be accessed both by the WriteFreely and by its action extension. Then —as you might well expect— the action extension itself needed to be built.&#xA;&#xA;Moving the Persistent Stores&#xA;&#xA;div style=&#34;margin:0; padding:0 16px 8px 16px; background-color:#eee; color:#333; border-radius:16px&#34;&#xA;strongRelated pull requests:/strong&#xA;ul&#xA;lia href=&#34;https://github.com/writefreely/writefreely-swiftui-multiplatform/pull/195&#34;PR195, emMigrate persistent store to App Group/em/a/li&#xA;lia href=&#34;https://github.com/writefreely/writefreely-swiftui-multiplatform/pull/197&#34;PR197, emMove user defaults to App Group/em/a/li&#xA;/ul&#xA;/div&#xA;&#xA;WriteFreely for iOS stores information in one of three places on your device:&#xA;&#xA;Sensitive data like your access token, used send data back and forth between device and the WriteFreely server, is securely encrypted and stored in the system&#39;s Keychain.&#xA;Various preferences and settings like the default font for new posts, your preferred colour scheme, and the last draft you were working on are kept in User Defaults, a simple key-value store.&#xA;Your posts and blogs are stored in Core Data, backed by SQLite. Core Data isn&#39;t a database, but rather an object graph, and is great for storing data like this.&#xA;&#xA;Extensions have limitations on what data they can access, so if we wanted to be able to save your new draft to that same Core Data store, or read User Defaults to render the text in your preferred font, those stores first had to be moved to an App Group.&#xA;&#xA;Once that was done, the extension could be built.&#xA;&#xA;Building the Action Extension&#xA;&#xA;div style=&#34;margin:0; padding:0 16px 8px 16px; background-color:#eee; color:#333; border-radius:16px&#34;&#xA;strongRelated pull request:/strong&#xA;ul&#xA;lia href=&#34;https://github.com/writefreely/writefreely-swiftui-multiplatform/pull/198&#34;PR198, emAdd action extension for Safari/em/a/li&#xA;/ul&#xA;/div&#xA;&#xA;One of the goals of the WriteFreely app was to build as much of it as possible with SwiftUI, Apple&#39;s declarative framework for building an app&#39;s user interface. And since almost all of the app is built this way, why not try to do the same with the action extension?&#xA;&#xA;As it turns out, there really isn&#39;t very much information on how to build one in SwiftUI. Sure, there are great tutorials on building an action extension —this one on Hacking With Swift in particular was helpful in getting the initial functionality working— but they all assume you&#39;ll be working in UIKit.&#xA;&#xA;p style=&#34;display:block; text-align:center;&#34;  /p&#xA;&#xA;First, an aside.&#xA;&#xA;If your iOS app was originally built in UIKit, and you want to start converting it over to SwiftUI, there&#39;s an API for that: UIHostingController. And it&#39;s relatively simple to use:&#xA;&#xA;let swiftUIView = MySwiftUIView()&#xA;let hostingController = UIHostingController(rootView: swiftUIView)&#xA;&#xA;I&#39;m simplifying things a little bit, but this effectively embeds the SwiftUI view in the hosting controller, and you then use the hosting controller the way you would any other UIViewController.&#xA;&#xA;So, in this case, we&#39;re working in reverse to what you&#39;ll usually come across in a SwiftUI-lifecycle app like WriteFreely: the need to add a UIKit-based view via UIViewRepresentable. I wrote more about that here.&#xA;&#xA;p style=&#34;display:block; text-align:center;&#34;  /p&#xA;&#xA;Now, when you create a new action-extension target in your Xcode project, it spits out a template full of boilerplate code that you can use as a jumping-off point. And the key file you&#39;d start off with is… a view controller.&#xA;&#xA;(You see where this is going, right?)&#xA;&#xA;By pulling out the placeholder code and using that class as a hosting controller, you can write the user interface for your action extension in SwiftUI, in all its declarative glory.&#xA;&#xA;And that&#39;s what&#39;s going on in our action-extension target: we have the ActionViewController class, which sets up the environment and embeds the (SwiftUI) ContentView struct. The latter handles all of the layout and interaction, in just under 200 lines.&#xA;&#xA;(Looking a bit more closely, you&#39;ll see that instead of a UIHostingController, the ActionViewController is instead using a UIHostingView. This isn&#39;t an Apple API, but rather something I found while searching for a way to write this extension in SwiftUI, in Blear — Sindre Sorhus&#39; open-source iOS utility for turning photos into blurred background images.)&#xA;&#xA;And that&#39;s how the action extension was built. Let me know how you like it!&#xA;&#xA;!--references--&#xA;[wfapppage]: https://writefreely.org/apps/ios&#xA;[wa]: https://write.as/&#xA;[wfappstore]: https://apps.apple.com/app/writefreely/id1531530896&#xA;[hws]: https://www.hackingwithswift.com/read/19/3/adding-an-extension-nsextensionitem&#xA;[ghissue]: https://github.com/writefreely/writefreely-swiftui-multiplatform/issues/164&#xA;[uihostingcontroller]: https://developer.apple.com/documentation/swiftui/uihostingcontroller&#xA;[uiviewrepresentable]: https://write.as/angelo/rewriting-the-writefreely-for-ios-post-editor&#xA;[blear]: https://github.com/sindresorhus/Blear/&#xA;&#xA;---&#xD;&#xA;&#xD;&#xA;Enter your email to subscribe to updates:&#xD;&#xA;&#xD;&#xA;!--emailsub--&#xD;&#xA;&#xD;&#xA;You can also subscribe via RSS or follow @angelo@write.as on Mastodon.]]&gt;</description>
      <content:encoded><![CDATA[<p>Today, I want to talk a little bit about a new feature in <a href="https://writefreely.org/apps/ios">WriteFreely for iOS</a>: sharing from Safari.
</p>

<p>One thing I love to do with the app is write up quick little notes on my iPhone as “titleless” micro-blog posts. These get posted to one of my <a href="https://write.as/">Write.as</a> blogs, and are then syndicated to various social networks. Posts like this are great for sharing a quick thought, or a snippet from a web page.</p>

<p>Doing the latter, though, is a bit annoying — it involves several round trips between WriteFreely and Safari, copying and pasting bits of information (the URL, the page title, and any selection of text) between the two apps, one at a time. Like an animal.</p>

<p>With version 1.0.8 —now <a href="https://apps.apple.com/app/writefreely/id1531530896">available on the App Store</a>— we&#39;ve added an action extension for Safari that makes this task much easier.</p>

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

<p>Essentially, iOS exposes an extension system that developers can use to <em>extend</em> their apps: this includes things like home-screen widgets, Siri shortcuts, and action extensions.</p>

<p>The action extension is a bit special, in that it lets users interact with one app from <em>another</em> app (typically via the Share button). In the case of the WriteFreely for iOS extension, Safari&#39;s Share menu gets a new entry:</p>

<p><img alt="The action extension adds a &#39;Create WriteFreely draft&#39; entry to Safari&#39;s Share menu." src="https://i.snap.as/C638z0qA.png" style="display:block; margin:2em auto; width:300px"/></p>

<p>Tapping the “Create WriteFreely draft” option in the menu brings up the extension&#39;s user interface, which lets you create a note and save it as a local draft for a particular blog in your WriteFreely/Write.as account:</p>

<p><img alt="The action extension&#39;s user interface in, um, action." src="https://i.snap.as/qoMoUIvH.png" style="display:block; margin:2em auto; width:300px"/></p>

<p>The <strong>Title</strong> field is optional; if you want to create a new draft with a title, enter it here. If not, leave it blank.</p>

<p>The <strong>Content</strong> field gets pre-populated with a Markdown link using the title and URL of the current webpage. If you&#39;ve selected any text on the page, that&#39;s included too.</p>

<p>The formatting of the content is a little bit different, depending on whether there&#39;s selected text.</p>

<p>Without selected text, you just get the link. Add anything else you want to the <strong>Content</strong> field, before or after the link:</p>

<pre><code class="language-markdown">[Page Title](https://link-to-page)
</code></pre>

<p>If you <em>have</em> selected text on the webpage, then it&#39;s included as a blockquote before the link:</p>

<pre><code class="language-markdown">&gt; This text was selected on the webpage.

Via: [Page Title](https://link-to-page)
</code></pre>

<p>Below that, the <strong>Save To</strong> menu lets you choose the blog to which you&#39;d like to save the post. This is only saved to your iOS device, so you can always go back to the app and continue editing it before publishing.</p>

<h2 id="how-it-was-built" id="how-it-was-built">How It Was Built</h2>

<p>Building this functionality was done in a couple of steps (see the related <a href="https://github.com/writefreely/writefreely-swiftui-multiplatform/issues/164">GitHub issue</a>). First, some data and preferences needed to be moved to an App Group so they can be accessed both by the WriteFreely and by its action extension. Then —as you might well expect— the action extension itself needed to be built.</p>

<h3 id="moving-the-persistent-stores" id="moving-the-persistent-stores">Moving the Persistent Stores</h3>

<div style="margin:0; padding:0 16px 8px 16px; background-color:#eee; color:#333; border-radius:16px">
<strong>Related pull requests:</strong>
<ul><li><a href="https://github.com/writefreely/writefreely-swiftui-multiplatform/pull/195">PR195, <em>Migrate persistent store to App Group</em></a></li>
<li><a href="https://github.com/writefreely/writefreely-swiftui-multiplatform/pull/197">PR197, <em>Move user defaults to App Group</em></a></li></ul>
</div>

<p>WriteFreely for iOS stores information in one of three places on your device:</p>
<ol><li>Sensitive data like your access token, used send data back and forth between device and the WriteFreely server, is securely encrypted and stored in the system&#39;s Keychain.</li>
<li>Various preferences and settings like the default font for new posts, your preferred colour scheme, and the last draft you were working on are kept in User Defaults, a simple key-value store.</li>
<li>Your posts and blogs are stored in Core Data, backed by SQLite. Core Data isn&#39;t a database, but rather an object graph, and is great for storing data like this.</li></ol>

<p>Extensions have limitations on what data they can access, so if we wanted to be able to save your new draft to that same Core Data store, or read User Defaults to render the text in your preferred font, those stores first had to be moved to an App Group.</p>

<p>Once that was done, the extension could be built.</p>

<h3 id="building-the-action-extension" id="building-the-action-extension">Building the Action Extension</h3>

<div style="margin:0; padding:0 16px 8px 16px; background-color:#eee; color:#333; border-radius:16px">
<strong>Related pull request:</strong>
<ul><li><a href="https://github.com/writefreely/writefreely-swiftui-multiplatform/pull/198">PR198, <em>Add action extension for Safari</em></a></li></ul>
</div>

<p>One of the goals of the WriteFreely app was to build as much of it as possible with SwiftUI, Apple&#39;s declarative framework for building an app&#39;s user interface. And since almost all of the app is built this way, why not try to do the same with the action extension?</p>

<p>As it turns out, there really isn&#39;t very much information on how to build one in SwiftUI. Sure, there are great tutorials on building <em>an action extension</em> —<a href="https://www.hackingwithswift.com/read/19/3/adding-an-extension-nsextensionitem">this one on Hacking With Swift</a> in particular was helpful in getting the initial functionality working— but they all assume you&#39;ll be working in UIKit.</p>

<p style="display:block; text-align:center;">* * *</p>

<p>First, an aside.</p>

<p>If your iOS app was originally built in UIKit, and you want to start converting it over to SwiftUI, there&#39;s an API for that: <a href="https://developer.apple.com/documentation/swiftui/uihostingcontroller">UIHostingController</a>. And it&#39;s relatively simple to use:</p>

<pre><code class="language-swift">let swiftUIView = MySwiftUIView()
let hostingController = UIHostingController(rootView: swiftUIView)
</code></pre>

<p>I&#39;m simplifying things a little bit, but this effectively embeds the SwiftUI view in the hosting controller, and you then use the hosting controller the way you would any other UIViewController.</p>

<p>So, in this case, we&#39;re working in reverse to what you&#39;ll usually come across in a SwiftUI-lifecycle app like WriteFreely: the need to add a UIKit-based view via UIViewRepresentable. I wrote more about that <a href="https://write.as/angelo/rewriting-the-writefreely-for-ios-post-editor">here</a>.</p>

<p style="display:block; text-align:center;">* * *</p>

<p>Now, when you create a new action-extension target in your Xcode project, it spits out a template full of boilerplate code that you can use as a jumping-off point. And the key file you&#39;d start off with is… <em>a view controller</em>.</p>

<p>(You see where this is going, right?)</p>

<p>By pulling out the placeholder code and using that class as a hosting controller, you can write the user interface for your action extension in SwiftUI, in all its declarative glory.</p>

<p>And that&#39;s what&#39;s going on in our action-extension target: we have the ActionViewController class, which sets up the environment and embeds the (SwiftUI) ContentView struct. The latter handles all of the layout and interaction, in just under 200 lines.</p>

<p>(Looking a bit more closely, you&#39;ll see that instead of a UIHostingController, the ActionViewController is instead using a UIHostingView. This <em>isn&#39;t</em> an Apple API, but rather something I found while searching for a way to write this extension in SwiftUI, in <a href="https://github.com/sindresorhus/Blear/">Blear</a> — Sindre Sorhus&#39; open-source iOS utility for turning photos into blurred background images.)</p>

<p>And that&#39;s how the action extension was built. Let me know how you like it!</p>



<hr/>

<p>Enter your email to subscribe to updates:</p>



<p>You can also subscribe <a href="https://write.as/angelo/feed/">via RSS</a> or follow <code><a href="/@/angelo@write.as" class="u-url mention">@<span>angelo@write.as</span></a></code> on Mastodon.</p>
]]></content:encoded>
      <guid>https://write.as/angelo/building-the-writefreely-action-extension-in-swiftui</guid>
      <pubDate>Fri, 17 Dec 2021 21:54:33 +0000</pubDate>
    </item>
    <item>
      <title>WriteFreely for iOS v1.0.7 now available</title>
      <link>https://write.as/angelo/writefreely-for-ios-v1-0-7-now-available?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[Beyond some fixes for iOS 15, this update fixes an annoying navigation bug that would dump you back to the post list on iPhone if you were trying to get to your blog list.&#xA;!--more--&#xA;“A partial screenshot of an iPhone’s notification screen, showing notifications from App Store Connect indicating the progression of WriteFreely for iOS through App Review.”&#xA;&#xA;You should be seeing it on the App Store shortly.&#xA;&#xA;(Also, less than an hour from submission to approval. Nice.)&#xA;&#xA;As a reminder, I hold office hours, if you’d like to talk about contributing to the (completely open-source) project!&#xA;&#xA;---&#xD;&#xA;&#xD;&#xA;Enter your email to subscribe to updates:&#xD;&#xA;&#xD;&#xA;!--emailsub--&#xD;&#xA;&#xD;&#xA;You can also subscribe via RSS or follow @angelo@write.as on Mastodon.]]&gt;</description>
      <content:encoded><![CDATA[<p>Beyond some fixes for iOS 15, this update fixes an annoying navigation bug that would dump you back to the post list on iPhone if you were trying to get to your blog list.

<img src="https://i.snap.as/jXxNWCdC.jpeg" alt="“A partial screenshot of an iPhone’s notification screen, showing notifications from App Store Connect indicating the progression of WriteFreely for iOS through App Review.”"/></p>

<p>You should be seeing it <a href="https://apps.apple.com/app/writefreely/id1531530896">on the App Store</a> shortly.</p>

<p>(Also, less than an hour from submission to approval. Nice.)</p>

<p>As a reminder, I <a href="https://discuss.write.as/t/office-hours-for-writefreely-swift-projects/2788">hold office hours</a>, if you’d like to talk about contributing to the (completely open-source) project!</p>

<hr/>

<p>Enter your email to subscribe to updates:</p>



<p>You can also subscribe <a href="https://write.as/angelo/feed/">via RSS</a> or follow <code><a href="/@/angelo@write.as" class="u-url mention">@<span>angelo@write.as</span></a></code> on Mastodon.</p>
]]></content:encoded>
      <guid>https://write.as/angelo/writefreely-for-ios-v1-0-7-now-available</guid>
      <pubDate>Fri, 01 Oct 2021 19:41:40 +0000</pubDate>
    </item>
    <item>
      <title>Wrapping up some work on improvements to WriteFreely for iOS 15.</title>
      <link>https://write.as/angelo/wrapping-up-some-work-on-improvements-to-writefreely-for-ios-15?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[Wrapping up some work on improvements to WriteFreely for iOS 15. This should also make navigation work more smoothly in iOS 14, too!&#xA;&#xA;---&#xD;&#xA;&#xD;&#xA;Enter your email to subscribe to updates:&#xD;&#xA;&#xD;&#xA;!--emailsub--&#xD;&#xA;&#xD;&#xA;You can also subscribe via RSS or follow @angelo@write.as on Mastodon.]]&gt;</description>
      <content:encoded><![CDATA[<p>Wrapping up some work on improvements to WriteFreely for iOS 15. This should also make navigation work more smoothly in iOS 14, too!</p>

<hr/>

<p>Enter your email to subscribe to updates:</p>



<p>You can also subscribe <a href="https://write.as/angelo/feed/">via RSS</a> or follow <code><a href="/@/angelo@write.as" class="u-url mention">@<span>angelo@write.as</span></a></code> on Mastodon.</p>
]]></content:encoded>
      <guid>https://write.as/angelo/wrapping-up-some-work-on-improvements-to-writefreely-for-ios-15</guid>
      <pubDate>Fri, 24 Sep 2021 19:44:28 +0000</pubDate>
    </item>
  </channel>
</rss>