<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://liamnichols.eu/feed.xml" rel="self" type="application/atom+xml" /><link href="https://liamnichols.eu/" rel="alternate" type="text/html" /><updated>2024-03-22T14:47:25+00:00</updated><id>https://liamnichols.eu/feed.xml</id><title type="html">liamOS</title><subtitle></subtitle><author><name>Liam Nichols</name><email>liam.nichols.ln@gmail.com</email></author><entry><title type="html">Privacy Manifests</title><link href="https://liamnichols.eu/2024/03/22/privacy-manifests.html" rel="alternate" type="text/html" title="Privacy Manifests" /><published>2024-03-22T00:00:00+00:00</published><updated>2024-03-22T00:00:00+00:00</updated><id>https://liamnichols.eu/2024/03/22/privacy-manifests</id><content type="html" xml:base="https://liamnichols.eu/2024/03/22/privacy-manifests.html"><![CDATA[<p>During WWDC 2023, Apple announced the <a href="https://developer.apple.com/videos/play/wwdc2023/10060">introduction of Privacy Manifests</a>. The motivations behind Privacy Manifests are very much justified, but the way that this ecosystem-wide change is being forced upon developers with barely any support is frustrating to say the least.</p>

<p>I did originally write a lengthy rant about my experience, but I figure that it wasn’t entirely productive. Instead, if like me you are also frustrated and would like Apple to reconsider their approach, please submit a Feedback Request letting them know.</p>

<p>If you’ve never submitted a Feedback Request before, or you want help with what exactly you should write, read on. I’ll only take a couple of minutes!</p>

<ol>
  <li>Head to <a href="https://feedbackassistant.apple.com/"><strong>feedbackassistant.apple.com</strong></a> and Sign In with your developer account</li>
  <li>Click the <a href="https://feedbackassistant.apple.com/new-form-response"><strong>Compose New Feedback</strong></a> button at the top of the page</li>
  <li>Select <strong>Developer Tools &amp; Resources</strong></li>
  <li>
    <p>Enter the details below (or feel free to get creative yourself):</p>

    <p><strong>Please provide a descriptive title for your feedback:</strong></p>

    <p class="highlight" style="padding: 16px">
   Please better support developers with the upcoming Privacy Manifest requirements
 </p>

    <p><strong>Which area are you seeing an issue with?</strong></p>

    <p class="highlight" style="padding: 16px">
   Something else not on this list
 </p>

    <p><strong>What type of feedback are you reporting?</strong></p>

    <p class="highlight" style="padding: 16px">
   Suggestion
 </p>

    <p><strong>Please describe the issue and what steps we can take to reproduce it</strong></p>

    <div class="highlight" style="padding: 16px">
   <p>
     For developers like me, the rollout of the Privacy Manifest requirements has been difficult and frustrating.
   </p>

   <p>
     Please stop App Store Connect from reporting warnings about my Privacy Manifest and postpone any enforcement relating to the Privacy Manifest until you are able to better guide developers to implement the required changes. To help guide developers across the ecosystem, I suggest the following actions:
   </p>

   <p>
     1. Complete the integration within Xcode and Swift Package Manager - Currently, manifests provided by third party developers have no way of being incorporated into the App's main PrivacyManifest.xcprivacy file when dependencies are statically linked to a target. It seems that the only workaround is for the developer to manually copy the contents of the third party Privacy Manifest into their own which is not sustainable.
   </p>

   <p>
     2. Provide a tool or documentation to locally automate the analysis of required API usage - The warning emails from App Store Connect demonstrate that this is possible, so please provide a tool to assist developers in building Privacy Manifests instead of having to manually audit code against the documentation (which is subject to change). This tool should be usable by both app developers and third-party SDK developers.
   </p>

   <p>
     3. Provide more assistance to third-party dependency managers - CocoaPods is still used by hundreds of thousands of developers, and it is critical that this tool properly supports Privacy Manifests. Please make more effort to support CocoaPods and other third-party tools by offering support via Developer Relations and/or sponsoring any required work.
   </p>

   <p>
     4. Provide more documentation - Ensuring that Privacy Manifests are properly supplied and configured for first and third-party code is still not simple enough. Please provide more documentation, technical notes, and troubleshooting guides for ensuring that Privacy Manifests are set up correctly. There is currently no good resource on the internet for this, and developers are finding themselves overwhelmed having to figure it out against a deadline.
   </p>

   <p>
     I (and I am sure many others) want to help respect the privacy of my users and provide them with full transparency, but with the current friction around Privacy Manifests, it is hard to do so, and I fear that by upsetting developers, it will have a negative effect on the overall goals of this change. For Privacy Manifests and required API usage declarations to be meaningful and effective, the process needs to be well-understood and frictionless, and I hope that these suggestions can be applied in order to help achieve the goals.
   </p>
 </div>
  </li>
  <li>Click <strong>Submit</strong> and hope for the best</li>
</ol>

<hr />

<p><em>Updated on 2024-03-22 at 15:44 CET: Added additional request to the list of actions.</em></p>]]></content><author><name>Liam Nichols</name><email>liam.nichols.ln@gmail.com</email></author><summary type="html"><![CDATA[During WWDC 2023, Apple announced the introduction of Privacy Manifests. The motivations behind Privacy Manifests are very much justified, but the way that this ecosystem-wide change is being forced upon developers with barely any support is frustrating to say the least.]]></summary></entry><entry><title type="html">Taking your Strings Catalogs to the Next Level</title><link href="https://liamnichols.eu/2023/10/02/taking-your-strings-catalogs-to-the-next-level.html" rel="alternate" type="text/html" title="Taking your Strings Catalogs to the Next Level" /><published>2023-10-02T00:00:00+00:00</published><updated>2023-10-02T00:00:00+00:00</updated><id>https://liamnichols.eu/2023/10/02/taking-your-strings-catalogs-to-the-next-level</id><content type="html" xml:base="https://liamnichols.eu/2023/10/02/taking-your-strings-catalogs-to-the-next-level.html"><![CDATA[<p>In Xcode 15 Apple introduced Strings Catalogs, a new file format (<code class="language-plaintext highlighter-rouge">.xcstrings</code>) that can be used as a single source for all of your apps localized string content.</p>

<p>Prior to Strings Catalogs, you typically defined your standard strings in a <code class="language-plaintext highlighter-rouge">.strings</code> file and your plural variations in a <code class="language-plaintext highlighter-rouge">.stringsdict</code> plist. Not only were these formats quite verbose, but you also had to then maintain a copy of each file for every supported language too.</p>

<p>The friction in this process often leads to compromises in one way or another and these compromises ultimately end up impacting the users of our apps, so it’s an understatement to say that I’m excited about the improvements that have shipped with Xcode 15 this year 🎉</p>

<p>A single Strings Catalog file can now contain regular strings, plural variations and device variations across all of the languages that your app supports. What is even better is that at compile time in Xcode 15, the contents of your Strings Catalog are converted back into <code class="language-plaintext highlighter-rouge">.strings</code> and <code class="language-plaintext highlighter-rouge">.stringsdict</code> resources that allow you to take advantage of the file format without having to change your deployment target!</p>

<p><img src="/public/images/xcstrings/strings-catalog.png" alt="A screenshot of the Strings Catalog editor in Xcode" class="center-image" /></p>

<p>So if a Strings Catalog is so great already, how can we possibly go about taking it to the next level?</p>

<p>The short answer is to use a new tool that I created called <a href="https://github.com/liamnichols/xcstrings-tool">XCStrings Tool</a>, but if you’re interested in understanding why I developed this tool, then please do read on:</p>

<ul>
  <li><a href="#how-did-we-get-here"><strong>How did we get here?</strong></a>
    <ul>
      <li><a href="#localization-workflows---the-apple-way"><strong>Localization Workflows - The Apple Way</strong></a></li>
      <li><a href="#string-extraction---the-downsides"><strong>String Extraction - The downsides</strong></a></li>
      <li><a href="#creating-strings-in-your-strings-catalog"><strong>Creating Strings in your Strings Catalog</strong></a></li>
      <li><a href="#so-lets-define-some-constants"><strong>So lets define some constants</strong></a></li>
      <li><a href="#but-what-about-the-arguments"><strong>But what about the arguments?</strong></a></li>
      <li><a href="#recap"><strong>Recap</strong></a></li>
    </ul>
  </li>
  <li><a href="#xcstrings-tool"><strong>XCStrings Tool</strong></a></li>
</ul>

<h1 id="how-did-we-get-here">How did we get here?</h1>

