<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://mickf.net/tech/feed/by_tag/ios.xml" rel="self" type="application/atom+xml" /><link href="https://mickf.net/tech/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-02-10T16:06:23+00:00</updated><id>https://mickf.net/tech/feed/by_tag/ios.xml</id><title type="html">Mick F</title><subtitle>Notes for myself and fellow programmers,  with a focus on iOS, Swift and web frontend development.  Since 2009.</subtitle><entry><title type="html">Generating JWT Tokens in Swift for the App Store Connect API</title><link href="https://mickf.net/tech/jwt-tokens-app-store-connect-api-swift/" rel="alternate" type="text/html" title="Generating JWT Tokens in Swift for the App Store Connect API" /><published>2024-11-25T00:00:00+00:00</published><updated>2024-11-25T00:00:00+00:00</updated><id>https://mickf.net/tech/jwt-tokens-app-store-connect-api-swift</id><content type="html" xml:base="https://mickf.net/tech/jwt-tokens-app-store-connect-api-swift/">&lt;p&gt;Using the &lt;a href=&quot;https://developer.apple.com/documentation/appstoreconnectapi&quot;&gt;App Store Connect API&lt;/a&gt; can significantly streamline workflows. For
example, I use it to onboard new TestFlight testers for my apps much faster than
through &lt;code&gt;appstoreconnect.apple.com&lt;/code&gt;. While Apple provides a &lt;a href=&quot;https://developer.apple.com/documentation/appstoreconnectapi/generating-tokens-for-api-requests&quot;&gt;&lt;em&gt;Generating Tokens
for API Requests&lt;/em&gt;&lt;/a&gt; guide, this post shows how to create such tokens in Swift.&lt;/p&gt;
&lt;h2&gt;Generating JWT Tokens with JWTKit&lt;/h2&gt;
&lt;p&gt;The first step in using the API is to create JWT tokens. For this, I’ve used
&lt;a href=&quot;https://github.com/vapor/jwt-kit&quot;&gt;&lt;code&gt;vapor/jwt-kit&lt;/code&gt;&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h3&gt;Step 1: Define the Payload&lt;/h3&gt;
&lt;p&gt;Start by defining the payload structure expected by Apple:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;c1&quot;&gt;/// A JWT payload structure for App Store Connect APIs.&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppStoreConnectPayload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;JWTPayload&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;iss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;IssuerClaim&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;iat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;IssuedAtClaim&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ExpirationClaim&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;aud&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AudienceClaim&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]?&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// Creates a new payload for App Store Connect.&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;iss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;iat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;aud&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iss&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;IssuerClaim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;IssuedAtClaim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ExpirationClaim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aud&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AudienceClaim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aud&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// Verifies that the payload is valid and not expired.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// - Throws: An error if the payload is invalid or expired.&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;JWTAlgorithm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;verifyNotExpired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;Step 2: Generate the Token&lt;/h3&gt;
&lt;p&gt;Once the payload is ready, create the token. Below is an example using sample
data (based on Apple’s guide) and a dummy private key. Be sure to replace these
with your actual values:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
    -----BEGIN PRIVATE KEY-----
    MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgqIuh45D/4x9KGZ9v
    …
    uJcVQDj14FbV2QLpTbrNtk82W6ZMJT+FAZaJsx5Xiu9u83rbeICgZy3G
    -----END PRIVATE KEY-----
    &quot;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;JWTKeyCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;kid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;JWKIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2X9R4HXF34&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ES256PrivateKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ecdsa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;kid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;kid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keyIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;typ&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;JWT&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You now have a JWT token! 🎉&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Using the Code as a Dependency&lt;/h2&gt;
&lt;p&gt;If you prefer not to write your own implementation, you can use the
&lt;a href=&quot;https://github.com/dirtyhenry/swift-hoods&quot;&gt;&lt;code&gt;dirtyhenry/swift-hoods&lt;/code&gt;&lt;/a&gt; package I created. It provides a &lt;code&gt;jwtFactory&lt;/code&gt;
dependency, compatible with &lt;a href=&quot;https://github.com/pointfreeco/swift-dependencies&quot;&gt;&lt;code&gt;pointfreeco/swift-dependencies&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here’s how to integrate it:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;@Dependency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(\&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jwtFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;jwtFactory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And in your application’s single entry-point:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;jwtFactory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;LiveJWTFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jwtFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addES256Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;privateKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;keyIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keyIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;newTransport&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;withDependencies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jwtFactory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jwtFactory&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppStoreConnectAPIAuthorizationTransport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Additional Tips&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Managing Secrets.&lt;/strong&gt; Storing private keys and secrets securely is crucial.
Check out the &lt;a href=&quot;https://github.com/dirtyhenry/swift-blocks/blob/main/Sources/Blocks/Security/GenericPasswordKeychainItem.swift&quot;&gt;&lt;code&gt;GenericPasswordKeychainItem&lt;/code&gt;&lt;/a&gt; class for handling keychain
data on iOS and macOS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request Integration.&lt;/strong&gt; To include the token with every API request, consider
using &lt;a href=&quot;https://swiftpackageindex.com/dirtyhenry/swift-blocks/main/documentation/blocks/transport#Transports-Toolbox&quot;&gt;a dedicated &lt;code&gt;Transport&lt;/code&gt;&lt;/a&gt;. This makes it easy to compose behaviors
around a &lt;code&gt;URLSession&lt;/code&gt; instance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testing Tokens.&lt;/strong&gt; Use the &lt;code&gt;date&lt;/code&gt; dependency from
&lt;a href=&quot;https://github.com/pointfreeco/swift-dependencies&quot;&gt;&lt;code&gt;pointfreeco/swift-dependencies&lt;/code&gt;&lt;/a&gt; to simulate different expiration
scenarios when testing token verification&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these steps, you can efficiently generate JWT tokens in Swift and
streamline your use of the App Store Connect API. Happy coding!&lt;/p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol&gt;
&lt;li id=&quot;fn1&quot;&gt;
&lt;p&gt;One day, I would love to get rid of that dependency and use only &lt;code&gt;CryptoKit&lt;/code&gt;
to generate tokens. If you could help, let me know. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot;&gt;
&lt;p&gt;Check out &lt;a href=&quot;https://github.com/dirtyhenry/swift-hoods/blob/main/Tests/HoodsTests/JWTFactoryTests.swift&quot;&gt;&lt;code&gt;JWTFactoryTests&lt;/code&gt; in &lt;code&gt;dirtyhenry/swift-hoods&lt;/code&gt;&lt;/a&gt; to see those
tests. &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content><author><name>Mick F</name></author><category term="Open Sourcing" /><category term="macOS" /><category term="iOS" /><category term="Swift" /><summary type="html">Learn how to generate JWT tokens in Swift to authorize requests to the App Store Connect API. This guide covers creating payloads, signing tokens with JWTKit, managing secrets securely, and integrating the process into your macOS and iOS apps.</summary></entry><entry><title type="html">Slugify in Swift with applyingTransform</title><link href="https://mickf.net/tech/slugify-in-swift/" rel="alternate" type="text/html" title="Slugify in Swift with applyingTransform" /><published>2024-07-03T00:00:00+00:00</published><updated>2024-07-03T00:00:00+00:00</updated><id>https://mickf.net/tech/slugify-in-swift</id><content type="html" xml:base="https://mickf.net/tech/slugify-in-swift/">&lt;p&gt;I’ve recently added a function to my &lt;a href=&quot;https://github.com/dirtyhenry/swift-blocks&quot;&gt;Blocks&lt;/a&gt; library that allows you to
slugify any string.&lt;/p&gt;
&lt;h2&gt;What is a slug&lt;/h2&gt;
&lt;p&gt;A &lt;em&gt;slug&lt;/em&gt; is the part of a URL that identifies a specific page on a website in a
readable and concise way. For example, in the URL &lt;code&gt;www.example.com/about-us&lt;/code&gt;,
the slug is “about-us.”&lt;/p&gt;
&lt;p&gt;Slugs have various other uses: you can use them to create file names in your
file system, as Git branch names for issues you’re working on, and more.&lt;/p&gt;
&lt;h2&gt;What we need&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Essentials.&lt;/strong&gt; Slugs should only contain &lt;code&gt;[a-z0-9-]&lt;/code&gt; characters ie no spaces,
tabs, or underscores. Just letters, digits, and hyphens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Accents removal.&lt;/strong&gt; We want to remove accents from languages that use the Latin
alphabet. This means characters like &lt;code&gt;è&lt;/code&gt;, &lt;code&gt;é&lt;/code&gt;, &lt;code&gt;ê&lt;/code&gt;, &lt;code&gt;ë&lt;/code&gt;, &lt;code&gt;ě&lt;/code&gt;, &lt;code&gt;ẽ&lt;/code&gt;, &lt;code&gt;ē&lt;/code&gt;, &lt;code&gt;ė&lt;/code&gt;, and
&lt;code&gt;ę&lt;/code&gt; should all be transformed to &lt;code&gt;e&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Support for more alphabets.&lt;/strong&gt; Strings written in alphabets other than Latin
should be romanized as accurately as possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Basic emoji support.&lt;/strong&gt; Emojis should be removed from the slug, &lt;strong&gt;except&lt;/strong&gt; when
the string consists solely of emojis.&lt;/p&gt;
&lt;p&gt;We aim to achieve this without relying on external dependencies. Just Swift and
Foundation.&lt;/p&gt;
&lt;h2&gt;The magic of &lt;code&gt;applyingTransform&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;StringProtocol&lt;/code&gt; protocol provides a powerful function called
&lt;a href=&quot;https://developer.apple.com/documentation/swift/stringprotocol/applyingtransform(_:reverse:)/&quot;&gt;&lt;code&gt;applyingTransform&lt;/code&gt;&lt;/a&gt; that performs most of these transformations. By
applying a series of transformations, you can create an effective &lt;code&gt;slugify&lt;/code&gt;
function. The ones I used include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.toLatin&lt;/code&gt;: Transliterates a string from any script to the Latin script;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.stripDiacritics&lt;/code&gt;: Removes diacritics from a string;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.stripCombiningMarks&lt;/code&gt;: Removes combining marks from a string;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.toUnicodeName&lt;/code&gt;: Converts characters to their Unicode names.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Examples&lt;/h2&gt;
&lt;p&gt;Here are some examples of what &lt;code&gt;.slugify()&lt;/code&gt; does:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Hello Luka Dončić&lt;/code&gt; → &lt;code&gt;hello-luka-doncic&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;😀 LOL&lt;/code&gt; → &lt;code&gt;lol&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;😀&lt;/code&gt; → &lt;code&gt;grinning-face&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;🎸&lt;/code&gt; → &lt;code&gt;guitar&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;สวัสดีชาวโลก&lt;/code&gt; → &lt;code&gt;swasdi-chaw-lok&lt;/code&gt; (Thai for “Hello World”)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The code&lt;/h2&gt;
&lt;p&gt;You can check out &lt;a href=&quot;https://github.com/dirtyhenry/swift-blocks/blob/main/Sources/Blocks/Extensions/StringProtocol.swift&quot;&gt;an up-to-date version of slugify on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here’s the current implementation:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;StringProtocol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;slugify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;slug&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lowercased&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;applyingTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toLatin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;applyingTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stripDiacritics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;applyingTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stripCombiningMarks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;replacingOccurrences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;[^a-z0-9]+&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regularExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trimmingCharacters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CharacterSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;charactersIn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isEmpty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;extendedSelf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;applyingTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toUnicodeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;replacingOccurrences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;N&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extendedSelf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extendedSelf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;slugify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Mick F</name></author><category term="Open Sourcing" /><category term="iOS" /><category term="Swift" /><summary type="html">Foundation&apos;s `applyingTransform` function can help create slugs in Swift without external dependencies. Here&apos;s how.</summary></entry><entry><title type="html">Finding iOS simulators identifiers for CI</title><link href="https://mickf.net/tech/finding-ios-simulators-identifiers/" rel="alternate" type="text/html" title="Finding iOS simulators identifiers for CI" /><published>2024-03-29T00:00:00+00:00</published><updated>2024-03-29T00:00:00+00:00</updated><id>https://mickf.net/tech/finding-ios-simulators-identifiers</id><content type="html" xml:base="https://mickf.net/tech/finding-ios-simulators-identifiers/">&lt;p&gt;GitHub Actions are powerful tools and a great complement to Xcode Cloud for
Swift projects’ continuous integration.&lt;/p&gt;
&lt;p&gt;However, I encountered an issue: when using &lt;code&gt;xcodebuild test&lt;/code&gt; for iOS, a
&lt;em&gt;concrete device&lt;/em&gt; is required:&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;sh&quot;&gt;Cannot &lt;span class=&quot;nb&quot;&gt;test &lt;/span&gt;target “FooTests” on “Any iOS Device”: Tests must be run on a concrete device
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The challenge then arose: how to identify the simulator identifiers suitable for
the intended device?&lt;/p&gt;
&lt;p&gt;Of course, Apple provides a command for this:&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;sh&quot;&gt;xcrun simctl list devices available
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;But then CI requires parsing and filtering on this command’s output. Here are 3
solutions to do so.&lt;/p&gt;
&lt;h2&gt;Fastlane era&lt;/h2&gt;
&lt;p&gt;Fastlane provides a &lt;code&gt;FastlaneCore::DeviceManager&lt;/code&gt; that greatly assists in
inspecting available devices.&lt;/p&gt;
&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;fastlane_core/device_manager&apos;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Usage: latest_simulator_with_name(&apos;iPhone Xʀ&apos;).udid&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;latest_simulator_with_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;device_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;FastlaneCore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DeviceManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;simulators&lt;/span&gt;
                               &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                               &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;max_by&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Gem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;os_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;FastlaneCore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;UI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;device_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; missing. Please select one of the following:&quot;&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;FastlaneCore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DeviceManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;simulators&lt;/span&gt;
                             &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;FastlaneCore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;UI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;os_version&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Device with name &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;device_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; cannot be found. Please install it.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Despite my extensive use of Fastlane, I’m starting to perceive a decline in its
