<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Hi, I’m Valentin! on vknabel</title><link>https://vknabel.com/</link><description>Recent content in Hi, I’m Valentin! on vknabel</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><copyright>© Valentin Knabel</copyright><lastBuildDate>Wed, 25 Feb 2026 14:19:26 +0100</lastBuildDate><atom:link href="https://vknabel.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Prima Facie</title><link>https://vknabel.com/library/prima-facie/</link><pubDate>Sat, 27 Jun 2026 16:21:06 +0200</pubDate><guid>https://vknabel.com/library/prima-facie/</guid><description>&lt;p&gt;I devoured this book. Told from the perspective of a defense attorney, it explores sexual offenses, critiques the legal system, and exposes the immense obstacles victims face in seeking justice, as well as the alarmingly high number of unreported cases.&lt;/p&gt;
&lt;p&gt;The story is compelling and beautifully written—you feel and live every moment alongside the protagonist. The pacing is expertly controlled, with intense passages balanced by quieter, reflective ones. If you&amp;rsquo;re looking for an emotional, well-crafted book that reads effortlessly, you&amp;rsquo;ll find it here—and be delighted.&lt;/p&gt;</description></item><item><title>James</title><link>https://vknabel.com/library/james/</link><pubDate>Mon, 15 Jun 2026 21:31:58 +0200</pubDate><guid>https://vknabel.com/library/james/</guid><description>&lt;p&gt;The book follows a slave in the southern USA during his escape journey. Based on &lt;em&gt;Huckleberry Finn&lt;/em&gt;: a book I&amp;rsquo;ve never read.
Hence I read it with relatively few preconceptions. Topic-wise &lt;em&gt;James&lt;/em&gt; tackles a dark chapter of humanity, particularly of the USA, but only touches lightly on the harsh sufferings.&lt;/p&gt;
&lt;p&gt;The ending feels a bit rushed and has a somewhat forced happy ending. It’s enjoyable to read, yet I felt the author unnecessarily held back on his criticism.&lt;/p&gt;</description></item><item><title>Nationales Sicherheits-Amt</title><link>https://vknabel.com/library/nationales-sicherheits-amt/</link><pubDate>Mon, 25 May 2026 00:00:00 +0000</pubDate><guid>https://vknabel.com/library/nationales-sicherheits-amt/</guid><description>&lt;p&gt;What if electric computers and the internet existed before the nazi regime and the second world war? This book begins with a glimpse what surveillance and exessive data collection can be used for in a totalitarian regime, effectively even worsening the holocaust.&lt;/p&gt;
&lt;p&gt;The story is written around two characters, their role in the hypothetical &amp;ldquo;Nationales Sicherheits-Amt&amp;rdquo; (National Security Agency) and ultimately their personal fate. The story is gripping, but in my opinion the story is a bit too much focues on the characters and a more broad view is missing. While at the first half of the book you can closely follow the development of a dystopian system, the story disconnects and solely focuses on the characters. Only at the very end, when there isn&amp;rsquo;t anything to tell anymore, the consequences to the system and the people within are show. I wish there would have been more details here. The end felt a little bit too abrupt for me.&lt;/p&gt;
&lt;p&gt;Nevertheless I still enjoyed the read and recommend this book.&lt;/p&gt;</description></item><item><title>Bikepacking: Descent to Italy</title><link>https://vknabel.com/posts/bikepacking-descent-to-italy/</link><pubDate>Fri, 10 Apr 2026 21:30:00 +0200</pubDate><guid>https://vknabel.com/posts/bikepacking-descent-to-italy/</guid><description>&lt;p&gt;Today, I would like to invite you to join me on a calming adventure through winding streets, quiet little towns and magical forests. Make yourself a drink, get comfortable and relax.
Take a sip. Take your time. I’d like to take you to a place at the back of my mind.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This blog post has been written for the &lt;a href="https://indieweb.org/IndieWeb_Carnival"&gt;IndieWeb Carnival&lt;/a&gt; topic &lt;a href="https://lifeofpablo.com/blog/indieweb-carnival-2026-adventure"&gt;&lt;strong&gt;Adventure&lt;/strong&gt;&lt;/a&gt; hosted by &lt;a href="https://lifeofpablo.com"&gt;Pablo&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Once you wake up, it’s still early in the morning. You get out of your tent. The sun slowly climbs up the mountains and shines down on the valley below. You grab a snack, pack up your tent, and load everything onto your bicycle. Let&amp;rsquo;s hop on and start our day!&lt;/p&gt;
&lt;p&gt;Your path follows a nearby river through a ravine. You can hear the water rushing and the leaves rustling. On the opposite bank, near the cliff, you can see a medieval outpost. Birds fly around, visible through the trees. Follow the path alongside the river. After crossing the river on a small bridge, you find yourself at a mountain pass.&lt;/p&gt;
&lt;p&gt;This is the final challenge you must overcome before reaching Italy. Slow and steady. The path ahead is smooth. You know the drill. Slow and steady. You start climbing.&lt;/p&gt;
&lt;p&gt;After a few switchbacks, you suddenly notice the breathtaking view. You look down into the valley, which is waking up in patches of sunlight. Forests cover the mountainsides, basking in the spotlight. The light warms your skin, giving you a boost of energy.&lt;/p&gt;
&lt;p&gt;You continue climbing. A drop of sweat runs down your forehead. You are getting close to the top. It’s still early morning and it’s still cool. You greet some forest workers along the way and take the final winding path. You are almost there. Suddenly, a new view opens up on the other side of the mountain.&lt;/p&gt;
&lt;p&gt;This side is shrouded in mist and clouds. Below, you can see a small, picturesque Austrian mountain village. Groups of trees break through the mist.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/posts/Bikepacking-Descent-to-Italy/misty-forest.jpg" alt="A misty forest on a mountain side"&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s time for a break. You sit down on a bench, grab a snack and take a sip, enjoying the view for a few minutes. This side is less misty, but cloudier. You put on your wind jacket and begin your descent. The road is wide and fairly straight. The village gets closer and closer. The wind hits your face and lifts the corners of your mouth.&lt;/p&gt;
&lt;p&gt;You brake to slow down and drive through the alpine village, with its wooden houses adorned with flowers on the balconies. You follow the roads that lead you out of the village and into the green, grassy valley beyond. It starts to rain lightly.&lt;/p&gt;
&lt;p&gt;In front of you is the last hill. It&amp;rsquo;s small and flat, but it obscures your view of what lies beyond. You still have lots of energy left, and you know there&amp;rsquo;s a reward waiting for you at the top. You stand up on your bike and start pedalling hard. In about half a minute, you reach the top.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/posts/Bikepacking-Descent-to-Italy/reschen-resia-lake.jpg" alt="A tower in a mountain lake"&gt;&lt;/p&gt;
&lt;p&gt;The view opens up to reveal a blue lake surrounded by mountains. In the middle of the lake, a lonely church tower rises from the depths of the water.&lt;/p&gt;
&lt;p&gt;Continue along the pavement on the left side of the lake. Follow it along the riverside, cross the river and find yourself on the opposite bank in a forest.
The narrow road is surrounded by a beautiful green, mossy atmosphere. Birds sing around you and sunbeams filter through the canopy of trees.&lt;/p&gt;
&lt;p&gt;Focus on the moment. The winding road leads you downhill.
You accelerate. Press the right pedal, lean to the left, and take the curve.
Your eyes are fixed on the next bend. Right. You flow through the curves.
You lean down and let the bike do the work.
Left. It&amp;rsquo;s just you and your bike. Right. The rain stopped long ago.
Left.&lt;/p&gt;
&lt;p&gt;Suddenly, the canopy of trees opens up to reveal the blue sky behind.
You sit up straight and slow down.&lt;/p&gt;
&lt;p&gt;A town with a mix of alpine and Italian buildings appears in front of you. You drive down the cobbled streets and find a place to get some pizza and a cup of coffee.
You enjoy your break, reflecting on what you&amp;rsquo;ve seen and experienced today.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/posts/Bikepacking-Descent-to-Italy/castles.jpg" alt="Two castles on a mountain side"&gt;&lt;/p&gt;
&lt;p&gt;After some time, you continue your descent and leave the hilly village behind. The landscape changes. Now, you drive through apple orchards and vineyards. The mountains surrounding the valley become smaller and feature ancient ruins and castles on their slopes. It slowly gets warmer and warmer. Occasionally, you are cooled down by a few droplets of water from sprinklers.&lt;/p&gt;
&lt;p&gt;You arrive in a tiny town with a small church near the town square. The buildings are made of sandstone and the streets are narrow and cobblestone. People are chatting in the town square. The sun brightens up the scenery.
You leave the painting untouched and continue your journey alongside a new river. The water is greyish-blue and fast-flowing.&lt;/p&gt;
&lt;p&gt;It is still flowing downwards, so you don&amp;rsquo;t have to pedal much. As you go along, you see more and more people around you. Some are walking and some are cycling. Compact tractors transport stacks of apple crates several metres high.&lt;/p&gt;
&lt;p&gt;Near the riverbank, you spot a small stall selling sweet apple juice. You stop to refill your bottle. You take a refreshing sip. After all that work, the sweet, sugary juice tickles your brain and a spark of joy hits your throat.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/posts/Bikepacking-Descent-to-Italy/valley.jpg" alt="The valley below with cities and apple orchards"&gt;&lt;/p&gt;
&lt;p&gt;You come to a small avenue lined with trees. At the end of it, huge chairs are perched on the edge of another valley. Dozens of metres above the ground, you can see the city. Your goal. Tiny cars and people move slowly around like toys.&lt;/p&gt;
&lt;p&gt;You take the route down, with bushes, trees and flowers on either side. You take each turn. A couple of minutes later, you arrive at the horizon that was just in front of you a few moments ago. The city. Cypress and palm trees line the roads. The quiet, peaceful atmosphere vanishes, replaced by a colourful medley of people, dogs, bicycles, delicious smells of food, and music.&lt;/p&gt;
&lt;p&gt;You spot a lovely café near the river. You sit back and watch the world go by, calming down as you do so. As you drink your espresso and eat your cake, you reflect on your day. How far you&amp;rsquo;ve come! What you&amp;rsquo;ve seen. The things you&amp;rsquo;ve felt along the way.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/posts/Bikepacking-Descent-to-Italy/bike.jpg" alt="My bike packed with everything I need"&gt;&lt;/p&gt;
&lt;p&gt;I hope you enjoyed this little trip through my memory. Adventures like this can&amp;rsquo;t be made online or at home. Whatever motivates you, let it lead you wherever you want, whenever you want. Load up your bicycle or backpack with everything you need and go out and explore the world.&lt;/p&gt;
&lt;p&gt;This was day three of my adventure from southern Germany to northern Italy from 2025. The route described starts in Austria just before the Reschen Pass (Passo di Resia) and ends in Merano. It follows the Via Claudia Augusta route.&lt;/p&gt;</description></item><item><title>Switching From Let's Encrypt to Actalis</title><link>https://vknabel.com/posts/switching-from-letsencrypt-to-actalis/</link><pubDate>Mon, 02 Mar 2026 16:41:56 +0100</pubDate><guid>https://vknabel.com/posts/switching-from-letsencrypt-to-actalis/</guid><description>&lt;p&gt;In my ongoing efforts to reduce the reliance on US companies and services, I decided from Let&amp;rsquo;s Encrypt to the Italian Actalis.
Sure, Let&amp;rsquo;s Encrypt works fine, is non-profit and had a huge impact on today&amp;rsquo;s internet security, but nonetheless it is still US based and required to comply with US law and government requests.
This could also include breaking the chain of trust for certificates while prohibiting to share this information with the public.&lt;/p&gt;
&lt;p&gt;Actalis, on the other hand, complies to European laws, regulations and standards.&lt;/p&gt;
&lt;p&gt;I operate a Kubernetes cluster with &lt;a href="https://cert-manager.io/"&gt;cert-manager&lt;/a&gt; and DNS01 challenges. Actalis provides these too, making the switch fairly easy.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create an Actalis account.&lt;/li&gt;
&lt;li&gt;Copy your ACME Key ID and the HMAC.&lt;/li&gt;
&lt;li&gt;Create a new secret in Kubernetes with these values.&lt;/li&gt;
&lt;li&gt;Create a new &lt;code&gt;Issuer&lt;/code&gt; or &lt;code&gt;ClusterIssuer&lt;/code&gt; in Kubernetes, referencing the secret created in step 3.&lt;/li&gt;
&lt;li&gt;Reference the new issuer in your &lt;code&gt;Certificate&lt;/code&gt; resources.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Secret&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actalis-eab&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;cert-manager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;stringData&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# no trailing `=` in the HMAC&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;hmac&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;$YOUR_HMAC&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;cert-manager.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ClusterIssuer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actalis-prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;acme&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;email&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;$YOUR_EMAIL&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;server&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;https://acme-api.actalis.com/acme/directory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;privateKeySecretRef&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actalis-dns01-account-key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;externalAccountBinding&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;keyID&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;$YOUR_KEY_ID&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;keySecretRef&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actalis-eab&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;hmac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;solvers&lt;/span&gt;: &lt;span style="color:#75715e"&gt;# if you are switching, just keep it as is&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;dns01&lt;/span&gt;: &lt;span style="color:#75715e"&gt;# due to my setup only DNS01 challenges make sense&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cloudflare&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;apiTokenSecretRef&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;cloudflare-credentials&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;api-token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Perfect, now we&amp;rsquo;ve got Actalis set up and we can start issuing certificates.&lt;/p&gt;
&lt;p&gt;But there is one downside: Actalis does not support multiple domains per certificate in the free plan.
Splitting these into multiple certs and secrets does solve this fairly easily though.&lt;/p&gt;
&lt;p&gt;I understand if this is a deal breaker for some, but in the end, these are just a few lines that are simple and easy to maintain.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;cert-manager.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Certificate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;first-domain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;secretName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;first-domain-tls&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;issuerRef&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actalis-prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ClusterIssuer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;dnsNames&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;first.domain&lt;/span&gt; &lt;span style="color:#75715e"&gt;# only one dns name supported&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;cert-manager.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Certificate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;second-domain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;secretName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;second-domain-tls&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;issuerRef&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actalis-prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ClusterIssuer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;dnsNames&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;second.domain&lt;/span&gt; &lt;span style="color:#75715e"&gt;# and here is the second one&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Ingress&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;both-domains&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# here we need to supply both certs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;tls&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;secretName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;first-domain-tls&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;hosts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;first.domain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;secretName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;second-domain-tls&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;hosts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;second.domain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# the rules stay untouched&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rules&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;host&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;first.domain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;http&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;paths&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;path&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;pathType&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Prefix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;backend&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;service&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service-http&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;http&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;host&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;second.domain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;http&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;paths&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;path&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;pathType&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Prefix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;backend&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;service&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service-http&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;http&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that&amp;rsquo;s it! For more services and ideas you could get rid of big tech and the US, check out &lt;a href="https://vknabel.com/sovereignty"&gt;Sovereignty&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>The Grand Redesign</title><link>https://vknabel.com/posts/the-grand-redesign/</link><pubDate>Thu, 26 Feb 2026 22:13:55 +0100</pubDate><guid>https://vknabel.com/posts/the-grand-redesign/</guid><description>&lt;p&gt;Nine years ago I launched this blog to share some thoughts about software development. Most of the time I have been very strict about content that I posted. If it wasn&amp;rsquo;t tech, I didn&amp;rsquo;t post it.&lt;/p&gt;
&lt;p&gt;Over the years my priorities shifted. I even started a new blog to open up a new niche. But in the end I decided to not restrict myself in terms of topics. Let&amp;rsquo;s see where this leads me to.&lt;/p&gt;
&lt;h2 id="how-it-started"&gt;How it started&lt;/h2&gt;
&lt;p&gt;Back in February 2017, that&amp;rsquo;s &lt;em&gt;nine freaking years&lt;/em&gt; ago, I launched this blog. I just picked some template and some static site generator to get started.
The blog was built using &lt;a href="https://www.gatsbyjs.com/docs/glossary/static-site-generator/"&gt;Gatsby&lt;/a&gt; with some simple theme.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/posts/The-Grand-Redesign/vknabel-2017.png" alt="screenshot of the design in 2017, dark, mostly grey"&gt;&lt;/p&gt;
&lt;h2 id="the-problem-with-gatsby"&gt;The problem with Gatsby&lt;/h2&gt;
&lt;p&gt;With the years I was increasingly frustrated with Gatsby. I wanted a simple blog, nothing too crazy client side stuff. But essentially that was the focus of Gatsby: web workers and lots of JavaScript.&lt;/p&gt;
&lt;p&gt;When I left day to day web development, I also switched to &lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt;. After all the hassle this was a breeze! I picked a new theme that was simple, but less basic and had a touch of color.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/posts/The-Grand-Redesign/vknabel-2022.png" alt="screenshot of the new markdown like design in 2022"&gt;&lt;/p&gt;
&lt;p&gt;Essentially I was pretty happy with my site. But as teased above not with the topics I was writing about.
I started a new website &lt;a href="https://vknabel.com/tags/plants"&gt;Aroid Garden&lt;/a&gt; about houseplants, but also lost focus there due to personal reasons. Eventually I sunsetted it.&lt;/p&gt;
&lt;h2 id="why-the-redesign"&gt;Why the redesign?&lt;/h2&gt;
&lt;p&gt;Now I decided to transform my former tech blog to a personal one. If I want to write something down, I don&amp;rsquo;t want to think about wether the topic fits or not.
Writing is a habit that must be built and maintained, but I regularly focus on other stuff than tech.&lt;/p&gt;
&lt;p&gt;Though the markdown-style design just doesn&amp;rsquo;t fit the new content I want to share. That&amp;rsquo;s why I decided to go with the redesign.
As it&amp;rsquo;s been a great choice I sticked to Hugo, but couldn&amp;rsquo;t find a theme that fit my needs, so I rolled my own. For technical details about this site see &lt;a href="https://vknabel.com/colophon"&gt;Colophon&lt;/a&gt;, which will be updated in the future.&lt;/p&gt;
&lt;p&gt;I know there are still some rough edges around the design, but better ship, than procrastinate.&lt;/p&gt;
&lt;h2 id="what-about-the-content"&gt;What about the content?&lt;/h2&gt;
&lt;p&gt;When mixing many topics, it makes sense to provide ways to navigate and filter it more easily.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;refactored the tags that grew over the years (I had &lt;code&gt;go&lt;/code&gt; and &lt;code&gt;golang&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;separate &lt;a href="https://vknabel.com/rss"&gt;RSS feeds&lt;/a&gt; for the blog, library and one for all content&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also provide additional content like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a book &lt;a href="https://vknabel.com/library"&gt;library&lt;/a&gt; to share book recommendatations&lt;/li&gt;
&lt;li&gt;a &lt;a href="https://vknabel.com/now"&gt;now&lt;/a&gt; page to share what I&amp;rsquo;m currently doing&lt;/li&gt;
&lt;li&gt;the &lt;a href="https://vknabel.com/colophon"&gt;colophon&lt;/a&gt; to share technical details about the site&lt;/li&gt;
&lt;li&gt;my list of &lt;a href="https://vknabel.com/projects"&gt;projects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;and &lt;a href="https://vknabel.com/sovereignity"&gt;sovereignity&lt;/a&gt; to share my efforts to regain control over my data and online presence&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also some updated content:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;a href="https://vknabel.com/about"&gt;about&lt;/a&gt; page was just outdated&lt;/li&gt;
&lt;li&gt;I imported the Aroid Garden posts to the &lt;a href="https://vknabel.com/tags/plants"&gt;plants tag&lt;/a&gt;, though the layout isn&amp;rsquo;t optimized for it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the end, when you have a look at &lt;a href="https://vknabel.com/"&gt;Home&lt;/a&gt;, you cannot come around to see that my content has changed.
And that this is just the beginning.&lt;/p&gt;</description></item><item><title>Now</title><link>https://vknabel.com/now/</link><pubDate>Thu, 26 Feb 2026 00:00:00 +0000</pubDate><guid>https://vknabel.com/now/</guid><description>&lt;p&gt;From time to time, I like to share what I am up to and how I prefer to spend my time.
Currently I mostly do some Homelab stuff, riding my bicyle and enjoying my life in general.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;My latest blog posts are &lt;a href="https://metal-stack.io/blog/2026/05-hack-the-garden"&gt;Hack the Garden No. 8 🔨 on metal-stack.io&lt;/a&gt; and &lt;a href="https://x-cellent.com/blog/infrastructure-as-code-erklart-mehr-kontrolle-weniger-manuelle-fehler"&gt;Infrastructure as Code erklärt: Mehr Kontrolle, weniger manuelle Fehler on x-cellent.com&lt;/a&gt; (German).&lt;/li&gt;
&lt;li&gt;The last book I finished is &lt;a href="https://vknabel.com/library/prima-facie"&gt;&lt;strong&gt;Prima Facie&lt;/strong&gt; by Suzie Miller&lt;/a&gt; after aborting Text by Dmitry Glukhovsky.&lt;/li&gt;
&lt;li&gt;I currently read &lt;strong&gt;Das Wesen des Lebens&lt;/strong&gt; by Iida Turpeinen.&lt;/li&gt;
&lt;li&gt;By day I work as a Kubernetes and Go Engineer at &lt;a href="https://x-cellent.com/"&gt;x-cellent&lt;/a&gt;. You might even spot me on their website, too.&lt;/li&gt;
&lt;li&gt;By night I mostly tinker with my Homelab.&lt;/li&gt;
&lt;li&gt;&lt;del&gt;If the weather is great there is a high chance I am riding my bicycle &lt;em&gt;right now&lt;/em&gt;.&lt;/del&gt; Yikes, I crashed in May.&lt;/li&gt;
&lt;li&gt;During rain or on the train, &lt;a href="https://vknabel.com/library"&gt;books&lt;/a&gt; are great!&lt;/li&gt;
&lt;li&gt;Trying to break 5.5k by bicycle. I am a few hundred kilometers behind my schedule.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At the moment I mostly focus on my homelab and digital &lt;a href="https://vknabel.com/sovereignty"&gt;sovereignty&lt;/a&gt;.
Other than that, most side &lt;a href="https://vknabel.com/projects"&gt;projects&lt;/a&gt; are currently on hold.&lt;/p&gt;
&lt;p&gt;Stuff outside of the digital world is just too joyful right now.&lt;/p&gt;
&lt;p&gt;In case you understand German, you can check out a quick and fun &lt;a href="https://www.linkedin.com/posts/x-cellent-technologies-gmbh_x-cellent-quickfire-episode-4-valentin-ugcPost-7448274515664703488-n1ZB"&gt;interview on LinkedIn&lt;/a&gt; of me made by &lt;a href="https://x-cellent.com"&gt;x-cellent&lt;/a&gt;.
Nothing too serious and not that long, but it gives you a little insight into how I am.&lt;/p&gt;</description></item><item><title>Sovereignty</title><link>https://vknabel.com/sovereignty/</link><pubDate>Wed, 25 Feb 2026 19:53:11 +0100</pubDate><guid>https://vknabel.com/sovereignty/</guid><description>&lt;p&gt;The development of global and especially US politics in the last decade made it clear that Europe must become more sovereign and independent from the US. And the same applies to everybody in the world regarding big tech.&lt;/p&gt;
&lt;p&gt;This here should be some kind of dashboard to reflect my current efforts and challenges. Maybe this inspires you to replace some technology with a better one.
I can recommend &lt;a href="https://european-alternatives.eu"&gt;European Alternatives&lt;/a&gt; to start with your research regarding alternatives.&lt;/p&gt;
&lt;p&gt;But beware: this conflict is not a personal one. Politics need to act and implement ntives to drive change.
That beign said, take all this with a grain of salt, but maybe this might help you, too.&lt;/p&gt;
&lt;h2 id="apple"&gt;Apple&lt;/h2&gt;
&lt;p&gt;I still own an iPhone and Mac. I will continue using these as long they still fit my use cases.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AppleTV&lt;/strong&gt;: still streaming.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;iCloud&lt;/strong&gt;: canceled subscription. Self-hosted &lt;a href="https://nextcloud.com/"&gt;NextCloud&lt;/a&gt; for files and &lt;a href="https://immich.app/"&gt;Immich&lt;/a&gt; for photos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apple Music&lt;/strong&gt;: canceled subscription. Moved to &lt;a href="https://www.spotify.com"&gt;Spotify&lt;/a&gt; long ago.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;macOS&lt;/strong&gt;: proud Linux user at work. &lt;em&gt;arch btw&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mail/Calendar&lt;/strong&gt;: switched to &lt;a href="https://proton.me/"&gt;Proton&lt;/a&gt;. There are other great alternatives.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="cloudflare"&gt;Cloudflare&lt;/h2&gt;
&lt;p&gt;This is a tough one for sure. Due to the architecture of my homelab and the fact that I do not get a public IPv4 address, I use Cloudflare Tunnel to expose my services to the internet. That paired with DDoS protection and great firewall capabilities (&lt;a href="https://mastodon.social/@vknabel/116086189818408755"&gt;looking at you Facebook&lt;/a&gt;) switching away from it will be pretty tough for me. Nonetheless I stop using more services.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;S3&lt;/strong&gt;: self-hosted &lt;a href="https://garagehq.deuxfleurs.fr/"&gt;Garage&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="github"&gt;GitHub&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s be honest &lt;strong&gt;GitHub&lt;/strong&gt; under Microsoft kind of sucks with all their bugs and AI stuff. Sadly there is no realistic way to get rid of it entirely. I depend on it at work and privately.&lt;/p&gt;
&lt;p&gt;Here is what I&amp;rsquo;ve done so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Self-host &lt;a href="https://forgejo.org/"&gt;Forgejo&lt;/a&gt; at &lt;a href="https://code.knabel.dev"&gt;code.knabel.dev&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I &lt;strong&gt;moved&lt;/strong&gt; nearly all my repositories there. Including private and public ones. Only a few that cannot be moved remain.&lt;/li&gt;
&lt;li&gt;I have a local CI/CD runner that is GitHub Action compatible.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Pages&lt;/strong&gt;: still using. Going to move to self-host using &lt;a href="https://garagehq.deuxfleurs.fr/"&gt;Garage&lt;/a&gt; as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="google"&gt;Google&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Search&lt;/strong&gt;: I switched to &lt;a href="https://www.ecosia.org/"&gt;Ecosia&lt;/a&gt; long ago. Seems less of a harm for the environment, is German and does not track me. Win-win.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Drive&lt;/strong&gt;: Self-hosted &lt;a href="https://nextcloud.com/"&gt;NextCloud&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Photos&lt;/strong&gt;: Self-hosted &lt;a href="https://immich.app/"&gt;Immich&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mail/Calendar&lt;/strong&gt;: switched to &lt;a href="https://proton.me/"&gt;Proton&lt;/a&gt;. There are other great alternatives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Youtube&lt;/strong&gt;: account deleted. Still watching there, but more and more videos are available on &lt;a href="https://www.spotify.com"&gt;Spotify&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Account&lt;/strong&gt;: deleted.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="netlify"&gt;Netlify&lt;/h2&gt;
&lt;p&gt;Currently I still host &lt;a href="https://zirric.knabel.dev"&gt;zirric.knabel.dev&lt;/a&gt; there, but I am going to move it to self-hosting with &lt;a href="https://garagehq.deuxfleurs.fr/"&gt;Garage&lt;/a&gt; as well.&lt;/p&gt;
&lt;h2 id="microsoft"&gt;Microsoft&lt;/h2&gt;
&lt;p&gt;Besides &lt;a href="#GitHub"&gt;GitHub&lt;/a&gt;, I still own an Xbox.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Xbox&lt;/strong&gt;: buy Games second hand or hard copies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mail/Calendar&lt;/strong&gt;: switched to &lt;a href="https://proton.me/"&gt;Proton&lt;/a&gt;. There are other great alternatives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VSCode&lt;/strong&gt;: switched to &lt;a href="https://helix-editor.com/"&gt;Helix&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VSCode Marketplace&lt;/strong&gt;: can&amp;rsquo;t delete account yet to keep extensions up.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="social-media"&gt;Social Media&lt;/h2&gt;
&lt;p&gt;Depending on the person, this might be easy or straigt up impossible.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;WhatsApp&lt;/strong&gt;: hard to get around this in Germany. I switched for most chats to &lt;a href="https://signal.org"&gt;Signal&lt;/a&gt;. A few group chats sadly cannot be moved.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Twitter / X&lt;/strong&gt;: account deleted. Move to &lt;a href="https://mastodon.social/@vknabel"&gt;Mastodon&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instagram&lt;/strong&gt;: account deleted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reddit&lt;/strong&gt;: account deleted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Youtube&lt;/strong&gt;: account deleted. Still watching there, but more and more videos are available on &lt;a href="https://www.spotify.com"&gt;Spotify&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="1password--password-manager"&gt;1Password / Password Manager&lt;/h2&gt;
&lt;p&gt;After long years of using it, I just wasn&amp;rsquo;t happy or comfortable anymore.
Switched to self-hosting &lt;a href="https://github.com/dani-garcia/vaultwarden"&gt;Vaultwarden&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="more-services"&gt;More Services&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Let&amp;rsquo;s Encrypt&lt;/strong&gt;: switched to &lt;a href="https://www.actalis.com/"&gt;Actalis&lt;/a&gt; for TLS certificates on their free plan. See my &lt;a href="https://vknabel.com/posts/switching-from-letsencrypt-to-actalis/"&gt;blog post&lt;/a&gt; for details.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Projects</title><link>https://vknabel.com/projects/</link><pubDate>Wed, 25 Feb 2026 18:34:13 +0100</pubDate><guid>https://vknabel.com/projects/</guid><description>&lt;p&gt;In my life I worked on many projects, both private and at work. Here I highlighted the most important and interesting ones. If there is no link, they won&amp;rsquo;t be listed here.&lt;/p&gt;
&lt;h2 id="private-projects"&gt;Private Projects&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;This very website. See &lt;a href="https://vknabel.com/colophon"&gt;Colophon&lt;/a&gt; for more details about the tech stack.&lt;/li&gt;
&lt;li&gt;My Homelab built on &lt;a href="https://kubernetes.io/"&gt;Kubernetes&lt;/a&gt; and &lt;a href="https://www.talos.dev/"&gt;Talos&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.knabel.dev/zirric-lang/zirric"&gt;Zirric&lt;/a&gt; is a small programming language that favors declarations, readable data modeling, and annotations that describe capabilities without interfaces. The successor of Lithia. &lt;em&gt;Go, Docker, GitHub CI, Tree-Sitter, Language Server and more.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zirric.knabel.dev"&gt;Zirric docs&lt;/a&gt; is the documentation site for Zirric. &lt;em&gt;&lt;a href="https://docmd.io"&gt;docmd&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="at-work"&gt;At Work&lt;/h2&gt;
&lt;p&gt;In general I essentially work on the open source bare metal Kubernetes provisioning platform &lt;a href="https://metal-stack.io"&gt;metal-stack&lt;/a&gt; and our reference &lt;a href="https//metalstack.cloud"&gt;metalstack.cloud&lt;/a&gt; that provides Kubernetes as a Service.&lt;/p&gt;
&lt;p&gt;Here are some open source highlights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/metal-stack/cluster-api-provider-metal-stack"&gt;cluster-api-provider-metal-stack&lt;/a&gt; Cluster API provider for metal-stack. Not just &lt;a href="https://gardener.cloud"&gt;Gardener&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/metal-stack-cloud/terraform-provider-metal-stack"&gt;terraform-provider-metal-stack&lt;/a&gt; to provision metalstack.cloud clusters with &lt;del&gt;Terraform&lt;/del&gt; OpenTofu.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="discontinued-projects"&gt;Discontinued Projects&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://code.knabel.dev/lithia-lang/lithia"&gt;Lithia&lt;/a&gt; is my experimental, functional programming language. &lt;em&gt;Go, Docker, GitHub CI, Tree-Sitter, Language Server, Typescript, VS Code extension and more.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.knabel.dev/swift-lang/vscode-swift-development-environment"&gt;Maintained Swift Development Environment (SDE)&lt;/a&gt; provides Swift autocompletion to VS Code on macOS and Linux. &lt;em&gt;Swift, Typescript, Docker, Language Server, VS Code extension and more.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.knabel.dev/puffery-app"&gt;Puffery&lt;/a&gt; is a SwiftUI iOS App and Vapor Server to send push notifications fueled by Siri Shortcuts. &lt;em&gt;Swift, Vapor, Docker, GitHub CI, Bitrise, Xcode Cloud, PostgreSQL, Redis.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.knabel.dev/go-lang/go-puffery"&gt;go puffery&lt;/a&gt;. The TUI for Puffery. &lt;em&gt;Go, Bubble Tea, GoReleaser.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.knabel.dev/swift-lang/vscode-swiftlint"&gt;SwiftLint for VS Code&lt;/a&gt;: a plugin for &lt;a href="https://github.com/realm/SwiftLint"&gt;SwiftLint&lt;/a&gt;. &lt;em&gt;Typescript, VS Code extension.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.knabel.dev/swift-lang/vscode-apple-swift-format"&gt;apple/swift-format for VS Code&lt;/a&gt;: a plugin for &lt;a href="https://github.com/apple/swift-format"&gt;apple/swift-format&lt;/a&gt;. &lt;em&gt;Typescript, VS Code extension.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.knabel.dev/swift-lang/vscode-swiftformat"&gt;SwiftFormat for VS Code&lt;/a&gt;: a plugin for &lt;a href="https://github.com/nicklockwood/SwiftFormat"&gt;SwiftFormat&lt;/a&gt; on macOS. &lt;em&gt;Typescript, VS Code extension.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.knabel.dev/swift-lang/Archery"&gt;Archery&lt;/a&gt; allows you to declare all your project&amp;rsquo;s metadata and what you can do with it in one single place. &lt;em&gt;Swift, Docker, GitHub CI.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;All my &lt;a href="https://marketplace.visualstudio.com/publishers/vknabel"&gt;VS Code extensions&lt;/a&gt; are discontinued.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Visit my &lt;a href="https://github.com/vknabel"&gt;GitHub profile&lt;/a&gt; or &lt;a href="https://code.knabel.dev"&gt;code.knabel.dev&lt;/a&gt; for all open source contributions.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Colophon</title><link>https://vknabel.com/colophon/</link><pubDate>Wed, 25 Feb 2026 00:00:00 +0000</pubDate><guid>https://vknabel.com/colophon/</guid><description>&lt;p&gt;This website is built using &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; and hosted on &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt;.
The whole source code and configuration can be found in the &lt;a href="https://github.com/vknabel/vknabel.github.io"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The theme is hand-written and also available in the same repository.
If you wonder what the previous template was: &lt;a href="https://github.com/athul/archie"&gt;Archie&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The fonts I use are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.cyreal.org/fonts/lora/"&gt;Lora&lt;/a&gt; by Cyreal. It&amp;rsquo;s mostly used for headings and the logo.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BlackFoundryCom/InriaFonts"&gt;Inria Serif&lt;/a&gt; by Black Foundry. Used for body text.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://monaspace.githubnext.com/"&gt;Monaspace Argon&lt;/a&gt; by GitHub. Used for code snippets.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The color scheme loosely oriented around these colors from the &lt;a href="https://coolors.co/palette/4f000b-720026-ce4257-ff7f51"&gt;Fiery Passion Blend&lt;/a&gt;, then piped through a Tailwind CSS color palette generator and now has different hexes.&lt;/p&gt;</description></item><item><title>Trophy</title><link>https://vknabel.com/library/trophy/</link><pubDate>Mon, 23 Feb 2026 14:53:25 +0100</pubDate><guid>https://vknabel.com/library/trophy/</guid><description>&lt;p&gt;This book captivates from the very first minute.
At its core are the moral beliefs of a hunter and where they can lead.
There is no judgment—just the story told from his perspective.&lt;/p&gt;
&lt;p&gt;At times, his thought processes are almost unbearable to read when they fundamentally clash with your own beliefs. Roughly halfway through, the story provides an outlet for the internal conflict you feel.&lt;/p&gt;
&lt;p&gt;The book takes a clear stance without preaching, without condemning, without jumping to conclusions. The story stands on its own and resolves the contradiction through its plot.&lt;/p&gt;
&lt;p&gt;I can wholeheartedly recommend it.&lt;/p&gt;</description></item><item><title>Kein Guter Mann</title><link>https://vknabel.com/library/kein-guter-mann/</link><pubDate>Fri, 06 Feb 2026 14:53:31 +0100</pubDate><guid>https://vknabel.com/library/kein-guter-mann/</guid><description>&lt;p&gt;It is slowly told and a bit sluggish in the middle. There are occasionally uncomfortable passages to read because they aren’t properly reflected upon at the time.&lt;/p&gt;
&lt;p&gt;The ending is beautiful and emotional. But, as you read, you often wonder why. Yet people like that existed and were considered normal for the time.&lt;/p&gt;</description></item><item><title>Discontinuing of Projects</title><link>https://vknabel.com/posts/2026-01-02-discontinuing-of-projects/</link><pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2026-01-02-discontinuing-of-projects/</guid><description>&lt;p&gt;Starting with this year, I have decided to discontinue all of my open source projects.&lt;/p&gt;
&lt;p&gt;Most notably this affects my VS Code extensions for Swift.
I archieved the repositories and requested to deprecate the extensions in the Visual Studio Marketplace.&lt;/p&gt;
&lt;p&gt;Also I decided to stop my work on my toy programming language.&lt;/p&gt;
&lt;h2 id="reasoning"&gt;Reasoning&lt;/h2&gt;
&lt;p&gt;Since a few years I shifted my focus from Swift development to Go and Kubernetes and switched my main editor from VS Code to Helix.&lt;/p&gt;
&lt;p&gt;And I found joy in so many different things that can be done without computers. Doing some side projects just isn&amp;rsquo;t worth the hassle for me anymore.&lt;/p&gt;
&lt;p&gt;Occasionally I will still write some blog posts and update my non-archived repositories.&lt;/p&gt;</description></item><item><title>Zirric: Going Virtual</title><link>https://vknabel.com/posts/2025-09-28-going-virtual/</link><pubDate>Sun, 28 Sep 2025 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2025-09-28-going-virtual/</guid><description>&lt;p&gt;A few years ago while I started working on &lt;a href="https://code.knabel.dev/lithia-lang/lithia"&gt;Lithia&lt;/a&gt;, I decided to use whatever takes me to my goal. Getting finished was the primary focus. And I did.&lt;/p&gt;
&lt;p&gt;With &lt;a href="https://code.knabel.dev/zirric-lang/zirric"&gt;Zirric&lt;/a&gt; I want to take less compromise and build a better language. Performance and portability aren&amp;rsquo;t completely irrelevant anymore.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This blog post is part of a &lt;a href="https://vknabel.com/posts/journey-about-creating-a-new-programming-language/"&gt;Journey about creating a new programming language&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="the-tree-walker"&gt;The Tree Walker&lt;/h2&gt;
&lt;p&gt;For Lithia I chose to implement a tree walker interpreter. It is simple to implement and easy to understand. The downside is that it&amp;rsquo;s slow.&lt;/p&gt;
&lt;p&gt;Here the interpreter walks the abstract syntax tree (AST) and executes the program directly. This means that every time a function is called, the AST nodes for that function have to be traversed again.&lt;/p&gt;
&lt;p&gt;In this approach every node in the AST has an &lt;code&gt;evaluate&lt;/code&gt; method that takes the current context as a parameter. The context holds variable bindings and other state information.&lt;/p&gt;
&lt;p&gt;Variables and constants were stored in a map. During every variable access, a lookup with the string name of the variable had to be performed. In case of a miss, a parent context had to be checked as well until the variable was found or the global context was reached.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-lithia" data-lang="lithia"&gt;let a = &amp;#34;global&amp;#34;

func do { =&amp;gt;
 let b = &amp;#34;local&amp;#34;

 print a // lookup &amp;#34;a&amp;#34; in current context -&amp;gt; miss -&amp;gt; lookup &amp;#34;a&amp;#34; in parent context -&amp;gt; hit
 print b // lookup &amp;#34;b&amp;#34; in current context -&amp;gt; hit
}

do // looks up the function &amp;#34;do&amp;#34; and calls it
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Additionally checking the type of a value wasn&amp;rsquo;t trivial as well as the types had to be looked up by name as well.&lt;/p&gt;
&lt;p&gt;Furthermore Lithia leverages lazy evaluation, which means that expressions are not evaluated until their value is needed. This adds additional overhead as every expression has to be wrapped in a thunk (a parameterless function).&lt;/p&gt;
&lt;p&gt;And this makes the second problem of this clear: everything is hidden behind pointers and in general everything is kind of costly. Not necessarily in terms of complexity, its just slow like hashing strings multiple times per variable access, checking if everything has been evaluated and last but not least it can&amp;rsquo;t be cached efficiently by the CPU.&lt;/p&gt;
&lt;h2 id="the-bytecode-interpreter"&gt;The Bytecode Interpreter&lt;/h2&gt;
&lt;p&gt;Zirric takes a different approach to this. It defines a virtual machine (VM) that executes bytecode instructions. Zirric&amp;rsquo;s VM is stack based, which is comparable for a simple calculator or a deck of cards: each operation works on the topmost elements of the stack.&lt;/p&gt;
&lt;p&gt;The bytecode itself is separated of all constants. Instead these are in a separate array or slice while the bytecode references them by index.
In case of the expression &lt;code&gt;40 + 2&lt;/code&gt;, the constant &lt;code&gt;40&lt;/code&gt; is at index &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;2&lt;/code&gt; is at index &lt;code&gt;1&lt;/code&gt;. The human readable bytecode to add these two numbers is then:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# 1 + 2
Cons 0 # Pushes the constant at index 0 (40) onto the stack
Cons 1 # Pushes the constant at index 1 (2) onto the stack
Add
# Result: 42
&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt;The actual bytecode currently looks like this: &lt;code&gt;01 00 00 01 00 01 10&lt;/code&gt;. Neat, huh?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The VM iterates over the bytecode instructions and executes them one by one.
Here &lt;code&gt;Cons&lt;/code&gt; loads the constant with the fitting index onto the stack. &lt;code&gt;Add&lt;/code&gt; pops the top two elements from the stack, adds them and pushes the result back onto the stack.
That way the stack of the VM grows and shrinks as needed.&lt;/p&gt;
&lt;p&gt;When it comes to variables, the VM uses a different approach as well: each variable gets an index assigned at compile time. That way variable access is just a matter of looking up an index of a single array.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s get back to our example from above:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-lithia" data-lang="lithia"&gt;let a = &amp;#34;global&amp;#34; // globals[0] = constants[0]

func do() { // functions are constants: constants[1]
 let b = &amp;#34;local&amp;#34; // locals[0] = constants[2]

 print(a) // globals[0]
 print(b) // locals[0]
}

do() // constants[1]()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;No more string lookups. No more parent contexts. Just direct index access.
And the best part of this: the CPU can cache this data much more efficiently.&lt;/p&gt;
&lt;p&gt;What about laziness? Almost everything is now eagerly evaluated. Expressions are evaluated as soon as they are encountered.
Though there are a few exceptions like logical operators (&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;) which skip their right hand side if the result is already determined by the left hand side. This is common and does not add much overhead.&lt;/p&gt;
&lt;p&gt;But in Zirric globals are still initialized lazily. More on this in a future blog post.&lt;/p&gt;
&lt;h2 id="why-that-hassle"&gt;Why that hassle?&lt;/h2&gt;
&lt;p&gt;Interpreters are much easier to implement and to understand. Is this actually worth it?&lt;/p&gt;
&lt;p&gt;Glad you asked! I made some micro benchmarks for Lithia and Zirric. In this case I wrote a simple recursive Fibonacci function in both languages and measured the time it takes to compute for several numbers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Microbenchmarks can be misleading and do not reflect real world performance. But as long as we keep all tests as similar as possible, we can get a rough idea of the performance difference.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here is the Lithia version:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-lithia" data-lang="lithia"&gt;func fib { n =&amp;gt;
 if (n &amp;lt; 2), n, (fib n - 1) + (fib n - 2)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here is the Zirric version:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-zirric" data-lang="zirric"&gt;func fib(n) {
	return if n &amp;lt; 2 {
		n
	} else {
		fib(n-1) + fib(n-2)				
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As both languages are implemented in Go, I used the builtin Go benchmarking for both to keep it as similar as possible.&lt;/p&gt;
&lt;p&gt;And these are the results on my machine:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Input&lt;/th&gt;
					&lt;th&gt;Repetitions&lt;/th&gt;
					&lt;th&gt;Lithia&lt;/th&gt;
					&lt;th&gt;Repetitions&lt;/th&gt;
					&lt;th&gt;Zirric&lt;/th&gt;
					&lt;th&gt;Factor&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;28&lt;/td&gt;
					&lt;td&gt;1&lt;/td&gt;
					&lt;td&gt;5,338 sec&lt;/td&gt;
					&lt;td&gt;1,000,000,000&lt;/td&gt;
					&lt;td&gt;0.1101 ns&lt;/td&gt;
					&lt;td&gt;4,848,486&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;30&lt;/td&gt;
					&lt;td&gt;1&lt;/td&gt;
					&lt;td&gt;13,990 sec&lt;/td&gt;
					&lt;td&gt;1,000,000,000&lt;/td&gt;
					&lt;td&gt;0.2860 ns&lt;/td&gt;
					&lt;td&gt;4,891,832&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;32&lt;/td&gt;
					&lt;td&gt;1&lt;/td&gt;
					&lt;td&gt;36,805 sec&lt;/td&gt;
					&lt;td&gt;1,000,000,000&lt;/td&gt;
					&lt;td&gt;0.7305 ns&lt;/td&gt;
					&lt;td&gt;5,038,386&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;40&lt;/td&gt;
					&lt;td&gt;0&lt;/td&gt;
					&lt;td&gt;(too long)&lt;/td&gt;
					&lt;td&gt;1&lt;/td&gt;
					&lt;td&gt;34,238 sec&lt;/td&gt;
					&lt;td&gt;(too long)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;When I first saw these results, I had to check if I messed something up. We are comparing seconds with nanoseconds here. That&amp;rsquo;s why I added another run with &lt;code&gt;fib(40)&lt;/code&gt; for Zirric to validate the tests. And they were right. Zirric is nearly &lt;em&gt;five million&lt;/em&gt; times faster than Lithia in this case.&lt;/p&gt;
&lt;p&gt;To get some more context, I also measured the Python and Ruby &lt;code&gt;fib(40)&lt;/code&gt; and got around 10 to 17 seconds here. Although these numbers are not accurate and probably estimated too high, they give a rough idea of the performance difference. Zirric is multiple times slower than these languages, but still in the same ballpark, while Lithia is outclassed by magnitudes.&lt;/p&gt;
&lt;p&gt;Zirric is still in early development. Through optimization, new language features but also additional safe guards the performance profile will change over time. And probably not always for the better. Also this is just a microbenchmark that is not in favor of Lithia. Real world performance might be different. Take these numbers with a grain of salt.&lt;/p&gt;
&lt;p&gt;But yes, this was worth the hassle.
In case you are curios about what&amp;rsquo;s up next or want a nerd talk, feel free to reach out to me &lt;a href="https://mastodon.social/@vknabel"&gt;@mastodon.social@vknabel&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Zirric: A new Beginning</title><link>https://vknabel.com/posts/2025-09-16-a-new-beginning/</link><pubDate>Tue, 16 Sep 2025 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2025-09-16-a-new-beginning/</guid><description>&lt;p&gt;After a long time of procrastination I finally resumed my work on my new programming language. &lt;a href="https://vknabel.com/posts/the-current-state-of-lithia-after-2-years/"&gt;Back then&lt;/a&gt; I wrote about the current state of &lt;a href="https://github.com/vknabel/lithia"&gt;Lithia&lt;/a&gt; and how I arrived in a dead end regarding the language design. Sure I proposed some large changes, but if a lazy evaluated programming language with a parensless call syntax becomes a strict evaluated programming language with a regular call syntax and multiple additional features, is it still the same &lt;del&gt;boat&lt;/del&gt;&amp;hellip; language?&lt;/p&gt;
&lt;p&gt;Literally every line of code would break. That&amp;rsquo;s why I decided to create a new, currently private, repository for my new programming language called &lt;strong&gt;zirric&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This blog post is part of a &lt;a href="https://vknabel.com/posts/journey-about-creating-a-new-programming-language/"&gt;Journey about creating a new programming language&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="what-will-zirric-be-like"&gt;What will zirric be like?&lt;/h2&gt;
&lt;p&gt;Similarly to Lithia, zirric will still be simple, but it will still be much more feature rich than Lithia.&lt;/p&gt;
&lt;p&gt;First to get the boring stuff out of the way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;zirric will be dynamically but strongly typed as Lithia is.&lt;/li&gt;
&lt;li&gt;zirric will be strict evaluated while Lithia is lazy evaluated.&lt;/li&gt;
&lt;li&gt;The call syntax might now look slightly familiar as &lt;code&gt;f(a, b)&lt;/code&gt; instead of &lt;code&gt;f a, b&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="annotations"&gt;Annotations&lt;/h3&gt;
&lt;p&gt;Regarding the type system zirric adds the new &lt;code&gt;annotation&lt;/code&gt; types to Lithia&amp;rsquo;s &lt;code&gt;data&lt;/code&gt; and &lt;code&gt;enum&lt;/code&gt; types.
An &lt;code&gt;annotation&lt;/code&gt; is declared like a &lt;code&gt;data&lt;/code&gt; type, but can be instantiated with an &lt;code&gt;@&lt;/code&gt; before declarations.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-zirric" data-lang="zirric"&gt;annotation Countable {
 @Returns(Int)
 length(@Has(Countable) value)
}

@Countable({ v -&amp;gt; v.length })
data Bag {
 @Array items
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In contrast to decorators in other languages, annotations only store data. They cannot change the behavior of functions or types.
To use annotations, a new reflection API will be provided.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-zirric" data-lang="zirric"&gt;import reflect

@Returns(Int)
func count(@Has(Countable) value) {
 return reflect.typeOf(value).annotation(Countable).length(value)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The effectively replaces the old but common &lt;a href="https://github.com/vknabel/lithia?tab=readme-ov-file#why-no-interfaces"&gt;witness pattern in Lithia&lt;/a&gt; with a more flexible and powerful system without introducing other new concepts like interfaces.&lt;/p&gt;
&lt;p&gt;As you might have noticed, the new annotations &lt;em&gt;can&lt;/em&gt; but don&amp;rsquo;t have to be used to provide type hints. In the long term these should be checked by the compiler and fuel the language server to provide better IDE support.&lt;/p&gt;
&lt;h3 id="control-flow"&gt;Control Flow&lt;/h3&gt;
&lt;p&gt;In Lithia there were no control flow constructs like &lt;code&gt;if&lt;/code&gt; statements or &lt;code&gt;for&lt;/code&gt; loops except the &lt;code&gt;type&lt;/code&gt; expression. Instead everything was expressed via functions and recursion. This was slow and cumbersome.&lt;/p&gt;
&lt;p&gt;zirric now comes with classic &lt;code&gt;if&lt;/code&gt; and &lt;code&gt;switch&lt;/code&gt; statements as well as &lt;code&gt;for&lt;/code&gt; loops.
&lt;code&gt;if&lt;/code&gt; statements will support multiple branches and &lt;code&gt;else if&lt;/code&gt; as well as inline variable declarations.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-zirric" data-lang="zirric"&gt;if condition {
 doSomething()
} else if otherCondition {
 doSomethingElse()
} else {
 defaultCase()
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;switch&lt;/code&gt; statements will support type matching as well as value matching.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-zirric" data-lang="zirric"&gt;switch value {
case @SomeType:
 doSomething(value)
case 42:
 doSomethingElse()
case _:
 defaultCase()
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;for&lt;/code&gt; loops will support iterating over ranges, arrays, maps and custom iterators and infinite loops with &lt;code&gt;for { }&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-zirric" data-lang="zirric"&gt;for {
 if condition {
 break
 }
}

for i in Range(0, 10) {
 println(i)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In contrast to other languages, there will also be &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;switch&lt;/code&gt; and &lt;code&gt;for&lt;/code&gt; expressions that can be used inline to assign values. In there only variable declarations and one expression per branch are allowed.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-zirric" data-lang="zirric"&gt;let filtered = for num -&amp;gt; items {
 if num % 13 == 0 {
 break
 } else if num % 2 == 0 &amp;amp;&amp;amp; num % 3 == 0 {
 &amp;#34;fizzbuzz&amp;#34;
 } else if num % 2 == 0 {
 &amp;#34;fizz&amp;#34;
 } else if num % 3 == 0 {
 &amp;#34;buzz&amp;#34;
 } else {
 continue
 }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="a-few-more-things"&gt;A few more things&lt;/h3&gt;
&lt;p&gt;zirric will come with a working package manager out of the box, it will have a new design and mascot (you might have noticed) and in the long term the language server and tooling will be much better and more accurate than Lithia&amp;rsquo;s.&lt;/p&gt;
&lt;p&gt;A much better performance is also a goal, but don&amp;rsquo;t expect miracles here. Lithia was just really slow.&lt;/p&gt;
&lt;p&gt;More on all of that later.&lt;/p&gt;
&lt;h2 id="what-is-the-current-state"&gt;What is the current state?&lt;/h2&gt;
&lt;p&gt;As of now, zirric is still in a very early stage. zirric can parse lots of the syntax although large parts are still missing. Though the execution side is still in its infancy and only supports a few basic mathematical operations, arrays, bools, and &lt;code&gt;if&lt;/code&gt; statements and expressions. Variables and functions will be the next big step.&lt;/p&gt;
&lt;p&gt;At the tooling side, the core of the package manager is already present.&lt;/p&gt;
&lt;p&gt;I guess I have quite a long way to go, but I am excited to finally work on a new programming language again. If you want to follow along or want a quick chat, feel free to reach out to me &lt;a href="https://mastodon.social/@vknabel"&gt;@mastodon.social@vknabel&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Die Mitternachtsbibliothek</title><link>https://vknabel.com/library/die-mitternachtsbibliothek/</link><pubDate>Tue, 01 Jul 2025 00:00:00 +0000</pubDate><guid>https://vknabel.com/library/die-mitternachtsbibliothek/</guid><description>&lt;p&gt;The book opens with a shocking moment. Trigger warning: themes of suicide and depression.&lt;/p&gt;
&lt;p&gt;After that, the pace slows and the story gradually draws you in.
While it may not be thrilling in the classic sense, it captivates on a different level.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a moving and wondrous book, written at a cosy pace that never becomes boring.
Overall, it&amp;rsquo;s still a feel-good book.&lt;/p&gt;</description></item><item><title>Ich, Sperling</title><link>https://vknabel.com/library/ich-sperling/</link><pubDate>Tue, 01 Apr 2025 00:00:00 +0000</pubDate><guid>https://vknabel.com/library/ich-sperling/</guid><description>&lt;p&gt;The story is told from the perspective of a young boy in ancient Rome who grows up in a brothel.&lt;/p&gt;
&lt;p&gt;Some intense topics are brought up and talked about. But a lot of it is left to the reader&amp;rsquo;s imagination. So, luckily, the most intense topics are left out but still talked about.&lt;/p&gt;
&lt;p&gt;At times, while reading, you already have a premonition of what will happen in the next chapter. You feel and share the excitement. An intense book, but one of my favorites.&lt;/p&gt;</description></item><item><title>The Queen of Anthuriums: Warocqueanum</title><link>https://vknabel.com/posts/2024-05-25-the-queen-of-anthuriums-warocqueanum/</link><pubDate>Sat, 25 May 2024 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2024-05-25-the-queen-of-anthuriums-warocqueanum/</guid><description>&lt;p&gt;One of the most iconic species of Anthurium is undoubtedly the queen of Anthuriums: the Warocqueanum. With its long, tapering, and often dark leaves, especially adult specimens know how to make a grand impression.&lt;/p&gt;
&lt;!-- ![](/images/aroid.garden/Tall-Anthurium-Warocqueanum.jpg) --&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The leaves of the Anthurium Warocqueanum can reach a length of over one meter. In this case, they are 65cm.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="caring-for-the-warocqueanum"&gt;Caring for the Warocqueanum&lt;/h2&gt;
&lt;p&gt;Our queen hails from the rainforests of Colombia and naturally occurs in both the humid lowlands and highlands. This relatively broad range of altitudes makes it somewhat less demanding compared to other members of its kind. With proper care, it is undemanding and can tolerate occasional dry stress, as long as there is no new leaf or bloom on the way. It requires slightly higher humidity than other Anthuriums.&lt;/p&gt;
&lt;p&gt;Despite its thick and robust-looking leaves, you should maintain a constant humidity level of over 50%. Otherwise, it can quickly suffer cosmetic leaf damage. Generally, the plant needs more time than usual to adjust to new conditions, so these should be kept relatively constant.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Warocqueanum-single-leaf.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Regarding substrate, the Warocqueanum does not have extraordinary requirements for Anthuriums and can be easily kept in semi-hydroponics. As with all Anthuriums, you should use high-quality, airy substrate.&lt;/p&gt;
&lt;h2 id="personal-experience"&gt;Personal Experience&lt;/h2&gt;
&lt;p&gt;Since I acquired my Warocqueanum, it has been kept under artificial light in a terrarium with humidity usually between 60 and 70%. I have had no problems with root rot or pests, even when other plants in the terrarium were affected. When the humidity dropped too much, there was often leaf damage.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Warocqueanum-new-leaf.jpg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The new leaves of the Warocqueanum start off in a dark green.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Most of my Anthuriums grow faster, but they usually produce smaller leaves. Over time, the Warocqueanum lives up to its name and rules the terrarium with its enormous leaves. It can’t grow much larger due to space constraints.&lt;/p&gt;
&lt;p&gt;Once the leaves of my plant reached a size of about 40cm, it slowly started to bloom. Since I wanted to avoid the stress of seeds and flowers, and because the new leaf was more important to me, I usually removed the flowers or just collected the pollen.&lt;/p&gt;
&lt;h2 id="hybrids"&gt;Hybrids&lt;/h2&gt;
&lt;p&gt;Fortunately, the Warocqueanum belongs to the large section Cardiolonchium and thus has many potential hybrids. I don’t have larger specimens yet, but I do have a few candidates with potential.&lt;/p&gt;
&lt;!-- ![]() --&gt;
&lt;!-- _Warocqueanum Dark x (Zara x Michelle)_ --&gt;
&lt;!-- ![]() --&gt;
&lt;!-- _Warocqueanum ‚Dark‘ x (Zara x Michelle)_ --&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Warocqueanum-Esmeralda.jpg" alt=""&gt;
&lt;em&gt;Warocqueanum ‚Esmeralda‘&lt;/em&gt;
&lt;img src="https://vknabel.com/images/aroid.garden/Warocqueanum-Esmeralda-x-Dark.jpg" alt=""&gt;
&lt;em&gt;Warocqueanum ‚Esmeralda‘ x ‚Dark‘&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Overview of the Philodendron Malay Gold</title><link>https://vknabel.com/posts/2024-02-10-overview-of-the-philodendron-malay-gold/</link><pubDate>Sat, 10 Feb 2024 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2024-02-10-overview-of-the-philodendron-malay-gold/</guid><description>&lt;p&gt;Featuring bright green to golden leaves with hints of red, the Philodendron Malay Gold is a classic representative of its kind – just brighter. For those who appreciate the color but not necessarily the growth form, alternatives such as Philodendron Hederaceaum ‚Lemon Lime‘ (a.k.a. Scandens) or Epipremnum Aureum ‚Neon‘ are available.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Philodendron-Malay-Gold-bright-leaves.jpg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;New leafs are bright and yellow.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;New leaves emerge neon yellow and gradually turn greener over time, maintaining a relatively light hue. The leaf stems, especially at the base, showcase a reddish tint. The more light it receives, the more intense the coloring becomes. The leaves have rounded bases, lack the typical „ears,“ and do not grow particularly large.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Philodendron-Malay-Golden-dark-leaves.jpg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Older leafs gradually darken.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="care"&gt;Care&lt;/h2&gt;
&lt;p&gt;Like most Philodendrons, the Malay Gold is exceptionally robust. The advantage of easily assessing the lighting conditions based on new leaves provides timely feedback after relocation. While it cannot thrive in as low light conditions as some of its counterparts, it does not demand an exceptionally bright location either.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Philodendron-Malay-Gold-tall.jpg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Four substantial Malay Gold plants in a single container. Fresh leaves exhibit a lighter hue and gradually darken over time&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It simply wants to grow tall and fast, with an inherent upward trajectory. Although the stem is typically broader than that of a Melanochrysum, a support structure becomes necessary at a certain height. While it would likely accept a moss pole, it is not strictly necessary.
Personal Experience&lt;/p&gt;
&lt;p&gt;The Malay Gold has adorned various corners of my home: in winter on the south side, centrally located, on the east and west sides directly by the window. Maintaining humidity levels between 30% and 60%. Notably, there were no significant issues in any of these placements.&lt;/p&gt;
&lt;p&gt;It has even stood directly next to the open balcony door in summer and fall, welcoming thrips that occasionally find their way in. While a mild infestation occurred, it caused no substantial harm. Other pests have never been a concern.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Philodendron-Malay-Gold-and-Epipremnum-Aureum-Neon.jpg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In the same pot, serving as a small contrasting companion, an Epipremnum Aureum ‚Neon‘ is thriving as a sublet. It flourishes better in this shared space than when grown alone.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Given its size, the water requirements for our plant are relatively modest. Thanks to its determination to grow upward, its space requirements are also manageable.&lt;/p&gt;
&lt;h2 id="propagation"&gt;Propagation&lt;/h2&gt;
&lt;p&gt;It’s needless to say that propagating a Philodendron is at best a challenging task. The Malay Gold is no exception and is most effectively propagated through cuttings. The advantage lies in its ability to develop aerial roots even in moderate humidity conditions, facilitating an effortless rooting process for cuttings that swiftly establish themselves as independent plants.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Philodendron-Malay-Gold-two-years-ago.jpg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;My plant two years ago.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Of Velvet and Silver: the Syngonium Wendlandii</title><link>https://vknabel.com/posts/2024-02-03-of-velvet-and-silver-the-syngonium-wendlandii/</link><pubDate>Sat, 03 Feb 2024 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2024-02-03-of-velvet-and-silver-the-syngonium-wendlandii/</guid><description>&lt;p&gt;The Syngonium Wendlandii is truly something special. With its deep green, velvety leaves, a silver stripe in the middle, and a relatively compact size, it sets itself apart from its counterparts.&lt;/p&gt;
&lt;p&gt;As an alternative, there is also the Syngonium Rayii, which, however, requires higher humidity but rewards with significantly darker leaves.
A Syngonium Wendlandii on a moss pole.&lt;/p&gt;
&lt;p&gt;The leaves themselves feel slightly velvety but also somewhat firmer. In the juvenile form, the leaves are arrow-shaped, and with age, the earlobes separate, transforming the leaf into three parts. This is not unusual for Syngoniums – here, it happens faster and more straightforwardly. However, with increasing leaf size, the silver color diminishes and thins out.
The juvenile leaves are arrow-shaped, as in other young Syngoniums.
The adult leaves are three-parted.&lt;/p&gt;
&lt;h2 id="care"&gt;Care&lt;/h2&gt;
&lt;p&gt;With few exceptions, Syngoniums are straightforward and have no special requirements for humidity. This holds true for Wendlandii, despite its velvety leaves. Although the leaves have a silvery sheen, it doesn’t need more light than its counterparts without silver and can thrive in darker locations.
Without high humidity, it won’t root into moss poles. A bamboo stake will do.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Syngonium-Wendlandii-from-top.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;A Wendlandii outside of display cases doesn’t necessarily need a moss pole. Below 60 to 70% humidity, too few aerial roots form, risking root rot if the substrate is not airy enough. However, a stake is recommended for a beautiful growth, especially if you desire the three-parted adult leaves.&lt;/p&gt;
&lt;h2 id="personal-experience"&gt;Personal Experience&lt;/h2&gt;
&lt;p&gt;My Syngonium is positioned by the east window, receiving direct morning sunlight and experiencing slightly cooler temperatures of 18 to 20°C in winter. Even during the height of summer, temperatures rarely exceed 25°C. The humidity usually hovers around 40 to 45%, occasionally dropping to 35%. In the past, I had it in a moss pole setup with humidity maintained at 60 to 70%, and it thrived.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Syngonium-Wendlandii-Moss-Pole.jpg" alt=""&gt;
However, the moss pole wasn’t necessary, as the plant never embraced it, often leading to overly damp substrate. Despite this, the support structure brought visible delight to the Syngonium.&lt;/p&gt;
&lt;p&gt;In general, it proves remarkably robust, enduring various conditions under my care, and certainly does not exhibit diva-like tendencies!&lt;/p&gt;
&lt;h2 id="propagation"&gt;Propagation&lt;/h2&gt;
&lt;p&gt;Like other Syngoniums, Wendlandii rarely blooms indoors. Thus, propagation through cuttings takes precedence.
It appreciates a support for climbing.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Syngonium-Wendlandii-juvenile-leaves.jpg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Juvenile arrowheads are found below.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Syngonium-Wendlandii-adult-leaves.jpg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Adult leaves grace the top.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As it grows quickly, branching out from the stem and becoming bushier, pruning is a wise choice. The cutting should then root well. However, more patience is required here compared to many other Syngoniums, as root initiation often takes time.&lt;/p&gt;</description></item><item><title>Anthurium Gracile: Care and Propagation</title><link>https://vknabel.com/posts/2024-01-31-anthurium-gracile-care-and-propagation/</link><pubDate>Wed, 31 Jan 2024 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2024-01-31-anthurium-gracile-care-and-propagation/</guid><description>&lt;p&gt;Those in search of an Anthurium characterized by uncomplicated care will find success with the Anthurium Gracile, and will be rewarded by its affordable price.&lt;/p&gt;
&lt;p&gt;The leaves of Anthurium Gracile are bright, thin, and feel somewhat leathery. In ample light, the leaf base can take on a reddish hue. Some cultivated forms also tend to show a reddish coloration on the stem.&lt;/p&gt;
&lt;p&gt;Its compactly growing stem gives it a bushy appearance. As the leaves protrude forward and gently taper towards the tip, the plant becomes quite expansive. Unlike Pallidiflorum and similar „Long-Boys,“ creating a leaf curtain without tilting the pot becomes challenging.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The Anthurium Gracile grows compact and has long belted leaves.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="care"&gt;Care&lt;/h2&gt;
&lt;p&gt;In comparison the Anthurium Gracile is easy to handle but requires a more aerated substrate due to its fleshy, almost orchid-like roots. This allows it to stay dry longer, reducing the risk of root rot.&lt;/p&gt;
&lt;p&gt;Semi-hydroponics is also a trouble-free alternative to organic substrates.&lt;/p&gt;
&lt;p&gt;The humidity can be slightly lower than for most other Anthurium species. Spider mites have not been an issue, with only a reported incidence of thrips.&lt;/p&gt;
&lt;h2 id="personal-experience"&gt;Personal Experience&lt;/h2&gt;
&lt;p&gt;I cultivated my Gracile almost exclusively in display cases, maintaining humidity between 50% and 80%, and providing artificial light for 12 to 14 hours daily. Despite occasional neglect, it took just under a year to reach the second generation from seeds. During the summer, the room temperature often soared between 20 and 28°C in the evenings.&lt;/p&gt;
&lt;p&gt;Now, it resides outside the display case and thrives even with 40% humidity.&lt;/p&gt;
&lt;p&gt;Spider mites posed no challenge, and I only encountered a thrips infestation. Despite visible leaf damage, the impact on growth was minimal.&lt;/p&gt;
&lt;h2 id="inflorescence"&gt;Inflorescence&lt;/h2&gt;
&lt;p&gt;Anthurium Gracile is highly prolific in flowering, typically producing a new bloom with each leaf. Being self-pollinating, no specific action is required for the development of red berries. The inflorescences are unusually located beneath the leaves.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Anthurium-Gracile-inflo-1536x1152.jpeg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The flowers of the Anthurium Gracile are unimpressive and usually grow below the leaves.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In contrast to other species, the berries ripen in just a few weeks rather than several months. Due to the low cost and high frequency of new seedlings, it’s advisable to trim the inflorescences in the long run to avoid unnecessary stress on the plant.&lt;/p&gt;
&lt;h2 id="crossbreeding"&gt;Crossbreeding&lt;/h2&gt;
&lt;p&gt;Belonging to the Leptanthurium section along with Vittariifolium, Anthurium Gracile has no known other species. However, successful pollination with closely related sections may occur with some luck.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Anthurium-Gracile-berries-1024x1024.jpeg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Five red berries of the Anthurium Gracile.&lt;/em&gt;
&lt;em&gt;The berries of the Anthurium Gracile are red and similar sized to the seeds of a Grapefruit. It usually contains 1 or 2 seeds.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Anthurium-Gracile-Spadix-with-berries-1024x768.jpeg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The spadix and a few berries.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>The current state of Lithia after 2 years</title><link>https://vknabel.com/posts/the-current-state-of-lithia-after-2-years/</link><pubDate>Thu, 18 Jan 2024 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/the-current-state-of-lithia-after-2-years/</guid><description>&lt;p&gt;In 2021 I started working on the current Go implementation of my own experimental programming language called &lt;a href="https://github.com/vknabel/lithia"&gt;Lithia&lt;/a&gt;. Now it is available in version v0.0.19. Infrequently I also wrote some words about it on this blog.
It has been designed to be an experiment, that is able to teach some lessons and that can be fulfilled.&lt;/p&gt;
&lt;p&gt;For a programming language being able to teach lessons, a Standard Library and of course some programs need to exist. Only the experience of its developers can lead to conclusions.
Now it’s time to derive those!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This blog post is part of a &lt;a href="https://vknabel.com/posts/journey-about-creating-a-new-programming-language/"&gt;Journey about creating a new programming language&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="the-current-state"&gt;The current state&lt;/h2&gt;
&lt;p&gt;But first let’s have a look on the current state of Lithia.&lt;/p&gt;
&lt;p&gt;Lithia has all planned features implemented. You can define &lt;code&gt;data&lt;/code&gt;-structures, &lt;code&gt;enum&lt;/code&gt;s similar to unions, functions, constants, modules and imports, closures, partial application of parameters and lazy evaluation.
The standard library currently consists of 19 modules providing base types, helpers around comparisons, strings, results and more, file system access and a micro-framework to write unit tests in and for Lithia. Every module is fully documented and is generated by a Lithia script.&lt;/p&gt;
&lt;p&gt;To declare packages and to make project-based scripts easily accessible, there is the &lt;code&gt;Potfile&lt;/code&gt; (comparable to &lt;code&gt;package.json&lt;/code&gt;, &lt;code&gt;Package.swift&lt;/code&gt;, &lt;code&gt;go.mod&lt;/code&gt;, &lt;code&gt;Gemfile&lt;/code&gt; &amp;amp; &lt;code&gt;Taskfile&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Although the performance of the Lithia interpreter has largely improved in the early days but it’s still slow and leaves room for improvements.&lt;/p&gt;
&lt;p&gt;Developing Lithia is fueled by the Visual Studio Code extension which relies on the Language Server within the Lithia binary and does not need to be installed separately.
It provides syntax highlighting, diagnostics and autocompletion. Additionally to the LSP there is a tree-sitter grammar which can be used in other modern editors.&lt;/p&gt;
&lt;h2 id="the-developer-experience"&gt;The Developer Experience&lt;/h2&gt;
&lt;p&gt;Obviously the development experience in such a young programming language doesn’t reach the ease of use of mature ones. But for a scripting it provides decent autocompletion.
Though there are no formatters or linters, yet. And currently there is no debugger support, which is a bummer. In practice this at least encourages the use of unit testing.&lt;/p&gt;
&lt;p&gt;Lithia has a dynamic but strict type system, which may lead to runtime errors, which is okay for scripting. But the mix of lazy evaluation often further delays and hides these runtime errors and make debugging unnecessarily hard. Especially when adding concurrency. This needs to change.&lt;/p&gt;
&lt;p&gt;Another option for improvement is the current parens-less call syntax: instead of &lt;code&gt;f(g(x), y)&lt;/code&gt; you write &lt;code&gt;f g x, y&lt;/code&gt;. With great syntax highlighting this is something you get around, but syntax should be self-explanatory.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s also missing is a real package manager although git submodules at least provide a workaround for this.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;In general Lithia as an experiment was successful and lead to really great results. The simplicity but power of the type system is a breeze. But Lithia needs a change. And at its core lazy evaluation needs to be dropped, while the overall tooling should evolve.
But what’s still missing is the perfect use case that perfectly fits Lithia’s language features.
The upcoming release v0.1.0 should provide answers and solutions for those pain points.&lt;/p&gt;
&lt;p&gt;If you wish, check out the open source repository of &lt;a href="https://github.com/vknabel/lithia"&gt;Lithia&lt;/a&gt;. If you have any questions don&amp;rsquo;t hesitate to ask me on &lt;a href="https://mastodon.social/@vknabel"&gt;@mastodon.social@vknabel&lt;/a&gt; or join the &lt;a href="https://github.com/vknabel/lithia/discussions"&gt;Lithia discussions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;</description></item><item><title>Why Plant Care Guides on the Internet Are Bullshit</title><link>https://vknabel.com/posts/2024-01-01-why-plant-care-guides-on-the-internet-are-bullshit/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2024-01-01-why-plant-care-guides-on-the-internet-are-bullshit/</guid><description>&lt;p&gt;There are already many websites offering plant care tips. The articles tend to be very long but contain little relevant information.&lt;/p&gt;
&lt;p&gt;Almost every plant is described as needing bright, indirect light and as being unable to tolerate root rot or requiring fertilisation in winter. It&amp;rsquo;s as if there are plants that like to rot away! It is often stated that houseplants need increased humidity. By how much? 50%? 80%? The exact specification is often missing.&lt;/p&gt;
&lt;p&gt;Oh, and by the way&amp;hellip; This beautiful plant is the perfect starter plant for you. It only costs $50 and will attract spider mites if it is not cared for properly. Completely altruistic, of course. Shops and websites with affiliate links or advertising are simply not reliable or independent sources.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Philodendron-Gloriosum-2048x1536.jpeg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;For example, a Philodendron Gloriosum is prone to spider mites when humidity is too low.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="what-matters"&gt;What matters?&lt;/h2&gt;
&lt;p&gt;Every plant is unique and grows in a unique environment: yours. Every tip for caring for a plant comes from someone with different watering habits, substrate and lighting conditions. Humidity, air circulation, temperature and fertilisation can all be different, too. Even the water probably has a completely different hardness level.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Various-cuttings-in-a-jar.jpg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Various cuttings in water: Philodendron Brandtianum &amp;amp; Birkin, Monstera Adansonii &amp;lsquo;Swedish Ghost&amp;rsquo;, Rhapidophora Tetrasperma, Epipremnum Cebu Blue and an Anthurium Rubrinervium.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This makes it impossible to make suggestions that are applicable everywhere—or that make sense. That&amp;rsquo;s why you see the same information everywhere. Nevertheless, there are differences. These must be pointed out.&lt;/p&gt;
&lt;h2 id="how-were-different"&gt;How we&amp;rsquo;re different&lt;/h2&gt;
&lt;p&gt;Rather than resorting to the same old clichés, we compare different plants and assume that you already have some. No bullshit.&lt;/p&gt;
&lt;p&gt;Read our care tips for anthuriums and compare them with other arum plants, such as alocasias and philodendrons. We also provide profiles or portraits of individual species for comparison.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Philodendron-Billietiae-1536x2048.jpeg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Philodendron billietiae on a climbing stick.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For example, let&amp;rsquo;s compare &lt;a href="https://vknabel.com/posts/2024-01-31-anthurium-gracile-care-and-propagation/"&gt;Anthurium gracile&lt;/a&gt; with other anthuriums. If you already have experience of caring for anthuriums, we only need to briefly address their special features. This will also give you a quick overview of the plant.&lt;/p&gt;
&lt;p&gt;Most species grow differently and can be displayed in various ways. If a plant has an unusual growth habit, we will mention that, too.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Anthurium-Gracile-overview.jpeg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;For example, an &lt;a href="https://vknabel.com/posts/2024-01-31-anthurium-gracile-care-and-propagation/"&gt;Anthurium gracile&lt;/a&gt; with burns on its leaves.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We only write about our own plants, not random or trendy ones. If the plant has a yellow leaf, this will be included in the photo. There will be content about common plants, as well as lesser-known plants and individual hybrids.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/aroid.garden/Anthurium-Veitchii-768x1024.jpeg" alt=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Anthurium Veitchii Jungpflanze&lt;/em&gt;
&lt;img src="https://vknabel.com/images/aroid.garden/Anthurium-Besseae-aff-768x1024.jpeg" alt=""&gt;
&lt;em&gt;Anthurium Besseae aff&lt;/em&gt;
&lt;img src="https://vknabel.com/images/aroid.garden/Anthurium-Villenaonarum-x-Besseae-768x1024.jpeg" alt=""&gt;
&lt;em&gt;Anthurium Villenaorum x Besseae&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>24hrs format for html inputs in Firefox</title><link>https://vknabel.com/posts/2023-11-09-firefox-24hrs-format-for-html-inputs/</link><pubDate>Thu, 09 Nov 2023 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2023-11-09-firefox-24hrs-format-for-html-inputs/</guid><description>&lt;p&gt;My system language on my linux machine is set to English, but as a German raised with 24-hour days, I simply cannot wrap my head around to get the 12-hours &lt;code&gt;am&lt;/code&gt;/&lt;code&gt;pm&lt;/code&gt; format intuitively. By the time I learned that &lt;code&gt;am&lt;/code&gt; is in the morning and &lt;code&gt;pm&lt;/code&gt; is in the evening. But when it comes to &lt;code&gt;12:00&lt;/code&gt; as the mid of the day and &lt;code&gt;0:00&lt;/code&gt; in the night, I&amp;rsquo;m lost.&lt;/p&gt;
&lt;p&gt;Of course I have already set the time format to 24-hours in my system settings (Ubuntu &lt;code&gt;Date &amp;amp; Time &amp;gt; Time Format&lt;/code&gt; set to &lt;code&gt;24-hour&lt;/code&gt;), but Firefox still uses the 12-hours format for html inputs with &lt;code&gt;type=&amp;quot;time&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As a solution you open &lt;a href="about:config"&gt;about:config&lt;/a&gt; and set &lt;code&gt;intl.regional_prefs.use_os_locales&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.
Now simple &lt;code&gt;&amp;lt;input type=&amp;quot;time&amp;quot;&amp;gt;&lt;/code&gt; will use the 24h format!&lt;/p&gt;</description></item><item><title>My Personal View on Acorn</title><link>https://vknabel.com/posts/my-personal-view-on-acorn/</link><pubDate>Mon, 12 Dec 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/my-personal-view-on-acorn/</guid><description>&lt;p&gt;&lt;a href="https://acorn.io"&gt;Acorn&lt;/a&gt; is an application deployment framework for k8s, that tries to simplify instead of introducing another layer of indirection. I won&amp;rsquo;t cover all details and problems it tries to solve here, instead I&amp;rsquo;d like to share some experiences and thoughts.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Acorn has been discontinued&lt;/strong&gt; and they are now doing some AI slop stuff.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="my-experience-with-acorn-and-kubernetes"&gt;My experience with Acorn and Kubernetes&lt;/h2&gt;
&lt;p&gt;Before we dive right in, I&amp;rsquo;d like to let you know about my level of experience with Kubernetes and Acorn.&lt;/p&gt;
&lt;p&gt;I started having a look on Acorn in August 2022 and played around with it. I did not have production experience with Kubernetes, but I started learning and playing around with it a few months before.&lt;/p&gt;
&lt;p&gt;To get a deeper understanding on k8s, I soon started to migrate my personal services hosted on multiple docker-compose-files. Later I had a look on Acorn and started to play around with it. I also translated my docker-compose-files and k8s manifests to Acorn and liked the result.&lt;/p&gt;
&lt;p&gt;Later I migrated my personal production services to k8s and Acorn. As &lt;a href="http://github.com/vknabel/puffery"&gt;Puffery&lt;/a&gt; only has a few users, there wasn&amp;rsquo;t much risk involved.&lt;/p&gt;
&lt;p&gt;On October 2022, I held a talk about &lt;a href="https://acorn.io"&gt;Acorn&lt;/a&gt; at the &lt;a href="https://community.cncf.io/events/details/cncf-kcd-munich-presents-kubernetes-community-days-munich-2022-1/"&gt;Kubernetes Community Days Munich 2022&lt;/a&gt;. And recently I wrote the &lt;a href="https://www.x-cellent.com/posts/getting-started-with-acorn-for-kubernetes"&gt;corresponding blog post&lt;/a&gt; for my employer&amp;rsquo;s blog to share the contents of the talk.&lt;/p&gt;
&lt;h2 id="secrets"&gt;Secrets&lt;/h2&gt;
&lt;p&gt;Acorn forces you to declare secrets in the &lt;code&gt;Acornfile&lt;/code&gt;, but it forbids you to provide them there. Instead it will automatically generate the secrets on the initial deployment. You don&amp;rsquo;t have to worry about secrets anymore.&lt;/p&gt;
&lt;p&gt;The weird part is, when you have an existing secret. You don&amp;rsquo;t override or change the generated secret. Instead you create a new secret at the command line and link both secrets together.&lt;/p&gt;
&lt;p&gt;But if you are used to it, it&amp;rsquo;s actually a nice way to solve this issue.&lt;/p&gt;
&lt;h2 id="referencing-resources"&gt;Referencing Resources&lt;/h2&gt;
&lt;p&gt;In Acorn you need to reference some resources like a specific secret or volume. Here you use an URL with the appropriate scheme. For example, to reference a secret, you use the &lt;code&gt;secret://&lt;/code&gt; scheme.&lt;/p&gt;
&lt;p&gt;In regards to volumes, this is awesome, as you can even mount a specific path of the volume into a directory. But when using secrets, this is sometimes a bit annoying, as you need to reference the whole secret and then extract the value you need, like &lt;code&gt;secret://db-password/token&lt;/code&gt;. The benefit: you can create a secret to mount multiple files into a container using an opaque secret.&lt;/p&gt;
&lt;p&gt;I understand the reasoning behind using resource URLs, as these resources technically need to be created and are not part of the Acorn Image. But in the beginning this was a bit confusing. Maybe this is just a documentation issue.&lt;/p&gt;
&lt;h2 id="development"&gt;Development&lt;/h2&gt;
&lt;p&gt;For development we always need a vast range of tools. Especially when working with multiple programming languages. I tend to avoid installing those on my local machine and instead use containers. In easier cases I use &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers"&gt;Remote Containers&lt;/a&gt; for VS Code. For more complex use cases that include databases, I rely on docker-compose. But if the application needs to run in a Kubernetes cluster, it would be twice the work to maintain the &lt;code&gt;docker-compose.yaml&lt;/code&gt; and the k8s manifests. With Acorn, I can use the same manifests for development and production. Of course I need to sprinkle some &lt;code&gt;if&lt;/code&gt; statements in the &lt;code&gt;Acornfile&lt;/code&gt; to make it work for both environments, but it feels much more natural than maintaining two different sets of manifests.&lt;/p&gt;
&lt;p&gt;And to be honest, I think an &lt;code&gt;Acornfile&lt;/code&gt; is far easier to read, than a &lt;code&gt;docker-compose.yaml&lt;/code&gt; or many k8s manifests.&lt;/p&gt;
&lt;h2 id="production-use"&gt;Production Use&lt;/h2&gt;
&lt;p&gt;I have been using Acorn in production for a few months now and I have not encountered any problems so far.
Instead I really like gathering all logs of one application with a single command out of the box: &lt;code&gt;acorn logs [app]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I also like the fact, that Acorn knows the concept of an app. That way it can provide a great overview about whole applications across multiple resource types.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I really like Acorn and I think it&amp;rsquo;s a great tool to simplify the deployment of applications to Kubernetes. It&amp;rsquo;s not perfect, but it&amp;rsquo;s a great start. At the moment I&amp;rsquo;m not sure if I would use it for a production application with a lot of users, but I would definitely use it for my personal projects.&lt;/p&gt;</description></item><item><title>Statically read files with go:embed</title><link>https://vknabel.com/posts/statically-read-files-with-go-embed/</link><pubDate>Thu, 18 Aug 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/statically-read-files-with-go-embed/</guid><description>&lt;p&gt;Did you ever want to access the contents of an auxiliary file from within your repository to access the contents at runtime? This file could include some static settings like the version of the application, translations for user-facing texts, GraphQL requests or SQL queries. The typical solution to this problem is shipping your binary with the auxiliary files.&lt;/p&gt;
&lt;p&gt;But this solution comes with great costs. You cannot ship one single executable anymore. Those files need to sit somewhere in the filesystem. And they might be misplaced, outdated or even missing.&lt;/p&gt;
&lt;p&gt;Thankfully there is a library and language feature introduced in Go 1.16 that allows you to embed files statically into your binary.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can &lt;a href="./project.zip"&gt;download&lt;/a&gt; this example project or jump directly to the source code on &lt;a href="https://github.com/vknabel/vknabel.github.io/tree/main/content/posts/Statically-read-files-with-go-embed/project"&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="lets-goembed-the-license-file"&gt;Let&amp;rsquo;s go:embed the license file&lt;/h2&gt;
&lt;p&gt;For now we want to start easy by embedding the contents a single file. In this case we wish to set our &lt;code&gt;license&lt;/code&gt; to the contents of our &lt;code&gt;LICENSE&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;To get started, we need to import the &lt;code&gt;embed&lt;/code&gt; package. As we won&amp;rsquo;t use any member of the embed package for now, we place an underscore before the package. This imports &lt;code&gt;embed&lt;/code&gt; solely for its side-effects. Otherwise go fmt would directly erase the import statement.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;embed&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let&amp;rsquo;s create a new variable &lt;code&gt;license&lt;/code&gt; and assign the contents of the &lt;code&gt;LICENSE&lt;/code&gt; file to it.
We simply initialize the variable by using the &lt;code&gt;go:embed&lt;/code&gt; compiler directive.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//go:embed LICENSE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;license&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that&amp;rsquo;s it. If we mistype the file name, we get an error. If we forget to import the &lt;code&gt;embed&lt;/code&gt; package, we get a warning. But there may be no space between in &lt;code&gt;//go:embed&lt;/code&gt; like in normal comments.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: embedding file contents only works for global variables, not for locals.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To actually understand how powerful this feature is, let&amp;rsquo;s write a test by manually reading the file and comparing both.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestEmbedsLicenseFile&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;testing&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;T&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// as usual, read the file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;bytes&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ReadFile&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;LICENSE&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// check for errors&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Fatal&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// convert to string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;want&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; string(&lt;span style="color:#a6e22e"&gt;bytes&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// actually use the contents&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;diff&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;cmp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Diff&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;want&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;license&lt;/span&gt;); &lt;span style="color:#a6e22e"&gt;diff&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Errorf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;unexpected license (-want +got):\n%s&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;diff&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this example, we declared the variable as &lt;code&gt;string&lt;/code&gt;, but in other cases you might want to use &lt;code&gt;[]byte&lt;/code&gt;. For example if you want to pin the signature of a remote certificate.&lt;/p&gt;
&lt;h2 id="goembed-the-file-system"&gt;go:embed the file system&lt;/h2&gt;
&lt;p&gt;In our previous example, we included a very special file that is unique in our repository. But sometimes we have many files, which should all be included in our binary.&lt;/p&gt;
&lt;p&gt;In this case we are going to embed our docs folder within our app. We don&amp;rsquo;t want our users to be forced to leave. Instead we want them to be able to explore and access the docs directly from within our app.&lt;/p&gt;
&lt;p&gt;In this case we are going to create an embedded file system.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;embed&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//go:embed docs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;docsFS&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;embed&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;FS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;embed.FS&lt;/code&gt; conforms to &lt;code&gt;fs.FS&lt;/code&gt; and as with all &lt;code&gt;fs.FS&lt;/code&gt;, we can read directory and file contents. The usage is pretty straightforward.
So let&amp;rsquo;s write a test which checks if the contents of the embedded &lt;code&gt;docs&lt;/code&gt; folder are equal to the contents of the &lt;code&gt;docs&lt;/code&gt; folder in our repository.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestEmbedsDocs&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;testing&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;T&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;dirFS&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;DirFS&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// get all files within the docs directory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;gotEntries&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fs&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ReadDir&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;docsFS&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;docs&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Fatal&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;wantEntries&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fs&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ReadDir&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;dirFS&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;docs&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Fatal&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; len(&lt;span style="color:#a6e22e"&gt;gotEntries&lt;/span&gt;) &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; len(&lt;span style="color:#a6e22e"&gt;wantEntries&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Errorf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;got %d entries, want %d&amp;#34;&lt;/span&gt;, len(&lt;span style="color:#a6e22e"&gt;gotEntries&lt;/span&gt;), len(&lt;span style="color:#a6e22e"&gt;wantEntries&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;range&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;wantEntries&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;diff&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;cmp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Diff&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;gotEntries&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;Name&lt;/span&gt;(), &lt;span style="color:#a6e22e"&gt;wantEntries&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;Name&lt;/span&gt;()); &lt;span style="color:#a6e22e"&gt;diff&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Errorf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;unexpected entry (-want +got):\n%s&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;diff&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#75715e"&gt;// read the files&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;got&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fs&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ReadFile&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;docsFS&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;docs/&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;gotEntries&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;Name&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Error&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;want&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fs&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ReadFile&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;dirFS&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;docs/&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;wantEntries&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;Name&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Error&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;diff&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;cmp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Diff&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;got&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;want&lt;/span&gt;); &lt;span style="color:#a6e22e"&gt;diff&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Errorf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;unexpected contents (-want +got):\n%s&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;diff&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;This unit test is probably a bit too much, but it&amp;rsquo;s always a good idea to ensure the embedded contents are valid. Are they valid json? Not empty? Do certain files exist?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Huh? No magic? Yep. As you can see in the test above, the acutal file system and the embedded file system are used exactly the same.
There are no real differences when using the readonly &lt;code&gt;fs.FS&lt;/code&gt;. But here&amp;rsquo;s the difference. Nobody guarantees that the files in the os file system has not been tampered with. Or if it has been set up correctly in the expected path. The compiler &lt;em&gt;proves&lt;/em&gt; that those files are accessible.&lt;/p&gt;
&lt;p&gt;Another benefit: you decide which files you want to embed. Are you only interested in markdown files? Use &lt;code&gt;//go:embed docs/*.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s add another file: the README.md.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//go:embed README.md&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//go:embed docs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;docsFS&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;embed&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;FS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yep, that&amp;rsquo;s it. We simply add another &lt;code&gt;//go:embed&lt;/code&gt; directive. But how do we know if both directives are respected?
Correct, by writing a quick test!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestEmbedsReadme&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;testing&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;T&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// read using the ReadFile method&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;got&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;docsFS&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ReadFile&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;README.md&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Fatal&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// as os has, too&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;want&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ReadFile&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;README.md&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Fatal&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;diff&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;cmp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Diff&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;got&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;want&lt;/span&gt;); &lt;span style="color:#a6e22e"&gt;diff&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Errorf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;unexpected README (-want +got):\n%s&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;diff&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;go:embed&lt;/code&gt; bundles files and directories into the binary at compile-time. Of course you shouldn&amp;rsquo;t embed all files and blow up your binary size, but it can greatly simplify your deployment process and prove that auxiliary files are accessible and immutable.&lt;/p&gt;
&lt;p&gt;Curious about more use cases? Here is a non-exhaustive list to get started:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;injecting the version of your software&lt;/li&gt;
&lt;li&gt;pinning the signature of a remote certificate&lt;/li&gt;
&lt;li&gt;embedding documentation&lt;/li&gt;
&lt;li&gt;linking against an environment (prod, staging, &amp;hellip;)&lt;/li&gt;
&lt;li&gt;translations / localizations&lt;/li&gt;
&lt;li&gt;GraphQL requests&lt;/li&gt;
&lt;li&gt;SQL queries&lt;/li&gt;
&lt;li&gt;snapshot testing (e.g. for integration tests)&lt;/li&gt;
&lt;li&gt;for loading defaults&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Atomic Design</title><link>https://vknabel.com/library/atomic-design/</link><pubDate>Wed, 17 Aug 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/library/atomic-design/</guid><description>&lt;p&gt;Today I stumbled upon &lt;a href="https://atomicdesign.bradfrost.com/chapter-2/"&gt;Atomic Design&lt;/a&gt;. It is some kind of architectural pattern for the user interface. The goal is to build a reusable UI that is easy to understand and maintain.&lt;/p&gt;
&lt;p&gt;It starts with atoms, which represent the smallest units of the UI like text, buttons or images. By composing atoms, you get molecules like a search input (label, input, button). Then there are organisms like the top navigation bar. Templates align organisms. In the end, Pages fill the templates with acutal data.&lt;/p&gt;
&lt;p&gt;From my perspective, only the Pages connect to non-UI parts of the application. Hence the Atomic Design is only focused about the structure of the view.&lt;/p&gt;</description></item><item><title>Open Policy Agent</title><link>https://vknabel.com/posts/2022-08-15-open-policy-agent/</link><pubDate>Mon, 15 Aug 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-08-15-open-policy-agent/</guid><description>&lt;p&gt;With OPA you implement decisions of policies, not their enforcement. This avoids confusion between multiple servers that might come from different implementations of the policies.&lt;/p&gt;
&lt;p&gt;The decisions themselves are implemented using the Regi programming language.&lt;/p&gt;
&lt;p&gt;Similar to Prolog, Rego is a declarative and logic programming language. A seldom choice, but a great fit for policies!&lt;/p&gt;</description></item><item><title>Overriding Go values with ldflags</title><link>https://vknabel.com/posts/2022-08-12-overriding-go-values-with-ldflags/</link><pubDate>Fri, 12 Aug 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-08-12-overriding-go-values-with-ldflags/</guid><description>&lt;p&gt;You can override Go values with ldflags during the build.&lt;/p&gt;
&lt;p&gt;That way, you can inject values like the current version, build date or the current commit hash.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go build &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -ldflags &lt;span style="color:#e6db74"&gt;&amp;#34;-X &amp;#39;github.com/metal-stack/v.Version=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;VERSION&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; -X &amp;#39;github.com/metal-stack/v.Revision=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;GITVERSION&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; -X &amp;#39;github.com/metal-stack/v.GitSHA1=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;SHA&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; -X &amp;#39;github.com/metal-stack/v.BuildDate=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;BUILDDATE&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Seen in the &lt;a href="https://github.com/metal-stack/v"&gt;metal-stack/v&lt;/a&gt; repository.&lt;/p&gt;</description></item><item><title>Kubernetes without Load Balancers?</title><link>https://vknabel.com/posts/2022-08-06-kubernetes-without-load-balancers/</link><pubDate>Sat, 06 Aug 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-08-06-kubernetes-without-load-balancers/</guid><description>&lt;p&gt;All the hosted Kubernetes solutions I know of want you to pay roughly 10$ pre month for each load balancer.&lt;/p&gt;
&lt;p&gt;If you currently don&amp;rsquo;t expect much traffic, but want to play around with k8s or if many docker-compose files don&amp;rsquo;t fit your needs anymore, this easily doubles your costs.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://downey.io/blog/skip-kubernetes-loadbalancer-with-hostport-daemonset/"&gt;Downey.io – save money and skip the kubernetes load balancer&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bonus learning: I didn&amp;rsquo;t heard of the &lt;code&gt;DaemonSet&lt;/code&gt; until now!&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Kind is the new minikube</title><link>https://vknabel.com/posts/2022-08-05-kind-is-the-new-minikube/</link><pubDate>Fri, 05 Aug 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-08-05-kind-is-the-new-minikube/</guid><description>&lt;p&gt;I have been playing around with minikube for quite some time now. Though as I run minikube on my Mac, I experienced multiple bugs regarding accessing the cluster and it&amp;rsquo;s published ports.&lt;/p&gt;
&lt;p&gt;From my perspective Kind works much better, is faster, more stable on macOS and allows the creation of multiple clusters. Though it is not bug-free on macOS. Although most bugs seem to be related to Docker itself on the Mac.&lt;/p&gt;
&lt;p&gt;You can even pause the cluster - just pause it&amp;rsquo;s docker container.&lt;/p&gt;</description></item><item><title>Bash scripts with Gum</title><link>https://vknabel.com/posts/2022-08-03-bash-scripts-with-gum/</link><pubDate>Wed, 03 Aug 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-08-03-bash-scripts-with-gum/</guid><description>&lt;p&gt;I gave the newest tool of Charm &lt;a href="https://github.com/charmbracelet/gum"&gt;Gum&lt;/a&gt; a try.
It allows to create interactive bash scripts in just a few lines of code.&lt;/p&gt;
&lt;p&gt;In my case I gave it a try for Kubernetes. I often want to look at the logs of a pod, but the exact k8s pod differs from time to time.
And sometimes I forget the exact namespace or the pod&amp;rsquo;s name includes random characters.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! /bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# let the user select a namespace&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;NAMESPACE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;kubectl get namespaces -o name | gum choose | cut -d/ -f2&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# let the user select a pod of the namespace&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;POD&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;kubectl get pods -n &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$NAMESPACE&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; | tail -n +2 | gum choose | cut -d&lt;span style="color:#e6db74"&gt;&amp;#39; &amp;#39;&lt;/span&gt; -f1&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# if you want to repeat, copy the command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;gt; kubectl logs -n \&amp;#34;&lt;/span&gt;$NAMESPACE&lt;span style="color:#e6db74"&gt;\&amp;#34; \&amp;#34;&lt;/span&gt;$POD&lt;span style="color:#e6db74"&gt;\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# print the logs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl logs -n &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$NAMESPACE&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$POD&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Go 1.19 new Garbage Collection Memory Limit</title><link>https://vknabel.com/posts/2022-07-30-go-1.19-new-garbage-collection-memory-limit/</link><pubDate>Sat, 30 Jul 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-07-30-go-1.19-new-garbage-collection-memory-limit/</guid><description>&lt;p&gt;Go 1.19 introduced a new option to customize the runtime Garbage Collection behavior: Memory Limit.
This might be interesting when embedding Go within a mobile application.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://changelog.com/gotime/240"&gt;Go Time – Episode #240: What&amp;rsquo;s new in Go 1.19&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Announcing Puffery for macOS and the command line</title><link>https://vknabel.com/posts/announcing-puffery-for-macos-and-the-command-line/</link><pubDate>Fri, 29 Jul 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/announcing-puffery-for-macos-and-the-command-line/</guid><description>&lt;p&gt;Two years ago, I created &lt;a href="https://apps.apple.com/de/app/puffery/id1508776889"&gt;Puffery for iOS&lt;/a&gt;, which allows sending push notifications to your iOS devices.&lt;/p&gt;
&lt;p&gt;You can follow other&amp;rsquo;s channels and directly receive updates. There is no algorithm that decides wether you should receive notifications or not.&lt;/p&gt;
&lt;p&gt;From the beginning scripting is part of puffery&amp;rsquo;s DNA: you were able to send messages with Siri Shortcuts and a simple &lt;code&gt;curl&lt;/code&gt; command.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to take a deeper look, feel free to check out the &lt;a href="https://github.com/vknabel/puffery"&gt;GitHub repository&lt;/a&gt; including the Swift Server written in Vapor, and the iOS app built with SwiftUI in a single mono-repo.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="puffery-for-macos"&gt;Puffery for macOS&lt;/h2&gt;
&lt;p&gt;&lt;img src="./puffery-macos-showcase.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;If you do no share your channels, sending and receiving messages and thus push notifications from the same iOS device is kind of useless in the long run.&lt;/p&gt;
&lt;p&gt;Some of your most productive time of the day, you are probably sitting in front of your Mac. And a macOS app better integrates into your daily workflow.&lt;/p&gt;
&lt;p&gt;Of course, you can use your Shortcuts from your iOS device. But what&amp;rsquo;s special on macOS is the ability to pin specific workflows to the dock or the status bar. Even adding workflows to your share menus is possible!&lt;/p&gt;
&lt;p&gt;No need to open Puffery directly if you don&amp;rsquo;t want to. No distractions.&lt;/p&gt;
&lt;p&gt;If you are a user of macOS widgets, you are covered, too! They work exactly the same way as on iOS.&lt;/p&gt;
&lt;h2 id="puffery-for-the-command-line"&gt;Puffery for the command line&lt;/h2&gt;
&lt;p&gt;&lt;img src="./puffery-cli-showcase.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;With the next announcement, we are slowly moving outside of the Apple ecosystem. Puffery is now available as a command line tool. Currently it has a limited scope and has not yet received feature parity with the iOS or macOS apps, but it does not only support macOS, but also Linux &lt;em&gt;and&lt;/em&gt; Windows.&lt;/p&gt;
&lt;p&gt;It provides a tui / text based interface to scroll through lists of channels and messages. You can also send messages to a previously selected channel.&lt;/p&gt;
&lt;p&gt;You can even signup or login with your existing account.&lt;/p&gt;
&lt;p&gt;To give it a try, have a look on the &lt;a href="https://github.com/vknabel/go-puffery#installation"&gt;puffery installation instructions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It has been built in Go and &lt;a href="https://charm.sh/"&gt;Charm&lt;/a&gt;&amp;rsquo;s &lt;a href="https://github.com/charmbracelet/bubbletea"&gt;Bubble Tea library&lt;/a&gt;. As all of Puffery, it is open source and licensed under the &lt;a href="https://github.com/vknabel/go-puffery/blob/main/LICENSE"&gt;MIT license&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Extracting Tailwind constants using theme</title><link>https://vknabel.com/posts/2022-07-25-extracting-tailwind-constants-using-theme/</link><pubDate>Mon, 25 Jul 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-07-25-extracting-tailwind-constants-using-theme/</guid><description>&lt;p&gt;When using tailwind, you might be forced to manually write SCSS by hand.
With &lt;code&gt;@apply some classes;&lt;/code&gt; you can embed whole classes with all their styles into your own rules.&lt;/p&gt;
&lt;p&gt;But sometimes you need to only acces one single constant. Like accessing an animation timeout, default paddings, fonts or a color for some cases.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s possible with a simple &lt;code&gt;theme('colors.primary')&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Hugo Assets placed in Folders</title><link>https://vknabel.com/posts/2022-07-24-hugo-assets-placed-in-folders/</link><pubDate>Sun, 24 Jul 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-07-24-hugo-assets-placed-in-folders/</guid><description>&lt;p&gt;If you need assets for a specific document or blog post, you can simply create a folder with your desired name and place your assets in there. Rename the markdown file to &lt;code&gt;index.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To reference the asset, just link it relative to the folder &lt;code&gt;[text](./my-file.ext)&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Hugo's Syntax Highlighting is static</title><link>https://vknabel.com/posts/2022-07-24-hugo-syntax-highlighting-without-js/</link><pubDate>Sun, 24 Jul 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-07-24-hugo-syntax-highlighting-without-js/</guid><description>&lt;p&gt;Unlinke many other static site generators, the &lt;a href="https://gohugo.io/content-management/syntax-highlighting/"&gt;syntax highlighting of Hugo&lt;/a&gt; does not use client-side JavaScript, which is great!&lt;/p&gt;
&lt;p&gt;The underlying library is available as Go module &lt;a href="https://github.com/alecthomas/chroma"&gt;github.com/alecthomas/chroma&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Overriding internal Hugo templates</title><link>https://vknabel.com/posts/2022-07-24-overriding-internal-hugo-templates/</link><pubDate>Sun, 24 Jul 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-07-24-overriding-internal-hugo-templates/</guid><description>&lt;p&gt;You can override internal Hugo templates with your own templates.&lt;/p&gt;
&lt;p&gt;Simply create it in the &lt;code&gt;layouts&lt;/code&gt; folder and you are done!&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s how I overrode &lt;code&gt;_internal/google_analytics_async.html&lt;/code&gt; with my own template - to not use GA!
That way I could avoid forking &lt;a href="https://github.com/athul/archie.git"&gt;athul&amp;rsquo;s archie&lt;/a&gt; template.&lt;/p&gt;</description></item><item><title>Templates for specific folders in Hugo</title><link>https://vknabel.com/posts/2022-07-24-templates-for-specific-folders/</link><pubDate>Sun, 24 Jul 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-07-24-templates-for-specific-folders/</guid><description>&lt;p&gt;In Hugo you can simply create a folder within &lt;code&gt;content/&lt;/code&gt; and a template within &lt;code&gt;layouts/_default/&lt;/code&gt;. It will be used for the index route of that folder - even if your template did not specify a layout.&lt;/p&gt;
&lt;p&gt;This allowed me to create a separate template for all my learnings / til section!&lt;/p&gt;</description></item><item><title>Ultra-fast Hugo GitHub actions</title><link>https://vknabel.com/posts/2022-07-24-ultra-fast-hugo-github-actions/</link><pubDate>Sun, 24 Jul 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-07-24-ultra-fast-hugo-github-actions/</guid><description>&lt;p&gt;I created a &lt;a href="https://github.com/vknabel/vknabel.github.io/blob/main/.github/workflows/pages.yml"&gt;GitHub workflow&lt;/a&gt; to automatically build and deploy my website to GitHub Pages.&lt;/p&gt;
&lt;p&gt;It uses &lt;a href="https://github.com/peaceiris/actions-hugo"&gt;peaceiris/actions-hugo@v2&lt;/a&gt; to build the website with Hugo and &lt;a href="https://github.com/peaceiris/actions-gh-pages"&gt;peaceiris/actions-gh-pages@v3&lt;/a&gt; to deploy it to GitHub Pages.&lt;/p&gt;</description></item><item><title>About</title><link>https://vknabel.com/about/</link><pubDate>Fri, 22 Jul 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/about/</guid><description>&lt;p&gt;I’m Valentin Knabel, a developer, a cyclist, coffee nerd, vegan and a human.
When possible I prefer real-world stuff, open source and privacy.&lt;/p&gt;
&lt;p&gt;I value simplicity, kindness and honesty. It is fine to be imperfect, as long as we are honest and have fun along the way.&lt;/p&gt;
&lt;p&gt;Usually I am &lt;code&gt;@vknabel&lt;/code&gt; everywhere like on &lt;a href="https://github.com/vknabel"&gt;GitHub&lt;/a&gt; and &lt;a href="https://mastodon.social/@vknabel"&gt;@mastodon.social@vknabel&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I work at &lt;a href="https://www.x-cellent.com"&gt;x-cellent&lt;/a&gt; on &lt;a href="https://metal-stack.io"&gt;metal-stack&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="curious-for-more"&gt;Curious for more?&lt;/h2&gt;
&lt;p&gt;If you want to dig deeper into my thoughts, priorities and projects, feel free to check out the following pages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vknabel.com/now"&gt;Now&lt;/a&gt; - describes my priorities and what&amp;rsquo;s on my mind and what I’m currently working on.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vknabel.com/colophon"&gt;Colophon&lt;/a&gt; - how this website is built and what tools and technologies I use to build it.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vknabel.com/projects"&gt;Projects&lt;/a&gt; - a list of projects I maintain or regularly contribute to.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vknabel.com/sovereignty"&gt;Sovereignty&lt;/a&gt; - my current progress towards digital sovereignty using European services or self-hosted software.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Designing and scoping my programming language Lithia</title><link>https://vknabel.com/posts/designing-and-scoping-my-language-lithia/</link><pubDate>Fri, 22 Jul 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/designing-and-scoping-my-language-lithia/</guid><description>&lt;p&gt;Once you have decided you want to create your own programming language, you need to create a broader concept. Every programming language is unique in its own way. Your goal is to find out in which way. And you need to find your reason &lt;em&gt;why&lt;/em&gt; you want to create it in the first place.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do you want to solve a particular problem?&lt;/li&gt;
&lt;li&gt;Is it for learning purposes?&lt;/li&gt;
&lt;li&gt;For who do you build the language?&lt;/li&gt;
&lt;li&gt;Who should use it?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keep the answers in mind during the whole design process: they will guide you into the right direction.&lt;/p&gt;
&lt;p&gt;In my case, I designed &lt;a href="https://github.com/vknabel/lithia"&gt;Lithia&lt;/a&gt; for myself to learn and explore all sorts of tooling around programming and to create them myself. And that&amp;rsquo;s also the focus of the language: tooling around itself. Learning by doing is king here.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This blog post is part of a &lt;a href="https://vknabel.com/posts/journey-about-creating-a-new-programming-language/"&gt;Journey about creating a new programming language&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="my-initial-take-on-designing-lithia"&gt;My initial take on designing Lithia&lt;/h2&gt;
&lt;p&gt;I really like functional programming, but I value being able to perform changes imperatively when I need to. I like the idea of lazy evaluation and being able to create infinite data structures, but to only execute needed parts. And the stronger your type system is, the more errors can be caught at compile time. Some downsides like verbosity can be reduced by type inference.&lt;/p&gt;
&lt;p&gt;Writing functional algorithms really work well with enumerations where every case may have attributes or associated values. It should also be possible to add scoped methods to already declared classes or data types.&lt;/p&gt;
&lt;p&gt;And generics are great, let‘s stuff generics in there. Great nice language. How long will I take to implement it? Well…, scrap that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Let‘s start all over.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Every programming language special on its own way. To define each characteristic of it, you not only need to know the language features, but the intentionally missing ones, naming conventions, typical patterns, the standard library and how everything is tied together.&lt;/p&gt;
&lt;p&gt;That all defines the programming language. Everything is a tradeoff. For example, every piece of syntactic sugar makes your language more expressive, but harder to read. Every new feature needs to be learned and understood correctly. And every feature may be used in the wrong places or not at all.&lt;/p&gt;
&lt;h2 id="what-language-is-worth-to-implement"&gt;What language is worth to implement?&lt;/h2&gt;
&lt;p&gt;Let‘s be honest: programming languages tend to be on the bigger side of projects and we all have limited time, energy and motivation. And there are plenty of programming languages out there.&lt;/p&gt;
&lt;p&gt;Where does your language fit between all those other already finished languages? Are you solving niche problems? Are you experimenting with new language concepts or are you mixing seemingly unrelated paradigms? And more importantly: why do &lt;strong&gt;you&lt;/strong&gt; want to implement it yourself? Are you able to even finish it? How do you keep your motivation up in the long term?&lt;/p&gt;
&lt;p&gt;If you decided to start working on a compiler or interpreter, be sure to be able to finish it and expect you will be the only one using it.&lt;/p&gt;
&lt;p&gt;So what‘s my case? Lithia does not solve a particular problem, but it enables me to learn about the inner workings of compilers, interpreters and the tooling around it. I want a green field to build all kinds of tooling. And in case there is a limitation or bug, I can fix or embrace and document it. What can be achieved in a specific field is not limited by external factors, only by my time, energy and motivation.&lt;/p&gt;
&lt;h2 id="its-about-the-scope-and-milestones"&gt;It’s about the scope and milestones&lt;/h2&gt;
&lt;p&gt;We know, the planned Lithia is not going to be able to compete with whole language ecosystems or programming languages that shine through lots of features. I wouldn’t be able to finish it.&lt;/p&gt;
&lt;p&gt;Instead, the scope must be reduced. Even more as the language should be accompanied by tooling, coming with a huge scope increase!&lt;/p&gt;
&lt;p&gt;But great tooling requires meta data like types to operate on and the compiler needs to support all that.&lt;/p&gt;
&lt;p&gt;There need to be compromises.&lt;/p&gt;
&lt;p&gt;And I need continuous wins to keep my motivation high. Once there is a basic implementation, I want to do some simple stuff, while working on a complex subsystem.&lt;/p&gt;
&lt;p&gt;Lithia needs to shine through the absence of many features. Instead it should focus on few key concepts, that support requirements for tooling. And after all, it should be a language I like to program in.&lt;/p&gt;
&lt;p&gt;To sum up, Lithia…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;is a simple language with only a few features&lt;/li&gt;
&lt;li&gt;supports tooling&lt;/li&gt;
&lt;li&gt;allows a few quick wins&lt;/li&gt;
&lt;li&gt;is conceptually attracting&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="lithia-is-an-experiment"&gt;Lithia is an experiment&lt;/h2&gt;
&lt;p&gt;I personally prefer mostly functional programming languages with a shot of imperative programming. In that context I also favour lazy over strict evaluation and love currying.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lazy vs strict evaluation:&lt;/strong&gt; expressions will only be evaluated if their value is actually needed in lazy evaluation. With strict evaluation they will always be evaluated.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Currying&lt;/strong&gt;: let’s imagine you have a function with two parameters, but you currently have just the first parameter. With currying you can pass just the first one and you will receive a function that accepts the second parameter. Without currying you‘d need to manually create that function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Lithia won’t allow direct mutations of variables. There will only be constants. I didn’t want to explicitly forbid any mutations, but at this stage I didn’t know how they fit into the language, yet.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; lazy evaluation and directly mutating variables might lead to weird behaviour. And what about types of a variable? May they change at any time?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The type system should be easy to bootstrap, but should enable static analysis in the future.&lt;/p&gt;
&lt;p&gt;That’s my baseline.&lt;/p&gt;
&lt;h3 id="type-system"&gt;Type System&lt;/h3&gt;
&lt;p&gt;Variables, parameters and functions won&amp;rsquo;t have an explicit type annotation like Java&amp;rsquo;s &lt;code&gt;String name&lt;/code&gt;, Swift&amp;rsquo;s &lt;code&gt;let name: String&lt;/code&gt; or Go&amp;rsquo;s &lt;code&gt;var name string&lt;/code&gt;. If there would be type annotations, Lithia would need to enforce them right from the beginning.&lt;/p&gt;
&lt;p&gt;Instead Lithia should have an easy to bootstrap dynamic type system, that still enables static analysis. The easiest way is not to allow structural manipulation of types.&lt;/p&gt;
&lt;p&gt;How far can we get with type inference?&lt;/p&gt;
&lt;h3 id="base-types"&gt;Base Types&lt;/h3&gt;
&lt;p&gt;There are two basic kinds of types to support:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;types like classes and structs with independent properties.&lt;/li&gt;
&lt;li&gt;types like enums that list all possible values. In some languages enums may have associated properties, thus mixing sum and product types.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another aspect are reference and value types and semantics. How should changes propagate through the different parts of your program?&lt;/p&gt;
&lt;p&gt;In Lithia there will be one type with properties called &lt;em&gt;data&lt;/em&gt; without any inheritance or methods like classes have. They will have a list of untyped properties.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Don&amp;rsquo;t worry if upcoming code snippets look unfamiliar – we will cover the syntax in another article.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;data Example {
 firstProperty
 secondProperty
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But here is an optimisation for tooling: if you want to store a function within your &lt;em&gt;data&lt;/em&gt;, you can add all its parameters &lt;code&gt;firstProperty param, and, more&lt;/code&gt;. When the tooling and the compiler mature, they can detect errors and improve autocompletion.&lt;/p&gt;
&lt;p&gt;In Lithia, the &lt;em&gt;enum&lt;/em&gt; type breaks with common interpretations. Here an &lt;em&gt;enum&lt;/em&gt; isn’t a direct list of possible values, but a list of possible types, called a union.&lt;/p&gt;
&lt;p&gt;This change from individual values to more generic types should make the currently rather static language feel more dynamic and like a scripting language for lovers of strong typing.&lt;/p&gt;
&lt;h3 id="the-type-switch"&gt;The type switch&lt;/h3&gt;
&lt;p&gt;Now that we have enums, we need to resolve those types. A type switch maps every type to a function. It requires the enum definition and returns a function that receives the value.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;let increaseOptional = type Optional {
 Some: { some =&amp;gt; some.value + 1 },
 None: { none =&amp;gt; none }
}
increaseOptional (Some 1)
increaseOptional None
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The goal is to provide functions to work on the enums within one module, that are reusable and higher level. This should reduce the amount of external type switches.&lt;/p&gt;
&lt;h3 id="standard-library"&gt;Standard Library&lt;/h3&gt;
&lt;p&gt;The stdlib is a great place to get quick wins from once the most basic implementation of the language has been finished.&lt;/p&gt;
&lt;p&gt;In Lithia the interpreter’s responsibility ends early. It defines Strings, Characters, numeric types, Functions and some more basic types.&lt;/p&gt;
&lt;p&gt;The Standard Library called &lt;em&gt;prelude&lt;/em&gt; starts with famous types like Boolean and List.&lt;/p&gt;
&lt;p&gt;In here there are many possibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;Docs generation&lt;/li&gt;
&lt;li&gt;Sorting&lt;/li&gt;
&lt;li&gt;All kinds of basic data manipulations&lt;/li&gt;
&lt;li&gt;And much, much more!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="what-didnt-make-it-to-lithia"&gt;What didn’t make it to Lithia&lt;/h3&gt;
&lt;p&gt;The list of all features that did not find any place in Lithia is longer than what did. All have their particular reasons and their absence fit into the concept of Lithia. Here are some honorable mentions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Interfaces / protocols&lt;/li&gt;
&lt;li&gt;Extensions / adding new methods to existing types&lt;/li&gt;
&lt;li&gt;Methods&lt;/li&gt;
&lt;li&gt;Type casts and direct type testing&lt;/li&gt;
&lt;li&gt;Custom operators&lt;/li&gt;
&lt;li&gt;Overloading&lt;/li&gt;
&lt;li&gt;Variational arguments&lt;/li&gt;
&lt;li&gt;Exceptions&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this post, I summarised my take on creating your own programming language. Don’t do it for glory, do it for yourself and the experience you gain.&lt;/p&gt;
&lt;p&gt;Design your scope and concept. Make sure you stay motivated until the end. Don’t hesitate to reevaluate your decisions. Take tiny steps.&lt;/p&gt;
&lt;p&gt;If you wish, check out the open source repository of &lt;a href="https://github.com/vknabel/lithia"&gt;Lithia&lt;/a&gt;. If you have any questions don&amp;rsquo;t hesitate to ask me on &lt;a href="https://mastodon.social/@vknabel"&gt;@mastodon.social@vknabel&lt;/a&gt; or join the &lt;a href="https://github.com/vknabel/lithia/discussions"&gt;Lithia discussions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;</description></item><item><title>Starting the journey about creating a new programming language</title><link>https://vknabel.com/posts/journey-about-creating-a-new-programming-language/</link><pubDate>Fri, 22 Jul 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/journey-about-creating-a-new-programming-language/</guid><description>&lt;p&gt;This is planned to be a series of blog posts about designing and developing my own programming language, where I want to share some pitfalls, my learnings, design decisions, experience and the development process.&lt;/p&gt;
&lt;p&gt;While this series will not teach you how to write your first own programming language on its own, it might be supplementary.&lt;/p&gt;
&lt;p&gt;After years of playing with the idea of writing my own programming language, I finally started. In the past I read books and blog posts about creating my own compiler, and I actually have written some parsers, but I never implemented the whole language itself. I know some theory, but I do not know how to actually apply it in practice.&lt;/p&gt;
&lt;p&gt;I felt stuck between theory and simple proof of concept implementations like creating another Lisp.&lt;/p&gt;
&lt;h2 id="whats-in-this-series"&gt;What&amp;rsquo;s in this series?&lt;/h2&gt;
&lt;p&gt;These are all finished blog posts so far.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vknabel.com/posts/designing-and-scoping-my-language-lithia/"&gt;Designing and scoping my language Lithia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vknabel.com/posts/the-current-state-of-lithia-after-2-years/"&gt;The current state of Lithia after 2 years&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vknabel.com/posts/2025-09-16-a-new-beginning/"&gt;Zirric: A new Beginning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vknabel.com/posts/2025-09-28-going-virtual/"&gt;Zirric: The Virtual Machine&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="my-motivations-and-background"&gt;My motivations and background&lt;/h2&gt;
&lt;p&gt;As soon as I learned programming, I was inspired by all the tooling like linters, editors and the compiler itself. All developed in order to develop. They were those magical blackboxes, that felt beyond everything I could achieve.&lt;/p&gt;
&lt;p&gt;Later I played around with various language features and writing my own standard library replacement for existing languages. Lots of fun, but not productive.&lt;/p&gt;
&lt;p&gt;With the years, I learned new paradigms like object orientation, logical, constraint and functional programming. And beyond those big paradigms, there are many different concepts that define the languages character like the module system, dependencies, inheritance, generics, operator overloading, interfaces, type classes, strong or weak typing, dynamic or static, side-effects, monads and many more.&lt;/p&gt;
&lt;p&gt;I started experimenting with new programming language concepts, mixing a few and thinking about the consequences. How do these features integrate and what does it mean for a different group of features? And which syntax do you need to support the special characteristics?&lt;/p&gt;
&lt;p&gt;For fun, I worked on small libraries and tooling in existing programming language ecosystems. Nothing special or widespread.
I read about compilers and interpreters and even though I implemented small lisp-like languages and custom parsers, I felt like I couldn’t write a compiler on my own.&lt;/p&gt;
&lt;p&gt;I kept the wish to design and create my own programming language from ground up. With tooling. I want to learn every aspect of it. Run into problems. Solve them and gradually increase the maturity of the language, the tooling and the compiler.&lt;/p&gt;
&lt;p&gt;That being said, I don’t want my language to be used in wild, heck I encourage you to &lt;em&gt;not&lt;/em&gt; use it. The journey and what we learn along the way is the treasure.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;I decided to create yet another programming language and let’s be honest: nobody will ever use it and I’m fine with it. But anyway I will still provide tooling and documentation for it.&lt;/p&gt;
&lt;p&gt;I will update and link all my compiler and language design blog posts here.&lt;/p&gt;
&lt;p&gt;If you wish, check out the open source repository of &lt;a href="https://github.com/vknabel/lithia"&gt;Lithia&lt;/a&gt;. If you have any questions don&amp;rsquo;t hesitate to ask me on &lt;a href="https://mastodon.social/@vknabel"&gt;@mastodon.social@vknabel&lt;/a&gt; or join the &lt;a href="https://github.com/vknabel/lithia/discussions"&gt;Lithia disussions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;</description></item><item><title>Angular *ngFor trackBy</title><link>https://vknabel.com/posts/2022-07-19-angular-ngfor-trackby/</link><pubDate>Tue, 19 Jul 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-07-19-angular-ngfor-trackby/</guid><description>&lt;p&gt;When iterating over an array of objects in Angular, the change detection compares the references of the objects to detect changes.&lt;/p&gt;
&lt;p&gt;If the object reference now change too, like when updating local data from the network, Angular will discard the identity of all nested components within the &lt;code&gt;ngFor&lt;/code&gt; and re-render the entire list. Even if the objects deeply equal, the change detection will still detect the change.&lt;/p&gt;
&lt;p&gt;To help our loop caching components and instead changing their inputs, we can add a &lt;code&gt;trackBy&lt;/code&gt; function to the &lt;code&gt;ngFor&lt;/code&gt; directive.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;div&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;checkbox-row&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;ngFor&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;let option of field.options; trackBy: fieldOptionTrackBy&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And in TypeScript, we add identity tracking function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;fieldOptionTrackBy&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;index&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;number&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;option&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;AssistantStepOption&lt;/span&gt;)&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;option&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;identifier&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Background:&lt;/strong&gt; Angular did reset my &lt;code&gt;mat-checkbox&lt;/code&gt; within the &lt;code&gt;ngFor&lt;/code&gt;. That lead to a discard of the freshly changed values.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Go Data Race Detector</title><link>https://vknabel.com/posts/2022-07-09-go-data-race-detector/</link><pubDate>Sat, 09 Jul 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-07-09-go-data-race-detector/</guid><description>&lt;p&gt;You can run your Go program or your tests with the Data Race detection enabled. This might cost some performance and memory, but in case a data race is being detected, that&amp;rsquo;s worth it!&lt;/p&gt;
&lt;p&gt;Perfect for your CI!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go run --race ./cmd/app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test --race ./...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Kubernetes Overview for Beginners</title><link>https://vknabel.com/posts/2022-05-25-kubernetes-overview/</link><pubDate>Wed, 25 May 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-05-25-kubernetes-overview/</guid><description>&lt;p&gt;I have never done anything productive with Kubernetes before. But it is a really tough topic to get started with. It&amp;rsquo;s overwhelming.&lt;/p&gt;
&lt;p&gt;Luckily I found an excellent &lt;a href="https://www.youtube.com/watch?v=X48VuDVv0do"&gt;Kubernetes tutorial on Youtube&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I made some personal notes while watching. Mostly to keep some overview for all the different concepts.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Today I haven&amp;rsquo;t finished the video, so the overview is only a rough draft. Some missing concepts are Storage Classes, Ingress Controllers and more!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="kubernetes-concepts"&gt;Kubernetes Concepts&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;Pod&lt;/strong&gt; is an abstraction of usually one container.&lt;/li&gt;
&lt;li&gt;all &lt;strong&gt;IP addresses&lt;/strong&gt; are assigned and changed with every restart&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pods&lt;/strong&gt; communicate using &lt;strong&gt;Services&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;External Services&lt;/strong&gt; are accessible by IP and port&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingress&lt;/strong&gt; is used for external access using domains&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;ConfigMap&lt;/strong&gt; holds configuration data. Changing the config map prevents whole rebuilds and redeploys of specifc pods&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secrets&lt;/strong&gt; are like &lt;strong&gt;ConfigMaps&lt;/strong&gt;, but for sensitive data. Values are by default base64 encoded in the YAML.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Volumes&lt;/strong&gt; connect storage (local or remote) to a &lt;strong&gt;Pod&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Kubernetes does not handle storage!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LoadBalancers&lt;/strong&gt; are a type of &lt;strong&gt;Services&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Replicas&lt;/strong&gt; are the amount of &lt;strong&gt;Deployments&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deployments&lt;/strong&gt; are blueprints for &lt;strong&gt;Pods&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;StatefulSets&lt;/strong&gt; are like &lt;strong&gt;Deployments&lt;/strong&gt;, but stateful.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They are designed to databases etc to avoid data inconsistency.&lt;/li&gt;
&lt;li&gt;Harder to get right than a simple &lt;strong&gt;Deployment&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Databases are often deployed outside the cluster.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;StatefulSet&lt;/strong&gt; has volume claim templates, to create independent storage for each replica.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;volumeClaimTemplates&lt;/code&gt; create a volume claim for each replica.&lt;/p&gt;
&lt;h2 id="kubernetes-architecture"&gt;Kubernetes Architecture&lt;/h2&gt;
&lt;p&gt;Master nodes and worker nodes are kept separate to keep the cluster controllable. Imagine you couldn&amp;rsquo;t manage your cluster to increase the number of worker nodes or replicas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Worker Nodes&lt;/strong&gt;
Runs multiple pods.&lt;/p&gt;
&lt;p&gt;3 processes are running on each node:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;container runtime&lt;/li&gt;
&lt;li&gt;the Kubelet connects container runtime and the configuration&lt;/li&gt;
&lt;li&gt;the kube proxy routes requests to services)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Master Nodes&lt;/strong&gt;
Manage the worker nodes.&lt;/p&gt;
&lt;p&gt;Runs 4 processes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Api Server / cluster gateway / authentication&lt;/li&gt;
&lt;li&gt;the Scheduler that decides which worker node gets workload&lt;/li&gt;
&lt;li&gt;Controller Manager observes the state of the cluster and makes changes&lt;/li&gt;
&lt;li&gt;etcd is a Key-Value-Store for k8n&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Iterating over a map in Go</title><link>https://vknabel.com/posts/2022-05-17-iterating-over-a-map-in-go/</link><pubDate>Tue, 17 May 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-05-17-iterating-over-a-map-in-go/</guid><description>&lt;p&gt;Iterating over a map in Go is not predictable.
This is a design decision to avoid relying on memory layout.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;k&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;v&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;range&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;map&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// always prints different results!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;%s: %s\n&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;k&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;v&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>NSDataAsset reduces Code signing overhead</title><link>https://vknabel.com/posts/2022-05-03-nsdataasset-reduces-code-signing-overhead/</link><pubDate>Tue, 03 May 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-05-03-nsdataasset-reduces-code-signing-overhead/</guid><description>&lt;p&gt;I had completely forgotten about NSDataAsset. Maybe I should move some of my JSON files into asset catalogs. According to @emergetools that should remove some code signing overhead from the app size.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/simonbs/status/1521257570983419910?s=21&amp;amp;t=haWN_mAlrHjNyeRb-7nD7g"&gt;Tweet by @simonbs&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Attaching to UIScrollView's parent with Auto Layout</title><link>https://vknabel.com/posts/2022-03-25-uiscrollview-autolayout-parent/</link><pubDate>Fri, 25 Mar 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-03-25-uiscrollview-autolayout-parent/</guid><description>&lt;p&gt;When using auto layout with a &lt;code&gt;UIScrollView&lt;/code&gt;, subviews may not attach to the top edge of the scroll view&amp;rsquo;s parent. Else you cannot scroll, but the handle shrinks the more you scroll.&lt;/p&gt;
&lt;p&gt;Setting left and right is ok to fix the width if you do not plan to scroll horizontally.&lt;/p&gt;</description></item><item><title>SwiftPM can detect breaking api changes</title><link>https://vknabel.com/posts/2022-03-17-swiftpm-breaking-api-changes/</link><pubDate>Thu, 17 Mar 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-03-17-swiftpm-breaking-api-changes/</guid><description>&lt;p&gt;Starting with Swift 5.6 the Swift Package Manager can automatically detect Breaking API changes. Really great for libs!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/fabianfett/status/1504393754412822530?s=12"&gt;https://twitter.com/fabianfett/status/1504393754412822530?s=12&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Missing Async XCTests on Linux</title><link>https://vknabel.com/posts/2022-02-26-async-xctests-on-linux/</link><pubDate>Sat, 26 Feb 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-02-26-async-xctests-on-linux/</guid><description>&lt;p&gt;Async tests do not work on Linux, only on macOS. Instead you need to implement a helper function that runs the test async by relying on expectations.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.swiftbysundell.com/articles/unit-testing-code-that-uses-async-await/"&gt;https://www.swiftbysundell.com/articles/unit-testing-code-that-uses-async-await/&lt;/a&gt;&lt;/p&gt;</description></item><item><title>WKWebView Configuration Cookies not working</title><link>https://vknabel.com/posts/2022-02-21-wkwebview-cookies-not-working/</link><pubDate>Mon, 21 Feb 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-02-21-wkwebview-cookies-not-working/</guid><description>&lt;p&gt;Cookies in WKWebView are broken. When manually setting a Cookie using the Configuration, it will be ignored by the web view.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Workaround:&lt;/strong&gt; add a &lt;code&gt;UserScript&lt;/code&gt; with the following code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;document.&lt;span style="color:#a6e22e"&gt;cookies&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;cookie_optin=essential:1|analytics:0&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s a great way to automatically reject Cookies and especially to disable tracking to either propagate the user&amp;rsquo;s ATT decision or to avoid implementing ATT in the beginning.&lt;/p&gt;</description></item><item><title>Getting your IP address</title><link>https://vknabel.com/posts/2022-02-14-getting-your-ip-address/</link><pubDate>Mon, 14 Feb 2022 20:53:32 +0000</pubDate><guid>https://vknabel.com/posts/2022-02-14-getting-your-ip-address/</guid><description>&lt;p&gt;Sometimes you need your local IP address on the command line to automatically pass it to a script.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ifconfig en0 |awk &lt;span style="color:#e6db74"&gt;&amp;#39;/inet / {print $2; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Swift custom table of contents lib</title><link>https://vknabel.com/posts/2022-02-10-swift-custom-table-of-contents-lib/</link><pubDate>Thu, 10 Feb 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2022-02-10-swift-custom-table-of-contents-lib/</guid><description>&lt;p&gt;If the default Table Of Contents / Index Set for iOS does not fit your use cases. This library might be interesting:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://iosexample.com/table-of-contents-selector-built-with-swift/"&gt;https://iosexample.com/table-of-contents-selector-built-with-swift/&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Debugging Swift in VS Code in 2022</title><link>https://vknabel.com/posts/debugging-swift-in-vs-code-in-2022/</link><pubDate>Tue, 08 Feb 2022 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/debugging-swift-in-vs-code-in-2022/</guid><description>&lt;p&gt;Back in 2019 I wrote an &lt;a href="https://vknabel.com/pages/Debugging-Swift-in-VS-Code"&gt;article about how to debug&lt;/a&gt; your Swift Package Manager projects in Visual Studio Code. In late december 2021, the Swift Server Working group released a brand &lt;a href="https://marketplace.visualstudio.com/items?itemName=sswg.swift-lang"&gt;new extension for vscode&lt;/a&gt;, which dramatically improves debugging your Swift code. Especially for linux! Time for an update!&lt;/p&gt;
&lt;p&gt;Running, debugging and developing your targets in Visual Studio Code is not prepared by default. Especially for us Swift developers this might come unexpected, especially in comparison to Xcode.
In VS Code we require extensions and configs for this purpose.&lt;/p&gt;
&lt;p&gt;First we need to install the mentioned extension: &lt;a href="https://marketplace.visualstudio.com/items?itemName=sswg.swift-lang"&gt;Swift for Visual Studio Code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now let’s create a new project on open it in VS Code!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ swift package init --type executable
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating executable package: MyProject
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating Package.swift
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating README.md
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating .gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating Sources/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating Sources/MyProject/main.swift
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating Tests/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating Tests/MyProjectTests/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating Tests/MyProjectTests/MyProjectTests.swift
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ code . &lt;span style="color:#75715e"&gt;# if not found: open -a &amp;#34;Visual Studio Code&amp;#34; .&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Swift extension for VS Code will now generate some launch configurations within a &lt;code&gt;.vscode/launch.json&lt;/code&gt; file.
It generates release and debug &lt;a href="https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb"&gt;LLDB&lt;/a&gt;-launch-configurations for each target and one launch configuration for each test target. Previously this step would have been manual.&lt;/p&gt;
&lt;p&gt;As we currently have one target &lt;code&gt;MyProject&lt;/code&gt; and one test target &lt;code&gt;MyProjectTests&lt;/code&gt;, we will have the following launch configurations:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/Debugging-Swift-in-VS-Code-in-2022/example_launch_configs.png" alt="Launch configs"&gt;&lt;/p&gt;
&lt;p&gt;Also note, that the &lt;code&gt;preLaunchTask&lt;/code&gt;s have been created, too!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;configurations&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;lldb&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;request&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;launch&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Debug MyProject&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;program&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;${workspaceFolder:MyProject}/.build/debug/MyProject&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;: [],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;cwd&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;${workspaceFolder:MyProject}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;preLaunchTask&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;swift: Build Debug MyProject&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;lldb&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;request&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;launch&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Release MyProject&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;program&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;${workspaceFolder:MyProject}/.build/release/MyProject&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;: [],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;cwd&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;${workspaceFolder:MyProject}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;preLaunchTask&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;swift: Build Release MyProject&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;lldb&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;request&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;launch&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Test MyProject&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;program&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;/Applications/Xcode.app/Contents/Developer/usr/bin/xctest&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;.build/debug/MyProjectPackageTests.xctest&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;cwd&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;${workspaceFolder:MyProject}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;preLaunchTask&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;swift: Build All&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we are ready to start debugging!
Let&amp;rsquo;s open &lt;code&gt;Sources/MyProject/main.swift&lt;/code&gt;, and add a breakpoint before executing &lt;code&gt;print(&amp;quot;Hello, world!&amp;quot;)&lt;/code&gt; by left-clicking the empty space before the line number.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/Debugging-Swift-in-VS-Code-in-2022/example_breakpoint.png" alt="Breakpoint in main.swift"&gt;&lt;/p&gt;
&lt;p&gt;Next, switch to the &lt;code&gt;Run and Debug&lt;/code&gt; tab on the left, make sure &lt;code&gt;Debug MyProject&lt;/code&gt; is selected, and click the green run button.&lt;/p&gt;
&lt;p&gt;Now your project will be compiled, run and stops at the breakpoint!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/Debugging-Swift-in-VS-Code-in-2022/example_debugging.png" alt="Stopped at breakpoint in main.swift"&gt;&lt;/p&gt;
&lt;p&gt;Sadly this approach currently does not work when debugging iOS or macOS apps, but Swift Package Manager projects and CLIs work great!
I hope you enjoy your increased productivity!&lt;/p&gt;</description></item><item><title>UINavigationBar black after Xcode 13 upgrade</title><link>https://vknabel.com/posts/2021-11-19-uinavigationbar-black-after-xcode-13-upgrade/</link><pubDate>Fri, 19 Nov 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-11-19-uinavigationbar-black-after-xcode-13-upgrade/</guid><description>&lt;p&gt;In case your &lt;code&gt;UINavigationBar&lt;/code&gt; has been set to a custom color and the navigation bar is not translucent, you will experience a visual regression when updating to Xcode 13.
The navigation bar background will be black - until you start scrolling. Then it behaves as expected.&lt;/p&gt;
&lt;p&gt;Thankfully there is a workaround and an easy fix:
&lt;a href="https://developer.apple.com/forums/thread/682420"&gt;Apple Developer Forums: barTintColor not working in iOS 15&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Reminder: websites contain weird characters</title><link>https://vknabel.com/posts/2021-11-16-reminder-websites-contain-weird-characters/</link><pubDate>Tue, 16 Nov 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-11-16-reminder-websites-contain-weird-characters/</guid><description>&lt;p&gt;Copying contents from the web often copies weird characters, like the invisible character &lt;code&gt;U+FEFF&lt;/code&gt; or &amp;ldquo;Zero Width No-Break Space&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Code editors like VS Code might highlight those characters depending on your config, but other websites don&amp;rsquo;t. In my case I copied a secret for my CI pipeline including the invisible character.&lt;/p&gt;
&lt;p&gt;An easy workaround for this on the mac: Spotlight removes some of these characters. A simple &lt;code&gt;cmd+space&lt;/code&gt;, &lt;code&gt;cmd+v&lt;/code&gt;, &lt;code&gt;cmd+a&lt;/code&gt;, &lt;code&gt;cmd+c&lt;/code&gt; and &lt;code&gt;esc&lt;/code&gt; cleans the copied text.&lt;/p&gt;
&lt;p&gt;And suddenly the inserted password is correct!&lt;/p&gt;</description></item><item><title>There is a modular version manager: asdf!</title><link>https://vknabel.com/posts/2021-11-16-there-is-a-modular-version-manager-asdf/</link><pubDate>Tue, 16 Nov 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-11-16-there-is-a-modular-version-manager-asdf/</guid><description>&lt;p&gt;Today I learned about &lt;a href="https://github.com/asdf-vm/asdf"&gt;asdf&lt;/a&gt;, which is a pluggable version manager.&lt;/p&gt;
&lt;p&gt;Previously I used nvm, rvm, swiftenv and more. I now use asdf. Just add the plugin of your choice and install your desired versions.&lt;/p&gt;
&lt;p&gt;The only drawback is installing completely new versions of the tools. You are only able to install exact versions. You can&amp;rsquo;t install &lt;em&gt;some major 16 nodejs version&lt;/em&gt;.
But you can still install &lt;code&gt;asdf install &amp;lt;plugin&amp;gt; latest&lt;/code&gt; or in case of nodejs &lt;code&gt;lts-*&lt;/code&gt; versions.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t forget to pick the version locally or globally.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;some side-tip: there is an &lt;a href="https://github.com/asdf-community/asdf-direnv"&gt;asdf plugin for direnv&lt;/a&gt;!&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Adopting Swift Async Await in Vapor</title><link>https://vknabel.com/posts/adopting-swift-async-await-in-vapor/</link><pubDate>Mon, 15 Nov 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/adopting-swift-async-await-in-vapor/</guid><description>&lt;p&gt;A few months ago Swift 5.5 has been released and made &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; available. And in &lt;a href="https://github.com/vapor/vapor/releases/tag/4.50.0"&gt;4.50.0&lt;/a&gt; Vapor added support for it, too!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you are still on Vapor 3, you first need to &lt;a href="https://www.vknabel.com/pages/Upgrading-a-server-side-Swift-project-to-Vapor-4/"&gt;upgrade your server to Vapor 4&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now we can migrate most usages of Swift NIO&amp;rsquo;s &lt;code&gt;EventLoopFuture&lt;/code&gt; with &lt;code&gt;async&lt;/code&gt;. But we don&amp;rsquo;t have to! This is not a breaking change. I recently performed this upgrade for the server of my app &lt;a href="https://github.com/vknabel/puffery"&gt;Puffery&lt;/a&gt; and as both, the client and the server are open source I will include links to the respective git commits.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Puffery&lt;/strong&gt; is an app to send messages into channels using Shortcuts or HTTP. This will trigger a push notification to all clients that have subscribed. Within the app you can view your messages and channels.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I wouldn&amp;rsquo;t recommend to directly replace all occurrences of &lt;code&gt;EventLoopFuture&lt;/code&gt;. If you aren&amp;rsquo;t going to touch specific code paths in a while, there is no need to migrate those. But we&amp;rsquo;ll come back to that later.&lt;/p&gt;
&lt;h2 id="upgrading-to-swift-55"&gt;Upgrading to Swift 5.5&lt;/h2&gt;
&lt;p&gt;If you haven&amp;rsquo;t already, you need to upgrade your Swift Tools Version within your &lt;code&gt;Package.swift&lt;/code&gt;-manifest:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// swift-tools-version:5.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;PackageDescription&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now a few lines later we need to upgrade to a newer macOS version, because &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; not only requires Swift 5.5, but also macOS 12 Monterey. Make sure you have upgraded accordingly. Otherwise you&amp;rsquo;d need to work on a linux machine or within a docker container.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; package = Package(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: &lt;span style="color:#e6db74"&gt;&amp;#34;PufferyServer&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; platforms: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .macOS(.v12), &lt;span style="color:#75715e"&gt;// upgrade to .v12&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next up, we need to bump our dependencies. As we want to rely on special features of the new Vapor, we explicitly go &lt;code&gt;from: &amp;quot;4.50.0&amp;quot;&lt;/code&gt;. Repeat this with other dependencies like Fluent.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#75715e"&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dependencies: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 &lt;span style="color:#75715e"&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 .package(url: &lt;span style="color:#e6db74"&gt;&amp;#34;https://github.com/vapor/vapor.git&amp;#34;&lt;/span&gt;, from: &lt;span style="color:#e6db74"&gt;&amp;#34;4.50.0&amp;#34;&lt;/span&gt;), &lt;span style="color:#75715e"&gt;// upgrade to 4.50.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 &lt;span style="color:#75715e"&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		],
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, to silence a warning, we need to explicitly declare our &lt;code&gt;Run&lt;/code&gt; target as &lt;code&gt;executableTarget&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; targets: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 &lt;span style="color:#75715e"&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 .executableTarget(name: &lt;span style="color:#e6db74"&gt;&amp;#34;Run&amp;#34;&lt;/span&gt;, dependencies: [&lt;span style="color:#e6db74"&gt;&amp;#34;App&amp;#34;&lt;/span&gt;]),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 &lt;span style="color:#75715e"&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you use a Dockerfile, build &lt;code&gt;FROM swift:5.5 as build&lt;/code&gt;. Also if present don&amp;rsquo;t forget to update your &lt;code&gt;.swift-version&lt;/code&gt;-file and your CI.&lt;/p&gt;
&lt;p&gt;Now update your packages using &lt;code&gt;swift package update&lt;/code&gt;. If you use Xcode, also update your dependencies using &lt;code&gt;File &amp;gt; Packages &amp;gt; Update to Latest Package Versions&lt;/code&gt; to keep them in sync. In theory &lt;code&gt;swift build&lt;/code&gt; and &lt;code&gt;swift test&lt;/code&gt; should run without any errors. If it does, fix those and proceed.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/vknabel/puffery/commit/1c41e5ae5c49748c1389b4491e03d595e5b0f406"&gt;&lt;code&gt;git commit -am &amp;quot;Upgraded PufferyServer to Swift 5.5&amp;quot;&lt;/code&gt;&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="adopting-async-await"&gt;Adopting Async Await&lt;/h2&gt;
&lt;p&gt;Now that we upgraded our new Swift version and updated our dependencies, let&amp;rsquo;s get started with our migration.&lt;/p&gt;
&lt;p&gt;We will incrementally do tiny steps and migrate every function after another. But it doesn&amp;rsquo;t make sense to migrate all functions immediately. If you haven&amp;rsquo;t touched specific files in a while, there is no need to do so now. A great example are your database migrations. You won&amp;rsquo;t touch them anyways. Just write new ones with &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; and you are fine.&lt;/p&gt;
&lt;p&gt;In my opinion, controllers are the easiest place to get started. Later you can tackle migrate &lt;code&gt;Jobs&lt;/code&gt; or &lt;code&gt;ScheduledJob&lt;/code&gt;s. Then your services and your repositories.&lt;/p&gt;
&lt;p&gt;The easiest places to upgrade will most likely be your Fluent queries: there are overloads for &lt;code&gt;.find()&lt;/code&gt; and &lt;code&gt;.all()&lt;/code&gt; to return &lt;code&gt;EventLoopFuture&lt;/code&gt; and &lt;code&gt;async throws&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="migrate-the-function-signature"&gt;Migrate the function signature&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;- 	func messagesForAllChannels(_ req: Request) throws -&amp;gt; EventLoopFuture&amp;lt;[MessageResponse]&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;+ 	func messagesForAllChannels(_ req: Request) async throws -&amp;gt; [MessageResponse] {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now fix all issues within the function. Then fix the errors of all callers.&lt;/p&gt;
&lt;p&gt;If you temporarily converted invocations of this method from &lt;code&gt;EventLoopFuture&lt;/code&gt; to an async function using &lt;code&gt;.get()&lt;/code&gt;, it is now time to remove it.&lt;/p&gt;
&lt;h3 id="migrate-protocol-methods-if-directly-affected"&gt;Migrate Protocol Methods if directly affected&lt;/h3&gt;
&lt;p&gt;Most protocols need to be prefixed with &lt;code&gt;Async&lt;/code&gt; like &lt;code&gt;AsyncJob&lt;/code&gt; or &lt;code&gt;AsyncScheduledJob&lt;/code&gt;. Then you can replace all function signatures.&lt;/p&gt;
&lt;h3 id="i-need-async-but-i-have-an-eventloopfuture"&gt;I need async, but I have an EventLoopFuture&lt;/h3&gt;
&lt;p&gt;To convert a not yet converted &lt;code&gt;EventLoopFuture&lt;/code&gt;, we call &lt;code&gt;EventLoopFuture&amp;lt;V&amp;gt;.get() async throws -&amp;gt; V&lt;/code&gt;. You can migrate the function later.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; await theEventLoopFuture.&lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="i-need-an-eventloopfuture-but-i-have-an-async-function"&gt;I need an EventLoopFuture, but I have an async function&lt;/h3&gt;
&lt;p&gt;Sometimes I decided to keep some function signatures as they were and I did not migrate them. For those cases I created a small helper function to create an &lt;code&gt;EventLoopFuture&lt;/code&gt; from an async task.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;EventLoop&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt;&amp;lt;T&amp;gt;(task: @escaping () async &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; T) -&amp;gt; EventLoopFuture&amp;lt;T&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; promise = makePromise(of: T.&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; promise.completeWithTask { &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; await task() }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; promise.futureResult
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For example executing multiple futures in parallel is easy with &lt;code&gt;eventLoop.flatten&lt;/code&gt;, but it&amp;rsquo;s much harder with &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="migrate-flatmap"&gt;Migrate &lt;code&gt;.flatMap&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Migrate &lt;code&gt;.flatMap({ messages in doSomething(messages) })&lt;/code&gt; to &lt;code&gt;let result = try await doSomething(messages).get()&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="migrate-flatmapthrowing"&gt;Migrate &lt;code&gt;.flatMapThrowing&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Migrate &lt;code&gt;.flatMapThrowing({ messages in doSomething(messages) })&lt;/code&gt; to &lt;code&gt;let result = try doSomething(messages)&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="migrate-eventloopflatten"&gt;Migrate &lt;code&gt;eventLoop.flatten&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Executing multiple futures in parallel is easy with &lt;code&gt;eventLoop.flatten&lt;/code&gt;, but it&amp;rsquo;s much harder with &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d recommend to keep this part as is, and to keep this part as &lt;code&gt;EventLoopFuture&lt;/code&gt;.
See [I need an EventLoopFuture, but I have async](#I need an EventLoopFuture, but I have async).&lt;/p&gt;
&lt;h3 id="migrate-transformto"&gt;Migrate &lt;code&gt;.transform(to:)&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This is straight forward: use the value directly. Typically you&amp;rsquo;d return this.&lt;/p&gt;
&lt;p&gt;Sometimes I used &lt;code&gt;transform&lt;/code&gt; within a &lt;code&gt;flatMap&lt;/code&gt; to keep the same return value. Now, just &lt;code&gt;try await&lt;/code&gt; these side effects.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;-	.flatMap({ user in
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;-		user.update(on: req.db)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;-			.transform(to: user)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;-	})
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;+	try await update(on: req.db)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="migrate-always_"&gt;Migrate &lt;code&gt;.always(_:)&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;.always&lt;/code&gt; will be executed when an &lt;code&gt;EventLoopFuture&lt;/code&gt; fails and when it succeeds. This is the same behaviour of &lt;code&gt;defer&lt;/code&gt; with &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;-	return computeSomething()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;- .always { _ in
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;- 	doSomething()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;- }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;+ defer {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;+		doSomething()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;+ }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;+	return try await computeSomething()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Attention:&lt;/strong&gt; you probably need to move your defer up. Using &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; will likely introduce more return and throw statements which will exit your functions early.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="returning-constant-futures"&gt;Returning constant futures&lt;/h3&gt;
&lt;p&gt;If you currently throw a failing future, just throw the error directly.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;- return req.eventLoop.future(error: Abort(.notFound))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;+ throw Abort(.notFound)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To replace a succeeding future, return the value directly.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;- return req.eventLoop.future(success: value)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;+ return value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If thee &lt;code&gt;future(error:)&lt;/code&gt; was embedded within a &lt;code&gt;do&lt;/code&gt;-&lt;code&gt;catch&lt;/code&gt; to lift errors to an &lt;code&gt;EventLoopFuture&lt;/code&gt;, you can probably remove the &lt;code&gt;do&lt;/code&gt;-&lt;code&gt;catch&lt;/code&gt; and mark the function as &lt;code&gt;throws&lt;/code&gt; instead.&lt;/p&gt;
&lt;h3 id="test-and-commit"&gt;Test and Commit&lt;/h3&gt;
&lt;p&gt;Do not forget to regularly run your tests and to keep your project in a green state. From time to time, do some commits.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/vknabel/puffery/commit/17825477cb1d2709dc16e0669a9b943e2d978fd4"&gt;&lt;code&gt;git commit -am &amp;quot;Use async/await for Vapor&amp;quot;&lt;/code&gt;&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="real-world-examples"&gt;Real World Examples&lt;/h2&gt;
&lt;p&gt;In case you need guidance, here are typical examples for Vapor-endpoints. These examples should look familiar.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;All code snippets are actual code from Puffery.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="example-for-a-fluent-query"&gt;Example for a Fluent query&lt;/h3&gt;
&lt;p&gt;This function is part of the &lt;code&gt;SubscriptionRepository&lt;/code&gt;. It is meant to be used from &lt;code&gt;Controllers&lt;/code&gt; to consistently access, filter and sort the channel subscriptions of a user.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;all&lt;/span&gt;(of user: User) -&amp;gt; EventLoopFuture&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;[Subscription]&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; Subscription.query(on: db)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			.filter(&lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;Subscription.&lt;span style="color:#960050;background-color:#1e0010"&gt;$&lt;/span&gt;user.&lt;span style="color:#960050;background-color:#1e0010"&gt;$&lt;/span&gt;id == user.requireID())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			.sort(&lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;.&lt;span style="color:#960050;background-color:#1e0010"&gt;$&lt;/span&gt;createdAt, .descending)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			.all()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	} &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; eventLoop.future(error: error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We start by changing the type signature to &lt;code&gt;async throws&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To fix the type errors, we could drop &lt;code&gt;do&lt;/code&gt;-&lt;code&gt;catch&lt;/code&gt; as the new variant is throwing. Previously it wasn&amp;rsquo;t throwing as there is no overload of &lt;code&gt;EventLoopFuture.flatMap&lt;/code&gt; that accepts throwing &lt;code&gt;EventLoopFuture&lt;/code&gt;s. Therefore &lt;code&gt;all(of:)&lt;/code&gt; was required to lift thrown errors to futures.&lt;/p&gt;
&lt;p&gt;As there is no distinction between directly throwing and a query failure with &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; we can get rid of the &lt;code&gt;do&lt;/code&gt;-&lt;code&gt;catch&lt;/code&gt;. And as Fluent has overloads for both &lt;code&gt;EventLoopFuture&lt;/code&gt; and &lt;code&gt;async throws&lt;/code&gt; we&amp;rsquo;re done here.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;all&lt;/span&gt;(of user: User) async &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; [Subscription] {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; await Subscription.query(on: db)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 .filter(&lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;Subscription.&lt;span style="color:#960050;background-color:#1e0010"&gt;$&lt;/span&gt;user.&lt;span style="color:#960050;background-color:#1e0010"&gt;$&lt;/span&gt;id == user.requireID())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .sort(&lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;.&lt;span style="color:#960050;background-color:#1e0010"&gt;$&lt;/span&gt;createdAt, .descending)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .all()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="example-migrations-for-simple-read-only-endpoints"&gt;Example Migrations for simple read-only endpoints&lt;/h3&gt;
&lt;p&gt;My &lt;code&gt;MessageController&lt;/code&gt; looked like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MessageController&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;messagesForAllChannels&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; req: Request) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; EventLoopFuture&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;[MessageResponse]&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; user = &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; req.auth.require(User.&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; req.subscriptions.all(of: user)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 .flatMap(req.messages.latestSubscribed(&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;:))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .flatMapThrowing { messages &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; messages.map {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; MessageResponse($0.message, subscription: $0.subscription)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// other endpoints ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This code should be familiar to any Vapor developer. I started migration with the function signature, replaced &lt;code&gt;flatMap&lt;/code&gt; and &lt;code&gt;flatMapThrowing&lt;/code&gt; and inserted the &lt;code&gt;.get()&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;messagesForAllChannels&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; req: Request) async &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; [MessageResponse] {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; user = &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; req.auth.require(User.&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; subs = &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; await req.subscriptions.all(of: user).&lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; messages = &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; await req.messages.latestSubscribed(&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;: subs).&lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; messages.map {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; MessageResponse($0.message, subscription: $0.subscription)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After I migrated my &lt;code&gt;SubscriptionRepository&lt;/code&gt;, I could even get rid of the trailing &lt;code&gt;.get()&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="example-migration-for-simple-write-endpoints"&gt;Example Migration for simple write-endpoints&lt;/h3&gt;
&lt;p&gt;This function&amp;rsquo;s migration path was more complex.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;confirmEmailIfNeeded&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; user: User) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; EventLoopFuture&amp;lt;Void&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;guard&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; emailAddress = user.email &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; req.eventLoop.future()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; confirmation = &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; Confirmation(scope: &lt;span style="color:#e6db74"&gt;&amp;#34;email&amp;#34;&lt;/span&gt;, snapshot: emailAddress, user: user)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; confirmation.create(on: req.db)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		.flatMapThrowing { &lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; Email(&lt;span style="color:#75715e"&gt;/*...*/&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		.flatMap { email &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.req.queue.dispatch(SendEmailJob.&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;, email)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we could completely remove the empty &lt;code&gt;req.eventLoop.future()&lt;/code&gt;. A simple, blank &lt;code&gt;return&lt;/code&gt; statement is enough. And creating models doesn&amp;rsquo;t force us anymore to nest everything one level deeper. We &lt;code&gt;await&lt;/code&gt; the result, but we discard it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;confirmEmailIfNeeded&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; user: User) async &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;guard&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; emailAddress = user.email &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; confirmation = &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; Confirmation(scope: &lt;span style="color:#e6db74"&gt;&amp;#34;email&amp;#34;&lt;/span&gt;, snapshot: emailAddress, user: user)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; await confirmation.create(on: req.db)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; email = &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; Email(&lt;span style="color:#75715e"&gt;/*...*/&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; await req.queue.dispatch(SendEmailJob.&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;, email)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;Within this post we upgraded our Swift version, Package manifest, docker / CI Swift versions and our dependencies. Then we incrementally migrated portions of our codebase by following a set of rules. What was your migration like? Did you experience any problems?&lt;/p&gt;
&lt;p&gt;If you wish, check out the open source repository of &lt;a href="https://github.com/vknabel/puffery"&gt;Puffery&lt;/a&gt; or check it out on the &lt;a href="https://apps.apple.com/de/app/puffery/id1508776889"&gt;App Store&lt;/a&gt;. If you have any questions or feedback don&amp;rsquo;t hesitate to ask me on &lt;a href="https://mastodon.social/@vknabel"&gt;@mastodon.social@vknabel&lt;/a&gt; or join the &lt;a href="https://github.com/vknabel/puffery/discussions"&gt;Puffery disussions&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Having two admin dev accounts is a bad idea</title><link>https://vknabel.com/posts/2021-11-15-having-two-admin-dev-accounts-is-a-bad-idea/</link><pubDate>Mon, 15 Nov 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-11-15-having-two-admin-dev-accounts-is-a-bad-idea/</guid><description>&lt;p&gt;I tried to keep my work stuff separate from my personal stuff on the same machine.
I created two admin users, set up a group for homebrew and set the permissions accordingly.&lt;/p&gt;
&lt;p&gt;Sadly this didn&amp;rsquo;t work out in the long run. So I started to run Homebrew as my preferred admin user.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;whoami&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; !&lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;vknabel&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sudo -S --login -u vknabel bash -- &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;which brew&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$@&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; brew &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$@&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This approach worked and was muich more reliable than the previous approach.&lt;/p&gt;
&lt;p&gt;Then came Flutter. I had to add additional permissions and eventually change their ownership from time to time as flutter messed them up.&lt;/p&gt;</description></item><item><title>Converting simple iOS apps to tvOS is easy!</title><link>https://vknabel.com/posts/2021-08-02-converting-simple-ios-apps-to-tvos-is-easy/</link><pubDate>Mon, 02 Aug 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-08-02-converting-simple-ios-apps-to-tvos-is-easy/</guid><description>&lt;p&gt;Converting a simple and small iOS app was far easier than expected. Of course webviews, gesture recognizers, navbars, etc. are not supported or work differently, but at a first glance there are not a lot parts with visual regressions!&lt;/p&gt;</description></item><item><title>UIApplication.shared not in Extensions</title><link>https://vknabel.com/posts/2021-08-02-uiapplication-shared-not-in-extensions/</link><pubDate>Mon, 02 Aug 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-08-02-uiapplication-shared-not-in-extensions/</guid><description>&lt;p&gt;&lt;code&gt;UIApplication.shared&lt;/code&gt; isn&amp;rsquo;t available within an iOS app extension.
To annotate your code paths not needed in an extension, but using &lt;code&gt;UIApplication.shared&lt;/code&gt;, just use the &lt;code&gt;@available(iOSApplicationExtension, unavailable)&lt;/code&gt; annotation.&lt;/p&gt;
&lt;p&gt;Now you will receive the same error when using the annotated api from an extension!&lt;/p&gt;</description></item><item><title>Nestjs has great swagger decorators!</title><link>https://vknabel.com/posts/2021-07-16-nestjs-has-great-swagger-decorators/</link><pubDate>Fri, 16 Jul 2021 11:01:55 +0000</pubDate><guid>https://vknabel.com/posts/2021-07-16-nestjs-has-great-swagger-decorators/</guid><description>&lt;p&gt;Nestjs has very great decorators to annotate your routes, dtos and controllers.
This makes it really easy to keep your Swagger or OpenAPI documentation up to date.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.nestjs.com/openapi/types-and-parameters"&gt;https://docs.nestjs.com/openapi/types-and-parameters&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Only downside:&lt;/em&gt; as TypeScript interfaces do not exist at runtime, you need to declare all your data transfer objects as classes, which might lead you to add convenience or validation methods to them. Don&amp;rsquo;t do it. These classes are just decorations.&lt;/p&gt;</description></item><item><title>XPath is powerful</title><link>https://vknabel.com/posts/2021-07-13-xpath-is-powerful/</link><pubDate>Tue, 13 Jul 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-07-13-xpath-is-powerful/</guid><description>&lt;p&gt;XPath is a really powerful tool to query XML documents, if you are forced to. But in contrast to JSON queries, XPath does not return the actual values, but the nodes or attribtes you queried.&lt;/p&gt;
&lt;p&gt;XPath ist ziemlich mächtig und man kommt relativ flott an das gewünschte Ziel. Anders als bei JSON queries erhält man aber nicht die eigentlichen Werte, sonder den Node oder das Attribut selber.&lt;/p&gt;</description></item><item><title>Static Table enums</title><link>https://vknabel.com/posts/2021-07-12-static-table-enums/</link><pubDate>Mon, 12 Jul 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-07-12-static-table-enums/</guid><description>&lt;p&gt;Using enums in Swift for static Table View Data Sources is really great.
Especially when declaring them as &lt;code&gt;enum X: Int&lt;/code&gt; and using the &lt;code&gt;rawValue&lt;/code&gt; to reflect the section or row index.&lt;/p&gt;
&lt;p&gt;You can even use the &lt;code&gt;CaseIterable&lt;/code&gt; protocol to count the number of sections or rows.&lt;/p&gt;</description></item><item><title>Reminder: base64 is not secure!</title><link>https://vknabel.com/posts/2021-07-02-reminder-base64-is-not-secure/</link><pubDate>Fri, 02 Jul 2021 16:17:27 +0000</pubDate><guid>https://vknabel.com/posts/2021-07-02-reminder-base64-is-not-secure/</guid><description>&lt;p&gt;I have never actually seen serious code that&amp;rsquo;s running in production, that uses base64 encoding for password for over a decade.
Today I did.&lt;/p&gt;
&lt;p&gt;Here is the not so friendly reminder:&lt;/p&gt;
&lt;p&gt;base64 is no encryption or hashing algorithm. You should not use it to encode passwords.
And you should&amp;rsquo;t send the password over HTTP and Basic auth (no HTTPS, not even Digest auth).
Even if you do, trade the password for a token. Don&amp;rsquo;t repeat your mistakes with every single request.
And do not store the password locally. Store an encrypted session key instead.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t follow these basics, delete your code and shut down all your services.&lt;/p&gt;</description></item><item><title>SwiftGen has evolved!</title><link>https://vknabel.com/posts/2021-06-30-swiftgen-has-evolved/</link><pubDate>Wed, 30 Jun 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-06-30-swiftgen-has-evolved/</guid><description>&lt;p&gt;I haven&amp;rsquo;t used &lt;a href="https://github.com/SwiftGen/SwiftGen"&gt;SwiftGen&lt;/a&gt; for a long time, but wow, it has improved a lot. I am a bit impressed.&lt;/p&gt;</description></item><item><title>iOS apps require account deletion</title><link>https://vknabel.com/posts/2021-06-08-ios-apps-require-account-deletion/</link><pubDate>Tue, 08 Jun 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-06-08-ios-apps-require-account-deletion/</guid><description>&lt;p&gt;Since WWDC21 all iOS apps that provide registration are also required to provide the deletion of those accounts.&lt;/p&gt;</description></item><item><title>Invalid Gradle Private Key</title><link>https://vknabel.com/posts/2021-06-07-invalid-gradle-private-key/</link><pubDate>Mon, 07 Jun 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-06-07-invalid-gradle-private-key/</guid><description>&lt;p&gt;Gradle did reject my ssh key due to &lt;code&gt;invalid privatekey&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The solution: converting it into a different format.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-keygen-p -f id_rsa -m pem
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>CSS Rotate und Skew</title><link>https://vknabel.com/posts/2021-06-02-css-rotate-and-skew/</link><pubDate>Wed, 02 Jun 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-06-02-css-rotate-and-skew/</guid><description>&lt;p&gt;With CSS &lt;code&gt;skew&lt;/code&gt; you can simulate a perspective effect.
If you want to additionally &lt;code&gt;rotate&lt;/code&gt; the element, use &lt;code&gt;rotate&lt;/code&gt; first then &lt;code&gt;skew&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Otherwise you might get a really wierd result.&lt;/p&gt;</description></item><item><title>Swift and Firebase</title><link>https://vknabel.com/posts/2021-06-02-swift-and-firebase/</link><pubDate>Wed, 02 Jun 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-06-02-swift-and-firebase/</guid><description>&lt;p&gt;Nothing especially new here, but a reminder:
Firebase might be a good choice if you need realtime updates.&lt;/p&gt;
&lt;p&gt;Instead of implementing and connecting to websockets and implementing one-time requests and observing differently, you can simply use &lt;code&gt;getData&lt;/code&gt; or &lt;code&gt;observeValue&lt;/code&gt; as they are already implemented.&lt;/p&gt;
&lt;p&gt;Even some errors vanish when observing: you start by the cache and if there is no update due to errors, you don&amp;rsquo;t get new data.
But beware: Firebase still might not be the best choice, depending on your app or the circumstances.&lt;/p&gt;</description></item><item><title>SwiftUI @main AppDelegate</title><link>https://vknabel.com/posts/2021-05-27-swiftui-main-appdelegate/</link><pubDate>Thu, 27 May 2021 11:56:02 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-27-swiftui-main-appdelegate/</guid><description>&lt;p&gt;When using &lt;code&gt;@main&lt;/code&gt; on a &lt;code&gt;SwiftUI.App&lt;/code&gt; and &lt;code&gt;@UIApplicationDelegateAdaptor(AppDelegate.self)&lt;/code&gt; you can still have an &lt;code&gt;AppDelegate&lt;/code&gt; without handling SwiftUI manually as without &lt;code&gt;@main&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You don&amp;rsquo;t even need a &lt;code&gt;SceneDelegate&lt;/code&gt;! A &lt;em&gt;huge&lt;/em&gt; difference for tiny apps!&lt;/p&gt;</description></item><item><title>Android Permission now requires a description</title><link>https://vknabel.com/posts/2021-05-27-android-permission-now-requires-a-description/</link><pubDate>Thu, 27 May 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-27-android-permission-now-requires-a-description/</guid><description>&lt;p&gt;Now, Android apps require a description for each permission. Back then, it was optional.&lt;/p&gt;
&lt;p&gt;In contrast to iOS, this is only a guideline and not enforced by the Android SDK. Instead the developer needs to implement their own alert.
But with great power, comes great responsibility as it could be abused.&lt;/p&gt;</description></item><item><title>Updating Bitrise binary uploads</title><link>https://vknabel.com/posts/2021-05-20-updating-bitrise-binary-uploads/</link><pubDate>Thu, 20 May 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-20-updating-bitrise-binary-uploads/</guid><description>&lt;p&gt;Bitrise binary uploads cannot be updated after saving. Instead they need to be deleted and recreated.
Thus all workflow steps need to be updated if they rely on the data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pro-tip:&lt;/strong&gt; when copy-pasting, skip the &lt;code&gt;BITRISE_IO&lt;/code&gt; and &lt;code&gt;_URL&lt;/code&gt; parts – they will be inserted automatically when using the web UI.&lt;/p&gt;</description></item><item><title>Localization order</title><link>https://vknabel.com/posts/2021-05-18-localization-order/</link><pubDate>Tue, 18 May 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-18-localization-order/</guid><description>&lt;p&gt;When maintaining multiple locales within your project, try to keep line numbers consistent across all locales. Also keep the order of translations consistent.&lt;/p&gt;
&lt;p&gt;That makes differences easy to spot.&lt;/p&gt;</description></item><item><title>SwiftUI translations and UIKit</title><link>https://vknabel.com/posts/2021-05-18-swiftui-translations-and-uikit/</link><pubDate>Tue, 18 May 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-18-swiftui-translations-and-uikit/</guid><description>&lt;p&gt;Nothing new today, but a quick reminder:&lt;/p&gt;
&lt;p&gt;Even if you don&amp;rsquo;t need to explicitly localize &lt;code&gt;String&lt;/code&gt; literals for &lt;code&gt;Text&lt;/code&gt; manually, you can still need to when passing Strings to &lt;code&gt;UIAlertController&lt;/code&gt; or other UIKit classes.&lt;/p&gt;
&lt;p&gt;Of course. But sometimes I still forget while writing the code.&lt;/p&gt;</description></item><item><title>ftp-simple vscode workspace</title><link>https://vknabel.com/posts/2021-05-12-ftp-simple-vscode-workspace/</link><pubDate>Wed, 12 May 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-12-ftp-simple-vscode-workspace/</guid><description>&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=humy2833.ftp-simple"&gt;ftp-simple&lt;/a&gt; allows opening FTP connections as VS Code workspace!&lt;/p&gt;</description></item><item><title>Localizcable vs Swift unicode escapes</title><link>https://vknabel.com/posts/2021-05-12-localizcable-vs-swift-unicode-escapes/</link><pubDate>Wed, 12 May 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-12-localizcable-vs-swift-unicode-escapes/</guid><description>&lt;p&gt;&lt;code&gt;Localizable.strings&lt;/code&gt; doesn&amp;rsquo;t support unicode &lt;code&gt;\unnnn&lt;/code&gt; escapes, but only &lt;code&gt;\Unnnn&lt;/code&gt;!
In Swift the &lt;code&gt;\U&lt;/code&gt; is for UTF-16 escapes like &lt;code&gt;\Unnnnnnnn&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Android XML Translations</title><link>https://vknabel.com/posts/2021-05-11-android-xml-translations/</link><pubDate>Tue, 11 May 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-11-android-xml-translations/</guid><description>&lt;p&gt;The Android Studio tooling for Android apps checks and validates all translation keys. Even if they are present within the base language. This comes in handy after greater refactorings.&lt;/p&gt;
&lt;p&gt;But sadly the XML does not support XML escapes as &lt;code&gt;&amp;amp;#39;&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>NSDataDetector</title><link>https://vknabel.com/posts/2021-05-11-nsdatadetector/</link><pubDate>Tue, 11 May 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-11-nsdatadetector/</guid><description>&lt;p&gt;How to detect a URL in a String using NSDataDetector - free Swift 5.1 example code and tips
&lt;a href="https://www.hackingwithswift.com/example-code/strings/how-to-detect-a-url-in-a-string-using-nsdatadetector"&gt;https://www.hackingwithswift.com/example-code/strings/how-to-detect-a-url-in-a-string-using-nsdatadetector&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Coordinator [unowned self]</title><link>https://vknabel.com/posts/2021-05-10-coordinator-unowned-self/</link><pubDate>Mon, 10 May 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-10-coordinator-unowned-self/</guid><description>&lt;p&gt;An &lt;code&gt;[unowned self]&lt;/code&gt; in a Coordinator is okay.&lt;/p&gt;
&lt;p&gt;As the coordinator should live longer than any of its screens, a crash hints to lifecycle issues.&lt;/p&gt;</description></item><item><title>Fucking SwiftUI</title><link>https://vknabel.com/posts/2021-05-10-fucking-swiftui/</link><pubDate>Mon, 10 May 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-10-fucking-swiftui/</guid><description>&lt;p&gt;This cheat sheet is actually not bad!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://fuckingswiftui.com/"&gt;https://fuckingswiftui.com/&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Missing UIView animation delay</title><link>https://vknabel.com/posts/2021-05-10-missing-uiview-animation-delay/</link><pubDate>Mon, 10 May 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-10-missing-uiview-animation-delay/</guid><description>&lt;p&gt;UIView animate does &lt;strong&gt;not delay&lt;/strong&gt; if there are &lt;strong&gt;no changes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The completion block will be called immediately!&lt;/p&gt;</description></item><item><title>SwiftUI random colors</title><link>https://vknabel.com/posts/2021-05-10-swiftui-random-colors/</link><pubDate>Mon, 10 May 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-10-swiftui-random-colors/</guid><description>&lt;p&gt;When setting the background color randomly in SwiftUI, you will be able to detect every single repaint! Might be helpful while debugging.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/steipete/status/1379483193708052480?s=12"&gt;https://twitter.com/steipete/status/1379483193708052480?s=12&lt;/a&gt;&lt;/p&gt;</description></item><item><title>UICollectionViewCell alpha</title><link>https://vknabel.com/posts/2021-05-10-uicollectionviewcell-alpha/</link><pubDate>Mon, 10 May 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-10-uicollectionviewcell-alpha/</guid><description>&lt;p&gt;Wnat your &lt;code&gt;UICollectionViewCell&lt;/code&gt; transparent? Set &lt;code&gt;alpha&lt;/code&gt; on &lt;code&gt;contentView&lt;/code&gt;!
=&amp;gt; Setting &lt;code&gt;alpha&lt;/code&gt; on the &lt;code&gt;UICollectionViewCell&lt;/code&gt; itself doesn&amp;rsquo;t work.&lt;/p&gt;</description></item><item><title>UIImageView is not animatable</title><link>https://vknabel.com/posts/2021-05-10-uiimageview-is-not-animatable/</link><pubDate>Mon, 10 May 2021 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/2021-05-10-uiimageview-is-not-animatable/</guid><description>&lt;p&gt;UIImageView image is not animatable.
Instead use transitions!&lt;/p&gt;</description></item><item><title>Swift Coverage for VS Code</title><link>https://vknabel.com/posts/swift-coverage-for-vs-code/</link><pubDate>Sat, 29 Aug 2020 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/swift-coverage-for-vs-code/</guid><description>&lt;p&gt;When developing Swift in Visual Studio Code there were a few cases, where I preferred Xcode. Beside the obvious cases like iOS Development, managing certificates and provisioning profiles, there was one big case left: Writing unit tests.&lt;/p&gt;
&lt;p&gt;With perfect TDD (unit tests first, then code) this is no real problem, but if you want to write tests for already existing code, it is essential to get feedback about your current progress and test coverage.&lt;/p&gt;
&lt;p&gt;In these situations, I still opened Xcode, to check the coverage. As I did this too regular and missed my VS Code setup, I decided to give code coverage in VS Code a try.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/Swift-Coverage-for-VS-Code/example-xcode.png" alt="Code Coverage in Xcode"&gt;&lt;/p&gt;
&lt;p&gt;After some research, I mostly found code coverage extensions for JS or other languages. Though I never found an extension supporting the llvm-cov-format produced by &lt;code&gt;swift test --enable-code-coverage&lt;/code&gt; at &lt;code&gt;.build/*/debug/codecov/*.json&lt;/code&gt; out of the box. There are solutions like converting this coverage file to &lt;code&gt;lcov.info&lt;/code&gt;, but this would always require some setup in every project. So I started my own.&lt;/p&gt;
&lt;p&gt;As I really liked the idea of other extensions to highlight the code itself, I decided to adopt this idea. Though many highlight covered code using a green background, which feels too heavy for always being activated. So I decided to only highlight covered expressions by default.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/Swift-Coverage-for-VS-Code/example-vscode.png" alt="Code Coverage in VS Code with Swift Coverage"&gt;&lt;/p&gt;
&lt;p&gt;To get started, install &lt;a href="https://marketplace.visualstudio.com/items?itemName=vknabel.swift-coverage"&gt;Swift Coverage - Visual Studio Marketplace&lt;/a&gt; and run your tests using &lt;code&gt;swift test --enable-code-coverage&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Whenever you need to update your coverage statistics, simple re-run your tests.
That&amp;rsquo;s it!&lt;/p&gt;
&lt;p&gt;Are you using &lt;a href="https://github.com/vknabel/vscode-swift-development-environment"&gt;Maintained Swift Development Environment&lt;/a&gt;?
To keep your coverage always up to date set &lt;code&gt;sde.swiftBuildingParams&lt;/code&gt; setting to &lt;code&gt;[&amp;quot;test&amp;quot;, &amp;quot;--enable-code-coverage&amp;quot;]&lt;/code&gt; to run all your unit tests on every change. Might not be a good idea with a slow test suite, but in smaller projects, this is super great.&lt;/p&gt;
&lt;p&gt;Is there something you are missing for Swift and VS Code?
Do you have any questions or tips?
Let&amp;rsquo;s get in touch on &lt;a href="https://mastodon.social/@vknabel"&gt;@mastodon.social@vknabel&lt;/a&gt; or via &lt;a href="mailto:swift-coverage-for-vscode@vknabel.com"&gt;email&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Have you found a bug or need help with &lt;a href="https://github.com/vknabel/vscode-swift-coverage"&gt;Swift Coverage&lt;/a&gt;? It&amp;rsquo;s open source, just head to GitHub and &lt;a href="https://github.com/vknabel/vscode-swift-coverage/issues/new"&gt;open an issue&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Upgrading a server-side Swift project to Vapor 4</title><link>https://vknabel.com/posts/upgrading-a-server-side-swift-project-to-vapor-4/</link><pubDate>Wed, 22 Apr 2020 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/upgrading-a-server-side-swift-project-to-vapor-4/</guid><description>&lt;p&gt;The past few days I created a new server using &lt;a href="https://vapor.codes"&gt;Vapor&lt;/a&gt; and hit &lt;code&gt;vapor new &amp;lt;project&amp;gt; --auth&lt;/code&gt; which created a Vapor 3 server. Later I upgraded the young project to Vapor 4, but found some lack of practical information about the upgrade on the internet. So here I share my subjective experience and try to give you some tips.&lt;/p&gt;
&lt;p&gt;The app itself is relatively simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it has user authentication and registration&lt;/li&gt;
&lt;li&gt;users don‘t have any profile and cannot interact with each other&lt;/li&gt;
&lt;li&gt;on certain events, we notify multiple users on all of their devices&lt;/li&gt;
&lt;li&gt;users have their personal list of notifications&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After a while, when most parts of the MVP were finished, I wanted to add &lt;a href="https://github.com/vapor/apns"&gt;vapor/apns&lt;/a&gt;, which required the new Vapor 4. But hasn’t Vapor 4 been released recently? Yes, but it seems like the Vapor team probably decided to keep Vapor 3 the default until the documentation and all surrounding has been finished (which is a good thing!).&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://forums.swift.org/t/whats-new-in-vapor-4/31832"&gt;list of changes&lt;/a&gt; reads nicely, new services API, new model API built on top of property wrappers looks gorgeous, synchronously decoding contents improves controllers far more than you might expect and eager loading is great to tune up performance and to even reduce the amount of nested futures to be handled.&lt;/p&gt;
&lt;p&gt;As mentioned I was especially interested in APNS. Additionally I need background jobs, which come as &lt;a href="https://github.com/vapor/queues"&gt;vapor/queues&lt;/a&gt; , too.
For me the decision was an obvious one: let‘s upgrade the code base!&lt;/p&gt;
&lt;h2 id="starting-the-migration-from-vapor-3-to-vapor-4"&gt;Starting the Migration from Vapor 3 to Vapor 4&lt;/h2&gt;
&lt;p&gt;At that time, the server had only 4 controllers, 10 routes, 13 request_response structs_enums, 6 models, only empty migrations, zero services and zero repositories. It was still using an SQLite in-memory database with SQLite imports and types spread across the whole project. Also it obviously didn‘t send any Push Notifications (although they were already stored).&lt;/p&gt;
&lt;p&gt;So as a first step to upgrade Vapor, I head over to their &lt;a href="https://docs.vapor.codes/4.0/upgrading/"&gt;Upgrading Docs&lt;/a&gt; and started with updating the Package.swift manifest dependencies and platforms as proposed. Apparently I could even drop the vapor/auth dependency as it is now included in Vapor 4. You might stumble upon &lt;code&gt;platforms: [.macOS(.v10_15)]&lt;/code&gt;: no worries, it still supports Linux. 👍&lt;/p&gt;
&lt;h3 id="configure"&gt;Configure&lt;/h3&gt;
&lt;p&gt;Next I copied the new proposed contents of &lt;code&gt;Sources/Run/main.swift&lt;/code&gt;, deleted &lt;code&gt;Sources/App/app.swift&lt;/code&gt;, &lt;code&gt;Sources/App/boot.swift&lt;/code&gt; (it was still empty) and changed &lt;code&gt;configure(_:_:_:)&lt;/code&gt; and &lt;code&gt;routes(_:_:)&lt;/code&gt; to be of type &lt;code&gt;(Application) throws -&amp;gt; Void&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As I already touched &lt;code&gt;configure(_:)&lt;/code&gt; and &lt;code&gt;routes(_:)&lt;/code&gt;, I wanted to make those two files compile first, before moving to the next files, but the upgrading docs didn‘t really serve me well here. After some time reading through docs, I decided to generate a new Vapor 4 reference project using &lt;code&gt;vapor-beta new Example&lt;/code&gt; which used &lt;a href="https://github.com/vapor/template"&gt;vapor/template&lt;/a&gt; as template.&lt;/p&gt;
&lt;p&gt;When comparing &lt;a href="https://github.com/vapor/api-template/blob/master/Sources/App/configure.swift"&gt;Vapor 3 api-template configure.swift&lt;/a&gt; with the &lt;a href="https://github.com/vapor/template/blob/master/Sources/App/configure.swift"&gt;new one&lt;/a&gt; I came to the conclusion, I could drop the most code.&lt;/p&gt;
&lt;p&gt;In Vapor 3 we usually started creating a &lt;code&gt;XxxConfig&lt;/code&gt; where we added types of migrations or used middlewares or databases.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// &lt;/span&gt;&lt;span style="color:#75715e"&gt;MARK:&lt;/span&gt;&lt;span style="color:#75715e"&gt; - Vapor 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Vapor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Fluent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;FluentSQLite&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;configure&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; config: &lt;span style="color:#66d9ef"&gt;inout&lt;/span&gt; Config, &lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; env: &lt;span style="color:#66d9ef"&gt;inout&lt;/span&gt; Environment, &lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; services: &lt;span style="color:#66d9ef"&gt;inout&lt;/span&gt; Services) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// Register providers first&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; services.register(FluentSQLiteProvider())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// Register routes to the router&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; router = EngineRouter.&lt;span style="color:#66d9ef"&gt;default&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; routes(router)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	services.register(router, &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt;: Router.&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// Configure a SQLite database&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; sqlite = &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; SQLiteDatabase(storage: .memory)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// Register the configured SQLite database to the database config.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; databases = DatabasesConfig()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	databases.add(database: sqlite, &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt;: .sqlite)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	services.register(databases)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// Register middleware&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; middlewares = MiddlewareConfig()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	middlewares.use(ErrorMiddleware.&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	services.register(middlewares)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// Register migrations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; migrations = MigrationConfig()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	migrations.add(model: User.&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;, database: .sqlite) &lt;span style="color:#75715e"&gt;// Note: it‘s a type!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	services.register(migrations)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With Vapor 4, we just add instances of those types to &lt;code&gt;app.xxx&lt;/code&gt; directly instead. Much more readable!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// &lt;/span&gt;&lt;span style="color:#75715e"&gt;MARK:&lt;/span&gt;&lt;span style="color:#75715e"&gt; - Vapor 4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Vapor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Fluent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;FluentSQLiteDriver&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;configure&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; app: Application) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; app.databases.use(.sqlite(.memory), &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt;: DatabaseID.sqlite)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	app.middleware.use(ErrorMiddleware.&lt;span style="color:#66d9ef"&gt;default&lt;/span&gt;(environment: app.environment))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	app.migrations.add(CreateUser()) &lt;span style="color:#75715e"&gt;// Note: here it‘s an instance!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; routes(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now our &lt;code&gt;configure.swift&lt;/code&gt; should only complain about our migrations, so let‘s head over to fix those!&lt;/p&gt;
&lt;h3 id="models"&gt;Models&lt;/h3&gt;
&lt;p&gt;Before we can fix our migrations, we should upgrade our models! For this step, it’s best to start bottom up from your simplest models to your most complex ones with lots of relations. Be patient and do one step at a time.&lt;/p&gt;
&lt;p&gt;Here is our example model in Vapor 3:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// &lt;/span&gt;&lt;span style="color:#75715e"&gt;MARK:&lt;/span&gt;&lt;span style="color:#75715e"&gt; - Vapor 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Vapor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;FluentSQLite&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DeviceToken&lt;/span&gt;: SQLiteModel {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;typealias&lt;/span&gt; Database = SQLiteDatabase
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; createdAtKey: TimestampKey? = &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;.createdAt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; id: Int? &lt;span style="color:#75715e"&gt;// Note the Int? As id!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; value: String
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; userID: User.ID
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; createdAt: Date?
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; user: Parent&amp;lt;DeviceToken, User&amp;gt; { parent(&lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;.userID) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(id: Int? = &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;, value: String, user: User) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.id = id
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.value = value
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		 &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.userID = &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; user.requireID()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And here is the interesting part: our new Vapor 4 variant of &lt;code&gt;DeviceToken&lt;/code&gt;!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Fluent&lt;/span&gt; &lt;span style="color:#75715e"&gt;// 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// &lt;/span&gt;&lt;span style="color:#75715e"&gt;MARK:&lt;/span&gt;&lt;span style="color:#75715e"&gt; - Vapor 4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DeviceToken&lt;/span&gt;: Model {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; schema = &lt;span style="color:#e6db74"&gt;&amp;#34;device_tokens&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;// 2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	@ID(key: .id) &lt;span style="color:#75715e"&gt;// 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; id: Int?
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	@Field(key: &lt;span style="color:#e6db74"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;// 4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; value: String
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	@Parent(key: &lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;// 5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; user: User
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	@Timestamp(key: &lt;span style="color:#e6db74"&gt;&amp;#34;created_at&amp;#34;&lt;/span&gt;, on: .create) &lt;span style="color:#75715e"&gt;// 6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; createdAt: Date?
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;() {} &lt;span style="color:#75715e"&gt;// 7 this is required, but in my case always empty ^^&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(id: UUID? = &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;, value: String, user: User) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.id = id
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.value = value
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#75715e"&gt;// 8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#75715e"&gt;// ATTENTION: using self.user = user crashes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.&lt;span style="color:#960050;background-color:#1e0010"&gt;$&lt;/span&gt;user.id = &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; user.requireID()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.&lt;span style="color:#960050;background-color:#1e0010"&gt;$&lt;/span&gt;user.value = user
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Quite a lot to explain here, although it should be rather straight-forward. Essentially we:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;drop the fluent database import&lt;/li&gt;
&lt;li&gt;replace the database typealias with the name of our schema (e.g. table name)&lt;/li&gt;
&lt;li&gt;add the &lt;code&gt;@ID(key: .id)&lt;/code&gt; property wrapper&lt;/li&gt;
&lt;li&gt;mark every field with &lt;code&gt;@Field(key: &amp;quot;name_of_field_in_schema&amp;quot;)&lt;/code&gt; (giving the column name)&lt;/li&gt;
&lt;li&gt;declare the parent
&lt;ol&gt;
&lt;li&gt;remove old &lt;code&gt;var userID: User.ID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;replace old computed &lt;code&gt;var user: Parent&amp;lt;DeviceToken, User&amp;gt; { parent(\.userID) }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;with &lt;code&gt;@Parent(key: &amp;quot;user_id&amp;quot;) var user: User&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The same rules apply to children and siblings&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Instead of declaring static &lt;code&gt;createdAtKey&lt;/code&gt;, &lt;code&gt;updatedAtKey&lt;/code&gt; and &lt;code&gt;deletedAtKey&lt;/code&gt; use &lt;code&gt;@Timestamp(key: FieldKey, on: TimestampTrigger)&lt;/code&gt; as seen above&lt;/li&gt;
&lt;li&gt;add an empty &lt;code&gt;init() {}&lt;/code&gt;. Not sure when to customize and what its for 🤔&lt;/li&gt;
&lt;li&gt;Here you need to pay attention! You may never set your relations directly, always use &lt;code&gt;$user.value =&lt;/code&gt; or &lt;code&gt;$user.id =&lt;/code&gt;. In this case we set both resulting in an already eager loaded &lt;code&gt;user&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Oh and if your models conform to &lt;code&gt;Parameter&lt;/code&gt;: just remove it. It has been removed.&lt;/p&gt;
&lt;h3 id="migrations"&gt;Migrations&lt;/h3&gt;
&lt;p&gt;As our models are fine now, let’s dive into their migrations!&lt;/p&gt;
&lt;p&gt;Previously it was a common practice to conform your models to &lt;code&gt;Migration&lt;/code&gt; and to even let Fluent derive a default migration from your &lt;code&gt;Model&lt;/code&gt;. Though with Vapor 4 we need to actually implement those. I mean, conforming your models to migration isn‘t scalable anyways. So after all it‘s a good change.&lt;/p&gt;
&lt;p&gt;Although I am generally a fan of doing just one single change at a time, I think this a great opportunity to move your migrations into separate files inside a &lt;code&gt;Migrations&lt;/code&gt; folder, if they are still in the same file as their models.&lt;/p&gt;
&lt;p&gt;From all I know, your existing migrations will break, but they should be rather easy to migrate. Anyways I didn‘t have real migrations, just the default ones:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// &lt;/span&gt;&lt;span style="color:#75715e"&gt;MARK:&lt;/span&gt;&lt;span style="color:#75715e"&gt; - Vapor 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Fluent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DeviceToken&lt;/span&gt;: Migration { }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Alternatively manual migrations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;FluentPostgreSQL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Domain&lt;/span&gt;: Migration {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;prepare&lt;/span&gt;(on conn: PostgreSQLConnection) -&amp;gt; Future&amp;lt;Void&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; PostgreSQLDatabase.create(DeviceToken.&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;, on: conn) { builder &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; builder.field(&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;: &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;.id, isIdentifier: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; builder.field(&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;: &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;.value)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; builder.field(&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;: &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;.userID)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; builder.reference(from: &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;.userID, to: &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;User.id)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			 builder.field(&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;: &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;.createdAt)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			 builder.unique(on: &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;.value)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Some personal note:&lt;/strong&gt; I prefer to use the actual relation entities the in initializers. That way I have compiler guarantees, that their id actually exist!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So let‘s get our hands dirty and write some migrations in Vapor 4.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// &lt;/span&gt;&lt;span style="color:#75715e"&gt;MARK:&lt;/span&gt;&lt;span style="color:#75715e"&gt; - Vapor 4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Fluent&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Note: we don‘t have to specify the database type!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;CreateDeviceToken&lt;/span&gt;: Migration {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;prepare&lt;/span&gt;(on: database: Database) -&amp;gt; EventLoopFuture&amp;lt;Void&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; database.schema(&lt;span style="color:#e6db74"&gt;&amp;#34;device_tokens&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			.id() &lt;span style="color:#75715e"&gt;// &amp;lt;- oops! This is always .uuid, not .int which is not available on SQLite&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			.field(&lt;span style="color:#e6db74"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;, .string, .&lt;span style="color:#66d9ef"&gt;required&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			.field(&lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;, .uuid, .&lt;span style="color:#66d9ef"&gt;required&lt;/span&gt;, .references(&lt;span style="color:#e6db74"&gt;&amp;#34;users&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;id“))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;			.field(&amp;#34;&lt;/span&gt;created_at&lt;span style="color:#e6db74"&gt;&amp;#34;, .datetime, .required)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;			.unique(on: &amp;#34;&lt;/span&gt;value&lt;span style="color:#e6db74"&gt;&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;			.create()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;	func revert(on database: Database) -&amp;gt; EventLoopFuture&amp;lt;Void&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;		return database.schema(&amp;#34;&lt;/span&gt;device_tokens&lt;span style="color:#e6db74"&gt;&amp;#34;).delete()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At first, we notice much more strings than key paths and that all migrations have to rewritten! Though the upgrade should be comparably easy. If you have a lot migrations, you might have a though job as &lt;code&gt;revert(on:)&lt;/code&gt; is mandatory now. Hopefully we will never need to revert any migrations in production!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Another thing to note: I was using SQLite, but the &lt;code&gt;.id()&lt;/code&gt;-shorthand is hard-coded to use type &lt;code&gt;.uuid&lt;/code&gt;:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// From Fluent’s source code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;SchemaBuilder&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;() -&amp;gt; &lt;span style="color:#66d9ef"&gt;Self&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 		&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.field(.id, .uuid, .identifier(auto: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;As the models and migrations were already independent of the database technology, I decided to switch from SQLite to Postgres, especially as models are now independent of the underlaying database (you import &lt;code&gt;Fluent&lt;/code&gt; instead of &lt;code&gt;Fluent{Database}&lt;/code&gt;), this change was quite easy and did only affect &lt;code&gt;Package.swift&lt;/code&gt; and &lt;code&gt;configure.swift&lt;/code&gt;. If you don‘t, you probably need to use &lt;code&gt;.field(.id, .int, .identifier(auto: true))&lt;/code&gt; instead of &lt;code&gt;.id&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you don’t use any authentication, &lt;code&gt;configure.swift&lt;/code&gt;, all your models and migrations should compile without any errors!&lt;/p&gt;
&lt;h3 id="authentication"&gt;Authentication&lt;/h3&gt;
&lt;p&gt;As I didn’t tune my current authentication implementation, I tried to stick as close as possible to the new &lt;a href="https://docs.vapor.codes/4.0/authentication/"&gt;Vapor: Security → Authentication&lt;/a&gt; docs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Because the app will have a password less login, I didn’t use any basic authentication, which is therefore missing below. But according to the docs, it should be relatively easy to implement.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// &lt;/span&gt;&lt;span style="color:#75715e"&gt;MARK:&lt;/span&gt;&lt;span style="color:#75715e"&gt; Vapor 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Vapor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Authentication&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;User&lt;/span&gt;: TokenAuthenticatable {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/// See `TokenAuthenticatable`.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;typealias&lt;/span&gt; TokenType = UserToken
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;UserToken&lt;/span&gt;: SQLiteModel {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create&lt;/span&gt;(userID: User.ID) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; UserToken {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// generate a random 128-bit, base64-encoded string.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; string = &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; CryptoRandom().generateData(count: &lt;span style="color:#ae81ff"&gt;16&lt;/span&gt;).base64EncodedString()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// init a new `UserToken` from that string.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .&lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(string: string, userID: userID)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/// Allows this model to be used as a TokenAuthenticatable’s token.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;UserToken&lt;/span&gt;: Token {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/// See `Token`.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;typealias&lt;/span&gt; UserType = User
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/// See `Token`.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; tokenKey: WritableKeyPath&amp;lt;UserToken, String&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;.string
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/// See `Token`.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; userIDKey: WritableKeyPath&amp;lt;UserToken, User.ID&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;.userID
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Actually upgrading this, took me some time, since parts of the logic are now reversed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// &lt;/span&gt;&lt;span style="color:#75715e"&gt;MARK:&lt;/span&gt;&lt;span style="color:#75715e"&gt; - Vapor 4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Vapor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;User&lt;/span&gt;: Authenticatable {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 &lt;span style="color:#75715e"&gt;// 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;generateToken&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; UserToken {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; UserToken(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; value: [UInt8].random(count: &lt;span style="color:#ae81ff"&gt;16&lt;/span&gt;).base64,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user: &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;UserToken&lt;/span&gt;: ModelTokenAuthenticatable {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; valueKey = &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;UserToken.&lt;span style="color:#960050;background-color:#1e0010"&gt;$&lt;/span&gt;value
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 	&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; userKey = &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;UserToken.&lt;span style="color:#960050;background-color:#1e0010"&gt;$&lt;/span&gt;user
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// 5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; isValid: Bool {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 Date() &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; expiresAt ?? Date()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;First of all: drop the Authentication import. Vapor is enough.&lt;/li&gt;
&lt;li&gt;Now we mark &lt;code&gt;User&lt;/code&gt; as &lt;code&gt;Authenticatable&lt;/code&gt; instead of &lt;code&gt;TokenAuthenticatable&lt;/code&gt;. This allows you to decode it in your controllers!&lt;/li&gt;
&lt;li&gt;Essentially we moved the static &lt;code&gt;UserToken.create&lt;/code&gt; to &lt;code&gt;User.generateToken&lt;/code&gt; and updated it to use Swift’s latest APIs. Completely optional.&lt;/li&gt;
&lt;li&gt;The old &lt;code&gt;Token&lt;/code&gt; protocol will be replaced by &lt;code&gt;ModelTokenAuthenticatable&lt;/code&gt; where we get rid of the &lt;code&gt;UserType&lt;/code&gt;-typealias and rename the static constants for key paths. And we prefix them with &lt;code&gt;$&lt;/code&gt; to select the field property wrappers instead of their values.&lt;/li&gt;
&lt;li&gt;The docs proposed &lt;code&gt;isValid&lt;/code&gt; to always be &lt;code&gt;true&lt;/code&gt;, though as I kept &lt;code&gt;expiredAt&lt;/code&gt;, I chose a real implementation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At this point, your models, migrations and your configure should be free from errors.&lt;/p&gt;
&lt;p&gt;Some small changes in your routes and we can put a check at authentication. Though as these are very well documented and highly specific to your application, I’ll keep this short!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// &lt;/span&gt;&lt;span style="color:#75715e"&gt;MARK:&lt;/span&gt;&lt;span style="color:#75715e"&gt; - Vapor 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; bearer = router.grouped(User.tokenAuthMiddleware())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// &lt;/span&gt;&lt;span style="color:#75715e"&gt;MARK:&lt;/span&gt;&lt;span style="color:#75715e"&gt; - Vapor 4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; bearer = app.grouped(UserToken.authenticator()) &lt;span style="color:#75715e"&gt;// note the UserToken instead of User&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="services-and-repositories"&gt;Services and Repositories&lt;/h3&gt;
&lt;p&gt;As I didn’t use services and repositories yet, I have no more detailed help for you, but from reading the appropriate upgrading chapters &lt;a href="https://docs.vapor.codes/4.0/upgrading/#services"&gt;Upgrading Services&lt;/a&gt; and &lt;a href="https://docs.vapor.codes/4.0/upgrading/#repositories"&gt;Upgrading Repositories&lt;/a&gt;, it should be straightforward anyways.&lt;/p&gt;
&lt;h3 id="routes-and-controllers-learnings"&gt;Routes and Controllers, Learnings&lt;/h3&gt;
&lt;p&gt;Now only routes and controllers should be left. As routes and controllers are tied together, I both simultaneously. One route and method at a time.&lt;/p&gt;
&lt;p&gt;Here I won’t provide lots of actual code diffs as above, because even the list of all subtle changes is impressive and far from complete.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;instead of &lt;code&gt;Model.parameter&lt;/code&gt; in your routes name it &lt;code&gt;&amp;quot;:model_id&amp;quot;&lt;/code&gt;, in your controller replace &lt;code&gt;req.parameters.next(_:)&lt;/code&gt;with &lt;code&gt;Model.find(req.parameters.get(&amp;quot;model_id&amp;quot;), on: req.db)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;if you have route components with a &lt;code&gt;/&lt;/code&gt; inside, split them up&lt;/li&gt;
&lt;li&gt;There are less extensions on &lt;code&gt;req&lt;/code&gt;, but more vars
_ Use &lt;code&gt;req.auth.require(_:)&lt;/code&gt;instead of&lt;code&gt;req.requireAuthenticated(_:)&lt;/code&gt;
_ &lt;code&gt;DeviceToken.query(on: req.db)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;req.content.decode(_:)&lt;/code&gt; is now synchronous&lt;/li&gt;
&lt;li&gt;In queries, your key paths should end with fields (just share some $)
_ &lt;code&gt;.filter(\.$token == deviceToken)&lt;/code&gt;_&lt;code&gt;.filter(\.$user.$id == user.id)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.save(on:)&lt;/code&gt; now returns &lt;code&gt;Void&lt;/code&gt;
_ either add a new func &lt;code&gt;saveAndReturn(on database: Database) -&amp;gt; EventLoopFuture&amp;lt;Self&amp;gt;&lt;/code&gt; on &lt;code&gt;Model&lt;/code&gt;
_ or use &lt;code&gt;x.save(on: req.db).transform(to: x)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Async
_ &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;flatMap&lt;/code&gt; may not throw anymore (you can temporarily add overloads marked as &lt;code&gt;@available(_, deprecated)&lt;/code&gt;to get warnings) * there is no global&lt;code&gt;flatMap(_:_:)&lt;/code&gt;anymore, instead use&lt;code&gt;.and(_:).flatMap(_:)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Relations
_ direct access of relations like &lt;code&gt;token.user&lt;/code&gt; crashes if not loaded eagerly using &lt;code&gt;.with(\.$user)&lt;/code&gt; (&lt;code&gt;QueryBuilder&amp;lt;Model&amp;gt;.with(_:KeyPath&amp;lt;Self, Relation&amp;gt;)&lt;/code&gt;) _ for save, synchronous access use&lt;code&gt;token.$user.value?&lt;/code&gt;or for async access&lt;code&gt;token.$user.get(on:)&lt;/code&gt;or&lt;code&gt;.query(on:)&lt;/code&gt;_ directly setting&lt;code&gt;token.user =&lt;/code&gt;always fails; use&lt;code&gt;token.$user.value =&lt;/code&gt;or&lt;code&gt;token.$user.id =&lt;/code&gt;_ when setting&lt;code&gt;token.$user.value =&lt;/code&gt;, it does not update&lt;code&gt;token.$user.id&lt;/code&gt;! You need to do both, but then your data has been loaded eagerly!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="where-to-go-from-here"&gt;Where to go from here?&lt;/h2&gt;
&lt;p&gt;Most of your server should be migrated. What’s missing are view renderers, your tests (though they should not break) and more advanced feature. But most effort should be finished.&lt;/p&gt;
&lt;p&gt;Even in my small application, the upgrade required a reasonable amount of work, though it was mostly about diffing existing code and documentation. On the other hand the Vapor team did a great job to produce compile errors instead of runtime errors!
As someone who upgraded several large single page web apps, this was a bless!&lt;/p&gt;
&lt;p&gt;Did this help you? Have you found a bug? What was your upgrade like? Let’s have a chat on &lt;a href="https://mastodon.social/@vknabel"&gt;@mastodon.social@vknabel&lt;/a&gt; and feel free to &lt;a href="https://github.com/vknabel/vknabel.github.io/issues/new"&gt;open an issue on GitHub&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Swift, VS Code and you</title><link>https://vknabel.com/posts/swift-vs-code-and-you/</link><pubDate>Wed, 22 Jan 2020 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/swift-vs-code-and-you/</guid><description>&lt;p&gt;Editors like Visual Studio Code live from a wide range of extensions and customization. In contrast there are IDEs like Xcode and AppCode, which have everything set up and are ready to go. In order to provide a rich set of features, they cannot not offer the same level of flexibility. Which editor you might want to use is a highly personal decision.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;em&gt;Disclaimer:&lt;/em&gt; I am the maintainer of the extensions &lt;a href="https://marketplace.visualstudio.com/items?itemName=vknabel.vscode-swift-development-environment"&gt;Maintained Swift Development Environment&lt;/a&gt;, &lt;a href="https://www.github.com/vknabel/sourcekite"&gt;sourcekite&lt;/a&gt;, &lt;a href="https://marketplace.visualstudio.com/items?itemName=vknabel.vscode-swiftlint"&gt;SwiftLint&lt;/a&gt;, &lt;a href="https://marketplace.visualstudio.com/items?itemName=vknabel.vscode-swiftformat"&gt;SwiftFormat&lt;/a&gt;, &lt;a href="https://marketplace.visualstudio.com/items?itemName=vknabel.vscode-apple-swift-format"&gt;apple-swift-format&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="autocompletion"&gt;Autocompletion&lt;/h2&gt;
&lt;p&gt;Although Visual Studio Code ships with basic syntax highlighting, it won’t give you any suggestions or diagnostics. In order to get autocompletion working, you have two major possibilities:
Using &lt;a href="https://github.com/apple/sourcekit-lsp"&gt;Apple’s official VS Code extension&lt;/a&gt; or using &lt;a href="https://marketplace.visualstudio.com/items?itemName=vknabel.vscode-swift-development-environment"&gt;Maintained Swift Development Environment&lt;/a&gt; (or in short SDE). Apple’s extension needs to be compiled manually with Node JS, SDE has already been published to the marketplace.&lt;/p&gt;
&lt;p&gt;SDE supports two underlaying drivers: Apple’s SourceKit LSP and its own Sourcekite, while Apple does not. Support thee same degree of freedom.
If you are not sure install SDE and try both drivers out. Stick with the one that fits your needs best.&lt;/p&gt;
&lt;h3 id="sourcekit-lsp-in-action"&gt;sourcekit-lsp in action&lt;/h3&gt;
&lt;p&gt;At the moment of writing, &lt;a href="https://github.com/apple/sourcekit-lsp"&gt;sourcekit-lsp&lt;/a&gt; is more reliable in autocompletion. And as it’s Apple’s official implementation, it will further improve on the long term.
It does only support Swift Package Manager projects.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/Swift-VS-Code-and-you/SDE-sourcekit-lsp.gif" alt="SDE with sourcekit-lsp"&gt;&lt;/p&gt;
&lt;h3 id="sourcekite-in-action"&gt;sourcekite in action&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/apple/sourcekite"&gt;&lt;code&gt;sourcekite&lt;/code&gt;&lt;/a&gt; works out of the box with Swift Package Manager projects, too. Additionally you can manually configure your project structure to support standalone files, Xcode projects, Tensorflow or UIKit projects.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/Swift-VS-Code-and-you/SDE-sourcekite.gif" alt="SDE with sourcekite"&gt;&lt;/p&gt;
&lt;h2 id="debugging"&gt;Debugging&lt;/h2&gt;
&lt;p&gt;Every piece of code needs to be tested and debugged. In contrast to Xcode, you need to manually configure your debugging targets.&lt;/p&gt;
&lt;p&gt;For this purpose use LLDB Debugger. In case you need more details, read my blog post on &lt;a href="https://vknabel.com/pages/Debugging-Swift-in-VS-Code/"&gt;Debugging Swift in VS Code | Valentin Knabel&lt;/a&gt;. On Linux you might need some additional setup.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/Swift-VS-Code-and-you/LLDB.png" alt="LLDB Debugger"&gt;&lt;/p&gt;
&lt;h2 id="swiftlint"&gt;SwiftLint&lt;/h2&gt;
&lt;p&gt;You probably know SwiftLint. Wouldn&amp;rsquo;t it be great to see all its warnings and errors in VS Code? It is with &lt;a href="https://marketplace.visualstudio.com/items?itemName=vknabel.vscode-swiftlint"&gt;SwiftLint&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Of course you need to install &lt;a href="https://github.com/realm/SwiftLint#installation"&gt;SwiftLint&lt;/a&gt; manually. If you add SwiftLint as dependency to your Swift Package Manager project, the extension will prefer the local version.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/Swift-VS-Code-and-you/Swiftlint.png" alt="SwiftLint"&gt;&lt;/p&gt;
&lt;h2 id="swiftformat"&gt;SwiftFormat&lt;/h2&gt;
&lt;p&gt;If you like code formatters, you have two options.
First &lt;a href="https://marketplace.visualstudio.com/items?itemName=vknabel.vscode-swiftformat"&gt;SwiftFormat&lt;/a&gt; which uses &lt;a href="https://www.github.com/nicklockwood/SwiftFormat"&gt;nicklockwood/SwiftFormat&lt;/a&gt; under the hood and second &lt;a href="https://marketplace.visualstudio.com/items?itemName=vknabel.vscode-apple-swift-format"&gt;apple-swift-format&lt;/a&gt; which relies on &lt;a href="https://www.github.com/apple/swift-format"&gt;apple/swift-format&lt;/a&gt;.
Pick the used extension for your project&amp;rsquo;s tool to avoid unnecessary reformats and merge conflicts.&lt;/p&gt;
&lt;p&gt;In both cases, you need to install the command line tools manually: either &lt;a href="https://github.com/nicklockwood/SwiftFormat#command-line-tool"&gt;nicklockwood/SwiftFormat&lt;/a&gt; or &lt;a href="https://github.com/apple/swift-format#swift-format"&gt;apple/swift-format&lt;/a&gt;.
In both cases: if you add the formatter of your choice as dependency to your Swift Package Manager project, the extension will prefer the local version.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pro tip: enable &lt;code&gt;editor.formatOnSave&lt;/code&gt;!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/Swift-VS-Code-and-you/swift-format.gif" alt="SwiftFormat"&gt;&lt;/p&gt;
&lt;h2 id="templating"&gt;Templating&lt;/h2&gt;
&lt;p&gt;There is a lot of tooling built on top of templating languages like Stencil of Leaf. Of course there are some extensions for these languages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;a href="https://marketplace.visualstudio.com/items?itemName=svanimpe.stencil"&gt;Stencil&lt;/a&gt; for VS Code if you are using Sourcery or Kitura. No additional dependencies are are required.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/Swift-VS-Code-and-you/Stencil.png" alt="Stencil"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vapor users may be interested in &lt;a href="https://marketplace.visualstudio.com/items?itemName=Francisco.html-leaf"&gt;Leaf HTML&lt;/a&gt; for VS Code. Nothing to install here either.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://vknabel.com/images/Swift-VS-Code-and-you/Leaf.png" alt="Leaf"&gt;&lt;/p&gt;
&lt;h2 id="whats-left"&gt;What’s left&lt;/h2&gt;
&lt;p&gt;In general there are many features missing when using VS Code for Swift development.
Here is a non-exhaustive list of all features missing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No Xcode project support&lt;/li&gt;
&lt;li&gt;No Playground support&lt;/li&gt;
&lt;li&gt;No iOS debugging&lt;/li&gt;
&lt;li&gt;No refactoring, yet&lt;/li&gt;
&lt;li&gt;No Swift migrations&lt;/li&gt;
&lt;li&gt;Missing debugger snippets&lt;/li&gt;
&lt;li&gt;Missing tasks and commands for SPM&lt;/li&gt;
&lt;li&gt;Incomplete Language Servers&lt;/li&gt;
&lt;li&gt;Manual installation of dependencies&lt;/li&gt;
&lt;li&gt;Convenience plugins for external Tooling (Mint, Sourcery, Rocket, &amp;hellip;)&lt;/li&gt;
&lt;li&gt;Limited Linux support&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vknabel.com/pages/Swift-Coverage-for-VS-Code/"&gt;Code Coverage&lt;/a&gt; &lt;em&gt;updated&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What about you? Have you already tried different editors?
Do you know any additional extensions or have any questions?
Let&amp;rsquo;s get in touch on &lt;a href="https://mastodon.social/@vknabel"&gt;@mastodon.social@vknabel&lt;/a&gt; or via &lt;a href="mailto:swift-vscode-and-you@vknabel.com"&gt;email&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Debugging Swift in VS Code the old way</title><link>https://vknabel.com/posts/debugging-swift-in-vs-code/</link><pubDate>Thu, 21 Nov 2019 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/debugging-swift-in-vs-code/</guid><description>&lt;p&gt;Running and debugging your targets in Visual Studio Code is not prepared by default. Especially for us Swift developers this might come unexpected, especially in comparison to Xcode.
In VS Code we require extensions and configs for this purpose.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update from 2022:&lt;/strong&gt; the Swift Server Work Group released their own &lt;a href="https://marketplace.visualstudio.com/items?itemName=sswg.swift-lang"&gt;official VS Code extension&lt;/a&gt; which dramatically improves the debugging user experience. &lt;a href="https://vknabel.com/pages/Debugging-Swift-in-VS-Code-in-2022/"&gt;Here is the new, updated blog post&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Within this blog post, we will set up debugging for a Swift Package Manager project. As a bonus we will also prepare debugging your unit tests.&lt;/p&gt;
&lt;p&gt;First we need to install an extension as VS Code does not come with Swift debugger: &lt;a href="https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb"&gt;LLDB Debugger&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now let’s create a new project on open it in VS Code!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ swift package init --type executable
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating executable package: MyProject
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating Package.swift
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating README.md
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating .gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating Sources/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating Sources/MyProject/main.swift
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating Tests/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating Tests/LinuxMain.swift
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating Tests/MyProjectTests/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating Tests/MyProjectTests/MyProjectTests.swift
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Creating Tests/MyProjectTests/XCTestManifests.swift
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ code . &lt;span style="color:#75715e"&gt;# if not found: open -a &amp;#34;Visual Studio Code&amp;#34; .&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As we’ve prepared all prerequisites, we are ready to set up our first debugging target! In the debuggers tab in your vscode window, select the drop down and then &amp;ldquo;Add configuration&amp;hellip;&amp;rdquo;. This will now create a new file called &lt;code&gt;.vscode/launch.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Below is an example configuration supporting running executable targets, unit tests on macOS and Linux. Relevant files will be compiled using the pre-launch-tasks.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// .vscode/launch.json
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;0.2.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;configurations&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Running executables
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;lldb&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;request&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;launch&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Run your Executable&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;program&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;${workspaceFolder}/.build/debug/your-executable&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;: [],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;cwd&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;${workspaceFolder}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;preLaunchTask&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;swift-build&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Running unit tests
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;lldb&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;request&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;launch&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Debug tests on macOS&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;program&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;lt;path to xctest executable&amp;gt;&amp;#34;&lt;/span&gt;, &lt;span style="color:#75715e"&gt;//For example /Applications/Xcode.app/Contents/Developer/usr/bin/xctest
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;${workspaceFolder}/.build/debug/&amp;lt;xctest bundle name&amp;gt;.xctest&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;preLaunchTask&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;swift-build-tests&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;lldb&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;request&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;launch&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Debug tests on Linux&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;program&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;./.build/x86_64-unknown-linux/debug/YourPackageTests.xctest&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;preLaunchTask&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;swift-build-tests&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And here are the promised pre-launched-tasks:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// .vscode/tasks.json
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2.0.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;tasks&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// compile your SPM project
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;label&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;swift-build&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;shell&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;swift build&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;// for TensorFlow add -Xlinker -ltensorflow
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// compile your SPM tests
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;label&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;swift-build-tests&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;process&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;swift&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;group&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;build&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;build&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;--build-tests&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// for TensorFlow add &amp;#34;-Xlinker&amp;#34;, &amp;#34;-ltensorflow&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sadly this approach currently does not work when debugging iOS or macOS apps, but Swift Package Manager projects and CLIs work great!
I hope you enjoy your increased productivity!&lt;/p&gt;</description></item><item><title>InferIt: a Constraint Solving Package Manager</title><link>https://vknabel.com/posts/inferit-a-constraint-solving-package-manager/</link><pubDate>Tue, 15 Oct 2019 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/inferit-a-constraint-solving-package-manager/</guid><description>&lt;p&gt;The initial idea behind InferIt was to create some mixture of a constraint solver and a dependency manager: you would just tell it what to install and it would gather as much information as possible to install it.&lt;/p&gt;
&lt;p&gt;The goal is to fulfill a requirement. InferIt would then try to resolve all variables by trying to fulfill several requirements. If a requirement has been met, the value can be propagated.&lt;/p&gt;
&lt;p&gt;The current example does not include side effects or file system lookups. Though this essentially is the key behind this idea and is possible for synchronous operations.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Originally written at 2018-01-15&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="example"&gt;Example&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; c = Context()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; name = Variable&amp;lt;String&amp;gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; githubRepository = Variable&amp;lt;String&amp;gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;githubRepository&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; workingDirectory = Variable&amp;lt;Path&amp;gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;workingDirectory&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; inferConfig = Variable&amp;lt;InferConfig&amp;gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;inferConfig&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; repositoryUrl = Variable&amp;lt;URL&amp;gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;respositoryUrl&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; projectDirectory = Variable&amp;lt;Path&amp;gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;projectDirectory&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c.provide(name).by { &lt;span style="color:#e6db74"&gt;&amp;#34;vknabel/rock&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c.provide(githubRepository).when(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name.with { $0.split(separator: &lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;).count == &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; { $0 }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c.provide(workingDirectory).by { Path.current }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c.provide(inferConfig).by(InferConfig.provider)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c.provide(repositoryUrl).when(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; githubRepository,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; { URL(string: &lt;span style="color:#e6db74"&gt;&amp;#34;https://github.com/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;$0&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;.git&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;!&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c.provide(repositoryUrl).when(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name.asUrl.toBeDefined,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; { $0 }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c.provide(projectDirectory).when(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; repositoryUrl,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; workingDirectory,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; { &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; cloneRepository(from: $0, to: $1) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c.solve(requirement: projectDirectory) &lt;span style="color:#75715e"&gt;// this actually runs the application&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="implemenation"&gt;Implemenation&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Foundation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;InferConfig&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; projectDirectory: String {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;provider&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; InferConfig {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;throw&lt;/span&gt; ResolveError.notImplemented(&lt;span style="color:#66d9ef"&gt;#function&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;cloneRepository&lt;/span&gt;(from url: URL, to path: Path) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; Path {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; path &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; url.lastPathComponent
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;enum&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ResolveError&lt;/span&gt;: Error, CustomStringConvertible {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; notImplemented(String)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; undeclaredVariable(String)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; couldNotResolveVariable(String)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; conditionalCastFailed(Any, Any.&lt;span style="color:#66d9ef"&gt;Type&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; error: Error) -&amp;gt; ResolveError {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; error = error &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt;? ResolveError {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; error
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fatalError(&lt;span style="color:#e6db74"&gt;&amp;#34;An unknown error occurred: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;error&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; description: String {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .notImplemented(feature):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Not Implemented: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;feature&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .undeclaredVariable(variable):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Variable not declared: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;variable&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .couldNotResolveVariable(variable):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Variable could not be resolved: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;variable&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .conditionalCastFailed(value, to):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Type error: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;value&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt; is no &lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;to&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//: Variables don&amp;#39;t depend on a context.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;protocol&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Requirement&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; associatedtype Source
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; associatedtype Result
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; sourceName: String { &lt;span style="color:#66d9ef"&gt;get&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;apply&lt;/span&gt;(state: RequirementState&amp;lt;Source&amp;gt;) -&amp;gt; RequirementState&amp;lt;Result&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Variable&lt;/span&gt;&amp;lt;T&amp;gt;: Requirement {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; sourceName: String
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; name: String) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sourceName = name
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;apply&lt;/span&gt;(state: RequirementState&amp;lt;T&amp;gt;) -&amp;gt; RequirementState&amp;lt;T&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; state
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Requirement&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;with&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; filter: @escaping (Result) -&amp;gt; Bool) -&amp;gt; AnyRequirement&amp;lt;Source, Result&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; AnyRequirement(named: sourceName) { sourceState &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.apply(state: sourceState) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .resolved(source) &lt;span style="color:#66d9ef"&gt;where&lt;/span&gt; filter(source):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .resolved(source)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .resolved:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .failed(.notImplemented(&lt;span style="color:#e6db74"&gt;&amp;#34;validation mismatch&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .unresolved:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .unresolved
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .failed(error):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .failed(error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;flatMap&lt;/span&gt;&amp;lt;R&amp;gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; transform: @escaping (Result) -&amp;gt; RequirementState&amp;lt;R&amp;gt;) -&amp;gt; AnyRequirement&amp;lt;Source, R&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; AnyRequirement(named: sourceName) { sourceState &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.apply(state: sourceState) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .resolved(source):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; transform(source) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .resolved(result):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .resolved(result)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .failed(error):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .failed(error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .unresolved:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .unresolved
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .failed(error):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .failed(error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .unresolved:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .unresolved
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;map&lt;/span&gt;&amp;lt;R&amp;gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; transform: @escaping (Result) -&amp;gt; R) -&amp;gt; AnyRequirement&amp;lt;Source, R&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; AnyRequirement(named: sourceName) { sourceState &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.apply(state: sourceState) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .resolved(source):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .resolved(transform(source))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .failed(error):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .failed(error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .unresolved:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .unresolved
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AnyRequirement&lt;/span&gt;&amp;lt;Source, Result&amp;gt;: Requirement {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;typealias&lt;/span&gt; Transformation = (RequirementState&amp;lt;Source&amp;gt;) -&amp;gt; RequirementState&amp;lt;Result&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; sourceName: String
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; transformation: Transformation
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(named name: String, transform: @escaping Transformation) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sourceName = name
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; transformation = transform
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;apply&lt;/span&gt;(state: RequirementState&amp;lt;Source&amp;gt;) -&amp;gt; RequirementState&amp;lt;Result&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; transformation(state)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;enum&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;RequirementState&lt;/span&gt;&amp;lt;Value&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; resolved(Value)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; unresolved
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; failed(ResolveError)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(catching factory: () &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; Value) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt; = .resolved(&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; factory())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt; = .failed(.from(error))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(catching factory: () &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; RequirementState&amp;lt;Value&amp;gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt; = &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; factory()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt; = .failed(.from(error))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;map&lt;/span&gt;&amp;lt;R&amp;gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; transform: (Value) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; R) &lt;span style="color:#66d9ef"&gt;rethrows&lt;/span&gt; -&amp;gt; RequirementState&amp;lt;R&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .resolved(value):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; .resolved(transform(value))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .unresolved:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .unresolved
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .failed(error):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .failed(error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;flatMap&lt;/span&gt;&amp;lt;R&amp;gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; transform: (Value) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; RequirementState&amp;lt;R&amp;gt;) &lt;span style="color:#66d9ef"&gt;rethrows&lt;/span&gt; -&amp;gt; RequirementState&amp;lt;R&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .resolved(value):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; transform(value)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .unresolved:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .unresolved
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .failed(error):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .failed(error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;internal&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;casted&lt;/span&gt;&amp;lt;T&amp;gt;(to &lt;span style="color:#66d9ef"&gt;_&lt;/span&gt;: T.&lt;span style="color:#66d9ef"&gt;Type&lt;/span&gt; = T.&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;) -&amp;gt; RequirementState&amp;lt;T&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .resolved(value):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; value = value &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt;? T {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .resolved(value)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .failed(.conditionalCastFailed(value, T.&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .unresolved:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .unresolved
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .failed(error):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .failed(error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Variable&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;where&lt;/span&gt; T == String {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; asUrl: Variable&amp;lt;URL?&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; Variable&amp;lt;URL?&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(sourceName) &lt;span style="color:#75715e"&gt;// &lt;/span&gt;&lt;span style="color:#75715e"&gt;TODO:&lt;/span&gt;&lt;span style="color:#75715e"&gt; AnyRequirement&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Variable&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;where&lt;/span&gt; T == URL? {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; toBeDefined: Variable&amp;lt;URL&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; Variable&amp;lt;URL&amp;gt;(sourceName) &lt;span style="color:#75715e"&gt;// &lt;/span&gt;&lt;span style="color:#75715e"&gt;TODO:&lt;/span&gt;&lt;span style="color:#75715e"&gt; AnyRequirement&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Provider&lt;/span&gt;&amp;lt;T&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; context: Context
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; variable: Variable&amp;lt;T&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; variable: Variable&amp;lt;T&amp;gt;, &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; context: Context) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.context = context
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.variable = variable
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;by&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; provider: @escaping () &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; T) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context.bind(variable: variable, catching: provider)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;when&lt;/span&gt;&amp;lt;R: Requirement&amp;gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; precondition: R, &lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; resolve: @escaping (R.Result) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; T) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context.bind(variable: variable, to: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; solution: RequirementState&amp;lt;R.Result&amp;gt; = &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.context.solve(requirement: precondition)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; solution.flatMap { (value: R.Result) -&amp;gt; RequirementState&amp;lt;T&amp;gt; &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; RequirementState { &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; resolve(value) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;when&lt;/span&gt;&amp;lt;R0: Requirement, R1: Requirement&amp;gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; precondition0: R0, &lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; precondition1: R1, &lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; resolve: @escaping (R0.Result, R1.Result) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; T) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context.bind(variable: variable, to: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; solution0 = { &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.context.solve(requirement: precondition0) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; solution1 = { &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.context.solve(requirement: precondition1) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; solution0().flatMap { (value0: R0.Result) -&amp;gt; RequirementState&amp;lt;T&amp;gt; &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; solution1().flatMap { (value1: R1.Result) &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; RequirementState { &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; resolve(value0, value1) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fileprivate &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;VariableBindings&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;typealias&lt;/span&gt; Resolver = () -&amp;gt; RequirementState&amp;lt;Any&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; resolvers: [Resolver] = []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; state: RequirementState&amp;lt;Any&amp;gt; = .unresolved
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Context&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; bindings: [String: VariableBindings] = [:]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;provide&lt;/span&gt;&amp;lt;T&amp;gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; variable: Variable&amp;lt;T&amp;gt;) -&amp;gt; Provider&amp;lt;T&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; Provider(&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;: variable, &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;internal&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;bind&lt;/span&gt;&amp;lt;T&amp;gt;(variable: Variable&amp;lt;T&amp;gt;, catching resolver: @escaping () &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; T) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bind(variable: variable) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; RequirementState(catching: resolver)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;internal&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;bind&lt;/span&gt;&amp;lt;T&amp;gt;(variable: Variable&amp;lt;T&amp;gt;, to resolver: @escaping () -&amp;gt; RequirementState&amp;lt;T&amp;gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; binding = bindings[variable.sourceName] ?? VariableBindings()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; binding.resolvers.append({ resolver().casted() })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bindings[variable.sourceName] = binding
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(resolver: VariableBindings.Resolver, &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; sourceName: String) -&amp;gt; RequirementState&amp;lt;Any&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; result = resolver()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; result {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .failed(error):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;[FAILED] &lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;sourceName&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt; with &lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;error&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; .resolved(value):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;[SOLVED] &lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;sourceName&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt; as &lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;String&lt;span style="color:#e6db74"&gt;(&lt;/span&gt;reflecting: value&lt;span style="color:#e6db74"&gt;))&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .unresolved:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; result
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;solvePure&lt;/span&gt;(sourceName: String) -&amp;gt; RequirementState&amp;lt;Any&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;guard&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; binding = bindings[sourceName] &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;[FAILED] No rules for &lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;sourceName&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .failed(.undeclaredVariable(sourceName))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; binding.state {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .unresolved:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; binding.resolvers.reduce(.failed(.couldNotResolveVariable(sourceName))) { result, resolver &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .unresolved = result {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; resolver()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .failed(.couldNotResolveVariable(sourceName)) = result {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; log(resolver: resolver, &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;: sourceName)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; result
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; result:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; result
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;internal&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;solve&lt;/span&gt;(sourceName: String) -&amp;gt; RequirementState&amp;lt;Any&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; result = solvePure(sourceName: sourceName)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bindings[sourceName]?.state = result
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; result
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;solve&lt;/span&gt;&amp;lt;R: Requirement&amp;gt;(requirement: R) -&amp;gt; RequirementState&amp;lt;R.Result&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; variableState: RequirementState&amp;lt;R.Source&amp;gt; = solve(sourceName: requirement.sourceName).casted()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; requirement.apply(state: variableState)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Thus I still believe this to be an interesting topic, the amount of variables and requirements seem to explode and to be hard to debug without any additional tools.&lt;/p&gt;
&lt;p&gt;Furthermore this constraint solver still operates synchronously. To be production ready this needs to be implemented asynchrously, which would then enhance.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="./playground.zip"&gt;download this playground&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item><item><title>ReactifSwift: async function composition</title><link>https://vknabel.com/posts/reactifswift-async-function-composition/</link><pubDate>Tue, 15 Oct 2019 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/reactifswift-async-function-composition/</guid><description>&lt;p&gt;Async operators &lt;code&gt;debounce&lt;/code&gt;, &lt;code&gt;throttle&lt;/code&gt; or &lt;code&gt;delay&lt;/code&gt; that functional reactive programming libraries as &lt;code&gt;RxSwift&lt;/code&gt; and &lt;code&gt;ReactiveSwift&lt;/code&gt; provide are super useful and expressive. Though their biggest benefit lays within composability.&lt;/p&gt;
&lt;p&gt;This playground tries to achieve the same using plain functions with little help of a global scheduler for a greater testing experience. For better composability it relies on &lt;a href="https://github.com/pointfreeco/swift-overture"&gt;Overture&lt;/a&gt; in version 0.2.0.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Originally written at 2018-06-10&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="example"&gt;Example&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; count = &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; countChars = with({ count &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; $0 }, pipe(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; map(&lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;(&lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;String.count)),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; throttle(time: &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; time = TestingScheduler()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ReactifCurrent.scheduler = time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;countChars(&lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time.tick(&lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;countChars(&lt;span style="color:#e6db74"&gt;&amp;#34;B&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time.tick(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;countChars(&lt;span style="color:#e6db74"&gt;&amp;#34;Abc&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;count &lt;span style="color:#75715e"&gt;// 4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="implementation"&gt;Implementation&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// import Overture&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Foundation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;typealias&lt;/span&gt; Unary&amp;lt;A&amp;gt; = (A) -&amp;gt; Void
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;protocol&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Invalidatable&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;invalidate&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Timer&lt;/span&gt;: Invalidatable {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;protocol&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Scheduler&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;now&lt;/span&gt;() -&amp;gt; Date
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;delay&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; time: TimeInterval, &lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; f: @escaping () -&amp;gt; Void) -&amp;gt; Invalidatable
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestingScheduler&lt;/span&gt;: Scheduler {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; delayed: [FakeTimer] = []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; currentInterval: TimeInterval = &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;() {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;FakeTimer&lt;/span&gt;: Invalidatable {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; date: Date
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; fire: (() -&amp;gt; Void)?
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(date: Date, fire: @escaping () -&amp;gt; Void) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.fire = fire
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.date = date
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; isValid: Bool {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; fire &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;tick&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; now: Date) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; fire = fire, now &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.date {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fire()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;invalidate&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.fire = &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;tick&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; interval: TimeInterval) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.currentInterval &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; interval
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; currently = now()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.delayed.forEach { $0.tick(currently) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.delayed = &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.delayed.filter { $0.isValid }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;now&lt;/span&gt;() -&amp;gt; Date {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; Date(timeIntervalSince1970: currentInterval)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;delay&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; interval: TimeInterval, &lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; f: @escaping () -&amp;gt; Void) -&amp;gt; Invalidatable {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; timer = FakeTimer(date: now().addingTimeInterval(interval), fire: f)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delayed.append(timer)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; timer
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TimeScheduler&lt;/span&gt;: Scheduler {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;() {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;now&lt;/span&gt;() -&amp;gt; Date {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; Date()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;delay&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; interval: TimeInterval, &lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; f: @escaping () -&amp;gt; Void) -&amp;gt; Invalidatable {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; Timer(timeInterval: interval, repeats: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;) { &lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ReactifRuntimeContext&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; scheduler: Scheduler = TimeScheduler()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; ReactifCurrent = ReactifRuntimeContext()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Sink&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;typealias&lt;/span&gt; Completion = () -&amp;gt; Void
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; call: (@escaping (@escaping Completion) -&amp;gt; Void) -&amp;gt; Bool
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; call: @escaping (@escaping (@escaping Completion) -&amp;gt; Void) -&amp;gt; Bool) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.call = call
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Sink&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;lock&lt;/span&gt;(name: String? = &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;) -&amp;gt; Sink {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; lock = NSLock()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lock.name = name
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; Sink { f &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; lock.&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f(lock.unlock)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;recursiveLock&lt;/span&gt;(name: String? = &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;) -&amp;gt; Sink {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; lock = NSRecursiveLock()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lock.name = name
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; Sink { f &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; lock.&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f(lock.unlock)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;synchronous&lt;/span&gt;() -&amp;gt; Sink {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; Sink { f &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f({})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;filter&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; includes: @escaping () -&amp;gt; Bool) -&amp;gt; (Sink) -&amp;gt; Sink {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { (sink: Sink) &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Sink { (f: @escaping Unary&amp;lt;Sink.Completion&amp;gt;) &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; includes() &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sink.call(f)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;reschedule&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; transform: @escaping (@escaping Unary&amp;lt;Sink.Completion&amp;gt;, @escaping Sink.Completion) -&amp;gt; Void) -&amp;gt; (Sink) -&amp;gt; Sink {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { (sink: Sink) &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Sink { (f: @escaping Unary&amp;lt;Sink.Completion&amp;gt;) &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sink.call { complete &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; transform(f, complete)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;delay&lt;/span&gt;(time: TimeInterval) -&amp;gt; (Sink) -&amp;gt; Sink {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; reschedule { f, completion &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ReactifCurrent.scheduler.delay(time) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f(completion)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;extend&lt;/span&gt;(time: TimeInterval) -&amp;gt; (Sink) -&amp;gt; Sink {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; reschedule { f, completion &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ReactifCurrent.scheduler.delay(time, completion)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;debounce&lt;/span&gt;(time: TimeInterval) -&amp;gt; (Sink) -&amp;gt; Sink {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; currentAttempt: Invalidatable?
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; reschedule { f, completion &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; currentAttempt?.invalidate()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; currentAttempt = ReactifCurrent.scheduler.delay(time) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f(completion)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;throttle&lt;/span&gt;(time: TimeInterval) -&amp;gt; (Sink) -&amp;gt; Sink {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; lastInvocation: Date?
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; filter {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; now = ReactifCurrent.scheduler.now()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;defer&lt;/span&gt; { lastInvocation = now }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; lastInvocation = lastInvocation, now.timeIntervalSince(lastInvocation) &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; time {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;schedule&lt;/span&gt;&amp;lt;A&amp;gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; factory: @escaping &lt;span style="color:#66d9ef"&gt;@autoclosure&lt;/span&gt; () -&amp;gt; Sink) -&amp;gt; (@escaping Unary&amp;lt;A&amp;gt;) -&amp;gt; Unary&amp;lt;A&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { f &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; sink = factory()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { a &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; sink.call { f(a);$0() } }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// &lt;/span&gt;&lt;span style="color:#75715e"&gt;TODO:&lt;/span&gt;&lt;span style="color:#75715e"&gt; throttle using time after completion instead of starting time&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;throttle&lt;/span&gt;&amp;lt;A&amp;gt;(time: TimeInterval) -&amp;gt; (@escaping Unary&amp;lt;A&amp;gt;) -&amp;gt; Unary&amp;lt;A&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; schedule(with(.synchronous(), throttle(time: time)))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;exhaustA&lt;/span&gt;&amp;lt;A&amp;gt;(ending time: TimeInterval? = &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;) -&amp;gt; (@escaping Unary&amp;lt;A&amp;gt;) -&amp;gt; Unary&amp;lt;A&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; schedule(with(.recursiveLock(), extend(time: &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;exhaust&lt;/span&gt;&amp;lt;A&amp;gt;(ending time: TimeInterval? = &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;) -&amp;gt; (@escaping Unary&amp;lt;A&amp;gt;) -&amp;gt; Unary&amp;lt;A&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { f &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; lock = NSLock()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { a &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; lock.&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f(a)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; time = time {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ReactifCurrent.scheduler.delay(time, lock.unlock)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lock.unlock()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;map&lt;/span&gt;&amp;lt;A, B&amp;gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; transform: @escaping (A) -&amp;gt; B) -&amp;gt; (@escaping Unary&amp;lt;B&amp;gt;) -&amp;gt; Unary&amp;lt;A&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { f &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; pipe(transform, f) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;filter&lt;/span&gt;&amp;lt;A&amp;gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; include: @escaping (A) -&amp;gt; Bool) -&amp;gt; (@escaping Unary&amp;lt;A&amp;gt;) -&amp;gt; Unary&amp;lt;A&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { f &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { a &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; include(a) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f(a)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;debounce&lt;/span&gt;&amp;lt;A&amp;gt;(time: TimeInterval) -&amp;gt; (@escaping Unary&amp;lt;A&amp;gt;) -&amp;gt; Unary&amp;lt;A&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { f &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; currentAttempt: Invalidatable?
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { a &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; currentAttempt?.invalidate()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; currentAttempt = ReactifCurrent.scheduler.delay(time) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f(a)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I think this is a quite cool idea for 240 lines of code. If just a few functions are required and using a FRP library would be too much just for a few functions, this might be a lightweight, but valuable alternative.&lt;/p&gt;
&lt;p&gt;Though as we now have &lt;code&gt;Combine&lt;/code&gt; by Apple, this is more likely to be a niche idea.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="./playground.zip"&gt;download this playground&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Swifducks: simple Redux store with multiple reducers</title><link>https://vknabel.com/posts/swifducks-simple-redux-store-with-multiple-reducers/</link><pubDate>Tue, 15 Oct 2019 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/swifducks-simple-redux-store-with-multiple-reducers/</guid><description>&lt;p&gt;A simple example implementation of the Redux pattern with multiple reducers and listeners.&lt;/p&gt;
&lt;p&gt;The name is derived from Swift + Redux = 🏎🦆&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Originally written at 2018-06-10&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="definitions"&gt;Definitions&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Store&lt;/span&gt;&amp;lt;State, Action&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;typealias&lt;/span&gt; Reducer = (Action, &lt;span style="color:#66d9ef"&gt;inout&lt;/span&gt; State) -&amp;gt; Void
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;set&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; state: State
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; reducers: [Reducer] = []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; callbacks: [Weak&amp;lt;Listener&amp;lt;State&amp;gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;] = []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(initial state: State) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.state = state
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;select&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; changes: @escaping (State) -&amp;gt; Void) -&amp;gt; Any {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; subscription = Listener(on: changes)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; callbacks.append(Weak(subscription))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; subscription
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;reduce&lt;/span&gt;(with reducer: @escaping Reducer) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; reducers.append(reducer)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dispatch&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; action: Action) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; state = reducers.reduce(into: state) { intermediate, reducer &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; reducer(action, &amp;amp;intermediate)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; callbacks = callbacks
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .compactMap { $0.value }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .map(Weak.&lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; callbacks.forEach { $0.value?.onChange(state) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Store&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;convenience&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(initial state: State, reducer: @escaping Reducer) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.&lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(initial: state)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; reduce(with: reducer)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;internal&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Weak&lt;/span&gt;&amp;lt;A: AnyObject&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;weak&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; value: A?
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; value: A?) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;.value = value
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;internal&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Listener&lt;/span&gt;&amp;lt;State&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; onChange: (State) -&amp;gt; Void
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(on change: @escaping (State) -&amp;gt; Void) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; onChange = change
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="example-usage"&gt;Example Usage&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;enum&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;IntAction&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; increase
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; decrease
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; root = Store&amp;lt;Int, IntAction&amp;gt;(initial: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) { action, state &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; action {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .increase:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; state &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .decrease:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; state &lt;span style="color:#f92672"&gt;-=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; sideEffect = &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; listener: Any? = root.select {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sideEffect = $0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;root.dispatch(.increase)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;root.state &lt;span style="color:#75715e"&gt;// will be 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sideEffect &lt;span style="color:#75715e"&gt;// will be 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;listener = &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;root.dispatch(.decrease)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;root.state &lt;span style="color:#75715e"&gt;// will be 0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sideEffect &lt;span style="color:#75715e"&gt;// will be 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Without explicit support for modularity, supporting multiple reducers as above is probably not needed. Instead some composability functions should be used.&lt;/p&gt;
&lt;p&gt;On the other hand: it is obvious, that are implementing the Redux pattern and embracing unidirectional data flow is relatively easy.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="./playground.zip"&gt;download this playground&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Archery 0.3.0 released</title><link>https://vknabel.com/posts/archery-0-3-0-released/</link><pubDate>Sat, 12 Oct 2019 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/archery-0-3-0-released/</guid><description>&lt;p&gt;&lt;em&gt;Archery&lt;/em&gt; is about doing something with your project’s metadata. The new version 0.3.0 puts everything on steroids and allows you to script your metadata.
A detailed overview of all changes can be found on &lt;a href="https://github.com/vknabel/Archery/blob/master/CHANGELOG.md#021"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="archerfile-loaders"&gt;Archerfile Loaders&lt;/h2&gt;
&lt;p&gt;At first you will notice the new option to load additional contents into your Archerfile an incredibly open field of new possibilities.
The most obvious use case is to collect metadata from multiple configuration files.
At a second look you can even script the generation of your metadata.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;loaders&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;cat Metadata/*.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;echo today: $(date +%Y-%m-%d)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;mirror file https://raw.githubusercontent.com/vknabel/Archery/0.3.0/Examples/MapSwiftTargetsToScripts.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But caution: your loader will be executed with every invocation of Archery. Make sure they run fast enough!&lt;/p&gt;
&lt;h2 id="sequence-of-scripts"&gt;Sequence of Scripts&lt;/h2&gt;
&lt;p&gt;Often one script requires multiple steps. In earlier versions we used &lt;code&gt;vknabel/ArcheryArrow&lt;/code&gt; for this purpose. Now we can be much more direct:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;scripts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;test&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;host, swift5.0, swift5.1]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;scripts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;host&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;swift test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;swift5.0&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;docker ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;swift5.1&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;docker ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="environment-variables"&gt;Environment variables&lt;/h2&gt;
&lt;p&gt;Let’s assume we have &lt;code&gt;Metadata/Scripts.yml&lt;/code&gt; with the following contents:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;scripts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;dump&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;echo $ARCHERY_METADATA&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In here we use our second new feature: using env variables to access our Archerfile contents!&lt;/p&gt;
&lt;p&gt;On execution you will receive the fully loaded metadata as JSON!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ archery dump
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;🏹 Running scripts ▶︎ dump
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;{&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;loaders&amp;#34;&lt;/span&gt;:&lt;span style="color:#f92672"&gt;[]&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;scripts&amp;#34;&lt;/span&gt;:&lt;span style="color:#f92672"&gt;{&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;dump&amp;#34;&lt;/span&gt;:&lt;span style="color:#f92672"&gt;{&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#34;echo &lt;/span&gt;$ARCHERY_METADATA&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;}}&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;today&amp;#34;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#34;2019-10-10&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A full list of all exposed environment variables visit &lt;a href="https://github.com/vknabel/Archery"&gt;Archery on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To accomplish this the whole underlying script execution mechanism has been rebuild from scratch.
Previously scripts relied on separate SwiftPM repositories, called Arrows, which would be installed by Archery using Mint. For many use cases this mechanism required too much ceremony around it. For example the &lt;code&gt;vknabel/BashArrow&lt;/code&gt; would have to be compiled before running your actual script.
Instead we focus on the easiest and fastest scripting mechanism: bash scripts.
Though this will not break: Archery will generate the correct Mint commands and will execute your arrows as before if you have Mint installed.&lt;/p&gt;
&lt;p&gt;Aside from the speed up for bash scripts, the &lt;code&gt;vknabel/ArcheryArrow&lt;/code&gt; will come pre-shipped too and doesn‘t take a whole Swift build to proceed.&lt;/p&gt;
&lt;h2 id="faq-and-examples"&gt;FAQ and Examples&lt;/h2&gt;
&lt;h3 id="how-do-i-update-to-030"&gt;How do I update to 0.3.0?&lt;/h3&gt;
&lt;p&gt;Your existing Archerfile still works the same as before. So nothing to do on this side!&lt;/p&gt;
&lt;p&gt;If you currently use the arrow syntax, make sure to have Mint installed as it moved from an internal to an external dependency. Also make sure to use Swift 5 as Swift 4 support has been dropped.&lt;/p&gt;
&lt;h3 id="is-there-a-new-plugin-concept"&gt;Is there a new plugin concept?&lt;/h3&gt;
&lt;p&gt;Previously Mint was the only mechanism to implement plugins. Now as we focus on more general solutions, the new mechanism needs to be more general, too.&lt;/p&gt;
&lt;p&gt;Though more general problems require more solutions or even more specializations. Mint will still be available as any other command line tool. To fill the more general gap, I created &lt;a href="https://github.com/vknabel/mirror-sh"&gt;Mirror&lt;/a&gt; which tries to solve similar problems on a more general basis.&lt;/p&gt;
&lt;h3 id="how-can-i-share-and-reuse-parts-of-my-metadata"&gt;How can I share and reuse parts of my metadata?&lt;/h3&gt;
&lt;p&gt;Now as you can script your metadata, you can also easily reuse parts of your configuration.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;loaders&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;mirror file https://raw.githubusercontent.com/vknabel/Archery/0.3.0/Examples/TestingOnMultiplePlatforms.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this example we used &lt;a href="https://github.com/vknabel/mirror-sh"&gt;Mirror&lt;/a&gt; from above as &lt;code&gt;curl&lt;/code&gt; would always download the file over and over. Mirror does it only once.&lt;/p&gt;
&lt;h3 id="project-bash"&gt;Project Bash&lt;/h3&gt;
&lt;p&gt;Some larger projects might already require their own CLI. Smaller ones only have a few helpers around.
The following script exposes
Linking all scripts into your current bash session.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Archerfile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;scripts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;alias&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;node Scripts/alias.js&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Scripts/alias.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scripts&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;JSON&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;parse&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;process&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;env&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ARCHERY_METADATA&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;scripts&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Object.&lt;span style="color:#a6e22e"&gt;keys&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;scripts&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;filter&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;!==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;alias&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;forEach&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;command&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;process&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;env&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ARCHERY&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;function &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;command&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;$@&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At the end we are capable of linking all helpers directly into our current bash or zsh session!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ eval &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;/Users/vknabel/Developer/vknabel/Archery/.build/debug/archery alias&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="how-can-i-test-on-multiple-operating-systems"&gt;How can I test on multiple operating systems?&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;scripts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;test&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;help&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Runs tests on your current host system, but also on supported linux versions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;host, swift5.0, swift5.1]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;scripts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;host&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;swift test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;swift5.0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;env&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;SWIFT_VERSION&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;command&lt;/span&gt;: |-&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; export CONTAINER=$(docker create —rm —workdir /archery swift:$SWIFT_VERSION swift test)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; docker cp . $CONTAINER:/archery
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; docker start —attach $CONTAINER&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;swift5.1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;env&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;SWIFT_VERSION&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;command&lt;/span&gt;: |-&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; export CONTAINER=$(docker create --rm --workdir /archery swift:$SWIFT_VERSION swift test)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; docker cp . $CONTAINER:/archery
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; docker start —attach $CONTAINER&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="how-to-override-with-private-configs"&gt;How to override with private configs?&lt;/h3&gt;
&lt;p&gt;Overriding data with Archery is quite easy: load the same data structure with different contents. If you wish some configs to be private, just put them into your &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;loaders&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# load default configs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;cat Config.default.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# override with private configs if existing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;cat Config.private.yml 2&amp;gt; /dev/null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using the same mechanism you can even override a few scripts, e.g. if you prefer to dockerize your dependencies.&lt;/p&gt;
&lt;h3 id="any-questions-left"&gt;Any questions left?&lt;/h3&gt;
&lt;p&gt;If you have any questions left or ideas for improvements, create an issue for &lt;a href="https://github.com/vknabel/Archery"&gt;Archery on GitHub&lt;/a&gt; or get in contact with &lt;a href="https://mastodon.social/@vknabel"&gt;@mastodon.social@vknabel&lt;/a&gt;. Thanks for reading!&lt;/p&gt;</description></item><item><title>SDE 2.7.0 released</title><link>https://vknabel.com/posts/sde-2-7-0-released/</link><pubDate>Thu, 11 Apr 2019 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/sde-2-7-0-released/</guid><description>&lt;p&gt;Today I released the new &lt;a href="https://github.com/vknabel/vscode-swift-development-environment/releases/tag/2.7.0"&gt;2.7.0 update to SDE for VS Code&lt;/a&gt; and the companion project sourcekite has been updated, too.&lt;/p&gt;
&lt;p&gt;The new sourcekite &lt;a href="https://github.com/vknabel/sourcekite/releases/tag/0.5.0"&gt;0.5.0&lt;/a&gt; now supports Swift 5, but drops support for Swift 3. If you still need support for Swift 3.1, I also tagged &lt;a href="https://github.com/vknabel/sourcekite/releases/tag/0.4.2"&gt;0.4.2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Since 2.6.0, SDE already supported Apple‘s official &lt;a href="https://github.com/apple/sourcekit-lsp"&gt;sourcekit-lsp&lt;/a&gt; by using the &lt;code&gt;&amp;quot;sde.languageServerMode&amp;quot;: &amp;quot;langserver&amp;quot;&lt;/code&gt; and the &lt;code&gt;swift.languageServerPath&lt;/code&gt; setting.
As announced in &lt;a href="https://www.vknabel.com/pages/Apples-SourceKit-LSP-and-SDE-Roadmap/"&gt;Apples SourceKit-LSP and SDE Roadmap&lt;/a&gt; SDE 2.7.0 now explicitly mirrors official SourceKit-LSP settings like &lt;code&gt;sourcekit-lsp.serverPath&lt;/code&gt; and &lt;code&gt;sourcekit-lsp.toolchainPath&lt;/code&gt;. These settings will only be respected when explicitly setting &lt;code&gt;&amp;quot;sde.languageServerMode&amp;quot;: &amp;quot;sourcekit-lsp&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I‘d suggest you to configure both &lt;code&gt;sourcekit-lsp&lt;/code&gt; and &lt;code&gt;sourcekite&lt;/code&gt; configs. Depending on your project, you may swap out the actually used LSP by updating &lt;code&gt;sde.languageServerMode&lt;/code&gt;. Both LSP implementations have different benefits. Apple‘s SourceKit-LSP is easier to install, under active development and more robust. On the other hand SDE‘s LSP is more flexible in terms of settings, works for standalone Swift files, Xcode projects (through manual configuration) and handles modules differently.&lt;/p&gt;
&lt;h2 id="upgrade-if-you-use-sourcekit-lsp"&gt;Upgrade if you use SourceKit-LSP&lt;/h2&gt;
&lt;p&gt;In your settings replace your &lt;code&gt;&amp;quot;sde.languageServerMode&amp;quot;: &amp;quot;langserver&amp;quot;&lt;/code&gt; with &lt;code&gt;&amp;quot;sde.languageServerMode&amp;quot;: &amp;quot;sourcekit-lsp&amp;quot;&lt;/code&gt; and &lt;code&gt;swift.languageServerPath&lt;/code&gt; with &lt;code&gt;sourcekit-lsp.serverPath&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That‘s it.&lt;/p&gt;
&lt;h2 id="upgrade-if-you-use-sourcekite"&gt;Upgrade if you use Sourcekite&lt;/h2&gt;
&lt;p&gt;Clone the latest version of sourcekite and open the repo within your terminal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On Linux:&lt;/strong&gt; assuming you have already linked your &lt;code&gt;sourcekitdInProc&lt;/code&gt;, you simply need to run &lt;code&gt;swift build -Xlinker -l:sourcekitdInProc -c release&lt;/code&gt; and overwrite your old binary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On macOS:&lt;/strong&gt; Run &lt;code&gt;make install LIB_DIR=/Library/Developer/Toolchains/swift-latest.xctoolchain/usr/lib&lt;/code&gt; if you have multiple Swift toolchains installed and &lt;code&gt;make install LIB_DIR=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib&lt;/code&gt; otherwise to install the new build to &lt;code&gt;/usr/local/bin/sourcekite&lt;/code&gt;. If you used a different path, override it by adding &lt;code&gt;PREFIX=&amp;lt;your-path&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Your &lt;code&gt;swift.path.sourcekite&lt;/code&gt; should still be valid. Make sure to restart your VS Code before further development.&lt;/p&gt;
&lt;h2 id="upgrade-if-you-use-ryan-loveletts-langserverswift"&gt;Upgrade if you use Ryan Lovelett‘s LangserverSwift&lt;/h2&gt;
&lt;p&gt;You have no upgrade steps to do, but you are now able to additionally use SourceKit-LSP.&lt;/p&gt;
&lt;p&gt;Install SourceKit-LSP and let the global &lt;code&gt;sourcekit-lsp.serverPath&lt;/code&gt; point to the release binary. Now you can swap your language server inside different projects by overriding &lt;code&gt;sde.languageServerMode&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Though as Ryan‘s Langserver is no longer maintained, I‘d recommend you to set your default &lt;code&gt;sde.languageServerMode&lt;/code&gt; to &lt;code&gt;sourcekite&lt;/code&gt; or &lt;code&gt;sourcekit-lsp&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Apple’s SourceKit LSP and SDE Roadmap</title><link>https://vknabel.com/posts/apples-sourcekit-lsp-and-sde-roadmap/</link><pubDate>Fri, 16 Nov 2018 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/apples-sourcekit-lsp-and-sde-roadmap/</guid><description>&lt;p&gt;Apple recently &lt;a href="https://forums.swift.org/t/new-lsp-language-service-supporting-swift-and-c-family-languages-for-any-editor-and-platform/17024/"&gt;announced&lt;/a&gt; to develop a language server for Swift and C-family languages. Or said more clearly: Apple started development to support every editor implementing the &lt;a href="https://langserver.org/"&gt;language server protocol&lt;/a&gt; like VS Code, Sublime Text, Jet Brains‘ IDEs and Atom.&lt;/p&gt;
&lt;p&gt;Later they published the &lt;a href="https://github.com/apple/sourcekit-lsp"&gt;source code in GitHub &lt;/a&gt; including &lt;a href="https://github.com/apple/sourcekit-lsp/tree/master/Editors"&gt;support for VS Code and Sublime Text&lt;/a&gt;. It will work on Linux but is currently limited to Swift snapshots and the VS Code extension hasn’t been published yet.&lt;/p&gt;
&lt;p&gt;I really love Apple’s decision to take over this responsibility. I think they will be able do a much better job than we as a community have done so far. There were quite a few LSPs for Swift, AFAIK all of them started as an experiment. Most of them got stuck at Swift 3.x and never supported Swift 4. &lt;a href="https://github.com/vknabel/vscode-swift-development-environment"&gt;SDE for VS Code&lt;/a&gt; was one of them until I started maintaining it.&lt;/p&gt;
&lt;h2 id="the-future-of-sde"&gt;The future of SDE&lt;/h2&gt;
&lt;p&gt;That being said, I feel responsible to inform users (and potential ones) about my plans regarding SDE. TL;DR I will still continue to maintain SDE and fix issues if I can. And, of course, I‘d be happy for any feedback and bug reports.&lt;/p&gt;
&lt;p&gt;I strongly believe Apple‘s SourceKit LSP will be the way to go once it is stable and supports stable Swift releases. My future goal for SDE is to smoothly prepare the transition to it and Apple’s still unpublished VS Code extension.&lt;/p&gt;
&lt;p&gt;As SDE comes with its own LSP implementation, I already released &lt;a href="https://github.com/vknabel/vscode-swift-development-environment/releases/tag/2.6.0"&gt;SDE 2.6.0&lt;/a&gt; to support alternative language server implementations like &lt;a href="https://github.com/RLovelett/langserver-swift"&gt;RLovelett‘s LangserverSwift&lt;/a&gt; and Apple’s (back then: unpublished) SourceKit LSP.&lt;/p&gt;
&lt;p&gt;The following roadmap isn’t final and may actually differ:
At first I will mirror all &lt;a href="https://github.com/apple/sourcekit-lsp/blob/master/Editors/vscode/package.json"&gt;settings of the VS Code extension&lt;/a&gt; as it reduces barriers and migration issues. &lt;a href="https://github.com/vknabel/vscode-swift-development-environment/issues/39"&gt;#39&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once the extension has been released to VS Code‘s extension registry and it is stable enough, I will add a warning to prefer that instead if not installed yet. &lt;a href="https://github.com/vknabel/vscode-swift-development-environment/issues/40"&gt;#40&lt;/a&gt; If it has been installed and activated, SDE will disable itself automatically. &lt;a href="https://github.com/vknabel/vscode-swift-development-environment/issues/41"&gt;#41&lt;/a&gt; These options should be opt-out. The goal within this phase is to decide which extension to use within which project.&lt;/p&gt;
&lt;p&gt;As soon as I don’t see any reason to keep maintaining SDE, I will add a message as explanation. If SDE will still have features which are not part of Apple’s extension, I will extract them into separate extensions which will still be maintained.&lt;/p&gt;
&lt;p&gt;I hope you agree with these future plans. If you have more ideas, feedback or if your don’t agree on this, please &lt;a href="https://github.com/vknabel/vscode-swift-development-environment/issues/new"&gt;open an issue&lt;/a&gt; or tell me on &lt;a href="https://mastodon.social/@vknabel"&gt;@mastodon.social@vknabel&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Elegant Objects</title><link>https://vknabel.com/library/elegant-objects/</link><pubDate>Tue, 09 Oct 2018 00:00:00 +0000</pubDate><guid>https://vknabel.com/library/elegant-objects/</guid><description>&lt;p&gt;Writing things down is part of my learning process. These thoughts came up while reading through Yegor Bugayenko’s book Elegant Objects about a more declarative and less procedural approach of object oriented programming.
This is more a personal document than a book review or summary and as I already knew some topics, I do not mention several chapters or details, I might explain concepts different than the original author and many examples from Java, Ruby or C++ cannot be applied to Swift as they would already solve the issue.&lt;/p&gt;
&lt;p&gt;The book itself is split into three parts of the lifetime of an object: the birth or initialization, education, employment and retirement or deinitialization.&lt;/p&gt;
&lt;p&gt;Elegant Objects teaches to &lt;strong&gt;treat all objects as human beings&lt;/strong&gt; and that we should talk with them &lt;code&gt;[].isEmpty&lt;/code&gt; instead of talking about them &lt;code&gt;[].count == 0&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="birth"&gt;Birth&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Objects are not bags of data, they are the data they represent&lt;/li&gt;
&lt;li&gt;Therefore objects are no algorithms&lt;/li&gt;
&lt;li&gt;Objects without data make no sense&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="11-never-use--er-names"&gt;1.1 Never use -er names&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.yegor256.com/2015/03/09/objects-end-with-er.html"&gt;https://www.yegor256.com/2015/03/09/objects-end-with-er.html&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Names describing algorithms and behavior of the objects instead of their identity are no real OOP. Typical suffixes are &lt;code&gt;er&lt;/code&gt; and &lt;code&gt;or&lt;/code&gt; (exceptions: user, computer, etc):
&lt;ul&gt;
&lt;li&gt;Formatter (a format can apply itself, right?)&lt;/li&gt;
&lt;li&gt;Converter (exchange rate? Metric system?)&lt;/li&gt;
&lt;li&gt;Manager (repository)&lt;/li&gt;
&lt;li&gt;Controller (this one is interesting. It holds the view, but how can we name it instead? In UIKit not practical?)&lt;/li&gt;
&lt;li&gt;Helper (well, yes)&lt;/li&gt;
&lt;li&gt;Validator (predicate)&lt;/li&gt;
&lt;li&gt;Router (routing table?)&lt;/li&gt;
&lt;li&gt;Dispatcher (queue would be too specific)&lt;/li&gt;
&lt;li&gt;Observer (outside Swift: target, addressee, recipient? Similar: output pipe)&lt;/li&gt;
&lt;li&gt;Listener (&lt;/li&gt;
&lt;li&gt;Sorter&lt;/li&gt;
&lt;li&gt;Encoder&lt;/li&gt;
&lt;li&gt;Decoder&lt;/li&gt;
&lt;li&gt;Observable (stream)&lt;/li&gt;
&lt;li&gt;Probably all &lt;code&gt;able&lt;/code&gt; names, too&lt;/li&gt;
&lt;li&gt;Encodable, Decodable (quite generic and hard)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Because of the focus on data: many initializers (5&amp;hellip;10), a few queries (), as little mutations as possible (). Reasoning: makes it much easier to use&lt;/li&gt;
&lt;li&gt;This data centric approach reminds me of some functional code I‘ve seen before, which impressed me a lot.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="12-make-one-constructor-primary"&gt;1.2 Make one constructor primary&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.yegor256.com/2015/05/28/one-primary-constructor.html"&gt;https://www.yegor256.com/2015/05/28/one-primary-constructor.html&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have only one primary initializer and many convenience inits&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="13-keep-constructors-code-free"&gt;1.3 Keep constructors code-free&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.yegor256.com/2015/05/07/ctors-must-be-code-free.html"&gt;https://www.yegor256.com/2015/05/07/ctors-must-be-code-free.html&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code-free constructors: actually had performance impacts on this in Ionic! Swift‘s &lt;code&gt;lazy&lt;/code&gt; helps a lot here! =&amp;gt; this kind of performance impact should be explicit&lt;/li&gt;
&lt;li&gt;Rule of thumb: don‘t touch the arguments&lt;/li&gt;
&lt;li&gt;Suggestion: creation of many, small classes (like a class just for parsing an int out of a string) (reducing overhead with &lt;code&gt;Taggable&lt;/code&gt; like solutions?)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="education"&gt;Education&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Keep objects small (this is a trend in Swift, right? &lt;code&gt;Taggable&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="21-encapsulate-as-little-as-possible"&gt;2.1 Encapsulate as little as possible&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Rule of thumb: max 4 values per object, otherwise compose&lt;/li&gt;
&lt;li&gt;What about configuration files? Should their object representations be passed through the whole program or should they be split into more maintainable objects?&lt;/li&gt;
&lt;li&gt;Every data within an object is part of its identity (in general, yes, but what about database entries and their id? Of course &lt;code&gt;==&lt;/code&gt; should be false if the id is the same, but the name is different)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="22-encapsulate-something-at-the-very-least"&gt;2.2 Encapsulate something at the very least&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.yegor256.com/2014/12/15/how-much-your-objects-encapsulate.html"&gt;https://www.yegor256.com/2014/12/15/how-much-your-objects-encapsulate.html&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Static methods are not pure OOP&lt;/li&gt;
&lt;li&gt;Objects without data are just static&lt;/li&gt;
&lt;li&gt;There is only one object encapsulating nothing: the universe (or in swift unit)&lt;/li&gt;
&lt;li&gt;Doesn‘t like &lt;code&gt;x * z&lt;/code&gt;, but probably not completely applicable to Swift (it changes the way you think)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="23-always-use-interfaces"&gt;2.3 Always use interfaces&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.yegor256.com/2014/11/20/seven-virtues-of-good-object.html"&gt;https://www.yegor256.com/2014/11/20/seven-virtues-of-good-object.html&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every public method should have their representation within an interface.&lt;/li&gt;
&lt;li&gt;Do not use object instances directly&lt;/li&gt;
&lt;li&gt;I think this approach is right, especially easy when using unidirectional data flow architecture. It requires just a few interfaces, but the gains for testability are awesome. If we need to write too much interfaces, we probably picked the wrong abstraction or have too much coupling.&lt;/li&gt;
&lt;li&gt;Note: in Swift you can use a struct with values of functions instead of methods if it better fits your use case than protocols.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="24-choose-method-names-carefully"&gt;2.4 Choose method names carefully&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.yegor256.com/2018/08/22/builders-and-manipulators.html"&gt;https://www.yegor256.com/2018/08/22/builders-and-manipulators.html&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rule of thumb: queries (builders) are nouns (with adjectives), mutations (manipulators) are verbs (with adverbs).&lt;/li&gt;
&lt;li&gt;Queries return&lt;/li&gt;
&lt;li&gt;Mutations mutate and return nothing&lt;/li&gt;
&lt;li&gt;Reduces side-effects&lt;/li&gt;
&lt;li&gt;If we need results from the mutation, we should create a new object, which decouples mutations and queries. Generally a good idea, but then you loose compiler safety, that the mutation should have been triggered before reading the results. Using callbacks would just hide the problem, as we would pass the result to the callback. Hence: the mutation would emit data. Exceptions from this would only be thins like &lt;code&gt;Observable&amp;lt;Void&amp;gt;&lt;/code&gt; and &lt;code&gt;Promise&amp;lt;Void&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Booleans should not include &lt;code&gt;is&lt;/code&gt; but should be readable with an &lt;code&gt;is&lt;/code&gt; (prefer is &lt;code&gt;equal(to:)&lt;/code&gt; over &lt;code&gt;equals(_:)&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="25-dont-use-public-constants"&gt;2.5 Don‘t use public constants&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.yegor256.com/2015/07/06/public-static-literals.html"&gt;https://www.yegor256.com/2015/07/06/public-static-literals.html&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not use public constants (this includes global constants, singletons, static properties, &amp;hellip;)&lt;/li&gt;
&lt;li&gt;Constants should be private&lt;/li&gt;
&lt;li&gt;Instead wrap the constants into a class which &lt;em&gt;uses&lt;/em&gt; it as intended.&lt;/li&gt;
&lt;li&gt;Example: &lt;code&gt;CRLFString&lt;/code&gt; which automatically appends &lt;code&gt;\r\n&lt;/code&gt; to all strings on &lt;code&gt;toString&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Regarding enums: &lt;code&gt;HTTPMethod&lt;/code&gt; are just constants, too! Instead favor distinct classes for &lt;code&gt;PostRequest&lt;/code&gt;, &lt;code&gt;GetRequest&lt;/code&gt;, &amp;hellip;&lt;/li&gt;
&lt;li&gt;In my eyes using enums with associated values is fine as they are much more advanced. E.g. &lt;code&gt;LocalizableString&lt;/code&gt; with &lt;code&gt;case priceLabel(amount: Int, currency: String)&lt;/code&gt; and &lt;code&gt;var localized: String { get }&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="26-be-immutable"&gt;2.6 Be Immutable&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.yegor256.com/2014/06/09/objects-should-be-immutable.html"&gt;https://www.yegor256.com/2014/06/09/objects-should-be-immutable.html&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Of course immutability is important!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="27-write-tests-instead-of-documentation"&gt;2.7 Write tests instead of documentation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Tests are good docs; tests should be easy to read as the code should be&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="28-dont-mock-use-fakes"&gt;2.8 Don‘t mock; use fakes&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.yegor256.com/2014/09/23/built-in-fake-objects.html"&gt;https://www.yegor256.com/2014/09/23/built-in-fake-objects.html&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mocking is fragile (internal knowledge needed)&lt;/li&gt;
&lt;li&gt;Fakes are more robust&lt;/li&gt;
&lt;li&gt;Fakes should provide as much customization as possible.&lt;/li&gt;
&lt;li&gt;It don’t think Fakes should necessarily be implemented next to the interfaces, but they are essential for unit tests and should always be shipped with libraries (either within module &lt;code&gt;LibraryTesting&lt;/code&gt; or in prod code)&lt;/li&gt;
&lt;li&gt;I even think more libraries (including my own) should ship a basic set of unit tests that each implementation should pass. &lt;a href="https://oleb.net/blog/2016/12/protocols-have-semantics/"&gt;Protocols are more than Bags of Syntax&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="29-keep-interfaces-short-use-smarts"&gt;2.9 Keep interfaces short; use smarts&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.yegor256.com/2016/04/26/why-inputstream-design-is-wrong.html"&gt;https://www.yegor256.com/2016/04/26/why-inputstream-design-is-wrong.html&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As everything, interfaces should only do one thing&lt;/li&gt;
&lt;li&gt;Applying defaults and providing overloads are more than this one thing: multiple implementations would still need to apply the same defaults. What if the defaults would be different? In that case we would mix up to problems to one n-to-m problem.&lt;/li&gt;
&lt;li&gt;The proposed solution is the introduction of &lt;code&gt;Smart&lt;/code&gt; classes. With an implementation given, it will provide all overloads, convenience functions and defaults. If the only connection to the outside world is an instance of the interface, it should be okay to keep the &lt;code&gt;Smart&lt;/code&gt;-suffix. In Swift we do not need this helper at all: we can just move all overloads to an extension.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="employment"&gt;Employment&lt;/h2&gt;
&lt;h3 id="expose-fewer-than-five-public-methods"&gt;Expose fewer than five public methods&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Suggests 5 as max amount of public methods in one single class&lt;/li&gt;
&lt;li&gt;It‘s about cohesion: every method should access all instance variables. Otherwise you might break it up.&lt;/li&gt;
&lt;li&gt;I guess these smart classes and Stdlib classes don’t fit into this rule&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="dont-use-static-methods"&gt;Don’t use static methods&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.yegor256.com/2014/05/05/oop-alternative-to-utility-classes.html"&gt;https://www.yegor256.com/2014/05/05/oop-alternative-to-utility-classes.html&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Should never be used and are no OOP&lt;/li&gt;
&lt;li&gt;Treat OOP as declarative paradigm instead of a imperative one&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="325-functional-programming"&gt;3.2.5 Functional Programming&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Did not understand benefits of declarative OOP over functional programming. (FP is simpler because it has no methods)&lt;/li&gt;
&lt;li&gt;Is it possible to combine FP with dOOP? Especially in Swift: could we replace Monads by dOOP?&lt;/li&gt;
&lt;li&gt;Maybe dOOP for the big picture and FP for the small one?&lt;/li&gt;
&lt;li&gt;Composability over decorators (higher order objects)&lt;/li&gt;
&lt;li&gt;dOOP would be a perfect fit for a rule editor! Probably more approachable than FP due to the decorators. „I want an array X. It should be an array with unique elements.“&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="34-be-loyal-and-immutable-or-constant"&gt;3.4 Be loyal and immutable, or constant&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.yegor256.com/2014/12/22/immutable-objects-not-dumb.html"&gt;https://www.yegor256.com/2014/12/22/immutable-objects-not-dumb.html&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Loyal: always represents the same real-world entity (e.g. never change the file path or the user id)&lt;/li&gt;
&lt;li&gt;Immutable: object-state will not change&lt;/li&gt;
&lt;li&gt;Constant objects are immutable&lt;/li&gt;
&lt;li&gt;Non-constant but immutable: changes if real-world changes (e.g. after changing files or after manipulating memory)&lt;/li&gt;
&lt;li&gt;My example: a redux store is immutable but not constant as it does only change accordingly if it‘s represented real-world data changes (the application state). But it must be loyal and must always represent the same application. When diving deeper, there must be a &lt;code&gt;class&lt;/code&gt; wrapper around the state-&lt;code&gt;struct&lt;/code&gt;. This wrapper can apply mutations to the state, but it cannot replace it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="36-dont-use-new-outside-of-secondary-ctors"&gt;3.6 Don‘t use &lt;code&gt;new&lt;/code&gt; outside of secondary ctors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Required &lt;code&gt;init&lt;/code&gt;s may never instantiate objects&lt;/li&gt;
&lt;li&gt;Funcs may never instantiate objects&lt;/li&gt;
&lt;li&gt;But still, not all initializers may be called within &lt;code&gt;convenience&lt;/code&gt; inits (think of greater dependencies! A convenience init can still hard-code dependencies because of laziness)&lt;/li&gt;
&lt;li&gt;When needed create a helper function in convenience inits and store it&lt;/li&gt;
&lt;li&gt;=&amp;gt; Unit tests will be much better&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="retirement"&gt;Retirement&lt;/h2&gt;
&lt;h3 id="412-alternatives-to-null"&gt;4.1.2 Alternatives to null&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;Optional&lt;/code&gt; only for data, but not for errors&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="421-dont-catch-unless-you-have-to"&gt;4.2.1 Don‘t catch unless you have to&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Fail early, catch late&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="423-recover-only-once"&gt;4.2.3 Recover only once&lt;/h3&gt;
&lt;h3 id="424-use-aspect-oriented-programming"&gt;4.2.4 Use aspect-oriented programming&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Using AOP is not required for this task! Simply create a higher-order function or an object decorator&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="43-be-either-final-or-abstract"&gt;4.3 Be either final or abstract&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.yegor256.com/2014/11/20/seven-virtues-of-good-object.html"&gt;https://www.yegor256.com/2014/11/20/seven-virtues-of-good-object.html&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In Swift there are no &lt;code&gt;abstract&lt;/code&gt; classes. Instead we have protocols with default implementations. This is a much better approach. As a result, all Swift classes should be final. Swift classes are implicitly final for external modules when not marked as open.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="44-use-raii"&gt;4.4 Use RAII&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Resource Acquisition Is Initialization&lt;/li&gt;
&lt;li&gt;Says to lock resources on init and release them on deinit&lt;/li&gt;
&lt;li&gt;In Swift this approach is already common: Subscription, Disposable, opaque objects for &lt;code&gt;NSNotificationCenter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="recap"&gt;Recap&lt;/h2&gt;
&lt;p&gt;I still don’t know what I am going to take away from this book as it particularly has strong opinions which do not align with mine. Yet, there are things I have experienced myself and got the same solution. What I found valuable are naming and the rules of thumb as I generally agree on them. Though as in most books, some sentences are too absolute:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IMHO it’s okay to use return values for mutations: if the only way to get information when performing side-effects, it should better be explicit.&lt;/li&gt;
&lt;li&gt;Sometimes we do not use boolean queries directly within conditions. In those cases it might read far better if we still keep the &lt;code&gt;is&lt;/code&gt; prefix. And: &amp;ldquo;is &lt;code&gt;containing&lt;/code&gt;&amp;rdquo; sounds awful when compared to &lt;code&gt;contains&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The approach to make it explicit what you want to optimize your code for and how your code should look like is interesting. Of course this may sound philosophical or too meta, but it may help prioritizing.&lt;/li&gt;
&lt;li&gt;Regarding static methods and global variables: in general? Agreed. Though, I think the global &lt;code&gt;Current&lt;/code&gt; as proposed by pointfree.co is worth it, as long as it is in-line with your goals and your business: execution-environment specific things should go into &lt;code&gt;Current&lt;/code&gt; (JS: &lt;code&gt;window.open&lt;/code&gt;). Things like sessions should preferably be implemented using classic DI (e.g. Tweetbot hay have multiple sessions).&lt;/li&gt;
&lt;li&gt;Functions have much less overhead than classes. The proposed declarative OOP snippets read mostly like FP on the usage side: you do not gain any benefits from the fact that objects have methods. On the declaration side, writing a class has a much bigger overhead when compared to a function. Though you gain the ability to replace the implementation when using protocols and polymorphism resulting in more verbosity. I think FP feels less like fighting against the language than declarative OOP would feel like.&lt;/li&gt;
&lt;li&gt;Of course all these techniques help to improve maintainability, but what‘s often left unsaid: you can also encapsulate dirty hacks that way. And what if we need to touch these parts again? Keep the tests, throw the code away and write everything from scratch, but in clean. If there is no code to maintain (deleted code is no code) and if we cannot break anything (we have tests for our dirty hack), we can easily maintain everything. In most cases these parts will only be written once. Though this approach requires being explicit about it.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>ArgumentOverture</title><link>https://vknabel.com/posts/argumentoverture/</link><pubDate>Sun, 30 Sep 2018 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/argumentoverture/</guid><description>&lt;p&gt;A Swift Playground aiming to provide some functional helpers to parse arguments for command line tools. It uses &lt;a href="https://github.com/pointfreeco/swift-overture"&gt;Overture&lt;/a&gt; and is build for high composability, flexibility and little impact on your project&amp;rsquo;s freedom to evolve.&lt;/p&gt;
&lt;p&gt;A central use case was &lt;a href="https://github.com/vknabel/archery"&gt;Archery&lt;/a&gt;&amp;rsquo;s: only actually interpreted arguments shall be consumed. Any others shall be collected (&lt;code&gt;remaining&lt;/code&gt;) or should prevent execution (&lt;code&gt;exhaust&lt;/code&gt;), depending on the current command.&lt;/p&gt;
&lt;p&gt;First of all an example usage.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Experiment&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; (isVerbose, whoToGreet, language, &lt;span style="color:#66d9ef"&gt;_&lt;/span&gt;) = &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; with([&lt;span style="color:#e6db74"&gt;&amp;#34;-v&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;hi&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Some string&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;--language&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;en&amp;#34;&lt;/span&gt;], chain(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; flag(&lt;span style="color:#e6db74"&gt;&amp;#34;verbose&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;v&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; positional(&lt;span style="color:#e6db74"&gt;&amp;#34;Name&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; argument(&lt;span style="color:#e6db74"&gt;&amp;#34;language&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;l&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exhaust
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;} &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;Command failed:&amp;#34;&lt;/span&gt;, error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The implementation of the micro-library itself.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Errors&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;quoted&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; string: String) -&amp;gt; String {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;string&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;protocol&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;StringConvertibleError&lt;/span&gt;: Error, CustomStringConvertible {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MissingArgumentError&lt;/span&gt;: StringConvertibleError {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; name: String?
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; description: String {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; with(name, pipe(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; map(quoted),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; map { &lt;span style="color:#e6db74"&gt;&amp;#34;Missing argument &lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;$0&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ??&lt;span style="color:#e6db74"&gt;&amp;#34;Missing argument&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NonExhaustiveArgumentsError&lt;/span&gt;: StringConvertibleError {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; remaining: [String]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; description: String {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Unused arguments: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;remaining&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Domain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;isArgument&lt;/span&gt;(named name: String) -&amp;gt; (String) -&amp;gt; Bool {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { $0 == &lt;span style="color:#e6db74"&gt;&amp;#34;-&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;name&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; $0 == &lt;span style="color:#e6db74"&gt;&amp;#34;--&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\(&lt;/span&gt;name&lt;span style="color:#e6db74"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;isOneArgument&lt;/span&gt;(of names: [String]) -&amp;gt; (String) -&amp;gt; Bool {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { passed &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; names.&lt;span style="color:#66d9ef"&gt;lazy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .map(isArgument(named:))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .reduce(&lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;, { $0 &lt;span style="color:#f92672"&gt;||&lt;/span&gt; $1(passed) })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;indexOfArguments&lt;/span&gt;(named names: [String]) -&amp;gt; ([String]) -&amp;gt; Array&amp;lt;String&amp;gt;.Index? {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { passed &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; passed.index(&lt;span style="color:#66d9ef"&gt;where&lt;/span&gt;: isOneArgument(of: names))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;flag&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; names: String...) -&amp;gt; ([String]) -&amp;gt; ([String], Bool) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { arguments &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; index = arguments.index(&lt;span style="color:#66d9ef"&gt;where&lt;/span&gt;: isOneArgument(of: names)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; result = arguments
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result.remove(at: index)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (result, &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (arguments, &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;positional&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; name: String? = &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;) -&amp;gt; ([String]) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; ([String], String) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { arguments &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; index = arguments.index(&lt;span style="color:#66d9ef"&gt;where&lt;/span&gt;: { &lt;span style="color:#f92672"&gt;!&lt;/span&gt;$0.starts(with: &lt;span style="color:#e6db74"&gt;&amp;#34;-&amp;#34;&lt;/span&gt;)})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; index = index {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; result = arguments
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result.remove(at: index)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (result, arguments[index])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;throw&lt;/span&gt; MissingArgumentError(name: name)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;optional&lt;/span&gt;&amp;lt;A&amp;gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; f: @escaping ([String]) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; ([String], A)) -&amp;gt; ([String]) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; ([String], A?) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { arguments &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; (remaining, parsed) = &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; f(arguments)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (remaining, .some(parsed))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;is&lt;/span&gt; MissingArgumentError {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (arguments, &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;throw&lt;/span&gt; error
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;argument&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; names: String...) -&amp;gt; ([String]) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; ([String], String) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { arguments &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; flagIndex = indexOfArguments(named: names)(arguments), arguments.count &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; flagIndex &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; value = arguments[flagIndex &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; rest = arguments
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rest.remove(at: flagIndex &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rest.remove(at: flagIndex)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (rest, value)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;throw&lt;/span&gt; MissingArgumentError(name: names.first)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;exhaust&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; arguments: [String]) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; -&amp;gt; ([String], Void) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; arguments.isEmpty {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; ([], ())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;throw&lt;/span&gt; NonExhaustiveArgumentsError(remaining: arguments)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;remaining&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; arguments: [String]) -&amp;gt; ([String], [String]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; ([], arguments)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next?&lt;/h2&gt;
&lt;p&gt;The same concept could be applied to processes and especially handling interrupts:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;with(Process(), concat(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; setWorkingDir(&lt;span style="color:#e6db74"&gt;&amp;#34;tmp&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; passInterrupt(),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; setBashCommand(&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;// is { concat(setLaunchPath(&amp;#34;/bin/bash&amp;#34;), setArguments([&amp;#34;-c&amp;#34;, $0]) }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;&lt;a href="./playground.zip"&gt;download this playground&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Refactoring RxJS Code in Angular</title><link>https://vknabel.com/posts/refactoring-rxjs-in-angular/</link><pubDate>Thu, 28 Sep 2017 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/refactoring-rxjs-in-angular/</guid><description>&lt;p&gt;Do you work with legacy RxJS code? Have you ever revisited your first few observables in your application? Do you need to fix bugs in an app of your learning phase? Are you still learning best practices for writing reactive code? This guide is for you. Even if you don’t have anything to do with Angular, you may find this interesting. I will show you a way of how to improve your reactive streams in order to understand their functionality in many isolated, but tiny steps. Some of them may offer external dependencies, but we will always show, how to do it manually.&lt;/p&gt;
&lt;p&gt;Within each step we isolate/eliminate side effects, get more explicit about lifetime and learn about how to prevent unintended behavior. If you use this guide to refactor some specific code, you should perform all single steps in order and rerun all tests after each change. If you can‘t find the shown pattern you can take the next step.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Unit Testing:&lt;/em&gt; If you don‘t have any test create them upfront. Each step is a small refactoring: you may break something. For the guys of you who sit in front of really untestable code: I understand your situation. All ”dangerous” steps are marked. You need to test much more and manually. At least introduce unit tests afterwards. Dependency injection is your friend.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the beginning, we start easy and just isolate our side effects from our subscriptions. Instead of passing our callbacks directly, we wrap them into &lt;code&gt;do&lt;/code&gt; operators. Whenever you read a &lt;code&gt;do&lt;/code&gt;, you know: that‘s a side effect.
If you want a slightly more expressive variant try &lt;code&gt;@molecule/do-next&lt;/code&gt;, &lt;code&gt;@molecule/do-error&lt;/code&gt; and &lt;code&gt;@molecule/do-complete&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// previous code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;myObservable$&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;handleNext&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;handleError&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; () =&amp;gt; &lt;span style="color:#a6e22e"&gt;handleCompletion&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// refactored code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;myObservable$&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;handleNext&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;handleError&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; () =&amp;gt; &lt;span style="color:#a6e22e"&gt;handleCompletion&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/* or with @molecule/do-next,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * @molecule/do-error,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * @molecule/do-complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;myObservable$&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;handleNext&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;handleError&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; () =&amp;gt; &lt;span style="color:#a6e22e"&gt;handleCompletion&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Well, that was easy. Now let‘s make all of our operators simpler. A good sign, that you tried to do more than one thing inside an operator is the use of closure blocks and explicit return statements (&lt;code&gt;() =&amp;gt; {}&lt;/code&gt;).
Especially if we extract all side effects from our operators, we know: &lt;em&gt;all&lt;/em&gt; side effects live within &lt;code&gt;do*&lt;/code&gt; and &lt;code&gt;finally&lt;/code&gt;!
Every statement that is not your &lt;code&gt;return&lt;/code&gt;-statement and no variable declaration, that is used for your &lt;code&gt;return&lt;/code&gt; is a side effect. If the operator is a &lt;code&gt;catch&lt;/code&gt;, move the side effects logically isolated (see above) to &lt;code&gt;do(undefined, yourErrorHandler)&lt;/code&gt;/&lt;code&gt;doError(yourErrorHandler)&lt;/code&gt;. Otherwise extract them to &lt;code&gt;do&lt;/code&gt;/&lt;code&gt;doNext&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// previous code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;myObservable$&lt;/span&gt;.&lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt; =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#960050;background-color:#1e0010"&gt;’&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Could&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;myObservable$&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;’&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Observable&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;empty&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// refactored code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;myObservable$&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	 &lt;span style="color:#75715e"&gt;// or use .do(undefined, errorHandler)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;doError&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt; =&amp;gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#960050;background-color:#1e0010"&gt;’&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Could&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;myObservable$&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;’&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt;(() =&amp;gt; &lt;span style="color:#a6e22e"&gt;Observable&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;empty&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In order to further simplify your operation handler, you can extract the whole block into a new &lt;code&gt;private&lt;/code&gt; method of your class. If it doesn’t even depend on &lt;code&gt;this&lt;/code&gt; you can actually make it &lt;code&gt;static&lt;/code&gt;. The following case may seem a bit awkward at first, but it will keep everything simple and stupid. You will see, one can totally understand &lt;code&gt;previewOfFavorites$&lt;/code&gt; without knowing all those details. And if the underlying API changes, our public methods with all of our business logic just don‘t care.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// previous code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;previewOfFavorites&lt;/span&gt;()&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Observable&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Favorite&lt;/span&gt;[]&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;previewSize$&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;settings$&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;map&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;settings&lt;/span&gt; =&amp;gt; &lt;span style="color:#a6e22e"&gt;settings&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;preview&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;size&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;favorites$&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;favorites$&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Observable&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;combineLatest&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;previewSize$&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;previewOfFavorites$&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		(&lt;span style="color:#a6e22e"&gt;size&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;favorites&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;favorites&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;slice&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// refactored code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;previewOfFavorites$&lt;/span&gt;()&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Observable&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Favorite&lt;/span&gt;[]&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Observable&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;combineLatest&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;previewSize$&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;previewOfFavorites$&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		(&lt;span style="color:#a6e22e"&gt;size&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;favorites&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;favorites&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;slice&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;previewSize$&lt;/span&gt;()&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Observable&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;number&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;settings$&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;map&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;settings&lt;/span&gt; =&amp;gt; &lt;span style="color:#a6e22e"&gt;settings&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;preview&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;size&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;favorites$&lt;/span&gt;()&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Observable&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Favorite&lt;/span&gt;[]&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;favorites$&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally we should have mostly single &lt;code&gt;return&lt;/code&gt;-statements as handlers and transformations. In that case we just use the short notation for closures. One exception are object literals, which need to be wrapped in parentheses (&lt;code&gt;() =&amp;gt; ({})&lt;/code&gt;) or leave the explicit return statement if you prefer it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// previous code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;myObservable$&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;map&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; [&lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// refactored code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;myObservable$&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;map&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;) =&amp;gt; [&lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next?&lt;/h2&gt;
&lt;p&gt;This blogpost has never been finished. It wasn&amp;rsquo;t touch for more than a year and I don&amp;rsquo;t understand all my remaining notes anymore.
Here they are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Replace instance variables through Observables they represent&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Store observable as &lt;code&gt;readonly&lt;/code&gt; and &lt;code&gt;shareReplay(1)&lt;/code&gt; and remove &lt;code&gt;subscribe&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Replace usages with &lt;code&gt;withLatestFrom&lt;/code&gt; or &lt;code&gt;this.xx$.first().mergeMap&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;When filtered procedural, move logic into observable method for later use&lt;/li&gt;
&lt;li&gt;Keep visibility (prefer &lt;code&gt;private&lt;/code&gt;, of course)&lt;/li&gt;
&lt;li&gt;Angular templates should use &lt;code&gt;|async&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;You can now remove &lt;code&gt;changeDetector.markForCheck()&lt;/code&gt; invocations&lt;/li&gt;
&lt;li&gt;If param is requires synchronously use &lt;code&gt;Observable.defer().shareReplay(1)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remove temporary observable and adjust &lt;code&gt;mergeMap&lt;/code&gt;s&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If extracted subject with object for parameter, adjust function parameters&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Trigger actions with temporary observable named &lt;code&gt;*Action&lt;/code&gt; (&lt;code&gt;Observable.empty().finally&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sideeffect at the begginning before current operator, otherwise behind&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Keep functions which prevent execution&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All subjects should always be private: add accessors &lt;code&gt;processDidChange&lt;/code&gt;, &lt;code&gt;processDidFail&lt;/code&gt;, &lt;code&gt;processDidComplete&lt;/code&gt;,&lt;code&gt;processObserver&lt;/code&gt; (just if required)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Never miss a dismiss in Ionic anymore</title><link>https://vknabel.com/posts/never-miss-a-dismiss-in-ionic-anymore/</link><pubDate>Tue, 26 Sep 2017 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/never-miss-a-dismiss-in-ionic-anymore/</guid><description>&lt;p&gt;Have you ever experienced, that some loading indicators just don’t want to dismiss and literally stay forever? Maybe while users tried to login? The short story: you probably forgot to dismiss a loading indicator in some cases.&lt;/p&gt;
&lt;p&gt;Within this blogpost you will learn how to declaratively present loading indicators and how to prevent forgetting to dismissing it again.&lt;/p&gt;
&lt;p&gt;In our showcase we have a login page, that shall present a &lt;a href="https://ionicframework.com/docs/components/#loading"&gt;loading&lt;/a&gt; indicator while the user will be authenticated. The page itself only consists of inputs for our credentials and a login button calling &lt;code&gt;LoginPage.submitLogin&lt;/code&gt; on click inside &lt;code&gt;ion-content&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;ion-content&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;padding&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;ion-input&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;[(&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;ngModel&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;)]=&amp;#34;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;username&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;ion-input&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;ion-input&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;[(&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;ngModel&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;)]=&amp;#34;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;password&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;password&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;ion-input&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;button&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ion-button&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;(&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;click&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;)=&amp;#34;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;submitLogin&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;()&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;ion-content&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our &lt;code&gt;LoginPage&lt;/code&gt; relies on an &lt;code&gt;AuthenticationService&lt;/code&gt;, mocked as follows.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;abstract&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AuthenticationService&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;abstract&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;login&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;username&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;string&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;password&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;string&lt;/span&gt;)&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Observable&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once the user clicked on the login button, we will create a &lt;code&gt;Loading&lt;/code&gt; using the &lt;code&gt;LoadingController&lt;/code&gt;, present it and perform the login itself. Thereafter we need to dismiss the same &lt;code&gt;Loading&lt;/code&gt; again.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;@&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;IonicPage&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;@&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Component&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;templateUrl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;./login.page.html&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LoginPage&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;username&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;password&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;constructor&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;readonly&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;authentication&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AuthenticationService&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;readonly&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;navCtrl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NavController&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;readonly&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;loadingCtrl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LoadingController&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ) {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;submitLogin&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// configure loading spinner
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;loading&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;loadingCtrl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;create&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// present it
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;loading&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;present&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(() =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// perform login
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;authentication&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;login&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;username&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;password&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;toPromise&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// on success dismiss
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(() =&amp;gt; &lt;span style="color:#a6e22e"&gt;loading&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;dismiss&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// and apply successful login
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(() =&amp;gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;navCtrl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;setRoot&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;MyEntryPage&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You might have noticed, but we already introduced a bug: if the login fails, our promise rejects and &lt;code&gt;.then(() =&amp;gt; loading.dismiss())&lt;/code&gt; will never be called, our &lt;code&gt;Loading&lt;/code&gt; will still be presented and the user will be forced to restart the whole app in order to try the correct credentials.
The most obvious fix would be to just insert a &lt;code&gt;catch&lt;/code&gt; which dismissed the loading, too. But this would not prevent any other usages of &lt;code&gt;Loading&lt;/code&gt; from being presented forever.&lt;/p&gt;
&lt;p&gt;In order to solve this problem, we will create a new controller, which will present loadings and automatically dismisses them once a given promise resolves or rejects.
We want our &lt;code&gt;submitLogin&lt;/code&gt; to be completely free from all &lt;code&gt;loading.dismiss()&lt;/code&gt; calls and even the requirement to even keep our &lt;code&gt;loading&lt;/code&gt; variable. Additionally we don’t want to be forced to either subscribe to an observable nor to convert it to a promise.&lt;/p&gt;
&lt;p&gt;The code we want inside &lt;code&gt;LoginPage.submitLogin&lt;/code&gt; should be focused and compact.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;submitLogin&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// present while ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;activityCtrl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;presentWhile&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ... logging in
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;authentication&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;login&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;username&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;password&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// on success, apply login
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(() =&amp;gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;navCtrl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;setRoot&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;MyEntryPage&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our &lt;code&gt;presentWhile&lt;/code&gt; will be implemented inside a service called &lt;code&gt;ActivityController&lt;/code&gt; and will either work with promises or observables. It handles dismissing our loading spinner and returns a new promise resolving to the same value (or rejecting to the same error).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;@&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Injectable&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ActivityController&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;constructor&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;readonly&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;loadingCtrl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LoadingController&lt;/span&gt;) {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;presentWhile&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;T&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;active&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; Promise&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;T&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Observable&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;T&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;&lt;span style="color:#f92672"&gt;?:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LoadingOptions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&lt;span style="color:#f92672"&gt;:&lt;/span&gt; Promise&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;T&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// create loading using optionally given options
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;spinner&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;loadingCtrl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;create&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;spinner&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;present&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// convert observables to promises
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; () =&amp;gt; (&lt;span style="color:#a6e22e"&gt;active&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;instanceof&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Observable&lt;/span&gt; &lt;span style="color:#f92672"&gt;?&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;active&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;toPromise&lt;/span&gt;() &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;active&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// dismiss and keep result
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;result&lt;/span&gt; =&amp;gt; &lt;span style="color:#a6e22e"&gt;spinner&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;dismiss&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(() =&amp;gt; &lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// keep the error, but dismiss
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt; =&amp;gt; &lt;span style="color:#a6e22e"&gt;spinner&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;dismiss&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(() =&amp;gt; Promise.&lt;span style="color:#a6e22e"&gt;reject&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In order to actually use &lt;code&gt;ActivityController&lt;/code&gt; in our &lt;code&gt;LoginPage&lt;/code&gt;, we need to inject it. As there Ionic’s controllers (&lt;code&gt;LoadingController&lt;/code&gt;, &lt;code&gt;NavController&lt;/code&gt;, etc.) will be injected for each page separately, we need to do the same for each page, too.
Inside your unit tests, you can &lt;a href="https://angular.io/guide/testing#component-override"&gt;override providers&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;@&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;IonicPage&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;@&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Component&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;templateUrl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;./login.page.html&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// add provider to component
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;provders&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&lt;span style="color:#a6e22e"&gt;ActivityController&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LoginPage&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// username, password
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;constructor&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;readonly&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;authentication&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AuthenticationService&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;readonly&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;navCtrl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NavController&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;readonly&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;activityCtrl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ActivityController&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ) {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// submitLogin()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Within this blogpost you have seen which problems may occur when using loading spinners and how to solve them in general. Furthermore you learnt how to create a new controller for Ionic and how to use it.&lt;/p&gt;
&lt;p&gt;Do you have more ideas for using a pattern like this?&lt;/p&gt;</description></item><item><title>CommandMine</title><link>https://vknabel.com/posts/commandmine/</link><pubDate>Wed, 08 Mar 2017 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/commandmine/</guid><description>&lt;p&gt;Command mine is a concept of a swift library for parsing command line arguments. It is designed to support asynchronous implementations of CLIS, that may even be used inside frameworks.&lt;/p&gt;
&lt;h2 id="definitions"&gt;Definitions&lt;/h2&gt;
&lt;p&gt;CommandMine is about extracting minerals out of your ore.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; goldmine = Mine&amp;lt;Gold&amp;gt;() &lt;span style="color:#75715e"&gt;// declare your mine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .drift( &lt;span style="color:#75715e"&gt;// One way to get to your gold&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; named: &lt;span style="color:#e6db74"&gt;&amp;#34;init&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; digging drift: Drift, &lt;span style="color:#75715e"&gt;// Prepares your Shaft&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; to shaft: execute &lt;span style="color:#75715e"&gt;// your actual program&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="shaft"&gt;Shaft&lt;/h3&gt;
&lt;p&gt;A protocol describing factories of &lt;em&gt;Rails&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id="mine"&gt;Mine&lt;/h3&gt;
&lt;p&gt;Your program. Is a Shaft.&lt;/p&gt;
&lt;h3 id="drift"&gt;Drift&lt;/h3&gt;
&lt;p&gt;A special form of a Shaft, that parses raw ore (&lt;code&gt;[String]&lt;/code&gt;) into minerals.&lt;/p&gt;
&lt;h3 id="ore"&gt;Ore&lt;/h3&gt;
&lt;p&gt;The arguments passed to your program.&lt;/p&gt;
&lt;h3 id="lore"&gt;Lore&lt;/h3&gt;
&lt;p&gt;An event wrapper around minerals.&lt;/p&gt;
&lt;h3 id="mineral"&gt;Mineral&lt;/h3&gt;
&lt;p&gt;The desired mineral of your mine.&lt;/p&gt;
&lt;h3 id="rail"&gt;Rail&lt;/h3&gt;
&lt;p&gt;Transports your filled Lores.
A simple typealias for &lt;em&gt;Observables&lt;/em&gt; of &lt;em&gt;Lores&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id="elevator"&gt;Elevator&lt;/h3&gt;
&lt;p&gt;The fastest connection to the outside. A Rx wrapper around print and read line.&lt;/p&gt;
&lt;p&gt;The idea behind the elevator is to make your CLI embeddable as library without any changes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; ele = Elevator&amp;lt;String, String&amp;gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ele.onNext(.error(&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;)) &lt;span style="color:#75715e"&gt;//.success(&amp;#34;&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Elevator.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="reusability"&gt;Reusability&lt;/h2&gt;
&lt;p&gt;CommandMine tries to keep your CLIs independent from STDIO and may be used asynchronously.&lt;/p&gt;
&lt;p&gt;Additionally it is important to keep parts of your code reusable and replaceable: your CLI may evolve.&lt;/p&gt;
&lt;h3 id="framework-support"&gt;Framework Support&lt;/h3&gt;
&lt;p&gt;When it comes to internal or higher level tooling, frameworks suite better than plain CLIs as it eliminates the need to deal with another binary in your path, that your users need to install and keep up to date. Instead it will be compiled within your own target.&lt;/p&gt;
&lt;p&gt;In order to split your project into a framework and CLI, you just declare your mine and drifts in &lt;code&gt;YourMine&lt;/code&gt;, everything else in &lt;code&gt;YourShaft&lt;/code&gt; and in your executable&amp;rsquo;s &lt;code&gt;main.swift&lt;/code&gt; you just start your mine.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Hint:&lt;/em&gt; you can create this project layout with &lt;code&gt;mine init&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;YourMine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yourMine.runMain()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So &lt;code&gt;YourShaft&lt;/code&gt; will be good for everyone who either wants to provide a complete new CLI using your logic, or for non CLIs.
At first exporting your mine and drifts into &lt;code&gt;YourMine&lt;/code&gt; seems awkward, but it may actually help others to embed your project as a subcommand (after all Mines are just complex Shafts) or to just reuse one drift.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Mine&lt;/code&gt;: your CLIs, tasks arguments&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Drift&lt;/code&gt;: parses arguments for Shaft, sync&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cage&lt;/code&gt;: the options&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Shaft&lt;/code&gt;: a command, async&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Elevator&lt;/code&gt;: user feedback&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A &lt;code&gt;Drift&lt;/code&gt; is an &lt;code&gt;Observable&lt;/code&gt; Factory that emits one single &lt;code&gt;Lore&lt;/code&gt; or an error with its help.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;CommandMine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; main = Shaft(named: &lt;span style="color:#e6db74"&gt;&amp;#34;rock&amp;#34;&lt;/span&gt;, summary: &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .drift(named: &lt;span style="color:#e6db74"&gt;&amp;#34;init&amp;#34;&lt;/span&gt;, explainedBy: &lt;span style="color:#e6db74"&gt;&amp;#34;Creates a new project&amp;#34;&lt;/span&gt;) { (cage: EmptyLore) &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; initObsi
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;main.run { result &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; result {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; .success(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt;), .usage(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;_&lt;/span&gt;), .error(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Shaft.name
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Shaft.instructions
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Shaft.drifts
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;enum&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MineResult&lt;/span&gt;&amp;lt;A&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; success(A)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; usage(String?, String?)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; error(Error) &lt;span style="color:#75715e"&gt;// thrown errors will be inserted&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Drift&lt;/span&gt;&amp;lt;Arguments, Result&amp;gt;: Drifty {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; bindTo: (Arguments) -&amp;gt; Observable&amp;lt;MineEvent&amp;lt;Result&amp;gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Drifty.map
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Drifty.flatMap
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Shaft&lt;/span&gt;&amp;lt;A&amp;gt;: Drifty {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; usage: (String?, String?)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; rootDrift: Drifts&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;[String], A&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Shaft&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt;(named: String, instructions: String? = &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;drift&lt;/span&gt;&amp;lt;Cage&amp;gt;(named: String, instructions: String? = &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;, cage: Cage.&lt;span style="color:#66d9ef"&gt;Type&lt;/span&gt; = Cage.&lt;span style="color:#66d9ef"&gt;self&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;_&lt;/span&gt;: @escaping Drift&amp;lt;Cage, A&amp;gt;) -&amp;gt; Shaft&amp;lt;A&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;extension&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Drift&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;positional&lt;/span&gt;(parameter: @escaping (String) -&amp;gt; Observable&amp;lt;A&amp;gt;) -&amp;gt; [String] -&amp;gt; Observable&amp;lt;MineResult&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;([String], A)&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; { args &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;guard&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; arg = args.first &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; .of(.usage(&lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; Jfkf
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;exhaustive&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;optional&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next?&lt;/h2&gt;
&lt;p&gt;An alternative concept for modeling CLIs in Swift is &lt;a href="https://vknabel.com/pages/ArgumentOverture"&gt;ArgumentOverture&lt;/a&gt;, which would have much less impact on the actual program.&lt;/p&gt;
&lt;p&gt;This concept is probably too big and hard to adapt for the benefit it will provide. Though the name is quite nice. 😅&lt;/p&gt;</description></item><item><title>Why I created Rock for Swift CLIs</title><link>https://vknabel.com/posts/why-i-created-rock-for-swift-clis/</link><pubDate>Wed, 22 Feb 2017 00:00:00 +0000</pubDate><guid>https://vknabel.com/posts/why-i-created-rock-for-swift-clis/</guid><description>&lt;p&gt;I excessively make use of the official Swift Package Manager as I usually do some CLIs or other stuff. For this use case it is really great. When developing Apps, Carthage and CocoaPods come in handy, which are great, too.
For Web Development there are yarn and NPM, which support project based and global installs. Then there are gem (global) and bundler (project) for ruby based dependencies.&lt;/p&gt;
&lt;p&gt;What I missed was an easy way of distribution for Swift CLIs for either projects or globally. Homebrew is great and can handle global installs great, but for some projects the overhead of submitting new formulas seems too high, especially as this has to be a contribution of a user, not its developer, which is exactly the behavior you want for your personal environment.
Furthermore it is not meant to pin certain versions of your dependencies. You simply can&amp;rsquo;t install one dependency in different versions just for some projects you&amp;rsquo;re working on.&lt;/p&gt;
&lt;p&gt;Exactly this is the reason why we have Swiftenv, RVM, pyenv, Bundler and much more:&lt;/p&gt;
&lt;p&gt;How can you ensure your whole team uses the same dependency versions?
You must declare it.&lt;/p&gt;
&lt;p&gt;For Swift CLI dependencies there is no such system yet.
And that has been the reason why I created Rock for the Swift Ecosystem.
I want everyone to be able to submit their own projects (like CocoaPods) but support decentralized (and private) architectures, too (like Carthage and SwiftPM).
Therefore Rock only focuses on Swift CLIs has been built to work seamlessly with Swift Package Manager projects.&lt;/p&gt;
&lt;p&gt;Do you see the same or other problems? Do you have a different opinion?
I would be really happy to receive some feedback from you.&lt;/p&gt;</description></item><item><title>RSS Feeds</title><link>https://vknabel.com/rss/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://vknabel.com/rss/</guid><description>&lt;p&gt;Hello, fellow RSS user, glad you made it here!
Besides the global RSS feed nearly every listing on this site has a separate one.&lt;/p&gt;
&lt;p&gt;Each of these feeds are linked on the website accordingly.&lt;/p&gt;
&lt;p&gt;Summed up, here are the available RSS feeds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://vknabel.com/"&gt;Home&lt;/a&gt; feed is &lt;code&gt;https://vknabel.com/index.xml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://vknabel.com/blog/"&gt;Blog&lt;/a&gt; feed is &lt;code&gt;https://vknabel.com/posts/index.xml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://vknabel.com/library/"&gt;Library&lt;/a&gt; feed is &lt;code&gt;https://vknabel.com/library/index.xml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Every tag has its own feed. Just append &lt;code&gt;index.xml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hope this fits your needs. Happy reading!&lt;/p&gt;</description></item></channel></rss>