<p>I’ve been working with Apple’s localization tooling for a while now, and if you have too, you might be familiar with some of the topics that I discuss. You might also be aware that there are existing tools out there such as <a href="https://github.com/mac-cain13/R.swift">R.swift</a> and <a href="https://github.com/SwiftGen/SwiftGen">SwiftGen</a>, but for the sake of a story, lets go all the way back to the beginning.</p>

<h2 id="localization-workflows---the-apple-way">Localization Workflows - The Apple Way</h2>

<p>When it comes to localizing your projects, Apple have always promoted a localization experience that typically starts with you defining your localized string content directly in your SwiftUI or UIKit view code using string literals.</p>

<p>Lets take some example code from Apple’s <a href="https://developer.apple.com/documentation/swiftui/food_truck_building_a_swiftui_multiplatform_app">Food Truck</a> sample app:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">SocialFeedPlusSettings</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">controller</span><span class="p">:</span> <span class="kt">StoreSubscriptionController</span>
    <span class="kd">@AppStorage</span><span class="p">(</span><span class="s">"showPlusPosts"</span><span class="p">)</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">showPlusPosts</span> <span class="o">=</span> <span class="kc">false</span>
    <span class="kd">@AppStorage</span><span class="p">(</span><span class="s">"advancedTools"</span><span class="p">)</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">advancedTools</span> <span class="o">=</span> <span class="kc">true</span>
    <span class="kd">@Environment</span><span class="p">(\</span><span class="o">.</span><span class="n">dismiss</span><span class="p">)</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">dismiss</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">List</span> <span class="p">{</span>
            <span class="kt">SubscriptionStatusView</span><span class="p">(</span><span class="nv">controller</span><span class="p">:</span> <span class="n">controller</span><span class="p">)</span>
            <span class="kt">Section</span><span class="p">(</span><span class="s">"Settings"</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">Toggle</span><span class="p">(</span><span class="s">"Highlight Social Feed+ posts"</span><span class="p">,</span> <span class="nv">isOn</span><span class="p">:</span> <span class="err">$</span><span class="n">showPlusPosts</span><span class="p">)</span>
                <span class="kt">Toggle</span><span class="p">(</span><span class="s">"Advanced engagement tools"</span><span class="p">,</span> <span class="nv">isOn</span><span class="p">:</span> <span class="err">$</span><span class="n">advancedTools</span><span class="p">)</span>
                <span class="kt">NavigationLink</span><span class="p">(</span><span class="s">"Social-media providers"</span><span class="p">)</span> <span class="p">{</span>
                    <span class="kt">EmptyView</span><span class="p">()</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="cp">#if os(iOS)</span>
            <span class="kt">Section</span> <span class="p">{</span>
                <span class="kt">NavigationLink</span> <span class="p">{</span>
                    <span class="kt">StoreSupportView</span><span class="p">()</span>
                <span class="p">}</span> <span class="nv">label</span><span class="p">:</span> <span class="p">{</span>
                    <span class="kt">Label</span><span class="p">(</span><span class="s">"Subscription support"</span><span class="p">,</span> <span class="nv">systemImage</span><span class="p">:</span> <span class="s">"questionmark.circle"</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="cp">#else</span>
            <span class="kt">Section</span><span class="p">(</span><span class="s">"Subscription support"</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">Button</span><span class="p">(</span><span class="s">"Restore missing purchases"</span><span class="p">)</span> <span class="p">{</span>
                    <span class="kt">Task</span><span class="p">(</span><span class="nv">priority</span><span class="p">:</span> <span class="o">.</span><span class="n">userInitiated</span><span class="p">)</span> <span class="p">{</span>
                        <span class="k">try</span> <span class="k">await</span> <span class="kt">AppStore</span><span class="o">.</span><span class="nf">sync</span><span class="p">()</span>
                    <span class="p">}</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="cp">#endif</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">navigationTitle</span><span class="p">(</span><span class="s">"Manage Social Feed+"</span><span class="p">)</span>
        <span class="o">.</span><span class="n">toolbar</span> <span class="p">{</span>
            <span class="cp">#if os(iOS)</span>
            <span class="k">let</span> <span class="nv">placement</span> <span class="o">=</span> <span class="kt">ToolbarItemPlacement</span><span class="o">.</span><span class="n">navigationBarTrailing</span>
            <span class="cp">#else</span>
            <span class="k">let</span> <span class="nv">placement</span> <span class="o">=</span> <span class="kt">ToolbarItemPlacement</span><span class="o">.</span><span class="n">cancellationAction</span>
            <span class="cp">#endif</span>
            <span class="kt">ToolbarItemGroup</span><span class="p">(</span><span class="nv">placement</span><span class="p">:</span> <span class="n">placement</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">Button</span> <span class="p">{</span>
                    <span class="nf">dismiss</span><span class="p">()</span>
                <span class="p">}</span> <span class="nv">label</span><span class="p">:</span> <span class="p">{</span>
                    <span class="kt">Label</span><span class="p">(</span><span class="s">"Dismiss"</span><span class="p">,</span> <span class="nv">systemImage</span><span class="p">:</span> <span class="s">"xmark"</span><span class="p">)</span>
                        <span class="cp">#if os(macOS)</span>
                        <span class="o">.</span><span class="nf">labelStyle</span><span class="p">(</span><span class="o">.</span><span class="n">titleOnly</span><span class="p">)</span>
                        <span class="cp">#endif</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>In the code above, there are 8 strings that need to be localized:</p>

<ul>
  <li>Settings</li>
  <li>Highlight Social Feed+ posts</li>
  <li>Advanced engagement tools</li>
  <li>Social-media providers</li>
  <li>Subscription support</li>
  <li>Restore missing purchases</li>
  <li>Manage Social Feed+</li>
  <li>Dismiss</li>
</ul>

<p>You aren’t expected to manually copy these strings into your Strings Catalog, instead, building your project with the <strong>Use Compiler to Extract Swift Strings</strong> build setting (enabled by default) will tell Xcode to automatically populate these strings into the Strings Catalog for you.</p>

<p>Sounds great? Well it actually is. But like many things with Apple, we can quickly find that this process doesn’t quite scale how we might hope that it does.</p>

<h2 id="string-extraction---the-downsides">String Extraction - The downsides</h2>

<p><em>The Apple Way</em> holds up pretty well for relatively small apps that don’t have a text-heavy UI, but as the complexity grows, the cracks in this process start to show.</p>

<p>Relying on the extraction of string literals alone is a great start, but what if we need to think about some other things:</p>

<ul>
  <li>Providing context to translators via comments</li>
  <li>Breaking out our translations across multiple localization tables</li>
  <li>Using translations from different targets/modules</li>
  <li>Scenarios where a phrase in the source language could mean different things based on context in another language</li>
</ul>

<p>For the extraction of a string literal like <code class="language-plaintext highlighter-rouge">Text("Settings")</code> to be sufficient, a few things have to be true:</p>

<ol>
  <li>The localization key is called <code class="language-plaintext highlighter-rouge">Settings</code>.</li>
  <li>The value of the localization in the default language is <strong>Settings</strong>.</li>
  <li>A translator can infer the context from the word <strong>Settings</strong> alone.</li>
  <li>The localizations for this phrase are in the file called <strong>Localizable.xcstrings</strong>.</li>
  <li>The Strings Catalog is found in the Apps main bundle (<code class="language-plaintext highlighter-rouge">Bundle.main</code>).</li>
</ol>

<p>In one of the more extreme scenarios, you might find that your simple string literal ends up having to become something like the following:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Text</span><span class="p">(</span>
  <span class="s">"Settings"</span><span class="p">,</span>
  <span class="nv">tableName</span><span class="p">:</span> <span class="s">"Social"</span><span class="p">,</span> <span class="c1">// Social.xcstrings</span>
  <span class="nv">bundle</span><span class="p">:</span> <span class="o">.</span><span class="n">module</span><span class="p">,</span> <span class="c1">// The correct Bundle for Swift Package target resources</span>
  <span class="nv">comment</span><span class="p">:</span> <span class="s">"A section heading title for the Social Feed Plus configuration options"</span>
<span class="p">)</span>
</code></pre></div></div>

<p>To cover all cases, we need 4-6 lines of code to properly describe the word <em>Settings</em>. Considering how far you can get with 4 lines of SwiftUI, it seems absurd that a single localized string might take up this much code in your view.</p>

<h2 id="creating-strings-in-your-strings-catalog">Creating Strings in your Strings Catalog</h2>

<p>Once you find that defining all of your localized Strings context in your Swift source code isn’t good enough, you will instead want to start manually defining the values in your Strings Catalog instead.</p>

<p>By doing so, we instantly gain two benefits:</p>

<ol>
  <li>We have more control over the localized string key</li>
  <li>We’ve moved our translator comments out of the source code</li>
</ol>

<p><img src="/public/images/xcstrings/settings-string-in-catalog.png" alt="A screenshot of the Strings Catalog editor in Xcode" class="center-image" /></p>

<p>In the String Catalog above, we’ve manually added a string with the key <code class="language-plaintext highlighter-rouge">settingsHeading</code> by clicking the <strong>+</strong> button at the top of the editor. You can tell that this string was manually added because the attributes inspector (right) shows it as <strong>Manually</strong> managed.</p>

<blockquote>
  <p><strong>Note:</strong> If a string is <strong>Automatically</strong> managed, Xcode will not allow you to edit its values in the source language within the Strings Catalog and it will delete the string if it cannot find the key referenced at compile time.</p>
</blockquote>

<p>The default value of this string (<em>Settings</em>) and the comment remain defined in the Strings Catalog, so when we can reference the string in our UI code, we can do so like the following:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Text</span><span class="p">(</span><span class="s">"settingsHeading"</span><span class="p">,</span> <span class="nv">tableName</span><span class="p">:</span> <span class="s">"Social"</span><span class="p">,</span> <span class="nv">bundle</span><span class="p">:</span> <span class="o">.</span><span class="n">module</span><span class="p">)</span>
</code></pre></div></div>

<p>Essentially, we’ve ditched the translator comment, which is an improvement, but by following this approach we’ve also gained another problem. Now that Xcode isn’t automatically managing the string in our Strings Catalog, the source of truth has shifted from the Source Code to the Strings Catalog.</p>

<p>This isn’t such a bad thing, but our Strings Catalog isn’t exposing any Swift code for us to reference in our project. Instead, we have to use string typed keys as if we’re living in the stone age.</p>

<h2 id="so-lets-define-some-constants">So lets define some Constants</h2>

<p>When you find yourself having to work with a string typed API repeatedly, especially if you need to reference the same key more than once, a common way to keep this under control is to define a set of constants to help reduce the risk of typos and maintain a bit of consistency in your project. For example:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">SocialStrings</span> <span class="p">{</span>
  <span class="kd">static</span> <span class="k">let</span> <span class="nv">settingsHeading</span><span class="p">:</span> <span class="kt">LocalizedStringKey</span> <span class="o">=</span> <span class="s">"settingsHeading"</span>
<span class="p">}</span>

<span class="c1">// ...</span>

<span class="kt">Text</span><span class="p">(</span><span class="kt">SocialStrings</span><span class="o">.</span><span class="n">settingsHeading</span><span class="p">,</span> <span class="nv">tableName</span><span class="p">:</span> <span class="s">"Social"</span><span class="p">,</span> <span class="nv">bundle</span><span class="p">:</span> <span class="o">.</span><span class="n">module</span><span class="p">)</span>
</code></pre></div></div>

<p>And starting in iOS 16 and macOS 13, we can even bring the table and bundle configuration into this constant using Foundation’s new <a href="https://developer.apple.com/documentation/foundation/localizedstringresource"><code class="language-plaintext highlighter-rouge">LocalizedStringResource</code></a> type:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">SocialStrings</span> <span class="p">{</span>
  <span class="kd">static</span> <span class="k">let</span> <span class="nv">settingsHeading</span> <span class="o">=</span> <span class="kt">LocalizedStringResource</span><span class="p">(</span>
    <span class="s">"settingsHeading"</span><span class="p">,</span>
    <span class="nv">table</span><span class="p">:</span> <span class="s">"Social"</span><span class="p">,</span>
    <span class="nv">module</span><span class="p">:</span> <span class="o">.</span><span class="nf">atURL</span><span class="p">(</span><span class="kt">Bundle</span><span class="o">.</span><span class="n">module</span><span class="o">.</span><span class="n">bundleURL</span><span class="p">)</span>
  <span class="p">)</span>
<span class="p">}</span>

<span class="c1">// ...</span>

<span class="kt">Text</span><span class="p">(</span><span class="kt">SocialStrings</span><span class="o">.</span><span class="n">settingsHeading</span><span class="p">)</span>
</code></pre></div></div>

<blockquote>
  <p><strong>Note:</strong> The <code class="language-plaintext highlighter-rouge">LocalizedStringResource</code> type isn’t supported directly by all SwiftUI views/modifiers so sometimes you need some workarounds (FB13221647).</p>

  <p>There are a few ways to workaround this in the meantime:</p>

  <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 1. Use an alternative method/overload that accepts Text</span>
<span class="kt">Button</span><span class="p">(</span><span class="nv">action</span><span class="p">:</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">})</span> <span class="p">{</span>
  <span class="kt">Text</span><span class="p">(</span><span class="kt">SocialStrings</span><span class="o">.</span><span class="n">dismissTitle</span><span class="p">)</span>
<span class="p">}</span>

<span class="c1">// 2. Use a method/overload that accepts LocalizedStringKey and then use string interpolation</span>
<span class="kt">Button</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="kt">SocialStrings</span><span class="o">.</span><span class="n">dismissTitle</span><span class="se">)</span><span class="s">"</span><span class="p">,</span> <span class="nv">action</span><span class="p">:</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">})</span>

<span class="c1">// 3. Use a method/overload that accepts String and resolve the localized value first</span>
<span class="c1">//    Note: by using this approach, custom locale information set in the environment might be ignored</span>
<span class="kt">Button</span><span class="p">(</span><span class="kt">String</span><span class="p">(</span><span class="nv">localized</span><span class="p">:</span> <span class="kt">SocialStrings</span><span class="o">.</span><span class="n">dismissTitle</span><span class="p">),</span> <span class="nv">action</span><span class="p">:</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">})</span>
</code></pre></div>  </div>
</blockquote>

<p>This is a great solution that is relatively straightforward to implement. It looks like a winner right?</p>

<h2 id="but-what-about-the-arguments">But what about the arguments?</h2>

<p>We haven’t looked at passing arguments into localized strings yet, so lets go back to the start quickly:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Text</span><span class="p">(</span><span class="s">"There are </span><span class="se">\(</span><span class="n">items</span><span class="o">.</span><span class="n">count</span><span class="se">)</span><span class="s"> pending posts"</span><span class="p">)</span>
</code></pre></div></div>

<p>With the example above, when the compiler extracts this string, it will assign the key <code class="language-plaintext highlighter-rouge">There are %lld pending posts</code> inside the Strings Catalog.</p>

<p>But we’ve decided to manually define our keys using an identifier style format and format specifiers don’t really fit in this pattern, so what do we do?</p>

<p>Because each string in the Strings Catalog has a distinct field for the Key and the Value, you can define your strings containing variables like so:</p>

<p><img src="/public/images/xcstrings/settings-variable.png" alt="A screenshot of the Strings Catalog editor in Xcode showing a string with an argument" class="center-image" /></p>

<p>The trick is then to use <code class="language-plaintext highlighter-rouge">LocalizedStringResource</code>’s <code class="language-plaintext highlighter-rouge">defaultValue</code> parameter:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">static</span> <span class="kd">func</span> <span class="nf">feedSummary</span><span class="p">(</span><span class="n">_</span> <span class="nv">count</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">LocalizedStringResource</span> <span class="p">{</span>
  <span class="kt">LocalizedStringResource</span><span class="p">(</span>
    <span class="s">"feedSummary"</span><span class="p">,</span>
    <span class="n">defaultValue</span><span class="p">:</span> <span class="s">"There are </span><span class="se">\(</span><span class="n">count</span><span class="se">)</span><span class="s"> pending posts"</span><span class="p">,</span>
    <span class="nv">table</span><span class="p">:</span> <span class="s">"Social"</span><span class="p">,</span>
    <span class="nv">module</span><span class="p">:</span> <span class="o">.</span><span class="nf">atURL</span><span class="p">(</span><span class="kt">Bundle</span><span class="o">.</span><span class="n">module</span><span class="o">.</span><span class="n">bundleURL</span><span class="p">)</span>
  <span class="p">)</span>
<span class="p">}</span>

<span class="c1">// ...</span>

<span class="kt">Text</span><span class="p">(</span><span class="kt">SocialStrings</span><span class="o">.</span><span class="nf">feedSummary</span><span class="p">(</span><span class="n">items</span><span class="o">.</span><span class="n">count</span><span class="p">))</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">defaultValue</code> parameter is not a <code class="language-plaintext highlighter-rouge">String</code>, but instead it’s a <a href="https://developer.apple.com/documentation/swift/string/localizationvalue"><code class="language-plaintext highlighter-rouge">String.LocalizationValue</code></a> type.</p>

<p>This type allows Foundation to track the values that are interpolated into the literal (such as <code class="language-plaintext highlighter-rouge">count</code>) and then use them when resolving the actual localized string from the Strings Catalog later on.</p>

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

<p>To recap on the journey that we’ve been on:</p>

<ul>
  <li>Apple encourage you to define your localized strings in source code and let the compiler copy them into your Strings Catalog.</li>
  <li>But this isn’t a great approach for long strings, scenarios where you need to provide comments, or when you need to specify a different table or bundle.</li>
  <li>The alternative is to reference manually managed strings, but this is done using a string-typed key that is prone to typos.</li>
  <li>To reduce the risk of typos, it’s a good practice to use static properties or methods to reference the localized strings in Swift instead.</li>
  <li>Using keys as identifiers to makes providing variables/arguments a bit trickier in the modern <code class="language-plaintext highlighter-rouge">LocalizedStringResource</code> type.</li>
</ul>

<p>So to conclude, we see a value in making the Strings Catalog the source of truth for all localized string content, but having to manually define helper/boilerplate accessors in Swift still has annoying downsides.</p>

<p>Hopefully you can see where I am going with this (spoiler: it’s not a Macro)…</p>

<h1 id="xcstrings-tool">XCStrings Tool</h1>

<p>I created <a href="https://github.com/liamnichols/xcstrings-tool">XCStrings Tool</a> as a modern solution to generating Swift code to interface with a Strings Catalog.</p>

<ol>
  <li>Integrate the XCStrings Tool Plugin to your target</li>
  <li>Manually define your Strings in your Strings Catalogs</li>
  <li>Reference your strings using the accessors that XCStrings Tool adds to <code class="language-plaintext highlighter-rouge">LocalizedStringResource</code></li>
</ol>

<p><img src="/public/images/xcstrings/demo.gif" alt="An animated gif showing the XCStrings Tool being used to replace string typed keys in an existing Xcode Project" /></p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Before</span>
<span class="kt">Text</span><span class="p">(</span><span class="s">"settingsHeading"</span><span class="p">,</span> <span class="nv">tableName</span><span class="p">:</span> <span class="s">"Social"</span><span class="p">,</span> <span class="nv">bundle</span><span class="p">:</span> <span class="o">.</span><span class="n">module</span><span class="p">)</span>
<span class="kt">Text</span><span class="p">(</span>
  <span class="kt">LocalizedStringResource</span><span class="p">(</span>
    <span class="s">"feedSummary"</span><span class="p">,</span>
    <span class="n">defaultValue</span><span class="p">:</span> <span class="s">"There are </span><span class="se">\(</span><span class="n">count</span><span class="se">)</span><span class="s"> pending posts"</span><span class="p">,</span>
    <span class="nv">table</span><span class="p">:</span> <span class="s">"Social"</span><span class="p">,</span>
    <span class="nv">module</span><span class="p">:</span> <span class="o">.</span><span class="nf">atURL</span><span class="p">(</span><span class="kt">Bundle</span><span class="o">.</span><span class="n">module</span><span class="o">.</span><span class="n">bundleURL</span><span class="p">)</span>
  <span class="p">)</span>
<span class="p">)</span>

<span class="c1">// After</span>
<span class="kt">Text</span><span class="p">(</span><span class="o">.</span><span class="n">social</span><span class="o">.</span><span class="n">settingsHeading</span><span class="p">)</span>
<span class="kt">Text</span><span class="p">(</span><span class="o">.</span><span class="n">social</span><span class="o">.</span><span class="nf">feedSummary</span><span class="p">(</span><span class="n">items</span><span class="o">.</span><span class="n">count</span><span class="p">))</span>
</code></pre></div></div>

<p>When added to either an Xcode Project or Swift Package target, the build tool will process each Strings Catalog and generate an extension on <code class="language-plaintext highlighter-rouge">LocalizedStringResource</code> that can be used to access each localized string within that catalog.</p>

<p>To get started with XCStrings Tool, check out the <a href="https://swiftpackageindex.com/liamnichols/xcstrings-tool/documentation/documentation#getting-started">documentation</a> hosted on the Swift Package Index.</p>

<p>You can also visit the <a href="https://github.com/liamnichols/xcstrings-tool/discussions">GitHub Discussions</a> for further support or to provide feedback.</p>]]></content><author><name>Liam Nichols</name><email>liam.nichols.ln@gmail.com</email></author><summary type="html"><![CDATA[In Xcode 15 Apple introduced Strings Catalogs, a new file format (.xcstrings) that can be used as a single source for all of your apps localized string content.]]></summary></entry><entry><title type="html">Using UIViewController’s viewIsAppearing method in Xcode 14 and earlier</title><link href="https://liamnichols.eu/2023/06/12/view-is-appearing.html" rel="alternate" type="text/html" title="Using UIViewController’s viewIsAppearing method in Xcode 14 and earlier" /><published>2023-06-12T00:00:00+00:00</published><updated>2023-06-12T00:00:00+00:00</updated><id>https://liamnichols.eu/2023/06/12/view-is-appearing</id><content type="html" xml:base="https://liamnichols.eu/2023/06/12/view-is-appearing.html"><![CDATA[<p>During WWDC 2023, Apple announced a new method on <code class="language-plaintext highlighter-rouge">UIViewController</code> called <a href="https://developer.apple.com/documentation/uikit/uiviewcontroller/4195485-viewisappearing"><code class="language-plaintext highlighter-rouge">viewIsAppearing(_:)</code></a>.</p>

<p>If you’ve ever spent way too much time trying to perfect appearance animations within your apps, this new method may just be the lifecycle callback that you had been looking for since it’s called prior to the actual appearance on-screen but after receiving the initial layout and traits.</p>

<p>What is even better is that it was announced that this method has been back-deployed all the way down to iOS 13, which is great, but if like me you want to use it in your apps today, you’ll find that unfortunately you still need to wait for Xcode 15 and the iOS 17…</p>

<p>Or do you?</p>

<h2 id="what-does-being-back-deployed-actually-mean-here">What does being back-deployed actually mean here?</h2>

<p>While Apple mentioned that this method <em>back-deploys all the way to iOS 13</em>, this is a little bit confusing. If you follow along with Swift Evolution proposals, you may well have understood this statement to have meant that the API was built using the new <code class="language-plaintext highlighter-rouge">@backDeployed</code> attribute that was proposed in <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0376-function-back-deployment.md">SE-0376</a> and implemented in Swift 5.8. This however is not the case here.</p>

<p>In this instance, the <code class="language-plaintext highlighter-rouge">viewIsAppearing(_:)</code> method has existed in UIKit since the iOS 13 SDK first shipped but the method was not made visible in the public headers that our code can see.</p>

<p>In the iOS 17 SDK, Apple have finally declared this method in the public SDK headers meaning that our code can now reference the previously private implementation that has been shipping since iOS 13. In fact, you can look for yourself in class dumps from older versions of the iOS SDK (<a href="https://developer.limneos.net/?ios=13.1.3&amp;framework=UIKitCore.framework&amp;header=UIViewController.h">example</a>).</p>

<h2 id="using-the-method-in-xcode-14-or-earlier">Using the method in Xcode 14 or earlier</h2>

<p>So if the method already existed in the iOS 13, 14, 15 and 16 SDKs, you might wonder what is stopping you from using it? Well it turns out that there is not a lot thanks to the fact that this portion of UIKit is still written in Objective-C!</p>

<p>In your project, add a new file called <strong>UIViewController+UpcomingLifecycleMethods.h</strong>:</p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#import "Availability.h"
</span>
<span class="cp">#if defined(__IPHONE_17_0)
#warning "UIViewController+UpcomingLifecycleMethods.h is redundant when compiling with the iOS 17 SDK"
#else
</span>
<span class="k">@import</span> <span class="n">UIKit</span><span class="p">;</span>

<span class="k">@interface</span> <span class="nc">UIViewController</span> <span class="p">(</span><span class="nl">UpcomingLifecycleMethods</span><span class="p">)</span>

<span class="c1">/// Called when the view is becoming visible at the beginning of the appearance transition,</span>
<span class="c1">/// after it has been added to the hierarchy and been laid out by its superview. This method</span>
<span class="c1">/// is very similar to -viewWillAppear: and is always called shortly afterwards (so changes</span>
<span class="c1">/// made in either callback will be visible to the user at the same time), but unlike</span>
<span class="c1">/// -viewWillAppear:, at the time when -viewIsAppearing: is called all of the following are</span>
<span class="c1">/// valid for the view controller and its own view:</span>
<span class="c1">///    - View controller and view's trait collection</span>
<span class="c1">///    - View's superview chain and window</span>
<span class="c1">///    - View's geometry (e.g. frame/bounds, safe area insets, layout margins)</span>
<span class="c1">/// Choose this method instead of -viewWillAppear: by default, as it is a direct replacement</span>
<span class="c1">/// that provides equivalent or superior behavior in nearly all cases.</span>
<span class="c1">///</span>
<span class="c1">/// - SeeAlso: https://developer.apple.com/documentation/uikit/uiviewcontroller/4195485-viewisappearing</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">viewIsAppearing</span><span class="p">:(</span><span class="n">BOOL</span><span class="p">)</span><span class="nv">animated</span> <span class="n">API_AVAILABLE</span><span class="p">(</span><span class="n">ios</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">tvos</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">watchos</span><span class="p">);</span>

<span class="k">@end</span>

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

<p>The next step depends on your current project setup:</p>

<ul>
  <li><strong>If you have an Objective-C project</strong>, you can go ahead and import <code class="language-plaintext highlighter-rouge">UIViewController+UpcomingLifecycleMethods.h</code> in any view controller that you need to access this method within and if you have a Swift project, you can expose this via the Bridging Header.</li>
  <li><strong>If you have a Swift project and don’t currently use a Bridging Header</strong>, open the Build Settings for your target and set <em>Objective-C Bridging Header</em> (<code class="language-plaintext highlighter-rouge">SWIFT_OBJC_BRIDGING_HEADER</code>) to <code class="language-plaintext highlighter-rouge">$(SRCROOT)/Path/To/UIViewController+UpcomingLifecycleMethods.h</code>.</li>
  <li><strong>If you have a Swift project and are already using a Bridging Header for other reasons</strong>, import <code class="language-plaintext highlighter-rouge">UIViewController+UpcomingLifecycleMethods.h</code> within your existing Bridging Header.</li>
</ul>

<p>In the header, we defined a category (extension in Swift) with the private (but soon-to-be-public) method signature. We don’t provide the method implementation because it already exists so this alone is enough to expose the method to the rest of our project.</p>

<p>In your <code class="language-plaintext highlighter-rouge">UIViewController</code> subclasses, you can now go ahead and override the method just like you can when using Xcode 15:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="kd">func</span> <span class="nf">viewIsAppearing</span><span class="p">(</span><span class="n">_</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">super</span><span class="o">.</span><span class="nf">viewIsAppearing</span><span class="p">(</span><span class="n">animated</span><span class="p">)</span>
    <span class="nf">prepareForAppearance</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Because the header file is checking for the <code class="language-plaintext highlighter-rouge">__IPHONE_17_0</code> definition, which is only available as part of Xcode 15 and the iOS 17 SDK, this header becomes redundant once you start using Xcode 15. I’ve used <code class="language-plaintext highlighter-rouge">#warning</code> to trigger a custom warning message that can serve as a helpful reminder to come back in September and clean up.</p>

<blockquote>
  <p><strong>Warning</strong>: While you can do this with many other private methods, remember that private API wasn’t necessarily designed to be consumed by other developers and its behavior might well change (or be removed entirely) in future releases, which would likely break your app.</p>

  <p>In this instance, we don’t have these same concerns because we know that Apple is making this API public moving forward. But you should still remember that this API is technically <em>private</em> today meaning that there is still a small chance that it might be rejected during App Review. I haven’t yet verified myself that apps currently referencing this API won’t be rejected, so submit for review at your own discretion.</p>
</blockquote>

<h3 id="alternative-approach">Alternative Approach</h3>

<p>Alternatively, if you work with a fully Swift-based project (i.e Swift Playgrounds) or don’t want the additional complexity of managing a bridging header, you could also achieve similar results with an extension:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#if swift(&lt;5.9) // A similar check for the iOS 17 SDK assuming you don't use custom toolchains</span>
<span class="kd">extension</span> <span class="kt">UIViewController</span> <span class="p">{</span>
    <span class="kd">@objc</span>
    <span class="kd">@available</span><span class="p">(</span><span class="n">iOS</span><span class="p">,</span> <span class="nv">introduced</span><span class="p">:</span> <span class="mf">13.0</span><span class="p">)</span>
    <span class="kd">@available</span><span class="p">(</span><span class="n">tvOS</span><span class="p">,</span> <span class="nv">introduced</span><span class="p">:</span> <span class="mf">13.0</span><span class="p">)</span>
    <span class="kd">func</span> <span class="nf">viewIsAppearing</span><span class="p">(</span><span class="n">_</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
        <span class="nf">assertionFailure</span><span class="p">(</span><span class="s">"The UIKit implementation was not called as expected"</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="cp">#endif</span>
</code></pre></div></div>

<p>This approach works in a similar way by exposing an <code class="language-plaintext highlighter-rouge">@objc</code> method called <code class="language-plaintext highlighter-rouge">viewIsAppearing(_:)</code> for your app, but it relies on a <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html#//apple_ref/doc/uid/TP40011210-CH6-SW4">method name clash</a> for the super calls to actually call the UIKit implementation rather than the one that you defined above.</p>

<p>I’ve tried this in a test project and it seems to work as expected, but there is a small risk involved here which is why it would be more preferred to define the Objective-C header instead if possible.</p>]]></content><author><name>Liam Nichols</name><email>liam.nichols.ln@gmail.com</email></author><summary type="html"><![CDATA[During WWDC 2023, Apple announced a new method on UIViewController called viewIsAppearing(_:).]]></summary></entry><entry><title type="html">Building Swift Packages as a Universal Binary</title><link href="https://liamnichols.eu/2020/08/01/building-swift-packages-as-a-universal-binary.html" rel="alternate" type="text/html" title="Building Swift Packages as a Universal Binary" /><published>2020-08-01T00:00:00+00:00</published><updated>2020-08-01T00:00:00+00:00</updated><id>https://liamnichols.eu/2020/08/01/building-swift-packages-as-a-universal-binary</id><content type="html" xml:base="https://liamnichols.eu/2020/08/01/building-swift-packages-as-a-universal-binary.html"><![CDATA[<p>So following Apple’s announcement during WWDC 2020 that they’ll be transitioning the Mac away from Intel processors to Apple Silicon it’s now time for everybody to get their software ready.</p>

<p>The transition this time can be considered somewhat easier for most people, especially those who are already supporting arm64 on iOS but there is still work to be done to ensure that tooling and pre-compiled distributions support both architectures ready for when Mac using Apple Silicon are made publicly available. If you haven’t already seen it, a lot of this is covered in the <a href="https://developer.apple.com/videos/play/wwdc2020/10214/">Port your Mac app to Apple Silicon</a> WWDC Session Video.</p>

<p>If you’re using Xcode to compile your command line tools then things are pretty simple as long as you are setting the <code class="language-plaintext highlighter-rouge">ARCHS</code> build setting to <code class="language-plaintext highlighter-rouge">$(ARCHS_STANDARD)</code> (the default). In Xcode 12, this value is described as <strong>Standard Architectures (64-bit Intel and ARM)</strong> but if you’re using Swift Package Manager to build and distribute your binary or library, there is no such option.</p>

<p>Instead, starting in Swift Package Manager for Swift 5.3 (Xcode 12), the <code class="language-plaintext highlighter-rouge">swift-build</code> executable has now introduced the <code class="language-plaintext highlighter-rouge">--arch</code> option (<a href="https://github.com/apple/swift-package-manager/pull/2787">apple/swift-package-manager#2787</a>).</p>

<h2 id="building-a-universal-binary">Building a Universal Binary</h2>

<p>Firstly, make sure that you are using the correct version of Xcode/Swift:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ xcrun swift build --version
Swift Package Manager - Swift 5.3.0
</code></pre></div></div>

<p><em><strong>Note:</strong> If this is not Swift 5.3 or greater, use <code class="language-plaintext highlighter-rouge">xcode-select -s</code> to switch to the Xcode 12 beta.</em></p>

<p>Now, when compiling your package, specify both architectures to compile a Universal Binary:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ xcrun swift build -c release --arch arm64 --arch x86_64
</code></pre></div></div>

<p>To verify that your built binary contains both architectures, you can use the <code class="language-plaintext highlighter-rouge">lipo -info</code> command to inspect a binary and confirm:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ lipo -info .build/apple/Products/Release/swiftlint
Architectures in the fat file: .build/apple/Products/Release/swiftlint are: x86_64 arm64
</code></pre></div></div>

<p>And there you have it, building your Swift Package as a Universal Binary is as simple as that!</p>]]></content><author><name>Liam Nichols</name><email>liam.nichols.ln@gmail.com</email></author><summary type="html"><![CDATA[So following Apple’s announcement during WWDC 2020 that they’ll be transitioning the Mac away from Intel processors to Apple Silicon it’s now time for everybody to get their software ready.]]></summary></entry><entry><title type="html">Firebase Remote Config and Swift</title><link href="https://liamnichols.eu/2017/02/06/firebase-remote-config-and-swift.html" rel="alternate" type="text/html" title="Firebase Remote Config and Swift" /><published>2017-02-06T00:00:00+00:00</published><updated>2017-02-06T00:00:00+00:00</updated><id>https://liamnichols.eu/2017/02/06/firebase-remote-config-and-swift</id><content type="html" xml:base="https://liamnichols.eu/2017/02/06/firebase-remote-config-and-swift.html"><![CDATA[<p>So you’ve seen Firebase Remote Config and decided that it would go great in your Swift project? You’re right, it will but if like me you’ve noticed that the design of the SDK doesn’t play very well with all of your other beautiful looking strictly typed Swift code then this post is for you.</p>

<p><em><strong>Note:</strong> I don’t cover setting up Firebase in this post, just using <code class="language-plaintext highlighter-rouge">FIRRemoteConfig</code> in a configured project. Check out <a href="https://www.raywenderlich.com/143712/firebase-remote-config-tutorial-for-ios">one</a> <a href="https://firebase.google.com/docs/remote-config/use-config-ios">of</a> <a href="https://www.youtube.com/watch?v=zdVc8aZZT-I">these</a> guides for help with the setup part.</em></p>

<p>Lets take this basic example:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">count</span> <span class="o">=</span> <span class="kt">FIRRemoteConfig</span><span class="o">.</span><span class="nf">remoteConfig</span><span class="p">()[</span><span class="s">"maximum_item_count"</span><span class="p">]</span><span class="o">.</span><span class="n">numberValue</span><span class="p">?</span><span class="o">.</span><span class="n">intValue</span> <span class="p">??</span> <span class="mi">10</span>
</code></pre></div></div>

<p>There are a few problems here:</p>

<ul>
  <li>Damn it doesn’t look great.</li>
  <li><code class="language-plaintext highlighter-rouge">numberValue</code> returns an optional.</li>
  <li>The key/value pattern means that you can’t guarantee that you’ve not made a typo or updated the key name somewhere else.</li>
  <li>Because of the optional, you either have to force unwrap or have a fallback.</li>
  <li>Carelessly force unwrapping is never a good thing and defeats the object of Swift’s type safety.</li>
  <li>Having a fallback defeats the object of the nice default values that you specify upon initialising Remote Config.</li>
  <li>Littering all your classes with <code class="language-plaintext highlighter-rouge">import FirebaseRemoteConfig</code> will probably be a pain to undo once you decide to move away from Firebase.</li>
</ul>

<p>It would be a lot nicer if we could do something like this instead:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">count</span> <span class="o">=</span> <span class="kt">Config</span><span class="o">.</span><span class="n">shared</span><span class="o">.</span><span class="n">maxItemCount</span>
</code></pre></div></div>

<hr />

<h1 id="configswift">Config.swift</h1>

<p>The interface for my <code class="language-plaintext highlighter-rouge">Config</code> class is pretty simple:</p>

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

<span class="kd">final</span> <span class="kd">class</span> <span class="kt">Config</span> <span class="p">{</span>

    <span class="c1">/// The shared instance of config to use</span>
    <span class="kd">static</span> <span class="k">let</span> <span class="nv">shared</span><span class="p">:</span> <span class="kt">Config</span> <span class="o">=</span> <span class="kt">Config</span><span class="p">()</span>

    <span class="c1">/// The maximum number of items that are allowed in this mystery app</span>
    <span class="k">let</span> <span class="nv">maxItemCount</span><span class="p">:</span> <span class="kt">Int</span>

    <span class="c1">/// The initialiser is private as intended use is via the `shared` static property.</span>
    <span class="kd">private</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>

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

<p>The idea is simple: Initialise all the properties on the shared instance after performing the initial fetch and then trigger another fetch after so that we can be ready to load any changes <strong>the next time the app launches</strong>.</p>

<p>This is intentional to ensure that the values in <code class="language-plaintext highlighter-rouge">Config</code> are all fetched from a consistent data source (i.e to avoid accidentally reading one default value before calling <code class="language-plaintext highlighter-rouge">activateFetched</code> and then another remote value after the fetch completed).</p>

<p>As a result, the initialiser looks like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 1. Configure for dev mode if we need it, otherwise a 1 hour expiration duration</span>
<span class="k">let</span> <span class="nv">remoteConfig</span> <span class="o">=</span> <span class="kt">FIRRemoteConfig</span><span class="o">.</span><span class="nf">remoteConfig</span><span class="p">()</span>
<span class="cp">#if DEBUG</span>
    <span class="k">let</span> <span class="nv">expirationDuration</span><span class="p">:</span> <span class="kt">TimeInterval</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">remoteConfig</span><span class="o">.</span><span class="n">configSettings</span> <span class="o">=</span> <span class="kt">FIRRemoteConfigSettings</span><span class="p">(</span><span class="nv">developerModeEnabled</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span><span class="o">!</span>
<span class="cp">#else</span>
    <span class="k">let</span> <span class="nv">expirationDuration</span><span class="p">:</span> <span class="kt">TimeInterval</span> <span class="o">=</span> <span class="mi">3600</span>
<span class="cp">#endif</span>

<span class="c1">// 2. Set our default values and keys</span>
<span class="n">remoteConfig</span><span class="o">.</span><span class="nf">setDefaults</span><span class="p">([</span>
    <span class="s">"maximum_item_count"</span><span class="p">:</span> <span class="mi">42</span> <span class="k">as</span> <span class="kt">NSNumber</span>
<span class="p">])</span>

<span class="c1">// 3. Activate any fetched values before we read anything back</span>
<span class="n">remoteConfig</span><span class="o">.</span><span class="nf">activateFetched</span><span class="p">()</span>

<span class="c1">// 4. Now set the properties on config based on what we have currently</span>
<span class="k">self</span><span class="o">.</span><span class="n">maxItemCount</span> <span class="o">=</span> <span class="n">remoteConfig</span><span class="p">[</span><span class="s">"maximum_item_count"</span><span class="p">]</span><span class="o">.</span><span class="n">numberValue</span><span class="o">!.</span><span class="n">intValue</span>

<span class="c1">// 5. Perform the next fetch so that it's ready when we re-launch</span>
<span class="n">remoteConfig</span><span class="o">.</span><span class="nf">fetch</span><span class="p">(</span><span class="nv">withExpirationDuration</span><span class="p">:</span> <span class="n">expirationDuration</span><span class="p">)</span> <span class="p">{</span> <span class="n">status</span><span class="p">,</span> <span class="n">_</span> <span class="k">in</span>
    <span class="nf">print</span><span class="p">(</span><span class="s">"[Config] Fetch completed with status:"</span><span class="p">,</span> <span class="n">status</span><span class="p">,</span> <span class="s">"(</span><span class="se">\(</span><span class="n">status</span><span class="o">.</span><span class="n">rawValue</span><span class="se">)</span><span class="s">)"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here is a breakdown of what we are doing:</p>

<ol>
  <li>If the app is running in debug mode, I enable dev mode and disable the <code class="language-plaintext highlighter-rouge">expirationDuration</code> so that the config refreshes each time. This is very handy during development but will get you throttled server side if you release something like that to production.</li>
  <li>Set the default keys and values. I’ve opted to do this in code and not by using a plist so that I can have visibility of all the keys and values later on when I fetch them.</li>
  <li>Activate any fetched parameters from the previous launch before we attempt to read them.</li>
  <li>Read the fetched or default parameters back and set them as instance variables.</li>
  <li>Perform a fetch asynchronously to get any changes that we can then activate the next time we launch the app.</li>
</ol>

<p>There are still a few non-swifty looking bits here because I’m not using any form of constants to define the duplicate usage of <code class="language-plaintext highlighter-rouge">maximum_item_count</code> and I’m also force unwrapping the value however it does come with the following upsides:</p>

<ul>
  <li>Only requires a single unit test to ensure that any of the force unwrapping isn’t causing a crash.</li>
  <li>All the non-swifty looking code is isolated in a single file instead of across my entire project.</li>
  <li>I could easily update the <code class="language-plaintext highlighter-rouge">Config</code> class in the future to completely remove the dependancy of Firebase from my project if I wanted to.</li>
  <li>The rest of my code looks fabulous (kinda).</li>
</ul>

<p>The complete class can be found <a href="https://gist.github.com/liamnichols/4f1122cef22d3ddafc8d0b87f034914c">here</a> if you wish to grab a copy. Enjoy!</p>

<hr />

<h3 id="note">Note</h3>

<p>Due to the nature of Swift, the static <code class="language-plaintext highlighter-rouge">shared</code> property won’t be initialised until you try to access it. This means that it might be useful doing something like the following in your AppDelegate if you want to ensure that the next fetch is performed as soon as possible:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">application</span><span class="p">(</span><span class="n">_</span> <span class="nv">application</span><span class="p">:</span> <span class="kt">UIApplication</span><span class="p">,</span> <span class="n">didFinishLaunchingWithOptions</span> <span class="nv">launchOptions</span><span class="p">:</span> <span class="p">[</span><span class="kt">UIApplicationLaunchOptionsKey</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]?)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>

    <span class="kt">FIRApp</span><span class="o">.</span><span class="nf">configure</span><span class="p">()</span>
    <span class="n">_</span> <span class="o">=</span> <span class="kt">Config</span><span class="o">.</span><span class="n">shared</span>

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

<p>I’m sure that could be done in a nicer way but I’ll let you figure that out :)</p>]]></content><author><name>Liam Nichols</name><email>liam.nichols.ln@gmail.com</email></author><summary type="html"><![CDATA[So you’ve seen Firebase Remote Config and decided that it would go great in your Swift project? You’re right, it will but if like me you’ve noticed that the design of the SDK doesn’t play very well with all of your other beautiful looking strictly typed Swift code then this post is for you.]]></summary></entry><entry><title type="html">Using Small Caps in UIKit</title><link href="https://liamnichols.eu/2016/09/11/using-small-caps-in-uikit.html" rel="alternate" type="text/html" title="Using Small Caps in UIKit" /><published>2016-09-11T00:00:00+00:00</published><updated>2016-09-11T00:00:00+00:00</updated><id>https://liamnichols.eu/2016/09/11/using-small-caps-in-uikit</id><content type="html" xml:base="https://liamnichols.eu/2016/09/11/using-small-caps-in-uikit.html"><![CDATA[<p>After watching the <a href="https://developer.apple.com/videos/play/wwdc2016/803/" target="\_blank">WWDC 2016 Session 803</a> video on Typography and Fonts I decided that this would be a good place to start with the whole blog posts thing.</p>

<p>It turns out that fonts are actually very complex things but to keep this short and sweet, I’m just going to talk about one thing that caught my eye.</p>

<h1 id="small-caps">Small Caps</h1>

<p>Usually you only ever come across uppercase or lowercase letters however, small caps bring an additional member to this group. They’re essentially a smaller version of uppercase letters that <em>almost</em><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> align with the lowercase letters in a font.</p>

<p><img src="/public/images/fonts/abc.png" alt="A comparison of each glyph type" class="center-image" />
<em>Good: The text in the centre is rendered using the same font but with the small caps feature enabled.</em></p>

<p>You might be wondering why you don’t just use a smaller point size instead? Well if you like your interfaces to be pixel perfect (don’t we all?) then you will notice that after you found the appropriate point size, other features of the font such as the weight and spacing are also adjusted meaning that your two fonts will look different next to each other.</p>

<p><img src="/public/images/fonts/abc-bad.png" alt="Why should you use small caps you ask?" class="center-image" />
<em>Bad: The text in the centre is the same font but 10 points smaller than the other labels.</em></p>

<p>Small caps allow the font designer to actually add an additional glyph specifically designed to be used in this case. This means that the font is correctly optimised resulting in something that looks a little like the first picture rather than the second. A minor difference but a good one.</p>

<p>This does however mean that in order to use small caps, the font designer must have explicitly supported this feature.</p>

<h1 id="use-cases">Use Cases</h1>

<p>Small caps are designed to be subtle and can come in very handy when you’re trying to perfect your designs. The case study from Apple demonstrates how they are used on the Apple TV to distinguish a title in a table without it drawing the users attention away from the actual content.</p>

<p><img src="/public/images/fonts/appletv.jpg" alt="An example used within the Apple TV" class="center-image" />
<em>The Director, Cast and Writer headings use small caps to keep the text size and alignment consistent with the rest of the content.</em></p>

<p>Another use could be to offer a subtle hierarchy of information. For example, if you wanted to display a number but did not want to emphasise the text next to it then small caps could be used to offer a nicer alternative to just using lowercase letters.</p>

<p><img src="/public/images/fonts/12am.png" alt="Example if a 12 hour clock" class="center-image" />
<em>An example could be showing the time in a 12 hour format.</em></p>

<h1 id="implementation">Implementation</h1>

<p>Small caps can be enabled on a font by enabling their relative Font Feature. The documentation for this is a bit patchy but for actual information around the available font features you can visit the <a href="https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html" target="\_blank">fonts section</a> of the developer site.</p>

<p>To actually take advantage of the font features in code you can do this at a fairly high level by modifying a <code class="language-plaintext highlighter-rouge">UIFontDescriptor</code> (The same also applies for <code class="language-plaintext highlighter-rouge">NSFontDescriptor</code> on macOS).</p>

<p>Lets take a look at a simple implementation:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">systemFont</span> <span class="o">=</span> <span class="kt">UIFont</span><span class="o">.</span><span class="nf">systemFont</span><span class="p">(</span><span class="nv">ofSize</span><span class="p">:</span> <span class="mf">24.0</span><span class="p">,</span> <span class="nv">weight</span><span class="p">:</span> <span class="kt">UIFontWeightLight</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">smallCapsDesc</span> <span class="o">=</span> <span class="n">systemFont</span><span class="o">.</span><span class="n">fontDescriptor</span><span class="o">.</span><span class="nf">addingAttributes</span><span class="p">([</span>
    <span class="kt">UIFontDescriptorFeatureSettingsAttribute</span><span class="p">:</span> <span class="p">[</span>
        <span class="p">[</span>
            <span class="kt">UIFontFeatureTypeIdentifierKey</span><span class="p">:</span> <span class="n">kUpperCaseType</span><span class="p">,</span>
            <span class="kt">UIFontFeatureSelectorIdentifierKey</span><span class="p">:</span> <span class="n">kUpperCaseSmallCapsSelector</span>
        <span class="p">]</span>
    <span class="p">]</span>
<span class="p">])</span>
<span class="k">let</span> <span class="nv">font</span> <span class="o">=</span> <span class="kt">UIFont</span><span class="p">(</span><span class="nv">descriptor</span><span class="p">:</span> <span class="n">smallCapsDesc</span><span class="p">,</span> <span class="nv">size</span><span class="p">:</span> <span class="n">systemFont</span><span class="o">.</span><span class="n">pointSize</span><span class="p">)</span>
</code></pre></div></div>

<p>Here is a breakdown of the above code:</p>

<ol>
  <li>Get an existing font descriptor from a font of our choice.</li>
  <li>Create a new font descriptor adding additional attributes via the <code class="language-plaintext highlighter-rouge">addingAttributes(_:)</code> method.</li>
  <li>Specify the additional font features we would like via the <code class="language-plaintext highlighter-rouge">UIFontDescriptorFeatureSettingsAttribute</code> attribute key.</li>
  <li>Create a new font object with the new font descriptor and the original point size.</li>
</ol>

<p>The <code class="language-plaintext highlighter-rouge">UIFontDescriptorFeatureSettingsAttribute</code> attribute is in a bit of a weird structure however it’s simple once you understand it.</p>

<blockquote>
  <p>An array of dictionaries representing non-default font feature settings. Each dictionary contains <code class="language-plaintext highlighter-rouge">UIFontFeatureTypeIdentifierKey</code> and <code class="language-plaintext highlighter-rouge">UIFontFeatureSelectorIdentifierKey</code>.</p>
</blockquote>

<p>So we essentially want an array of dictionaries containing both the feature selector and type identifier. These values map back to the values referenced in the TrueType Font Feature documentation I <a href="https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html" target="\_blank">linked to earlier</a>.</p>

<p>You can also find the provided constants in <code class="language-plaintext highlighter-rouge">&lt;CoreText/SFNTLayoutTypes.h&gt;</code>. Note that there is no nice link between feature types and their supported selectors so you just have to read the comments within the header.</p>

<h1 id="tips--tricks">Tips &amp; Tricks</h1>

<h2 id="different-combinations">Different Combinations</h2>

<p>There are actually a couple of ways to use small caps and there are some things to note because you might actually want to use them in a different way to get your desired outcome.</p>

<p>Looking back into the <code class="language-plaintext highlighter-rouge">SFNTLayoutTypes.h</code> header, you will see that there is both a <code class="language-plaintext highlighter-rouge">kUpperCaseType</code> and <code class="language-plaintext highlighter-rouge">kLowerCaseType</code> feature. This gives you two different ways in that you can apply small caps and it essentially means you either make all uppercase letters into small caps or make all lowercase letters into small caps.</p>

<p>By mixing the input text and the feature type that you use, you will get a different output. I’ve put together the table to show the differences.</p>

<p><img src="/public/images/fonts/comparison.png" alt="A comparison between the different options" class="center-image" /></p>

<p>As you can see from the above example, you will need to choose the correct combination of input text along with the feature type that you decide to use. For example, you will probably never want to use a capitalised string in conjunction with <code class="language-plaintext highlighter-rouge">kUpperCaseType</code>.</p>

<h2 id="numbers-and-punctuation">Numbers and Punctuation</h2>

<p>Numbers and punctuation will also be treated as uppercase letters when a small caps feature is applied meaning they are also shrunk down. This can be useful in some cases but if you don’t want to apply small caps to these then you need to make sure you use an <code class="language-plaintext highlighter-rouge">NSAttributedString</code> where you only apply the small caps font to the parts you wish to modify.</p>

<h2 id="extension">Extension</h2>

<p>You might have noticed that code example above to achieve small caps is kind of bloated when you compare it to a one line <code class="language-plaintext highlighter-rouge">UIFont</code> initialiser. Below you can find a sample extension I’ve put together to make this a little bit simpler.</p>

<!-- <script src="https://gist.github.com/liamnichols/56736b4988c57a33ad70086a0dc6018b.js"></script> -->
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">extension</span> <span class="kt">UIFont</span> <span class="p">{</span>

    <span class="c1">/// Helper method to create a UIFont with updated attributes applied to the UIFontDescriptor</span>
    <span class="c1">///</span>
    <span class="c1">/// - parameter attributes: The new attributes to apply to the fontDescriptor</span>
    <span class="c1">///</span>
    <span class="c1">/// - returns: A UIFont object with the new attributes appended to the receivers fontDescriptor</span>
    <span class="kd">func</span> <span class="nf">addingAttributes</span><span class="p">(</span><span class="n">_</span> <span class="nv">attributes</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span> <span class="p">:</span> <span class="kt">Any</span><span class="p">]</span> <span class="o">=</span> <span class="p">[:])</span> <span class="o">-&gt;</span> <span class="kt">UIFont</span> <span class="p">{</span>

        <span class="k">return</span> <span class="kt">UIFont</span><span class="p">(</span><span class="nv">descriptor</span><span class="p">:</span> <span class="n">fontDescriptor</span><span class="o">.</span><span class="nf">addingAttributes</span><span class="p">(</span><span class="n">attributes</span><span class="p">),</span> <span class="nv">size</span><span class="p">:</span> <span class="n">pointSize</span><span class="p">)</span>
    <span class="p">}</span>


    <span class="c1">/// Returns a UIFont object based on the receiver with small caps applied to upper case letters</span>
    <span class="k">var</span> <span class="nv">addingUpperCaseSmallCaps</span><span class="p">:</span> <span class="kt">UIFont</span> <span class="p">{</span>

        <span class="k">return</span> <span class="nf">addingAttributes</span><span class="p">([</span>

            <span class="kt">UIFontDescriptorFeatureSettingsAttribute</span><span class="p">:</span> <span class="p">[</span>
                <span class="p">[</span>
                    <span class="kt">UIFontFeatureTypeIdentifierKey</span><span class="p">:</span> <span class="n">kUpperCaseType</span><span class="p">,</span>
                    <span class="kt">UIFontFeatureSelectorIdentifierKey</span><span class="p">:</span> <span class="n">kUpperCaseSmallCapsSelector</span>
                <span class="p">]</span>
            <span class="p">]</span>
        <span class="p">])</span>
    <span class="p">}</span>

    <span class="c1">/// Returns a UIFont object based on the receiver with small caps applied to lower case letters</span>
    <span class="k">var</span> <span class="nv">addingLowerCaseSmallCaps</span><span class="p">:</span> <span class="kt">UIFont</span> <span class="p">{</span>

        <span class="k">return</span> <span class="nf">addingAttributes</span><span class="p">([</span>

            <span class="kt">UIFontDescriptorFeatureSettingsAttribute</span><span class="p">:</span> <span class="p">[</span>
                <span class="p">[</span>
                    <span class="kt">UIFontFeatureTypeIdentifierKey</span><span class="p">:</span> <span class="n">kLowerCaseType</span><span class="p">,</span>
                    <span class="kt">UIFontFeatureSelectorIdentifierKey</span><span class="p">:</span> <span class="n">kLowerCaseSmallCapsSelector</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>If you have any suggestions to improve the extension then please leave a comment on <a href="https://gist.github.com/liamnichols/56736b4988c57a33ad70086a0dc6018b#file-smallcaps-swift" target="\_blank">the gist</a>.</p>

<hr />

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p><a href="https://developer.apple.com/videos/play/wwdc2016-803/?time=1327" target="\_blank">Apple say</a> that the small caps glyph should be slightly larger than the lowercase alternative however they are exactly the same in the San Francisco font from what I can see. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Liam Nichols</name><email>liam.nichols.ln@gmail.com</email></author><summary type="html"><![CDATA[After watching the WWDC 2016 Session 803 video on Typography and Fonts I decided that this would be a good place to start with the whole blog posts thing.]]></summary></entry><entry><title type="html">Introduction</title><link href="https://liamnichols.eu/2016/09/09/introduction.html" rel="alternate" type="text/html" title="Introduction" /><published>2016-09-09T00:00:00+00:00</published><updated>2016-09-09T00:00:00+00:00</updated><id>https://liamnichols.eu/2016/09/09/introduction</id><content type="html" xml:base="https://liamnichols.eu/2016/09/09/introduction.html"><![CDATA[<h3 id="the-start-of-something-new">The start of something new</h3>

<p>I’ve just spent the last four days in Aberystwyth, Wales at <a href="http://iosdevuk.com/">iOS Dev UK</a>.
Something I’ve done two years in a row now and I’ve loved every minute of it.</p>

<p>Back in 2015 on the 4 hour train journey back to Bristol I told myself that I wanted to start getting involved with the community more. The idea was that I would start a blog to share things I learn as I go about my day to day job as an iOS Developer.</p>

<p>Fast forward a year and I find myself on the exact same journey home with the exact same feeling I had last year. This time I’m forcing myself to do something about it so here I am starting a blog. I’m pretty new to all of this so you’ll probably find that it’s going to be a bit crap to start with but hey, we’ve all got to start somewhere right? Feedback is welcome!</p>

<p>So anyway. I’ve got a day off work today and I’m about to embark on another 3 hour train journey to visit some family over the weekend so now is the perfect time to get an actual development related blog post up right? Wish me luck.</p>

<h3 id="about-me">About Me</h3>

<p>My name is Liam Nichols, I’m currently a Senior iOS Developer at Rockpool Digital in Bristol.
I also twitter as <a href="https://twitter.com/liamnichols_">@liamnichols_</a> on the Internet from time to time.</p>

<p>After finding <a href="https://www.amazon.co.uk/iPhone-Application-Development-Dummies-Computers/dp/0470487372">this book</a> in my local Library back sometime around 2011, I found myself bodging apps together when I wasn’t busy stacking shelves at Asda.
By doing this, I was able to work my way into a <em>real</em> job as an iOS Developer across the country in Bristol and with the support of some great people, I’m still here over 3 and a half years later and loving every minute of it.</p>

<p>That should probably do it for now but if for some reason you want to find out more then feel free to <a href="https://twitter.com/liamnichols_">tweet me</a> or email me at <a href="mailto:liam.nichols.ln@gmail.com">liam.nichols.ln@gmail.com</a>.</p>]]></content><author><name>Liam Nichols</name><email>liam.nichols.ln@gmail.com</email></author><summary type="html"><![CDATA[The start of something new]]></summary></entry></feed>