relevance with the emergence of Xcode Cloud.&lt;/p&gt;
&lt;h2&gt;Exploring the PointFreeCo Approach&lt;/h2&gt;
&lt;p&gt;As an admirer of the PointFreeCo team’s work, I investigated their methods and
discovered &lt;a href=&quot;https://github.com/pointfreeco/swift-composable-architecture/blob/2722a3466b78860ebf493103fc82ac85b076e1bd/Makefile&quot;&gt;a useful yet succinct shell command&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;language-makefile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;makefile&quot;&gt;&lt;span class=&quot;nv&quot;&gt;PLATFORM_IOS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; iOS Simulator,id&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;$(&lt;/span&gt;call udid_for,iOS 17.2,iPhone &lt;span class=&quot;se&quot;&gt;\d\+&lt;/span&gt; Pro &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;^M]&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;…&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;udid_for&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;shell&lt;/span&gt; xcrun simctl list devices available &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;[(&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;]&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{ print &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;$$(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;NF-3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;endef&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;While efficient, it lacks readability.&lt;/p&gt;
&lt;h2&gt;Implementing a Swift Solution&lt;/h2&gt;
&lt;p&gt;To address this challenge, I developed a Swift script to obtain the necessary
information. You can find &lt;a href=&quot;https://github.com/dirtyhenry/swift-blocks/blob/main/Scripts/ListDevices.swift&quot;&gt;the script on the Scripts section of my Blocks
project&lt;/a&gt; or as &lt;a href=&quot;https://github.com/dirtyhenry/swift-blocks/blob/main/Examples/BlocksCLI/Sources/BlocksCLI/DevTools/ListDevicesCommand.swift&quot;&gt;a command of the CLI&lt;/a&gt; that I provide with this tool.&lt;/p&gt;
&lt;p&gt;To use it in continuous integration (CI), follow these steps:&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;shell&quot;&gt;&lt;span class=&quot;c&quot;&gt;# Download the script and make it executable&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-sSL&lt;/span&gt; https://raw.githubusercontent.com/dirtyhenry/swift-blocks/main/Scripts/ListDevices.swift &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; findDevice
&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x findDevice

&lt;span class=&quot;c&quot;&gt;# Find the device identifier&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DEVICE_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;./findDevice &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TEST_IOS_VERSION&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TEST_IOS_SIMULATOR_MODEL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Run the test&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; pipefail &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; xcodebuild &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-skipMacroValidation&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-skipPackagePluginValidation&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-workspace&lt;/span&gt; foo.xcworkspace &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-scheme&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-destination&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;iOS Simulator,id=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEVICE_ID&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    | xcpretty
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;💡 I recommend setting &lt;code&gt;TEST_IOS_VERSION&lt;/code&gt; and &lt;code&gt;TEST_IOS_SIMULATOR_MODEL&lt;/code&gt; as
environment variables so that failures after Xcode updates can be resolved
without changing the code.&lt;/p&gt;</content><author><name>Mick F</name></author><category term="Open Sourcing" /><category term="iOS" /><category term="Swift" /><category term="GitHub Actions" /><summary type="html">Discover how to streamline iOS app testing with CI scripting. Learn how to overcome testing challenges, explore Fastlane and PointFreeCo approaches to find simulators identifiers, as well as my own Swift script. Optimize your CI workflow and ensure smooth testing processes for your Swift projects.</summary></entry><entry><title type="html">My Mission Statement as a Programmer</title><link href="https://mickf.net/tech/developer-mission-statement/" rel="alternate" type="text/html" title="My Mission Statement as a Programmer" /><published>2024-01-06T00:00:00+00:00</published><updated>2024-01-06T00:00:00+00:00</updated><id>https://mickf.net/tech/developer-mission-statement</id><content type="html" xml:base="https://mickf.net/tech/developer-mission-statement/">&lt;h2&gt;tl;dr&lt;/h2&gt;
&lt;p&gt;As a programmer, I create eco-conscious software with user-friendly design,
embracing IndieWeb principles. I’m cautious with big corporations, upholding
ethics and user rights. My goal is to produce durable and sustainable code,
prioritizing maintainability and developer well-being. I respond swiftly to
evolving challenges for impactful work in a changing tech landscape.&lt;/p&gt;
&lt;h2&gt;A longer version&lt;/h2&gt;
&lt;p&gt;Let me explain. There is a sense of uneasiness with the state of the world these
days. After having the privilege to spend a fun and peaceful holiday season, I
wanted to do a headcheck on where I stand with my role as a programmer and if I
am aligned with my aspirations and values. So I decided to write my mission
statement. I will develop each of its components with an example of how I think
I apply it.&lt;/p&gt;
&lt;h3&gt;🌍 Climate Friendliness&lt;/h3&gt;
&lt;p&gt;I need to consider all environmental impacts of the code I write: the hardware
it runs on, the energy it requires, its purpose for the people running it.&lt;/p&gt;
&lt;p&gt;Example. I work for &lt;a href=&quot;https://memo.bank/&quot;&gt;Memo Bank&lt;/a&gt;, a bank for small and medium-sized
companies, that &lt;a href=&quot;https://memo.bank/nos-engagements/&quot;&gt;prioritizes in a no bull-shit way&lt;/a&gt; the transition to a low
carbon economy. It has been recognised as such by &lt;a href=&quot;https://reclaimfinance.org/site/&quot;&gt;Reclaim Finance&lt;/a&gt;, and is
one of the recommended banks on their website &lt;a href=&quot;https://change-de-banque.org/&quot;&gt;Change de banque&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;🤝 Human Friendliness&lt;/h3&gt;
&lt;p&gt;I prioritise security and user experience, in that order, to promote the
well-being of end-users. It involves creating software that is intuitive,
inclusive, and positively impacts users’ lives.&lt;/p&gt;
&lt;p&gt;Example. I feel that it was my main motivations to start running the &lt;a href=&quot;https://www.statium.app/newsletter&quot;&gt;Statium
Newsletter&lt;/a&gt;, a minimalistic soccer newsletter written in French that focuses
solely on the game. It filters out the noise from meaningless controversies,
&lt;a href=&quot;https://www.statium.app/newsletter/posts/newsletter-222&quot;&gt;poor journalistic practices&lt;/a&gt;, invasive advertisement and poor user
experience of alternative sources. As of today, &lt;a href=&quot;https://www.statium.app/&quot;&gt;statium.app&lt;/a&gt; has a perfect
score of 100 in Performance, Accessibility, Best Practices and SEO with
Lighthouse.&lt;/p&gt;
&lt;h3&gt;⚡ Mastery&lt;/h3&gt;
&lt;p&gt;I want to continuously get better at my craft. Software is never “done”. A
skillset is never complete. I want to keep engaging on how to be a better
programmer, a better team member, and a better person.&lt;/p&gt;
&lt;p&gt;Example. I think a good practice of mastery is balancing being very curious with
just what it needs of focus. I wrote about &lt;a href=&quot;https://mickf.net/tech/side-projects-2022/&quot;&gt;why side-projects are so important
to me&lt;/a&gt; in the past: my curiosity is satisfied and the new things I learn have
many positive impacts on my job at Memo Bank.&lt;/p&gt;
&lt;h3&gt;🌐 IndieWeb Friendliness&lt;/h3&gt;
&lt;p&gt;I support decentralised and people-focused technologies and products that help
individuals to have greater control over their online presence and their data.&lt;/p&gt;
&lt;p&gt;Example. I use RSS daily, I &lt;a href=&quot;https://indieweb.org/POSSE&quot;&gt;POSSE&lt;/a&gt;, I run a newsletter using
&lt;a href=&quot;https://buttondown.email/&quot;&gt;Buttondown&lt;/a&gt;, my microblogging platform of choice is &lt;a href=&quot;https://micro.blog/&quot;&gt;micro.blog&lt;/a&gt;, I
favor community-run or independant sources when I use links in my blog posts
(examples: &lt;a href=&quot;https://www.themoviedb.org/about&quot;&gt;TMDB&lt;/a&gt;, &lt;a href=&quot;https://www.discogs.com/about&quot;&gt;Discogs&lt;/a&gt;, &lt;a href=&quot;https://www.sports-reference.com/&quot;&gt;Sports Reference&lt;/a&gt;, etc.). I make &lt;a href=&quot;https://www.mickf.net/default-apps&quot;&gt;my
default apps&lt;/a&gt; public.&lt;/p&gt;
&lt;h3&gt;🕵️‍♂️ Big Corporation Cautiousness&lt;/h3&gt;
&lt;p&gt;I am curious of the ethical considerations of big companies when engaging with
their products and technologies. I avoid those that compromise user privacy or
promote monopolistic behavior.&lt;/p&gt;
&lt;p&gt;Example. I stopped using Gmail, I don’t actively use Meta products or X. I am
wary of AI products and try to use them as sparingly and ethically as possible.&lt;/p&gt;
&lt;h3&gt;🌱 Durability &amp;amp; Sustainability&lt;/h3&gt;
&lt;p&gt;I focus on the longevity and sustainability of the software I write. I want my
code to run for a long time, I want my mind and body to remain healthy for as
long as possible, and I want my online presence to live long.&lt;/p&gt;
&lt;p&gt;Example. I pick dependencies to rely on very carefully and sparingly, I express
&lt;a href=&quot;https://micro.mickf.net/2023/10/31/144426.html&quot;&gt;gratitude towards Open-Source developers&lt;/a&gt;, I avoid and fix
&lt;a href=&quot;https://micro.mickf.net/2023/03/08/every-month-or.html&quot;&gt;dead-links&lt;/a&gt;, I maintain &lt;a href=&quot;https://micro.mickf.net/2023/12/26/good-old-fashioned.html&quot;&gt;a good work-life balance&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This mission statement serves as a guiding compass in my journey as a
programmer, reminding me of the values and principles I hold dear. It is a
commitment to creating software that not only meets the needs of the present but
also contributes positively to the future.&lt;/p&gt;</content><author><name>Mick F</name></author><category term="Column" /><category term="iOS" /><category term="Swift" /><summary type="html">Climate Friendliness, Human Friendliness, Mastery, IndieWeb Friendliness, Big Corporation Cautiousness, Durability &amp; Sustainability serves as a guiding compass in my journey as a programmer</summary></entry><entry><title type="html">From block party to ’hood party</title><link href="https://mickf.net/tech/blocks-and-hoods/" rel="alternate" type="text/html" title="From block party to ’hood party" /><published>2023-12-24T00:00:00+00:00</published><updated>2023-12-24T00:00:00+00:00</updated><id>https://mickf.net/tech/blocks-and-hoods</id><content type="html" xml:base="https://mickf.net/tech/blocks-and-hoods/">&lt;p&gt;Last year, I wrote about &lt;a href=&quot;https://mickf.net/tech/side-projects-2022/&quot;&gt;the joy sparked by side projects&lt;/a&gt;. This year, I
wanted to write another small recap of what I have accomplished in 2023. I kept
iterating on my projects but, more importantly, I kept consolidating the
packages that I use to share code between projects: &lt;a href=&quot;https://github.com/dirtyhenry/swift-blocks&quot;&gt;&lt;code&gt;swift-blocks&lt;/code&gt;&lt;/a&gt; and
&lt;a href=&quot;https://github.com/dirtyhenry/swift-hoods&quot;&gt;&lt;code&gt;swift-hoods&lt;/code&gt;&lt;/a&gt;. They are open-sourced and I wanted to formally introduce
them to the world. Here we are.&lt;/p&gt;
&lt;h2&gt;🧱 Welcome to my blocks&lt;/h2&gt;
&lt;p&gt;For the last 2 years, I have been building &lt;code&gt;swift-blocks&lt;/code&gt;, a &lt;strong&gt;dependency-free&lt;/strong&gt;
library of tools and patterns I use repeatedly on all my Swift projects.&lt;/p&gt;
&lt;p&gt;It is not my first attempt to build such a library but it is the first time I
feel really satisfied about its usefulness. What was different this time? Two
factors:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For one, Swift Package Manager provides a much more convenient way to share
code between projects than anything else in the past (Cocoapods, static
libraries, etc.);&lt;/li&gt;
&lt;li&gt;And then, I had a much better discipline to be consistent about
using/iterating/adding things to the library over time.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Today, I released version &lt;a href=&quot;https://github.com/dirtyhenry/swift-blocks/releases/tag/0.2.0&quot;&gt;0.2.0&lt;/a&gt; of the library. Here are the release notes.
Help yourself.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://swiftpackageindex.com/dirtyhenry/swift-blocks/0.2.0/documentation/blocks/networking&quot;&gt;&lt;strong&gt;🌐 Transport.&lt;/strong&gt;&lt;/a&gt; Added a tinier version of objcio’s
&lt;a href=&quot;https://github.com/objcio/tiny-networking&quot;&gt;&lt;code&gt;tiny-networking&lt;/code&gt;&lt;/a&gt; for concise
endpoint description, combining URL requests and response parsing. Introduced
&lt;a href=&quot;https://swiftpackageindex.com/dirtyhenry/swift-blocks/0.2.0/documentation/blocks/urlrequestheaderitem&quot;&gt;&lt;code&gt;URLRequestHeaderItem&lt;/code&gt;&lt;/a&gt;
struct mimicking Foundation’s &lt;code&gt;URLQueryItem&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://swiftpackageindex.com/dirtyhenry/swift-blocks/0.2.0/documentation/blocks/calendar&quot;&gt;&lt;strong&gt;📅 Calendar.&lt;/strong&gt;&lt;/a&gt; Improved internal architecture and developer experience
using &lt;em&gt;result builders&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;🔐 Security.&lt;/strong&gt; Introduced support for PKCE and associated helpers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;🛟 Error Management.&lt;/strong&gt; Introduced
&lt;a href=&quot;https://swiftpackageindex.com/dirtyhenry/swift-blocks/0.2.0/documentation/blocks/simplemessageerror&quot;&gt;&lt;code&gt;SimpleMessageError&lt;/code&gt;&lt;/a&gt;
for convenient error handling, eliminating the need for forced unwrapping.
Ideal for scenarios where a full error domain is not necessary.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;🎨 SwiftUI.&lt;/strong&gt; Introduced
&lt;a href=&quot;https://swiftpackageindex.com/dirtyhenry/swift-blocks/0.2.0/documentation/blocks/taskstatebutton&quot;&gt;&lt;code&gt;TaskStateButton&lt;/code&gt;&lt;/a&gt;
interface components, for representing asynchronous
&lt;a href=&quot;https://swiftpackageindex.com/dirtyhenry/swift-blocks/0.2.0/documentation/blocks/taskstate&quot;&gt;task states&lt;/a&gt;.
Supports four states: not started, running, completed (success), or failed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href=&quot;https://swiftpackageindex.com/&quot;&gt;documentation for this package&lt;/a&gt; is now hosted on the Swift Package
Index.&lt;/p&gt;
&lt;h2&gt;🏘️ Welcome to my ’hood&lt;/h2&gt;
&lt;p&gt;In 2023 though, I have been using
&lt;a href=&quot;https://github.com/pointfreeco/swift-composable-architecture&quot;&gt;The Composable Architecture&lt;/a&gt;
a lot. And I built things that were depending on it. So in addition to
&lt;code&gt;swift-blocks&lt;/code&gt;, I started building another library, &lt;code&gt;swift-hoods&lt;/code&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, which has
a slightly bigger footprint in a project but is starting to turn out very useful
as well.&lt;/p&gt;
&lt;p&gt;Today, it is still in the early stages, but it already provides two useful
items:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A keychain TCA dependency for easy testing of code that uses keychain items;&lt;/li&gt;
&lt;li&gt;A processor for markdown files that include YAML front-matters.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I will detail how these work in the future.&lt;/p&gt;
&lt;p&gt;🎄 Have a happy holiday and happy new year 🥂.&lt;/p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol&gt;
&lt;li id=&quot;fn1&quot;&gt;
&lt;p&gt;Hood standing for neighborhood… ie a larger version of a block. Get it?
Kudos to my lovely wife for helping me find a good name. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content><author><name>Mick F</name></author><category term="Journaling" /><category term="iOS" /><category term="Swift" /><summary type="html">In 2023, I consolidated a dependency-free `swift-blocks` package, and I started working on a bigger `swift-hoods` package that builds upon my favorite dependencies.</summary></entry><entry><title type="html">From the start, get rid of types you don’t control: a lesson (re)learned, Rolling Stones Edition</title><link href="https://mickf.net/tech/keep-control-of-types/" rel="alternate" type="text/html" title="From the start, get rid of types you don’t control: a lesson (re)learned, Rolling Stones Edition" /><published>2023-11-09T00:00:00+00:00</published><updated>2023-11-09T00:00:00+00:00</updated><id>https://mickf.net/tech/keep-control-of-types</id><content type="html" xml:base="https://mickf.net/tech/keep-control-of-types/">&lt;p&gt;In recent weeks, I’ve been reminded of a crucial lesson: “Do not use the objects
that are used by an API&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; outside of the network layer of your application.”&lt;/p&gt;
&lt;p&gt;While this might seem like common sense in a technical job interview, the
real-world scenario brings its own challenges, especially when dealing with (a)
an in-house designed API&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, (b) budget constraints, and (c) the task of
writing boilerplate to convert DTO into model objects. It’s surprisingly easy to
give in to the temptation.&lt;/p&gt;
&lt;p&gt;I’m writing this post to emphasize that the effort is always worth it. To drive
this point home, I’ll provide real-life examples to illustrate why.&lt;/p&gt;
&lt;h2&gt;Example 1: Future-proofing an API&lt;/h2&gt;
&lt;p&gt;Let’s delve into an API where a payload includes the name of a member of the
Rolling Stones. And just for the heck of it, you decide to write an enum to
represent this value. Because… why not?&lt;/p&gt;
&lt;p&gt;Here’s the enum to kick things off:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;c1&quot;&gt;/// 👅 Members of the Rolling Stones&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RollingStone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Mick Jagger&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keith&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Keith Richards&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bill&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Bill Wyman&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;charlie&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Charlie Watts&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brian&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Brian Jones&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now, imagine using a DTO as a model:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DTOAsModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;rollingStone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RollingStone&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RollingStone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And this works fine!&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;member1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;JSONDecoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;DTOAsModel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
        { &quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rollingStone&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;: &quot;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Mick&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Jagger&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; }
        &quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;using&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utf8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;But what if the backend adds “&lt;a href=&quot;https://en.wikipedia.org/wiki/Mick_Taylor&quot;&gt;Mick Taylor&lt;/a&gt;” as a new possible value that
your Swift code has no idea about?&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;member2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;JSONDecoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;DTOAsModel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
        { &quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rollingStone&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;: &quot;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Mick&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Taylor&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; }
        &quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;using&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utf8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Oops, we crash.&lt;/p&gt;
&lt;p&gt;There’s a quick and dirty way to fix this, and I’ll admit, I’ve been guilty of
doing it. What if we changed &lt;code&gt;rollingStone&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;  struct DTOAsModel2: Codable {
&lt;span class=&quot;gd&quot;&gt;-      let rollingStone: RollingStone
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+      let rollingStone: String
&lt;/span&gt;  }
&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+ extension DTOAsModel {
+     var typedRollingStone: RollingStone? {
+         RollingStone(rawValue: rollingStone)
+     }
+ }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;But then things get messy: objects are cluttered, autocompletion becomes hard to
read, initializers are not using the enum type, etc.&lt;/p&gt;
&lt;p&gt;So what I believe today is the right way to do things is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Limit yourself to JSON-supported types in your DTO;&lt;/li&gt;
&lt;li&gt;Create typed model-objects;&lt;/li&gt;
&lt;li&gt;Add extensions that can convert from DTO objects into model objects in your
network layer.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DTOAsJustDTO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;rollingStone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ProperModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;rollingStone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RollingStone&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ProperModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DTOAsJustDTO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;rollingStone&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RollingStone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rawValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rollingStone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rollingStone&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rollingStone&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;At the cost of a little boilerplate, you have two clean objects that do things
right.&lt;/p&gt;
&lt;p&gt;There are many options to support future values of the enum:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;a href=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization/#Failable-Initializers&quot;&gt;failable initializer&lt;/a&gt;, as in the example,&lt;/li&gt;
&lt;li&gt;A throwing initializer,&lt;/li&gt;
&lt;li&gt;A prop that can be optional,&lt;/li&gt;
&lt;li&gt;Adding an &lt;code&gt;.unknown&lt;/code&gt; rolling stone in the enum,&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is really up to you and what feels best.&lt;/p&gt;
&lt;h2&gt;Example 2: Improving testability of code you don’t control&lt;/h2&gt;
&lt;p&gt;Let’s take a look outside of the network layer, with other types of API that you
don’t control. The principles are similar.&lt;/p&gt;
&lt;p&gt;So now your app receives a push notification every time the Rolling Stones
release an album.&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UNNotification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;userInfo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;userInfo&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;albumName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;userInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;albumName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;albumName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;How can you test this? You cannot create instances of &lt;code&gt;UNNotification&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Instead, get rid of things out of your control early on. Take control of things.
Like so:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NotificationEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;newAlbum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NotificationEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UNNotification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;albumName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;userInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;albumName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newAlbum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;albumName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NotificationEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;NotificationEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newAlbum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;albumName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;notification&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;albumName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With this code, testing &lt;code&gt;NotificationEvent.init?(from:)&lt;/code&gt; will be challenging.
But testing &lt;code&gt;showNotification&lt;/code&gt; will be easy-peasy. Mission accomplished.&lt;/p&gt;
&lt;h2&gt;More examples to come?&lt;/h2&gt;
&lt;p&gt;For sure, I will encounter more examples in the future. And I’ll continue to
update this post as a reference for my future-self on how I want to code future
similar scenarios.&lt;/p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol&gt;
&lt;li id=&quot;fn1&quot;&gt;
&lt;p&gt;I will call them Data Transfer Objects &lt;em&gt;aka&lt;/em&gt; DTO for the rest of this post &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot;&gt;
&lt;p&gt;So you sort of control it, right? Well, not really. If an API is designed
for more than 1 client, it might include extra-details or complexity. So
while you can think you control it as an organization, your Swift project
does not. &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content><author><name>Mick F</name></author><category term="Journaling" /><category term="Swift" /><category term="iOS" /><summary type="html">With my special sauce to future-proof your API and boost testability!</summary></entry><entry><title type="html">The Joy Sparked by Side Projects, 2022 Edition</title><link href="https://mickf.net/tech/side-projects-2022/" rel="alternate" type="text/html" title="The Joy Sparked by Side Projects, 2022 Edition" /><published>2022-12-13T00:00:00+00:00</published><updated>2022-12-13T00:00:00+00:00</updated><id>https://mickf.net/tech/side-projects-2022</id><content type="html" xml:base="https://mickf.net/tech/side-projects-2022/">&lt;p&gt;🎄 December is here! Like every year, it is time to watch &lt;em&gt;Home Alone&lt;/em&gt; again and
to review the progress I made as a developer over the past 12 months. Side
projects contributed the most.&lt;/p&gt;
&lt;h2&gt;Why side projects are necessary for me&lt;/h2&gt;
&lt;p&gt;Yes, a paying job is your bread and butter. But having side projects is key to
staying ahead of the younger guns of the tech world, flexing your developer
muscles in new directions and anticipating what’s next for you at work.&lt;/p&gt;
&lt;p&gt;Last year, I started writing &lt;a href=&quot;https://www.statium.app/&quot;&gt;a newsletter about soccer/football&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; for
people based in France. The content is pretty basic. Still, I learned so much
just setting it up, running it, and trying to automate as many steps as possible
so that each edition would take as little time as possible to write, without
compromising the quality of the content.&lt;/p&gt;
&lt;h2&gt;Things I learned&lt;/h2&gt;
&lt;p&gt;Here is a non-exhaustive list of the things I trained on last year to run the
newsletter, in an approximative chronological order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/apple/swift-argument-parser&quot;&gt;&lt;strong&gt;Swift Argument Parser.&lt;/strong&gt;&lt;/a&gt; Writing a CLI in Swift is now my goto solution
when I need to write scripts. Including at work. It is so much easier to
write, document and maintain than Shell scripts.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://buttondown.email/&quot;&gt;&lt;strong&gt;Buttondown.&lt;/strong&gt;&lt;/a&gt; This is an indie tool to bootstrap newsletters. As part
of the side project journey, I believe it is worth exploring alternatives to
big corporations so I skipped Mailchimp, Mailjet and similar options. While I
really like Buttondown, I think the newsletter is now ready to grow out of it,
and finding a better option for my needs is on the roadmap for 2023.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linode.com/&quot;&gt;&lt;strong&gt;Linode.&lt;/strong&gt;&lt;/a&gt; Similarly, I didn’t want to go the AWS route. And Linode’s
interface is much clearer than AWS — though it is not as feature rich. Linode
got recently bought by Akamai, so I am not as attached to it as I used to be.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.terraform.io/&quot;&gt;&lt;strong&gt;Terraform.&lt;/strong&gt;&lt;/a&gt; Infrastructure as code. I use it to create server
instances, manage DNS for the domain names, create and manage buckets and
keys.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc5545&quot;&gt;&lt;strong&gt;iCalendar RFC.&lt;/strong&gt;&lt;/a&gt; Not the sexiest subject. But I had to reverse-engineer
how calendar software behaves to get the newsletter to work the way I wanted.
Did you know that the iOS calendar behaves differently if you serve an ICS
file with a 2XX HTTP status code than if you serve it via a 3XX? &lt;a href=&quot;https://stackoverflow.com/questions/69631672/how-to-prevent-ios-from-suggesting-to-subscribe-to-an-ics-file-online&quot;&gt;I did
not&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jsonfeed.org/&quot;&gt;&lt;strong&gt;JSONFeed.&lt;/strong&gt;&lt;/a&gt; An alternative to RSS written in JSON.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://micro.blog/statium&quot;&gt;&lt;strong&gt;Micro.blog.&lt;/strong&gt;&lt;/a&gt; An alternative to Twitter. Duh.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modern React web frameworks.&lt;/strong&gt; I explored &lt;a href=&quot;https://nextjs.org/&quot;&gt;Next.js&lt;/a&gt; first, then
&lt;a href=&quot;https://remix.run/&quot;&gt;Remix&lt;/a&gt;. Both are pretty amazing.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/docc&quot;&gt;&lt;strong&gt;DocC.&lt;/strong&gt;&lt;/a&gt; The new documentation framework by Apple. I used &lt;a href=&quot;https://github.com/realm/jazzy&quot;&gt;Jazzy&lt;/a&gt;
before, but DocC — albeit sometimes awkward to use — is much more complete. As
illustrated by &lt;a href=&quot;/tech/frenchkit-2022/&quot;&gt;Simon Støvring’s talk during FrenchKit 2022&lt;/a&gt;, you can
create amazing documentation with it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/xcode-cloud/&quot;&gt;&lt;strong&gt;Xcode Cloud.&lt;/strong&gt;&lt;/a&gt; First-party CI and automated distribution of apps. It
seems to cover some common ground with Fastlane, very convenient to distribute
the app I use to run the newsletter on my iPhone and iPad via TestFlight.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/tabulardata&quot;&gt;&lt;strong&gt;TabularData.&lt;/strong&gt;&lt;/a&gt; A new framework provided by Apple to organize and
explore data.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/xcode/swiftui/&quot;&gt;&lt;strong&gt;SwiftUI.&lt;/strong&gt;&lt;/a&gt; I am sure you heard of it already 😉.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/pointfreeco/swift-composable-architecture&quot;&gt;&lt;strong&gt;The Composable Architecture.&lt;/strong&gt;&lt;/a&gt; A 3rd-party architecture library that
solved all the problems that were holding me back when I first got my toes wet
with SwiftUI.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.google.com/optimization&quot;&gt;&lt;strong&gt;OR-Tools library.&lt;/strong&gt;&lt;/a&gt; Linear optimization library available in Python
among others. Picking the right tools to do the right job is essential. I
found this library to act like a Microsoft Excel solver.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these tools would deserve their own post. I shouldn’t run out of
inspiration for this blog next year.&lt;/p&gt;
&lt;h2&gt;The positive impacts on my paying job&lt;/h2&gt;
&lt;h3&gt;🧰 A better set of tools I can use&lt;/h3&gt;
&lt;p&gt;Part of my job at Memo Bank is to write the iOS app that serves as the 2FA for
critical operations. The app does not do much. But the things it does are
critical. Our customers depend on it to pay salaries to their employees, pay
taxes on time, and be safe with their transactions.&lt;/p&gt;
&lt;p&gt;The app was bootstrapped with compatibility going up to iOS 12. Now that I got
my hands dirty with SwiftUI and &lt;em&gt;The Composable Architecture&lt;/em&gt; on my side
projects, I feel comfortable adopting them in the app for upcoming features.
That would not have been the case without the time I spent toying with them
outside of work. I made many mistakes. I rewrote things multiple times. It is
part of learning.&lt;/p&gt;
&lt;p&gt;Could this time spent on my free time have been part of the job? Maybe. But I
also like not having to be held accountable for anything when I explore and want
to play around with new things. I do whatever I want, without having to justify
my decisions to anyone else but me. I am in complete control of every decision.
Freedom &amp;amp; Fun.&lt;/p&gt;
&lt;h3&gt;🤗 A greater empathy&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;“It should be easy, it is &lt;em&gt;just&lt;/em&gt; a button.”&lt;/p&gt;
&lt;p&gt;— Quite probably my past self, in a self-indulgent micro-agression towards
some designer from my team. Who the hell did I think I was?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I wear every possible hat running this newsletter. I am a developer, a product
manager, and a designer. I do customer support, manage the infrastructure, etc.&lt;/p&gt;
&lt;p&gt;I get a glimpse of the struggles that every job comes with. I get reminded that
good design is hard to get right, that keeping an infrastructure up takes
effort, that making product decisions is hard. It is a useful humbling reminder
that shit is hard to get right.&lt;/p&gt;
&lt;h2&gt;My tips and life lessons&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;🧱 Use a monorepo.&lt;/strong&gt; Over time, I realized that using a monorepo for my
projects was much more thrilling than having a million bunny projects with 5
commits each. The feeling of progress is much more satisfying.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;🏎 Take shortcuts towards the fun.&lt;/strong&gt; If you host your code on GitHub, don’t use
an organization and don’t open-source what you are doing until you feel ready.
For instance, at work, you should never leave secrets in your code. On a side
project, why not? If stakes are low, leave the boring required things you write
at work behind and sprint towards the fun parts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;⏱ How to find time for it?&lt;/strong&gt; Any opportunity is good to take. My wife agreed
to leave me off kids-duty on Thursday evenings. I try to write a little bit of
code everyday, and when you work remotely, the lunch break is ideal for that.
Over time, things build up and the output you get from a 5-minute slot grows.&lt;/p&gt;
&lt;p&gt;Also, I recommend reading &lt;a href=&quot;https://www.avanderlee.com/optimization/side-projects-10-tips-for-being-successful/&quot;&gt;these tips from Antoine van der Lee&lt;/a&gt;. They are
great to act as a guard rail against uncontrollable impulses to try this new
thing. His point is to keep focus, realistic expectations and priorities
straight.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Why are you still reading this? Go work on your side project!&lt;/p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol&gt;
&lt;li id=&quot;fn1&quot;&gt;
&lt;p&gt;I won’t discuss which term is best. I can’t put it better than &lt;a href=&quot;https://kottke.org/22/12/fox-sports-us-world-cup-coverage-is-an-unmissable-abomination&quot;&gt;Jason Kottke
does&lt;/a&gt;: “it’s the sort of debate that 4th graders have on the playground”. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content><author><name>Mick F</name></author><category term="Journaling" /><category term="Swift" /><category term="iOS" /><category term="JavaScript" /><category term="React" /><summary type="html">Reviewing 2022, the benefits I gained from running a newsletter as a side project are tremendous. Let me explain why.</summary></entry><entry><title type="html">Notes from FrenchKit 2022</title><link href="https://mickf.net/tech/frenchkit-2022/" rel="alternate" type="text/html" title="Notes from FrenchKit 2022" /><published>2022-10-07T00:00:00+00:00</published><updated>2022-10-07T00:00:00+00:00</updated><id>https://mickf.net/tech/frenchkit-2022</id><content type="html" xml:base="https://mickf.net/tech/frenchkit-2022/">&lt;p&gt;Previous editions: &lt;a href=&quot;/tech/frenchkit-2019/&quot;&gt;2019&lt;/a&gt; · &lt;a href=&quot;/tech/frenchkit-2018/&quot;&gt;2018&lt;/a&gt; · &lt;a href=&quot;/tech/frenchkit-2017/&quot;&gt;2017&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;🌟 The talks I recommend&lt;/h2&gt;
&lt;h3&gt;any Idea How to Use some Generics? — Antoine van der Lee&lt;/h3&gt;
&lt;p&gt;Dive into the difference between &lt;code&gt;some&lt;/code&gt; vs &lt;code&gt;any&lt;/code&gt; and how they can replace older
forms of expressing constraints on generics.&lt;/p&gt;
&lt;p&gt;Note the choice of case in the title. 🤣&lt;/p&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/xT5aaMyyR9c&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Building Custom Property Wrappers for SwiftUI (and how to test them) — Donny Wals&lt;/h3&gt;
&lt;p&gt;Implements
&lt;a href=&quot;https://github.com/donnywals/SwiftUIPropertyWrapperTalk&quot;&gt;a fetching data property wrapper for SwiftUI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Donny is quite interesting because, year after year, he focuses on predicting
what Apple plans for the future as opposed to investing in 3rd-party libraries
or frameworks.&lt;/p&gt;
&lt;p&gt;For instance, he seems genuinely convinced that, for Apple people, data fetchers
belong to views — following Apple’s own APIs — and that Combine and Core Data
are just as relevant in 2022 as they always were.&lt;/p&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/x3-GZzjeH9g&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Masterclass: Hacking iOS Apps - and trying to prevent it — Elliot Schrock&lt;/h3&gt;
&lt;p&gt;This talk provided a lot of valuable insights. Using a jailbroken iPhone, Elliot
went backwards from the demo to the theory of hacking, highlighting how to
control the app from the CLI. Elliot also mentioned this funny analogy about
security:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;🏃 🐻 If you’re being chased by a bear, you must run faster than the slowest
guy.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Tools mentioned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.cycript.org&quot;&gt;&lt;code&gt;cycript&lt;/code&gt;&lt;/a&gt; (pronounced ssssscript apparently);&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.hopperapp.com&quot;&gt;Hopper&lt;/a&gt; (disassembler);&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/KJCracks/Clutch&quot;&gt;Clutch&lt;/a&gt; (executable dumper);&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wiki.smhuda.com/pentesting/tool-usage/ipainstaller&quot;&gt;ipainstaller&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Swift Quiz — Vincent Pradeilles&lt;/h3&gt;
&lt;p&gt;Three questions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;✅ Retaining cycles. I got this one right.&lt;/li&gt;
&lt;li&gt;❌ Mutating function on a &lt;code&gt;struct&lt;/code&gt; with a &lt;code&gt;let&lt;/code&gt; property. You can reassign
&lt;code&gt;self&lt;/code&gt;. I got it wrong.&lt;/li&gt;
&lt;li&gt;❌ Empty enums. Does the Swift compiler accept a function that has a
signature that returns something but actually has an empty body, when the
argument is an empty enum (which cannot be instantiated)? It does. I got it
wrong too. But it explains why the signature of &lt;code&gt;fatalError&lt;/code&gt; is &lt;code&gt;Never&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/mUBBY6JCC0c&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;🔥 Hot Takes&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;I am so bad at socializing during conferences.&lt;/strong&gt; My interactions with other
human beings at FrenchKit consisted primarily of asking questions during Q&amp;amp;A
sessions, but it was hard for me to go beyond that. I tried to attend an event
on the Thursday evening. But a bar packed with 200 people, 95% of which are
nerdy dorks like myself — now quite younger — is not an environment I can thrive
in. I am taller and deafer than the average person so that would translate into
me bending over groups of people, pretending I can hear what is being said. Not
quite fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Next time, I will try to attend NSSpain.&lt;/strong&gt; It was the last edition of
FrenchKit per say. The hosts are willing to expand to other technologies — which
will be appreciated after the dot conferences disappeared after being bought by
Welcome to the Jungle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I will build next features at work for iOS 14 users.&lt;/strong&gt; Today, we support
iOS 12. Looking at iosref’s
&lt;a href=&quot;https://iosref.com/ios&quot;&gt;iOS version by device table&lt;/a&gt;, I realized that bumping
straight to iOS 14 will leave aside some devices stuck with iOS 12. But no
device is stuck with iOS 13 — or iOS 14 for that matter. So, at the end of the
day, iOS 14 sounds like a sweet spot to be able to benefit both from async/await
and a decent version of SwiftUI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I should give things a second chance.&lt;/strong&gt; I’m talking about you jailbreaking
phones — to put myself in hackers shoes — and UI testing.&lt;/p&gt;
&lt;h2&gt;🪣 The rest of my notes&lt;/h2&gt;
&lt;p&gt;Here I share raw notes from the other talks cause… why not?&lt;/p&gt;
&lt;h3&gt;Beyond Code: Skills for Impactful Development That Makes a Difference — Maxim Cramer&lt;/h3&gt;
&lt;p&gt;How to make impact beyond code? Mostly about communication. 🦒 The image of the
giraffe in a park that every can sort of picture vs feature descriptions. We
share very little of our knowledge when we communicate. Shocking fact: 2% of
funding is for female founders.&lt;/p&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/epYcQluY954&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;I 💕 Swift Concurrency — Tunde Adegoroye&lt;/h3&gt;
&lt;p&gt;Concurrency, &lt;code&gt;async let&lt;/code&gt;, tasks and tasks groups, actors.&lt;/p&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/HptV7UUTToQ&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;How not to develop an “Umm” detector using Create ML — Yono Mittlefehldt&lt;/h3&gt;
&lt;p&gt;Um detector. Not really about code but about the thought process. Some tools
mentioned worth bookmarking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sox CLI (&lt;a href=&quot;https://sox.sourceforge.net&quot;&gt;https://sox.sourceforge.net&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pytorch.org/&quot;&gt;PyTorch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tensorflow.org/&quot;&gt;TensorFlow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/machine-learning/&quot;&gt;Machine Learning - Apple Developer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/45hxH7-p5IM&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Documenting your project with DocC — Simon Støvring&lt;/h3&gt;
&lt;p&gt;Wrap up around DocC by the author of the
&lt;a href=&quot;https://apps.apple.com/fr/app/runestone-text-editor/id1548193893?l=en&quot;&gt;Runestone&lt;/a&gt;
text editor.&lt;/p&gt;
&lt;p&gt;I started using DocC with my &lt;a href=&quot;https://swiftpackageindex.com/dirtyhenry/swift-blocks/&quot;&gt;Blocks&lt;/a&gt; project, but I didn’t know about
&lt;a href=&quot;https://developer.apple.com/documentation/docc/tutorials&quot;&gt;tutorials&lt;/a&gt; with DocC.&lt;/p&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/paR2INQMc3s&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;SwiftUI at Scale — Thomas Ricouard&lt;/h3&gt;
&lt;p&gt;The point was that SwiftUI is production ready. And apparently, it’s tied to the
iOS version running the phone so it doesn’t make sense to use SwiftUI before
iOS 14. I guess this means that SwiftUI in iOS 13 is broken.&lt;/p&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/tsZBLvdcoTw&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;SwiftUI Navigation — Luis Ascorbe&lt;/h3&gt;
&lt;p&gt;Should we use SwiftUI or UIKit for navigation? It depends. He recommended the
following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NavigationStack.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vimeo.com/751580644&quot;&gt;Brandon Williams: SwiftUI Navigation &amp;amp; URL Routing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Taking a look at
&lt;a href=&quot;https://github.com/pointfreeco/swift-composable-architecture&quot;&gt;The Composable Architecture&lt;/a&gt;
aka &lt;em&gt;TCA&lt;/em&gt; which sounds like a good idea.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vimeo.com/751173570&quot;&gt;Composable Architecture at Scale&lt;/a&gt; by Krzysztof
Zablocki&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/jhO9gfVg6_U&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;From source code to executable: a day in the life of a Build System — Samuel Giddinis&lt;/h3&gt;
&lt;p&gt;Why do we need build systems and the complexity behind them. Some notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/realm/SwiftLint/blob/main/Makefile&quot;&gt;Swiftlint has a Makefile&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bazel.build&quot;&gt;Bazel&lt;/a&gt; is great for large monorepos but require a
significant investment and care&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/compiling.png&quot; alt=&quot;My Code’s Compiling&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/segiddins/Bazel-iOS-Workshop&quot;&gt;GitHub - segiddins/Bazel-iOS-Workshop: AppBuilders 2022 workshop on building an iOS app with Bazel&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/dpxqSjSKJYA&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Migrating your legacy code to local SPM packages — Zouhair Mahieddine&lt;/h3&gt;
&lt;p&gt;This title describes exactly the content of the talk. 😆&lt;/p&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/bXCuVzsdyDo&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Testing for Accessibility — Robin Kanatzar&lt;/h3&gt;
&lt;p&gt;A reminder that the accessibility tool is good. Robin recommended the following
tool:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.evinced.com/products/flow-analyzer-for-mobile&quot;&gt;Mobile Flow Analyzer - Evinced, Inc.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/59uLAKVB-d4&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Cloud Functions to the rescue — Zamzam Farzamipooya&lt;/h3&gt;
&lt;p&gt;The talk focuses on Firebase/Google. That’s kind of a non starter for my tastes.&lt;/p&gt;
&lt;p&gt;It reminded me of stuff I did for Flâneur and why I didn’t like Firebase: data
access boundaries are fragile and require significant effort.&lt;/p&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/DdGfFkOz9gQ&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;From App to Game Development — Michel-André Chirita&lt;/h3&gt;
&lt;p&gt;Interesting. The game is:
&lt;a href=&quot;https://apps.apple.com/fr/app/versus-arena/id1542795843?l=en&quot;&gt;Versus Arena&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It made me want to develop a Backgammon game again.&lt;/p&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/HLz0Cfkaiew&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;The journey of Widgets begins with one step — Audrey Sobgou-Zebaze&lt;/h3&gt;
&lt;p&gt;How to start implementing widgets with
&lt;a href=&quot;https://developer.apple.com/widgets/&quot;&gt;WidgetKit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;— &lt;a href=&quot;https://youtu.be/XRMcdUUA0HU&quot;&gt;📺 Watch&lt;/a&gt;&lt;/p&gt;</content><author><name>Mick F</name></author><category term="Conference Notes" /><category term="FrenchKit" /><category term="Swift" /><category term="iOS" /><summary type="html">Swift new features, SwiftUI and The Composable Architecture stole the show. As always, conferences help getting your coding juices flowing.</summary></entry><entry><title type="html">Dealing With Dates With No Time in Swift</title><link href="https://mickf.net/tech/date-with-no-time-operations-swift/" rel="alternate" type="text/html" title="Dealing With Dates With No Time in Swift" /><published>2022-03-02T00:00:00+00:00</published><updated>2022-03-02T00:00:00+00:00</updated><id>https://mickf.net/tech/date-with-no-time-operations-swift</id><content type="html" xml:base="https://mickf.net/tech/date-with-no-time-operations-swift/">&lt;h2&gt;Where the &lt;del&gt;Streets&lt;/del&gt; Dates Have No &lt;del&gt;Name&lt;/del&gt; Time&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Where the Streets Have No Name&lt;/em&gt;, the U2 song, was released on 31 August 1987.
At what time? It is not relevant. Just the date, ie the calendar day, matters.
If I had to translate this in a JSON API, I would write the following:
&lt;code&gt;{&amp;quot;release_date&amp;quot;: &amp;quot;1987-08-31&amp;quot;}&lt;/code&gt;, ie basic ISO 8601, no time or time zone.&lt;/p&gt;
&lt;p&gt;When dealing with such &lt;em&gt;dates with no time&lt;/em&gt;, some operations can be necessary.
For instance, what is the 7th day after 31 August 1987? No dependency allowed.
This blog post will answer this question. Easy peasy, right?&lt;/p&gt;
&lt;p&gt;But beware, the experienced developer should be humble when coding such
operations. Dealing with calendars is hard and I can easily prove it. Just
answer this simple question: is every minute in a calendar 60 seconds long? If
you answered “yes”, you failed the test.
&lt;a href=&quot;https://yourcalendricalfallacyis.com&quot;&gt;Go study and have fun&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hopefully, Swift’s &lt;code&gt;Foundation&lt;/code&gt; provides everything we need to avoid obvious
traps. Let’s see what a struct to handle &lt;em&gt;dates with no time&lt;/em&gt; could look like.&lt;/p&gt;
&lt;h2&gt;The implementation&lt;/h2&gt;
&lt;p&gt;This is the end result. A discussion follows.&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Foundation&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// A string that represents dates using their ISO 8601 representations.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// `PlainDate` is a way to handle dates with no time — such as `2022-03-02` for March 2nd of 2022 — to&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// perform operations with convenience including adding days, dealing with ranges, etc.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// ## Usage Overview&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// A plain date can be initiated from a string literal, and can be used to create ranges.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     let plainDate: PlainDate = &quot;2022-03-01&quot;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     let aWeekLater = plainDate.advanced(by: 7)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     for day in march1st ..&amp;lt; aWeekLater {&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///       print(day)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     }&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PlainDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// MARK: - Creating an instance&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// Returns a date string initialized using their ISO 8601 representation.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// - Parameters:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///   - dateAsString: The ISO 8601 representation of the date. For instance, `2022-03-02`for March 2nd of 2022.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///   - calendar: The calendar — including the time zone — to use. The default is the current calendar.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// - Returns: A date string, or `nil` if a valid date could not be created from `dateAsString`.&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dateAsString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Calendar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;formatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;createFormatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;timeZone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeZone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formatter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dateAsString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;formatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// Returns a date string initialized using their ISO 8601 representation.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// - Parameters:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///   - date: The date to represent.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///   - calendar: The calendar — including the time zone — to use. The default is the current calendar.&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Calendar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;formatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;createFormatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;timeZone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeZone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Calendar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;formatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ISO8601DateFormatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;formatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formatter&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// MARK: - Properties&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;formatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ISO8601DateFormatter&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Date&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Calendar&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createFormatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;timeZone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TimeZone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ISO8601DateFormatter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;formatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ISO8601DateFormatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;formatter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;formatOptions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;withFullDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;formatter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeZone&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeZone&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formatter&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PlainDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ExpressibleByStringLiteral&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stringLiteral&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PlainDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Strideable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PlainDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;timeInterval&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeInterval&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;86400.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;advanced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PlainDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;newDate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;byAdding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PlainDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;formatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;Just how bad is &lt;del&gt;DateString&lt;/del&gt; for a name?&lt;/h3&gt;
&lt;p&gt;Edit: the first name for this class was &lt;code&gt;DateString&lt;/code&gt;. I know. Since, I found out
about &lt;a href=&quot;https://tc39.es/proposal-temporal/docs/index.html&quot;&gt;the &lt;code&gt;Temporal&lt;/code&gt; proposal for JavaScript&lt;/a&gt; that introduces something very
similar to what I am trying to achieve here as &lt;code&gt;PlainDate&lt;/code&gt;. So I renamed this
accordingly.&lt;/p&gt;
&lt;h3&gt;Why are time zones involved in this code?&lt;/h3&gt;
&lt;p&gt;It’s the result of logical decisions I made from the tools I had at hand:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Converting an ISO 8601 string? Use &lt;code&gt;ISO8601DateFormatter&lt;/code&gt;!&lt;/li&gt;
&lt;li&gt;What type does &lt;code&gt;ISO8601DateFormatter&lt;/code&gt; convert the input &lt;code&gt;String&lt;/code&gt; into? As a
&lt;code&gt;Date&lt;/code&gt;!&lt;/li&gt;
&lt;li&gt;So I should use a &lt;code&gt;Date&lt;/code&gt; — with time — to store a &lt;em&gt;date with no time&lt;/em&gt;? Yes!&lt;/li&gt;
&lt;li&gt;How can I output the date back into a &lt;code&gt;String&lt;/code&gt;? Use &lt;code&gt;ISO8601DateFormatter&lt;/code&gt;
again!&lt;/li&gt;
&lt;li&gt;But look at this test, it’s getting buggy around days on which clocks change
to deal with daylight saving time? Use a time zone!&lt;/li&gt;
&lt;li&gt;So I have to carry a &lt;code&gt;Calendar&lt;/code&gt; and a &lt;code&gt;TimeZone&lt;/code&gt; around? No need: a &lt;code&gt;Calendar&lt;/code&gt;
does include a &lt;code&gt;TimeZone&lt;/code&gt;, just carry the &lt;code&gt;Calendar&lt;/code&gt; around!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Implementing ExpressibleByStringLiteral is so easy and convenient!&lt;/h3&gt;
&lt;p&gt;Yes, it is. Expressing a &lt;code&gt;PlainDate&lt;/code&gt; just as &lt;code&gt;&amp;quot;1987-08-31&amp;quot;&lt;/code&gt; is indeed pretty
awesome.&lt;/p&gt;
&lt;h3&gt;Wow, and what about implementing Strideable?&lt;/h3&gt;
&lt;p&gt;I know, right? On top of answering our original question
(&lt;code&gt;date.advanced(by: offset)&lt;/code&gt;), &lt;code&gt;Strideable&lt;/code&gt; helps doing things like:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;startDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PlainDate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2022-03-01&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;aWeekLater&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startDate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;advanced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;startDate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aWeekLater&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// do something.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;Wait a minute! Is this a hard-coded 86400 in the code? Isn’t that forbidden?&lt;/h3&gt;
&lt;p&gt;🕵️ Good eye. 86400 is the number of seconds in a typical day. But not all days
are 86400 seconds long. But the point here is to compute the distance in days
between two dates, and I think this implementation is OK. I’ll explain why:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Calendar&lt;/code&gt; is ignoring leap seconds. I know because a former Apple Foundation
employee &lt;a href=&quot;https://stackoverflow.com/a/71312395/455016&quot;&gt;told me so&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;See the &lt;code&gt;round&lt;/code&gt;? This is enough to deal with daylight savings according to my
tests and my understanding of how daylight savings work. Consider a
collection that contains the number of seconds of an arbitrary number of
&lt;strong&gt;consecutive&lt;/strong&gt; days. (a) There’s more than a 99% chance that a randomly
picked item is 86400. (b) Considering that the 2 possible outliers values
will alternate: if you find a 23-hour long day (82800 seconds) in the
collection, you will find a 25-hour long day (90000 seconds) before you find
another occurrence of a 23-hour long day. So the average of a set will tend
to 86400 as it gets bigger.&lt;/li&gt;
&lt;li&gt;Here is a more intellectually satisfying alternative for this computation. It
might be more solid if you’re working on something really time-sensitive. But
it is about &lt;strong&gt;2 times slower&lt;/strong&gt;. So as long as my fast implementation does not
fail me, I’ll stick to it.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ordinality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;era&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ordinality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;era&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;fatalError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The distance between 2 dates could not be computed.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Mission accomplished. Feel free to use this &lt;code&gt;PlainDate&lt;/code&gt; class as is. I’ve been
using it for a hobby project — a soccer newsletter — and it works great. If you
can read French and soccer is your thing, you can &lt;a href=&quot;https://www.statium.app/newsletter&quot;&gt;sign up for the
newsletter&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Addendum — iOS &amp;amp; macOS Time Zones&lt;/h2&gt;
&lt;p&gt;Here is a table of all the known time zones’ identifiers and abbreviations as
run on iOS 15.2 and macOS 12.2.1.&lt;/p&gt;
&lt;p&gt;For some reason, &lt;code&gt;UTC&lt;/code&gt; is an abbreviation to the &lt;code&gt;UTC&lt;/code&gt; identifier, but &lt;code&gt;UTC&lt;/code&gt; is
missing in the list of known identifiers. 🤷‍♂️ I initalized a time zone using
&lt;code&gt;UTC&lt;/code&gt;: it succeeded and querying the identifier returned &lt;code&gt;GMT&lt;/code&gt;. Double-🤷‍♂️.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Identifier&lt;/th&gt;
&lt;th&gt;Abbreviations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Abidjan&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Accra&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Addis_Ababa&lt;/td&gt;
&lt;td&gt;EAT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Algiers&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Asmara&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Bamako&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Bangui&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Banjul&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Bissau&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Blantyre&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Brazzaville&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Bujumbura&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Cairo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Casablanca&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Ceuta&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Conakry&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Dakar&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Dar_es_Salaam&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Djibouti&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Douala&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/El_Aaiun&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Freetown&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Gaborone&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Harare&lt;/td&gt;
&lt;td&gt;CAT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Johannesburg&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Juba&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Kampala&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Khartoum&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Kigali&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Kinshasa&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Lagos&lt;/td&gt;
&lt;td&gt;WAT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Libreville&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Lome&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Luanda&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Lubumbashi&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Lusaka&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Malabo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Maputo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Maseru&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Mbabane&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Mogadishu&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Monrovia&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Nairobi&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Ndjamena&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Niamey&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Nouakchott&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Ouagadougou&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Porto-Novo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Sao_Tome&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Tripoli&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Tunis&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Africa/Windhoek&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Adak&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Anchorage&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Anguilla&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Antigua&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Araguaina&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Argentina/Buenos_Aires&lt;/td&gt;
&lt;td&gt;ART&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Argentina/Catamarca&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Argentina/Cordoba&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Argentina/Jujuy&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Argentina/La_Rioja&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Argentina/Mendoza&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Argentina/Rio_Gallegos&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Argentina/Salta&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Argentina/San_Juan&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Argentina/San_Luis&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Argentina/Tucuman&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Argentina/Ushuaia&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Aruba&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Asuncion&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Atikokan&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Bahia&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Bahia_Banderas&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Barbados&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Belem&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Belize&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Blanc-Sablon&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Boa_Vista&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Bogota&lt;/td&gt;
&lt;td&gt;COT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Boise&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Cambridge_Bay&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Campo_Grande&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Cancun&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Caracas&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Cayenne&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Cayman&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Chicago&lt;/td&gt;
&lt;td&gt;CDT, CST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Chihuahua&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Costa_Rica&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Creston&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Cuiaba&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Curacao&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Danmarkshavn&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Dawson&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Dawson_Creek&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Denver&lt;/td&gt;
&lt;td&gt;MDT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Detroit&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Dominica&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Edmonton&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Eirunepe&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/El_Salvador&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Fort_Nelson&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Fortaleza&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Glace_Bay&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Godthab&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Goose_Bay&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Grand_Turk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Grenada&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Guadeloupe&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Guatemala&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Guayaquil&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Guyana&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Halifax&lt;/td&gt;
&lt;td&gt;ADT, AST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Havana&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Hermosillo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Indiana/Indianapolis&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Indiana/Knox&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Indiana/Marengo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Indiana/Petersburg&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Indiana/Tell_City&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Indiana/Vevay&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Indiana/Vincennes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Indiana/Winamac&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Inuvik&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Iqaluit&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Jamaica&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Juneau&lt;/td&gt;
&lt;td&gt;AKDT, AKST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Kentucky/Louisville&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Kentucky/Monticello&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Kralendijk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/La_Paz&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Lima&lt;/td&gt;
&lt;td&gt;PET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Los_Angeles&lt;/td&gt;
&lt;td&gt;PDT, PST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Lower_Princes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Maceio&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Managua&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Manaus&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Marigot&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Martinique&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Matamoros&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Mazatlan&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Menominee&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Merida&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Metlakatla&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Mexico_City&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Miquelon&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Moncton&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Monterrey&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Montevideo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Montreal&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Montserrat&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Nassau&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/New_York&lt;/td&gt;
&lt;td&gt;EDT, EST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Nipigon&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Nome&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Noronha&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/North_Dakota/Beulah&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/North_Dakota/Center&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/North_Dakota/New_Salem&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Nuuk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Ojinaga&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Panama&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Pangnirtung&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Paramaribo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Phoenix&lt;/td&gt;
&lt;td&gt;MST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Port-au-Prince&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Port_of_Spain&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Porto_Velho&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Puerto_Rico&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Punta_Arenas&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Rainy_River&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Rankin_Inlet&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Recife&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Regina&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Resolute&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Rio_Branco&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Santa_Isabel&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Santarem&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Santiago&lt;/td&gt;
&lt;td&gt;CLST, CLT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Santo_Domingo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Sao_Paulo&lt;/td&gt;
&lt;td&gt;BRST, BRT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Scoresbysund&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Shiprock&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Sitka&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/St_Barthelemy&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/St_Johns&lt;/td&gt;
&lt;td&gt;NDT, NST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/St_Kitts&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/St_Lucia&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/St_Thomas&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/St_Vincent&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Swift_Current&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Tegucigalpa&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Thule&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Thunder_Bay&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Tijuana&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Toronto&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Tortola&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Vancouver&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Whitehorse&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Winnipeg&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Yakutat&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;America/Yellowknife&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Antarctica/Casey&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Antarctica/Davis&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Antarctica/DumontDUrville&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Antarctica/Macquarie&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Antarctica/Mawson&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Antarctica/McMurdo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Antarctica/Palmer&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Antarctica/Rothera&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Antarctica/South_Pole&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Antarctica/Syowa&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Antarctica/Troll&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Antarctica/Vostok&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Arctic/Longyearbyen&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Aden&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Almaty&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Amman&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Anadyr&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Aqtau&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Aqtobe&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Ashgabat&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Atyrau&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Baghdad&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Bahrain&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Baku&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Bangkok&lt;/td&gt;
&lt;td&gt;ICT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Barnaul&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Beirut&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Bishkek&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Brunei&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Calcutta&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Chita&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Choibalsan&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Chongqing&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Colombo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Damascus&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Dhaka&lt;/td&gt;
&lt;td&gt;BDT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Dili&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Dubai&lt;/td&gt;
&lt;td&gt;GST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Dushanbe&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Famagusta&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Gaza&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Harbin&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Hebron&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Ho_Chi_Minh&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Hong_Kong&lt;/td&gt;
&lt;td&gt;HKT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Hovd&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Irkutsk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Jakarta&lt;/td&gt;
&lt;td&gt;WIT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Jayapura&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Jerusalem&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Kabul&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Kamchatka&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Karachi&lt;/td&gt;
&lt;td&gt;PKT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Kashgar&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Kathmandu&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Katmandu&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Khandyga&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Krasnoyarsk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Kuala_Lumpur&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Kuching&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Kuwait&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Macau&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Magadan&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Makassar&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Manila&lt;/td&gt;
&lt;td&gt;PHT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Muscat&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Nicosia&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Novokuznetsk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Novosibirsk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Omsk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Oral&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Phnom_Penh&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Pontianak&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Pyongyang&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Qatar&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Qostanay&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Qyzylorda&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Rangoon&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Riyadh&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Sakhalin&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Samarkand&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Seoul&lt;/td&gt;
&lt;td&gt;KST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Shanghai&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Singapore&lt;/td&gt;
&lt;td&gt;SGT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Srednekolymsk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Taipei&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Tashkent&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Tbilisi&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Tehran&lt;/td&gt;
&lt;td&gt;IRST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Thimphu&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Tokyo&lt;/td&gt;
&lt;td&gt;JST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Tomsk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Ulaanbaatar&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Urumqi&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Ust-Nera&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Vientiane&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Vladivostok&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Yakutsk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Yangon&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Yekaterinburg&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asia/Yerevan&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atlantic/Azores&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atlantic/Bermuda&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atlantic/Canary&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atlantic/Cape_Verde&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atlantic/Faroe&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atlantic/Madeira&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atlantic/Reykjavik&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atlantic/South_Georgia&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atlantic/St_Helena&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atlantic/Stanley&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia/Adelaide&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia/Brisbane&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia/Broken_Hill&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia/Currie&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia/Darwin&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia/Eucla&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia/Hobart&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia/Lindeman&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia/Lord_Howe&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia/Melbourne&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia/Perth&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia/Sydney&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Amsterdam&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Andorra&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Astrakhan&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Athens&lt;/td&gt;
&lt;td&gt;EEST, EET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Belgrade&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Berlin&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Bratislava&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Brussels&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Bucharest&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Budapest&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Busingen&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Chisinau&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Copenhagen&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Dublin&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Gibraltar&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Guernsey&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Helsinki&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Isle_of_Man&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Istanbul&lt;/td&gt;
&lt;td&gt;TRT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Jersey&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Kaliningrad&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Kiev&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Kirov&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Lisbon&lt;/td&gt;
&lt;td&gt;WEST, WET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Ljubljana&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/London&lt;/td&gt;
&lt;td&gt;BST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Luxembourg&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Madrid&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Malta&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Mariehamn&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Minsk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Monaco&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Moscow&lt;/td&gt;
&lt;td&gt;MSD, MSK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Oslo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Paris&lt;/td&gt;
&lt;td&gt;CEST, CET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Podgorica&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Prague&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Riga&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Rome&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Samara&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/San_Marino&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Sarajevo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Saratov&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Simferopol&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Skopje&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Sofia&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Stockholm&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Tallinn&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Tirane&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Ulyanovsk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Uzhgorod&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Vaduz&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Vatican&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Vienna&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Vilnius&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Volgograd&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Warsaw&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Zagreb&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Zaporozhye&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe/Zurich&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GMT&lt;/td&gt;
&lt;td&gt;GMT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indian/Antananarivo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indian/Chagos&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indian/Christmas&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indian/Cocos&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indian/Comoro&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indian/Kerguelen&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indian/Mahe&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indian/Maldives&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indian/Mauritius&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indian/Mayotte&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indian/Reunion&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Apia&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Auckland&lt;/td&gt;
&lt;td&gt;NZDT, NZST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Bougainville&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Chatham&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Chuuk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Easter&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Efate&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Enderbury&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Fakaofo&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Fiji&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Funafuti&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Galapagos&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Gambier&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Guadalcanal&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Guam&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Honolulu&lt;/td&gt;
&lt;td&gt;HST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Johnston&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Kanton&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Kiritimati&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Kosrae&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Kwajalein&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Majuro&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Marquesas&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Midway&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Nauru&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Niue&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Norfolk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Noumea&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Pago_Pago&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Palau&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Pitcairn&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Pohnpei&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Ponape&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Port_Moresby&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Rarotonga&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Saipan&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Tahiti&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Tarawa&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Tongatapu&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Truk&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Wake&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pacific/Wallis&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The code to output this table:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Foundation&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;identifier&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TimeZone&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;knownTimeZoneIdentifiers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;abbreviations&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TimeZone&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abbreviationDictionary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;identifier&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;| &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; | &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abbreviations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;joined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; |&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Mick F</name></author><category term="Journaling" /><category term="Swift" /><category term="iOS" /><summary type="html">Exploring some code wrappring dates with no time, ie represented as strings in the YYYY‑MM‑DD ISO 8601 format.</summary></entry><entry><title type="html">A Swift Recoverable Precondition That Can Throw</title><link href="https://mickf.net/tech/swift-precondition-that-throws/" rel="alternate" type="text/html" title="A Swift Recoverable Precondition That Can Throw" /><published>2021-12-17T00:00:00+00:00</published><updated>2021-12-17T00:00:00+00:00</updated><id>https://mickf.net/tech/swift-precondition-that-throws</id><content type="html" xml:base="https://mickf.net/tech/swift-precondition-that-throws/">&lt;p&gt;What on Earth is a &lt;em&gt;recoverable&lt;/em&gt; precondition? &lt;a href=&quot;https://www.swiftbysundell.com/articles/picking-the-right-way-of-failing-in-swift/&quot; title=&quot;Picking the right way of failing in Swift, by John Sundell&quot;&gt;Isn’t the point of a
precondition to be non-recoverable?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Yes. Sorry I couldn’t think of a better name&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2&gt;The Result&lt;/h2&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;c1&quot;&gt;/// Checks a necessary condition for making forward progress.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// The difference with `precondition` is that if an error is _thrown_ when the condition is executed, the error will&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// be _rethrown_ so that it can be recovered. But this recoverability does not apply if the condition executes&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// properly and its condition fails.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// In other words, this function is a wrapper around `precondition` so that it can be fed a condition closure that can&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// throw.&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;recoverablePrecondition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;@autoclosure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;@autoclosure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;StaticString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;#file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UInt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;#line&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rethrows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;preconditionFailure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;What is this?&lt;/h2&gt;
&lt;p&gt;Let me explain. Here is the code I had:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;nf&quot;&gt;precondition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;aLittleTenderness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;But then, I had to make &lt;code&gt;aLittleTenderness&lt;/code&gt; able to throw, and &lt;code&gt;precondition&lt;/code&gt;
was yelling at me about it:&lt;/p&gt;
&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;markdown&quot;&gt;Property access can throw, but it is not marked with &apos;try&apos; and it is executed in
a non-throwing autoclosure
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Even when I &lt;em&gt;tried&lt;/em&gt; a little tenderness&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;nf&quot;&gt;precondition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;aLittleTenderness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The compiler would complain:&lt;/p&gt;
&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;markdown&quot;&gt;Property access can throw, but it is executed in a non-throwing autoclosure
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And this alternative looked terrible:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;nf&quot;&gt;precondition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;aLittleTenderness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;So I created a &lt;em&gt;recoverable precondition&lt;/em&gt;, which means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;if the code throws, then try to recover from it;&lt;/li&gt;
&lt;li&gt;if the code does not throw, treat it as a precondition.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don’t hate me. &lt;a href=&quot;https://youtu.be/IQ9n2_5mbig&quot; title=&quot;Otis Redding - Try A Little Tenderness - Live 1967 (Reelin&apos; In The Years Archives)&quot;&gt;Listen to soul music instead&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Lessons Learned&lt;/h2&gt;
&lt;p&gt;Look at this badass signature! Of course I started from the signature of
&lt;a href=&quot;https://developer.apple.com/documentation/swift/1540960-precondition&quot; title=&quot;precondition(_:_:file:line:) Documentation&quot;&gt;&lt;code&gt;precondition&lt;/code&gt;&lt;/a&gt; and I adapted it to my needs. It features some keywords I
don’t use everyday. Each keyword deserves its own extensive explanation. But
wait, other people have already written about them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.swiftbysundell.com/articles/using-autoclosure-when-designing-swift-apis/&quot; title=&quot;Using @autoclosure when designing Swift APIs, by John Sundell&quot;&gt;&lt;code&gt;@autoclosure&lt;/code&gt;&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.hackingwithswift.com/example-code/language/how-to-use-the-rethrows-keyword&quot; title=&quot;How to use the rethrows keyword, by Paul Hudson&quot;&gt;&lt;code&gt;rethrows&lt;/code&gt;&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/ReferenceManual/Expressions.html#ID390&quot; title=&quot;Literal Expression section of the Expressions page of the Swift Reference Manual&quot;&gt;&lt;code&gt;#file&lt;/code&gt; and &lt;code&gt;#line&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol&gt;
&lt;li id=&quot;fn1&quot;&gt;
&lt;p&gt;If you can think of one, let me know. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot;&gt;
&lt;p&gt;Beware, a terrible music joke is hiding in this statement, can you spot it? &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content><author><name>Mick F</name></author><category term="Journaling" /><category term="Swift" /><category term="iOS" /><summary type="html">The what and the why of a Swift precondition helper that accepts statements that can throw recoverable errors.</summary></entry></feed>