<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[The infinite monkey theorem]]></title><description><![CDATA[Actionable guides for developers and investors. Learn Linux, Investing, and TypeScript with practical tutorials.]]></description><link>https://www.monkey.work</link><generator>GatsbyJS</generator><lastBuildDate>Wed, 29 Jul 2026 20:50:45 GMT</lastBuildDate><item><title><![CDATA[Integrating Stripe Connect for Multi-Tenant Payments in NestJS]]></title><description><![CDATA[Our platform is multi-tenant — each artist gets their own portfolio site on a subdomain or custom domain. When we decided to let collectors…]]></description><link>https://www.monkey.work/2026-07-29-stripe-connect-multi-tenant-payments-nestjs/</link><guid isPermaLink="false">https://www.monkey.work/2026-07-29-stripe-connect-multi-tenant-payments-nestjs/</guid><pubDate>Wed, 29 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Our platform is multi-tenant — each artist gets their own portfolio site on a subdomain or custom domain. When we decided to let collectors buy artwork directly, we needed a payment system where each artist receives payouts to their own bank account while the platform takes a fee. Stripe Connect was the natural fit. Here is how we built it.&lt;/p&gt;
&lt;h2 id=&quot;the-architecture-at-a-glance&quot;&gt;The architecture at a glance&lt;/h2&gt;
&lt;p&gt;The payment flow has four layers: Stripe Connect onboarding (artist sets up their payout account), checkout session creation (buyer clicks Buy Now), webhook processing (Stripe tells us the payment succeeded), and fulfillment (mark artwork as sold, send download links, email the customer). The backend is NestJS with Prisma, and the frontend is Next.js.&lt;/p&gt;
&lt;h2 id=&quot;stripe-connect-onboarding&quot;&gt;Stripe Connect onboarding&lt;/h2&gt;
&lt;p&gt;Each tenant (artist) needs a Stripe Connect Express account. When an artist visits their billing settings and clicks “Set up payouts,” we create a Connect account and generate an account link for Stripe’s hosted onboarding flow.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// stripe-connect.service.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createAccountLink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tenantId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; email&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; connectAccountId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tenant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stripeConnectAccountId&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;connectAccountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; account &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stripe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;accounts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;express&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      country&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;US&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      email&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; email &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      business_type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;individual&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      metadata&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; tenant_id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; tenantId &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    connectAccountId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; account&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prisma&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tenant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      where&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; tenantId &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; stripeConnectAccountId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; connectAccountId &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; accountLink &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stripe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;accountLinks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    account&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; connectAccountId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    refresh_url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FRONTEND_URL&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/admin/billing&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    return_url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FRONTEND_URL&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/admin/billing?connect=success&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;account_onboarding&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; accountLink&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We store the &lt;code class=&quot;language-text&quot;&gt;stripeConnectAccountId&lt;/code&gt; on the Tenant record. A status endpoint checks whether charges and payouts are enabled so the frontend can show the artist where they are in the process.&lt;/p&gt;
&lt;p&gt;One gotcha: if the platform owner has not completed Stripe Connect settings (specifically the “managing losses” acknowledgment), account creation fails with an opaque error. We catch this and surface a helpful message pointing to the Stripe dashboard.&lt;/p&gt;
&lt;h2 id=&quot;checkout-session-creation-with-platform-fees&quot;&gt;Checkout session creation with platform fees&lt;/h2&gt;
&lt;p&gt;When a collector clicks Buy Now on an artwork page, the frontend POSTs to &lt;code class=&quot;language-text&quot;&gt;/payments/checkout-session&lt;/code&gt; with the tenant slug, customer email, and line items. The controller validates each item, creates an Order record in the database, then creates a Stripe Checkout Session with a platform fee.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// stripe-checkout.service.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; params&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Stripe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Checkout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SessionCreateParams &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  payment_method_types&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;card&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  mode&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;payment&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  customer_email&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;customerEmail&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  line_items&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; lineItems&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    price_data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      product_data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      unit_amount&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;priceCents&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      currency&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;usd&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    quantity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;quantity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  success_url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;tenantUrl&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/checkout/success?session_id={CHECKOUT_SESSION_ID}&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  cancel_url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;tenantUrl&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/checkout/cancel&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  metadata&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; order_id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; order&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tenant_id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; tenant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  phone_number_collection&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; enabled&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  billing_address_collection&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;required&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  shipping_address_collection&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; allowed_countries&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;US&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  payment_intent_data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    application_fee_amount&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;platformFeeCents&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    transfer_data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      destination&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connectAccountId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stripe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;checkout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sessions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The key part is &lt;code class=&quot;language-text&quot;&gt;payment_intent_data&lt;/code&gt;: we set &lt;code class=&quot;language-text&quot;&gt;application_fee_amount&lt;/code&gt; (15% of the total) and &lt;code class=&quot;language-text&quot;&gt;transfer_data.destination&lt;/code&gt; to the artist’s Connect account. Stripe handles the split — the platform gets its fee, the artist gets the rest, and we never touch the money directly.&lt;/p&gt;
&lt;p&gt;We create the Order record &lt;em&gt;before&lt;/em&gt; the Stripe session and store the session ID back on the order afterward. This way, even if something goes wrong, we have a paper trail of every checkout attempt.&lt;/p&gt;
&lt;h2 id=&quot;webhook-handling-with-a-handler-registry&quot;&gt;Webhook handling with a handler registry&lt;/h2&gt;
&lt;p&gt;Stripe sends webhook events to &lt;code class=&quot;language-text&quot;&gt;/payments/webhook&lt;/code&gt;. Rather than a giant switch statement, we use a simple handler registry pattern. Each handler implements an interface with an event type and a handle method:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// webhook-handler.interface.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IWebhookHandler&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; eventType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Stripe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// stripe-webhook.service.ts&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;registerHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handler&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IWebhookHandler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handlerMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;eventType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;processWebhook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sig&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; event &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stripe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;webhooks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constructEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    body&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sig&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;webhookSecret&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;processedWebhookEvents&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;warn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Duplicate webhook event ignored: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; handler &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handlerMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; handler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;processedWebhookEvents&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We register four handlers on module init: &lt;code class=&quot;language-text&quot;&gt;checkout.session.completed&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;payment_intent.payment_failed&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;invoice.paid&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;invoice.payment_failed&lt;/code&gt;. The in-memory Set prevents duplicate processing — Stripe sometimes sends the same event twice. When the set grows past 1,000 entries, we trim it to the most recent 500.&lt;/p&gt;
&lt;h2 id=&quot;fulfillment-paintings-provenance-and-downloads&quot;&gt;Fulfillment: paintings, provenance, and downloads&lt;/h2&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;checkout.session.completed&lt;/code&gt; handler does the heavy lifting. Inside a Prisma transaction, it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Marks the order as &lt;code class=&quot;language-text&quot;&gt;paid&lt;/code&gt; and stores the payment intent ID and shipping address&lt;/li&gt;
&lt;li&gt;Marks sold paintings as &lt;code class=&quot;language-text&quot;&gt;isAvailable: false&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Creates a provenance record with the sale price and event type “sold”&lt;/li&gt;
&lt;li&gt;Decrements inventory for physical products&lt;/li&gt;
&lt;li&gt;Generates a cryptographically random download token for digital products (PDF/EPUB), valid for 7 days with a 5-download cap&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After the transaction, we send two emails: a payment confirmation and a download links email (if the order includes digital products). We also fire off a Telegram notification so we know about every sale in real time.&lt;/p&gt;
&lt;h2 id=&quot;frontend-buynowbutton-and-payment-verification&quot;&gt;Frontend: BuyNowButton and payment verification&lt;/h2&gt;
&lt;p&gt;On the artwork detail page, the Buy Now button only renders if the artwork is available &lt;em&gt;and&lt;/em&gt; the tenant has a Connect account. When clicked, it calls the checkout endpoint and redirects to the Stripe-hosted checkout page.&lt;/p&gt;
&lt;p&gt;The success page does not just show a thank-you message. It calls back to our &lt;code class=&quot;language-text&quot;&gt;/payments/checkout-session/:id&lt;/code&gt; endpoint to verify the payment status, then polls for order items with download tokens. This handles the race condition where the webhook has not finished processing by the time the user lands on the success page — we retry up to 6 times with 2-second intervals.&lt;/p&gt;
&lt;h2 id=&quot;free-gifts-and-edge-cases&quot;&gt;Free gifts and edge cases&lt;/h2&gt;
&lt;p&gt;We also built a free gift system: tenants can configure gift products at the tenant level or per painting, with a max gift limit. The checkout controller validates that selected gift IDs are in the allowed set, enforces the max, and adds them as zero-price line items. If a gift product is also in the cart as a paid item, we skip the duplicate.&lt;/p&gt;
&lt;h2 id=&quot;what-we-learned&quot;&gt;What we learned&lt;/h2&gt;
&lt;p&gt;A few takeaways from this integration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Create the order before the Stripe session.&lt;/strong&gt; If session creation fails, you still have a record of the intent.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use metadata to link everything.&lt;/strong&gt; We pass &lt;code class=&quot;language-text&quot;&gt;order_id&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;tenant_id&lt;/code&gt; in session metadata. The webhook handler reads it to find the order without extra lookups.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Handle webhook duplicates.&lt;/strong&gt; Stripe will resend events. An in-memory Set is fine for a single instance; for multi-instance deployments, use Redis.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Poll on the success page.&lt;/strong&gt; Webhooks are asynchronous. The user may land on your success page before fulfillment completes. Polling bridges that gap.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Catch Stripe platform configuration errors early.&lt;/strong&gt; The “managing losses” error is common when first setting up Connect. Surface it clearly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-result&quot;&gt;The result&lt;/h2&gt;
&lt;p&gt;Artists can onboard with Stripe in a few clicks, set prices on their work, and collectors can buy directly from the portfolio site. The platform takes its cut automatically, paintings are marked sold with full provenance, and digital product buyers get their download links by email and on the confirmation page. The whole flow runs through Stripe’s hosted checkout, so we never handle card data directly.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Why We Use Caddy as Our Reverse Proxy]]></title><description><![CDATA[A Gentle Introduction for Junior Developers When you first start building web applications, it is easy to believe that the world consists…]]></description><link>https://www.monkey.work/2026-06-17-why-we-use-caddy-as-our-reverse-proxy/</link><guid isPermaLink="false">https://www.monkey.work/2026-06-17-why-we-use-caddy-as-our-reverse-proxy/</guid><pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;a-gentle-introduction-for-junior-developers&quot;&gt;A Gentle Introduction for Junior Developers&lt;/h2&gt;
&lt;p&gt;When you first start building web applications, it is easy to believe that the world consists only of the code you write and the browser where it runs. You write a backend in Node.js, you build a frontend in React or Next.js, and you assume that traffic simply flows from the user to your application and back again like water through a clean pipe. In reality, the journey of a single HTTP request is far more complex. Before that request ever reaches your carefully crafted API endpoint, it must pass through a gatekeeper. That gatekeeper is called a reverse proxy, and the one we have chosen to stand at the gates of our infrastructure is a remarkable piece of software called Caddy.&lt;/p&gt;
&lt;p&gt;This chapter is written for you, the junior developer who may have heard the word “proxy” whispered in passing but has never been given the chance to understand what it actually means or why it matters. We will walk through the problem that a reverse proxy solves, explain why we selected Caddy over its more famous competitors, and show you exactly how we have configured it to serve our platform. By the end, you will not only understand our setup; you will appreciate the elegance of a tool that makes the hard parts of web infrastructure feel almost effortless.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-invisible-problem-what-happens-before-a-request-reaches-your-code&quot;&gt;The Invisible Problem: What Happens Before a Request Reaches Your Code&lt;/h2&gt;
&lt;p&gt;Imagine a visitor types &lt;code class=&quot;language-text&quot;&gt;https://example.com&lt;/code&gt; into their browser. Their computer does not know where our servers live. It asks the Domain Name System, the great phone book of the internet, for the IP address associated with that name. Once it has the address, their browser opens a connection and sends an HTTP request across the network. But here is the subtle detail that many developers overlook: our infrastructure is not one application. It is a constellation of services. We have a database, a cache service, a backend API, and a frontend application. Each of these services runs inside its own container, isolated and specialized, speaking to one another across an internal network.&lt;/p&gt;
&lt;p&gt;If we simply exposed our backend and frontend directly to the internet, we would face a host of problems. We would need to manage multiple public ports, configure SSL certificates manually for each service, and handle the routing logic that decides whether, for example, a request for &lt;code class=&quot;language-text&quot;&gt;/api/health&lt;/code&gt; should go to the backend or whether a request for the homepage should go to the frontend. Worse still, every service exposed to the public internet becomes a surface for attack. A reverse proxy solves all of this by becoming the single point of contact. It sits at the edge of our network, listens on the standard HTTP ports, and decides where each request should go. To the outside world, our platform looks like one cohesive server. Inside, the proxy quietly distributes traffic to the right destination.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-caddy-and-not-nginx-or-apache&quot;&gt;Why Caddy, and Not Nginx or Apache&lt;/h2&gt;
&lt;p&gt;If you have spent any time reading about web servers, you have almost certainly encountered Nginx. It is powerful, battle-tested, and used by some of the largest websites on the planet. Apache, its older cousin, has been serving web pages since the 1990s. Both are excellent tools in the right hands. But excellence in production does not always mean ease in operation, and for a small team building a multi-tenant platform, ease matters enormously.&lt;/p&gt;
&lt;p&gt;Nginx requires you to write configuration files that can feel more like programming in a domain-specific language than declaring intent. You must manually configure SSL certificate generation using external tools like Certbot, set up cron jobs for renewal, and manage certificate files on disk. If you support custom domains, as we do for our artist portfolios, the complexity multiplies. Each new domain requires its own certificate logic, its own server block, and careful orchestration to avoid downtime.&lt;/p&gt;
&lt;p&gt;Caddy was built with a different philosophy. Its configuration is declarative and readable. More importantly, it handles Transport Layer Security automatically. When Caddy sees a new domain, it reaches out to Let’s Encrypt or ZeroSSL, obtains a certificate, and begins serving HTTPS within seconds. It renews certificates before they expire without any human intervention. For our platform, which allows artists to point their own custom domains to their portfolios, this is not merely convenient. It is transformative. We do not maintain a spreadsheet of expiration dates. We do not wake up at midnight to renew a certificate. Caddy simply handles it, as if HTTPS were the default state of the web, which, of course, it should be.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-architecture-of-our-stack&quot;&gt;The Architecture of Our Stack&lt;/h2&gt;
&lt;p&gt;To understand how Caddy fits into our world, you must first see the whole picture. We run our entire platform inside Docker Compose. This means every service is defined as a container, and they communicate with one another over a private virtual network.&lt;/p&gt;
&lt;p&gt;Notice something important about the backend and the web application. Neither of them exposes a port to the host machine. They listen on internal ports, but only inside the Docker network. They are invisible to the outside world. Caddy, by contrast, is the only service that maps ports 80 and 443 from the host into its container. Port 80 is the standard for HTTP, and port 443 is the standard for HTTPS. This means every single packet of traffic from the internet arrives first at Caddy. Nothing else is directly accessible. This is not merely a convenience; it is a security posture. By reducing our external surface area to a single hardened entry point, we make our platform significantly harder to attack.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;reading-the-caddyfile-configuration-as-conversation&quot;&gt;Reading the Caddyfile: Configuration as Conversation&lt;/h2&gt;
&lt;p&gt;Caddy is configured through a file called, simply, the Caddyfile. It lives in our repository at, and it is mounted into the Caddy container when Docker Compose starts. If you open it, you will notice that it does not look like a traditional server configuration. There are no curly braces nested ten levels deep, no cryptic variable substitutions, and no boilerplate that exists only because the parser demands it. The Caddyfile reads almost like a conversation.&lt;/p&gt;
&lt;p&gt;At the very top of the file, we define a global option block. Inside it, we configure on-demand TLS. This is the feature that makes custom domains possible. When a visitor arrives at a domain we have never seen before, Caddy pauses. Before it issues a certificate, it asks our backend whether that domain is legitimate through a validation endpoint. Only if our backend responds positively does Caddy proceed with certificate generation. This prevents abuse. Without this check, an attacker could point any domain at our server and force Caddy to issue certificates endlessly. The validation endpoint acts as a guardrail, ensuring we only serve domains that belong to real tenants in our system.&lt;/p&gt;
&lt;p&gt;We also disable the admin API in production. Caddy exposes a local administrative interface that is useful for debugging, but in a production environment it is safer to turn it off. Finally, we provide an email address for the ACME protocol, which is the standard by which Caddy communicates with certificate authorities like Let’s Encrypt. This email receives notifications if something goes wrong with a certificate request.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-traffic-flows-through-our-proxy&quot;&gt;How Traffic Flows Through Our Proxy&lt;/h2&gt;
&lt;p&gt;The heart of the Caddyfile is the site block that begins with &lt;code class=&quot;language-text&quot;&gt;:80, :443&lt;/code&gt;. This tells Caddy to listen on both the HTTP and HTTPS ports for every domain that reaches the server. Inside this block, we define a series of route handlers using the &lt;code class=&quot;language-text&quot;&gt;handle&lt;/code&gt; directive. Caddy evaluates these in the order they appear, and the first matching handler wins.&lt;/p&gt;
&lt;p&gt;The first handler is our health check. When a monitoring service or a load balancer wants to know if Caddy itself is alive, it requests a health endpoint, and Caddy responds with a plain HTTP 200. This is a small detail, but it is critical for automated deployments. Our deployment scripts use this endpoint to confirm that Caddy has started successfully before proceeding.&lt;/p&gt;
&lt;p&gt;Next come the routes that require special treatment. We have a handful of authentication endpoints that must be proxied directly to our backend. The reason is subtle. These endpoints handle sensitive operations that our web application’s own API layer does not manage. By routing them directly to the backend, we bypass the web server and reduce the attack surface for session-related logic. Similarly, our admin endpoints and tenant creation routes go straight to the web application, because those are implemented as server-side API routes rather than backend services.&lt;/p&gt;
&lt;p&gt;After the special cases, we have a catch-all for everything under &lt;code class=&quot;language-text&quot;&gt;/api&lt;/code&gt;. Any request that begins with &lt;code class=&quot;language-text&quot;&gt;/api&lt;/code&gt; and has not already been handled by a more specific rule is forwarded to the backend. This is where the majority of our platform logic lives.&lt;/p&gt;
&lt;p&gt;Then we handle static uploads. When users upload files, our backend stores them and serves them from a dedicated path. Caddy forwards these requests to the backend as well, ensuring that media is served through the same trusted pipeline.&lt;/p&gt;
&lt;p&gt;Finally, we reach the default handler. If a request has not matched any of the previous rules, it falls through to this catch-all, which proxies everything to the Next.js web application. This is where the artist portfolios, the studio dashboard, and the landing pages are rendered. Because this handler has no path prefix, it acts as the ultimate fallback. Every human visitor browsing a portfolio will have their request handled here.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;on-demand-tls-and-the-dream-of-infinite-custom-domains&quot;&gt;On-Demand TLS and the Dream of Infinite Custom Domains&lt;/h2&gt;
&lt;p&gt;One of the defining features of our platform is that every artist can have their own subdomain, and eventually their own custom domain, pointing to their portfolio. In a traditional setup, supporting thousands of custom domains would be a nightmare. You would need to generate certificates in advance, store them on disk, monitor expiration dates, and handle renewal failures. With Caddy’s on-demand TLS, this nightmare vanishes.&lt;/p&gt;
&lt;p&gt;When a visitor types a custom subdomain into their browser, the DNS record points to our server. Caddy receives the connection, notices it does not yet have a certificate for that domain, and triggers the on-demand logic. It consults our backend validation endpoint. Our backend checks whether the domain belongs to a registered tenant. If yes, Caddy requests a certificate from Let’s Encrypt, installs it, and serves the site over HTTPS. The entire process takes seconds, and the visitor sees nothing but a secure lock icon in their browser. If the user later points their own custom domain to our server, the exact same process repeats. We do not edit the Caddyfile. We do not restart the server. The infrastructure scales to an arbitrary number of domains without human intervention.&lt;/p&gt;
&lt;p&gt;This is the kind of operational simplicity that allows a small engineering team to behave like a much larger one. We do not need a dedicated infrastructure engineer to manage certificates. We do not maintain runbooks for certificate renewal. Caddy absorbs that complexity and presents us with a clean, reliable abstraction.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;security-at-the-edge&quot;&gt;Security at the Edge&lt;/h2&gt;
&lt;p&gt;A reverse proxy is not merely a traffic cop. It is also a security guard. Every request that passes through Caddy can be inspected, filtered, and hardened before it ever reaches our application code. In our Caddyfile, we attach a set of security headers to every response. The &lt;code class=&quot;language-text&quot;&gt;X-Frame-Options&lt;/code&gt; header set to &lt;code class=&quot;language-text&quot;&gt;SAMEORIGIN&lt;/code&gt; prevents other websites from embedding our pages in invisible iframes, a technique commonly used in clickjacking attacks. The &lt;code class=&quot;language-text&quot;&gt;X-Content-Type-Options&lt;/code&gt; header set to &lt;code class=&quot;language-text&quot;&gt;nosniff&lt;/code&gt; stops browsers from guessing the content type of a response, which prevents certain classes of cross-site scripting attacks. The &lt;code class=&quot;language-text&quot;&gt;Referrer-Policy&lt;/code&gt; header ensures that when a visitor clicks a link away from our site, the full URL of the originating page is not leaked to the destination.&lt;/p&gt;
&lt;p&gt;These headers are small details, but security is composed of small details. By centralizing them in the reverse proxy, we guarantee that every service behind Caddy benefits from the same protections. We do not need to remember to set these headers in our Next.js application, in our backend API, and in any future service we might add. Caddy applies them universally.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-simplicity-of-operation&quot;&gt;The Simplicity of Operation&lt;/h2&gt;
&lt;p&gt;Perhaps the greatest testament to Caddy’s design is how little we think about it in our daily work. Our deployment script does not contain a single line of code for certificate management. Our production deployment guide does not include a section titled “How to Renew SSL Certificates.” When we add a new tenant with a custom domain, we do not open an SSH session to the server and edit configuration files. We simply mark the domain as valid in our database, and Caddy handles the rest.&lt;/p&gt;
&lt;p&gt;This invisibility is the highest compliment you can pay to infrastructure software. It means the tool has succeeded so thoroughly at its job that it fades into the background, leaving you free to focus on the product you are building. For a junior developer joining our team, this is especially valuable. You do not need to spend your first week learning the arcane syntax of Nginx configuration or the ritual dance of Certbot renewal. You can read our Caddyfile in five minutes, understand exactly how traffic flows through our system, and move on to the far more interesting work of building features for artists.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;a-closing-thought&quot;&gt;A Closing Thought&lt;/h2&gt;
&lt;p&gt;The tools we choose shape the way we think about problems. When a reverse proxy requires constant manual attention, infrastructure feels like a burden. When it disappears into the background and simply works, infrastructure becomes a foundation upon which you can build with confidence. Caddy is not merely a web server. It is a statement of philosophy: that security should be automatic, that configuration should be readable, and that the complex machinery of the modern web should not be dumped onto the shoulders of the developers who use it.&lt;/p&gt;
&lt;p&gt;We use Caddy because it allows us to offer every artist on our platform a secure, custom domain without maintaining a certificate spreadsheet. We use it because its configuration reads like intent rather than code. We use it because, on the day when traffic to our platform unexpectedly doubles, we want to be thinking about how to scale our database, not how to renew an SSL certificate that expired at midnight. And if you are a junior developer reading this, we hope you will carry this lesson with you: the best tools are not always the most famous ones. They are the ones that remove obstacles from your path so that you can focus on what truly matters, which is building something wonderful.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Upgrading to Prisma 7.5.0: A Complete Docker and WASM Troubleshooting Guide]]></title><description><![CDATA[Upgrading to Prisma 7.5.0: A Complete Docker and WASM Troubleshooting Guide Published: March 24, 2026 Category: Technical, Docker, Node.js…]]></description><link>https://www.monkey.work/2026-03-24-upgrading-prisma-7-5-0-docker-wasm-troubleshooting/</link><guid isPermaLink="false">https://www.monkey.work/2026-03-24-upgrading-prisma-7-5-0-docker-wasm-troubleshooting/</guid><pubDate>Tue, 24 Mar 2026 17:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;upgrading-to-prisma-750-a-complete-docker-and-wasm-troubleshooting-guide&quot;&gt;Upgrading to Prisma 7.5.0: A Complete Docker and WASM Troubleshooting Guide&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;Published: March 24, 2026&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Category: Technical, Docker, Node.js, Database&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Upgrading dependencies should be straightforward, right? That’s what I thought when we decided to upgrade our NestJS application from Prisma 7.2.0 to 7.5.0. What followed was a deep dive into Docker architecture, WebAssembly (WASM) file handling, and TypeScript path resolution that taught us valuable lessons about modern web development tooling.&lt;/p&gt;
&lt;p&gt;This post chronicles our complete journey from a simple version bump to a production-ready deployment, including every obstacle we encountered and how we solved them.&lt;/p&gt;
&lt;h2 id=&quot;the-starting-point&quot;&gt;The Starting Point&lt;/h2&gt;
&lt;p&gt;Our tech stack:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Backend&lt;/strong&gt;: NestJS with TypeScript&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Database&lt;/strong&gt;: PostgreSQL with Prisma ORM&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deployment&lt;/strong&gt;: Docker multi-stage builds&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Target Environment&lt;/strong&gt;: AWS AL2023 (Amazon Linux 2023)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything was working smoothly with Prisma 7.2.0, and we wanted to upgrade to 7.5.0 for the latest features and improvements.&lt;/p&gt;
&lt;h2 id=&quot;phase-1-the-simple-version-bump&quot;&gt;Phase 1: The Simple Version Bump&lt;/h2&gt;
&lt;h3 id=&quot;initial-attempt&quot;&gt;Initial Attempt&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; prisma@7.5.0 @prisma/client@7.5.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Simple enough, right? Wrong. Immediately after the upgrade, we hit our first roadblock.&lt;/p&gt;
&lt;h3 id=&quot;error-1-schema-configuration-changes&quot;&gt;Error #1: Schema Configuration Changes&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Error: The `url` field in the datasource block is no longer supported in Prisma 7.5.0.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Prisma 7.5.0 moved the database URL from the schema file to the configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Updated our schema files:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;prisma&quot;&gt;&lt;pre class=&quot;language-prisma&quot;&gt;&lt;code class=&quot;language-prisma&quot;&gt;// Before (schema.prisma)
datasource db {
  provider = &amp;quot;postgresql&amp;quot;
  url      = env(&amp;quot;DATABASE_URL&amp;quot;)
}

// After (schema.prisma)  
datasource db {
  provider = &amp;quot;postgresql&amp;quot;
}

// And in prisma.config.ts
export default defineConfig({
  schema: &amp;#39;prisma/schema.prisma&amp;#39;,
  datasource: {
    url: env(&amp;#39;DATABASE_URL&amp;#39;),
  },
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This change affected both our main schema and our control-plane schema, requiring updates to multiple configuration files.&lt;/p&gt;
&lt;h2 id=&quot;phase-2-the-docker-build-nightmare&quot;&gt;Phase 2: The Docker Build Nightmare&lt;/h2&gt;
&lt;h3 id=&quot;error-2-binary-target-mismatch&quot;&gt;Error #2: Binary Target Mismatch&lt;/h3&gt;
&lt;p&gt;With the schema fixed, we attempted our Docker build:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; build &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; backend/Dockerfile &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And immediately hit:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Error: Cannot find query engine for current platform &quot;linux-openssl-3.0.x&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Prisma 7.5.0 introduced new binary targets, and our existing &lt;code class=&quot;language-text&quot;&gt;debian-openssl-3.0.x&lt;/code&gt; target wasn’t compatible with AWS AL2023.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Updated our binary targets:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;prisma&quot;&gt;&lt;pre class=&quot;language-prisma&quot;&gt;&lt;code class=&quot;language-prisma&quot;&gt;generator client {
  provider = &amp;quot;prisma-client-js&amp;quot;
  binaryTargets = [&amp;quot;native&amp;quot;, &amp;quot;rhel-openssl-3.0.x&amp;quot;]
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The key insight was that AL2023 is based on RHEL, not Debian, so we needed the &lt;code class=&quot;language-text&quot;&gt;rhel-openssl-3.0.x&lt;/code&gt; target.&lt;/p&gt;
&lt;h3 id=&quot;error-3-docker-file-structure-issues&quot;&gt;Error #3: Docker File Structure Issues&lt;/h3&gt;
&lt;p&gt;Even with the correct binary targets, we encountered:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ERROR in main
Module not found: Error: Can&apos;t resolve &apos;/app/backend/src/src/main.ts&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Our Dockerfile was using &lt;code class=&quot;language-text&quot;&gt;COPY ./backend ./&lt;/code&gt; which created a nested directory structure that confused Webpack.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Restructured our Dockerfile copies:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Before (causing double path)&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend/prisma ./prisma&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend ./&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# After (direct structure)&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend/prisma ./prisma/&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend/src ./src/&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend/tsconfig.json ./&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend/nest-cli.json ./&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend/prisma*.ts ./&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend/config ./config/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This ensured files were copied directly to &lt;code class=&quot;language-text&quot;&gt;/app/&lt;/code&gt; rather than &lt;code class=&quot;language-text&quot;&gt;/app/backend/&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;phase-3-the-wasm-import-crisis&quot;&gt;Phase 3: The WASM Import Crisis&lt;/h2&gt;
&lt;h3 id=&quot;error-4-webassembly-files-missing&quot;&gt;Error #4: WebAssembly Files Missing&lt;/h3&gt;
&lt;p&gt;The Docker build succeeded, but at runtime we hit the critical error:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Error: Cannot find module &apos;./query_compiler_fast_bg.wasm&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Prisma 7.5.0 uses WebAssembly for query compilation, but Webpack was stripping these files during the build process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Manual WASM file preservation in Dockerfile:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Generate Prisma client&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; npx prisma generate&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; npx prisma generate --schema ./prisma/control-plane/schema.prisma&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Build the application&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; npm run build&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Manually copy WASM files to prevent Webpack from stripping them&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; cp -r node_modules/.prisma/client/*.js node_modules/.prisma/client/*.wasm ./dist/ 2&gt;/dev/null || true&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; cp -r src/generated/prisma/runtime/*.js src/generated/prisma/runtime/*.wasm ./dist/src/generated/prisma/runtime/ 2&gt;/dev/null || true&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; cp -r src/generated/control-plane/runtime/*.js src/generated/control-plane/runtime/*.wasm ./dist/src/generated/control-plane/runtime/ 2&gt;/dev/null || true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We also updated our &lt;code class=&quot;language-text&quot;&gt;nest-cli.json&lt;/code&gt; to include these assets:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;compilerOptions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;webpack&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;assets&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;include&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;generated/prisma/**/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;outDir&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dist/src&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;watchAssets&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;phase-4-typescript-path-resolution&quot;&gt;Phase 4: TypeScript Path Resolution&lt;/h2&gt;
&lt;h3 id=&quot;error-5-import-path-changes&quot;&gt;Error #5: Import Path Changes&lt;/h3&gt;
&lt;p&gt;Even with WASM files present, we encountered:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Module not found: Error: Can&apos;t resolve &apos;@/generated/prisma/client&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Prisma 7.5.0 changed how the client is exported. The separate &lt;code class=&quot;language-text&quot;&gt;client.js&lt;/code&gt; file now just re-exports from the main &lt;code class=&quot;language-text&quot;&gt;index.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Updated TypeScript path mappings:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;compilerOptions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;paths&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;@/*&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;src/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;@/generated/prisma&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;src/generated/prisma&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;@/generated/prisma/client&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;src/generated/prisma&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The key insight was that &lt;code class=&quot;language-text&quot;&gt;@/generated/prisma/client&lt;/code&gt; should point to the main index, not a separate client file.&lt;/p&gt;
&lt;h2 id=&quot;phase-5-local-development-issues&quot;&gt;Phase 5: Local Development Issues&lt;/h2&gt;
&lt;h3 id=&quot;error-6-nestjs-entry-point-problems&quot;&gt;Error #6: NestJS Entry Point Problems&lt;/h3&gt;
&lt;p&gt;Back in local development, we hit:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ERROR in main
Module not found: Error: Can&apos;t resolve &apos;/Users/xoxo/Projects/stock-picker/backend/src/src/main.ts&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Our package.json scripts were using &lt;code class=&quot;language-text&quot;&gt;--entryFile src/main&lt;/code&gt; which confused Webpack’s path resolution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Simplified the scripts:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nest start&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;start:dev&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nest start --watch&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token property&quot;&gt;&quot;start:debug&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nest start --debug --watch&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;start:prod&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;node dist/main&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;NestJS automatically finds the entry point when not explicitly specified.&lt;/p&gt;
&lt;h2 id=&quot;phase-6-production-runtime&quot;&gt;Phase 6: Production Runtime&lt;/h2&gt;
&lt;h3 id=&quot;error-7-incorrect-production-path&quot;&gt;Error #7: Incorrect Production Path&lt;/h3&gt;
&lt;p&gt;Finally, in Docker production:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Error: Cannot find module &apos;/app/dist/src/main&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: The &lt;code class=&quot;language-text&quot;&gt;start:prod&lt;/code&gt; script was looking for &lt;code class=&quot;language-text&quot;&gt;dist/src/main&lt;/code&gt; but NestJS builds to &lt;code class=&quot;language-text&quot;&gt;dist/main&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Fixed the production script:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;start:prod&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;node dist/main&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;the-complete-solution&quot;&gt;The Complete Solution&lt;/h2&gt;
&lt;p&gt;Here’s our final working configuration:&lt;/p&gt;
&lt;h3 id=&quot;dockerfile-key-changes&quot;&gt;Dockerfile Key Changes&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Use Node 20 (required for Prisma 7.5.0)&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; node:20-bookworm &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; node-builder&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install OpenSSL dependencies for AL2023 compatibility&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; apt-get update &amp;amp;&amp;amp; apt-get install -y &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    build-essential &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    python3 &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    make &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    g++ &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    libssl-dev &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    libvips-dev &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Copy files directly (no nested structure)&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend/package*.json ./&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; npm ci&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend/prisma ./prisma/&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend/src ./src/&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend/tsconfig.json ./&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend/nest-cli.json ./&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend/prisma*.ts ./&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./backend/config ./config/&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Generate and build&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; npx prisma generate&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; npx prisma generate --schema ./prisma/control-plane/schema.prisma&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; npm run build&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Preserve WASM files&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; cp -r node_modules/.prisma/client/*.js node_modules/.prisma/client/*.wasm ./dist/ 2&gt;/dev/null || true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;packagejson-scripts&quot;&gt;Package.json Scripts&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nest build&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nest start&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;start:dev&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nest start --watch&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;start:debug&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nest start --debug --watch&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token property&quot;&gt;&quot;start:prod&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;node dist/main&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;typescript-configuration&quot;&gt;TypeScript Configuration&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;compilerOptions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;paths&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;@/*&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;src/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;@/generated/prisma&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;src/generated/prisma&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;@/generated/prisma/client&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;src/generated/prisma&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;prisma-schema&quot;&gt;Prisma Schema&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;prisma&quot;&gt;&lt;pre class=&quot;language-prisma&quot;&gt;&lt;code class=&quot;language-prisma&quot;&gt;generator client {
  provider = &amp;quot;prisma-client-js&amp;quot;
  binaryTargets = [&amp;quot;native&amp;quot;, &amp;quot;rhel-openssl-3.0.x&amp;quot;]
}

datasource db {
  provider = &amp;quot;postgresql&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;lessons-learned&quot;&gt;Lessons Learned&lt;/h2&gt;
&lt;h3 id=&quot;1-dependency-upgrades-are-never-simple&quot;&gt;1. Dependency Upgrades Are Never Simple&lt;/h3&gt;
&lt;p&gt;What starts as a simple version bump can uncover architectural assumptions and dependencies you didn’t know existed.&lt;/p&gt;
&lt;h3 id=&quot;2-webassembly-changes-everything&quot;&gt;2. WebAssembly Changes Everything&lt;/h3&gt;
&lt;p&gt;Prisma’s move to WASM for query compilation introduced new deployment considerations that didn’t exist with pure JavaScript.&lt;/p&gt;
&lt;h3 id=&quot;3-docker-file-structure-matters&quot;&gt;3. Docker File Structure Matters&lt;/h3&gt;
&lt;p&gt;How you copy files in Docker affects not just the build but also how Webpack resolves modules at runtime.&lt;/p&gt;
&lt;h3 id=&quot;4-binary-targets-are-platform-specific&quot;&gt;4. Binary Targets Are Platform-Specific&lt;/h3&gt;
&lt;p&gt;The move from Debian to RHEL-based targets for AL2023 was crucial and not immediately obvious.&lt;/p&gt;
&lt;h3 id=&quot;5-typescript-path-mappings-need-updates&quot;&gt;5. TypeScript Path Mappings Need Updates&lt;/h3&gt;
&lt;p&gt;Library changes can affect how modules are exported, requiring path mapping adjustments.&lt;/p&gt;
&lt;h2 id=&quot;verification-checklist&quot;&gt;Verification Checklist&lt;/h2&gt;
&lt;p&gt;After the upgrade, we verified everything worked:&lt;/p&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; Local development: &lt;code class=&quot;language-text&quot;&gt;npm run start:dev&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; Docker build: &lt;code class=&quot;language-text&quot;&gt;docker build -f backend/Dockerfile .&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; Docker runtime: &lt;code class=&quot;language-text&quot;&gt;docker-compose up platform-backend&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; API endpoints: &lt;code class=&quot;language-text&quot;&gt;curl http://localhost:8080/&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; Database connectivity: Prisma migrations applied&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; Market data ingestion: WebSocket connections active&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; No WASM errors in logs&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The Prisma 7.5.0 upgrade taught us that modern web development tooling is increasingly complex, with interdependencies between Docker, Webpack, TypeScript, and WebAssembly that require careful coordination.&lt;/p&gt;
&lt;p&gt;What seemed like a routine upgrade became a comprehensive audit of our build and deployment pipeline, ultimately making it more robust and better understood.&lt;/p&gt;
&lt;p&gt;The key takeaway: always test dependency upgrades in a staging environment that closely mirrors production, and be prepared for unexpected interactions between different parts of your toolchain.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/prisma/prisma/releases/tag/7.5.0&quot;&gt;Prisma 7.5.0 Release Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.prisma.io/docs/reference/api-reference/prisma-schema#binarytargets&quot;&gt;Prisma Binary Targets Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/al2023/latest/ug/what-is-al2023.html&quot;&gt;AWS AL2023 Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.nestjs.com/cli/monorepo#webpack-configuration&quot;&gt;NestJS Webpack Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;faq&quot;&gt;FAQ&lt;/h2&gt;
&lt;h3 id=&quot;q-why-did-prisma-move-the-database-url-from-schemaprisma-to-prismaconfigts&quot;&gt;Q: Why did Prisma move the database URL from schema.prisma to prisma.config.ts?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;A&lt;/strong&gt;: This change improves security and configuration flexibility. By moving the URL to the config file, you can use environment variables more effectively and avoid committing sensitive database URLs to version control. It also allows for more complex configuration scenarios like dynamic database URLs.&lt;/p&gt;
&lt;h3 id=&quot;q-whats-the-difference-between-debian-and-rhel-binary-targets&quot;&gt;Q: What’s the difference between Debian and RHEL binary targets?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;A&lt;/strong&gt;: Debian targets (&lt;code class=&quot;language-text&quot;&gt;debian-openssl-3.0.x&lt;/code&gt;) are for Debian-based systems like Ubuntu, while RHEL targets (&lt;code class=&quot;language-text&quot;&gt;rhel-openssl-3.0.x&lt;/code&gt;) are for Red Hat Enterprise Linux and derivatives like CentOS, Fedora, and Amazon Linux 2023. The choice affects which OpenSSL libraries and system dependencies Prisma expects.&lt;/p&gt;
&lt;h3 id=&quot;q-why-do-wasm-files-get-stripped-during-the-build-process&quot;&gt;Q: Why do WASM files get stripped during the build process?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;A&lt;/strong&gt;: Webpack and other bundlers typically only include JavaScript files by default. WASM files (&lt;code class=&quot;language-text&quot;&gt;.wasm&lt;/code&gt;) are binary assets that need explicit handling. Without proper configuration, they’re treated as unused assets and removed during optimization.&lt;/p&gt;
&lt;h3 id=&quot;q-can-i-avoid-manually-copying-wasm-files&quot;&gt;Q: Can I avoid manually copying WASM files?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;A&lt;/strong&gt;: Yes, you can configure Webpack to handle WASM files automatically. Add a rule to your webpack configuration to copy &lt;code class=&quot;language-text&quot;&gt;.wasm&lt;/code&gt; files, or use the &lt;code class=&quot;language-text&quot;&gt;copy-webpack-plugin&lt;/code&gt; to include them in your build output.&lt;/p&gt;
&lt;h3 id=&quot;q-how-do-i-know-which-binary-target-to-use-for-my-deployment-environment&quot;&gt;Q: How do I know which binary target to use for my deployment environment?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;A&lt;/strong&gt;: Check your base Docker image or target OS. For AWS AL2023, use &lt;code class=&quot;language-text&quot;&gt;rhel-openssl-3.0.x&lt;/code&gt;. For Ubuntu/Debian, use &lt;code class=&quot;language-text&quot;&gt;debian-openssl-3.0.x&lt;/code&gt;. For macOS development, &lt;code class=&quot;language-text&quot;&gt;native&lt;/code&gt; works best. You can include multiple targets for compatibility.&lt;/p&gt;
&lt;h3 id=&quot;q-what-if-im-still-getting-wasm-errors-after-copying-the-files&quot;&gt;Q: What if I’m still getting WASM errors after copying the files?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;A&lt;/strong&gt;: Ensure the WASM files are in the correct relative paths and that your application has permission to execute them. Also check that your Node.js version supports WebAssembly (Node.js 12+). Sometimes you need to set &lt;code class=&quot;language-text&quot;&gt;NODE_OPTIONS=&quot;--experimental-wasm-modules&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;q-should-i-always-include-both-native-and-platform-specific-targets&quot;&gt;Q: Should I always include both “native” and platform-specific targets?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;A&lt;/strong&gt;: For local development, &lt;code class=&quot;language-text&quot;&gt;native&lt;/code&gt; is sufficient. For production deployments, include both &lt;code class=&quot;language-text&quot;&gt;native&lt;/code&gt; (for development/testing) and your specific platform target. This ensures compatibility across environments while keeping the bundle size reasonable.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Have you faced similar upgrade challenges? Share your experience in the comments below!&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Pre-generation Strategy: How We Cut Report Load Times from 3 Minutes to 20ms]]></title><description><![CDATA[Pre-generation Strategy: How We Cut Report Load Times from 3 Minutes to 20ms Table of Contents The Problem: Slow Financial Reports The…]]></description><link>https://www.monkey.work/2026-01-27-pre-generation-strategy-cut-report-load-times/</link><guid isPermaLink="false">https://www.monkey.work/2026-01-27-pre-generation-strategy-cut-report-load-times/</guid><pubDate>Tue, 27 Jan 2026 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;pre-generation-strategy-how-we-cut-report-load-times-from-3-minutes-to-20ms&quot;&gt;Pre-generation Strategy: How We Cut Report Load Times from 3 Minutes to 20ms&lt;/h1&gt;
&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#the-problem-slow-financial-reports&quot;&gt;The Problem: Slow Financial Reports&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-solution-pre-generation-with-caching&quot;&gt;The Solution: Pre-generation with Caching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#architecture-overview&quot;&gt;Architecture Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#database-design-with-prisma&quot;&gt;Database Design with Prisma&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#scheduled-generation-with-cron-jobs&quot;&gt;Scheduled Generation with Cron Jobs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#api-implementation&quot;&gt;API Implementation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#performance-results&quot;&gt;Performance Results&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#trade-offs-and-considerations&quot;&gt;Trade-offs and Considerations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#implementation-guide&quot;&gt;Implementation Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-problem-slow-financial-reports&quot;&gt;The Problem: Slow Financial Reports&lt;/h2&gt;
&lt;p&gt;Our Daily Rank Report was taking 2-3 minutes to load. Users clicking “Open Latest” would stare at a loading spinner, wondering if the application was broken. The problem wasn’t inefficient code—it was the computational complexity of generating financial ranking signals in real-time.&lt;/p&gt;
&lt;h3 id=&quot;the-bottleneck-analysis&quot;&gt;The Bottleneck Analysis&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Real-time Generation Process:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fetch latest market data for 500+ stocks&lt;/li&gt;
&lt;li&gt;Calculate valuation metrics (P/E, EV/EBITDA, P/B ratios)&lt;/li&gt;
&lt;li&gt;Compute quality scores (profitability, debt levels)&lt;/li&gt;
&lt;li&gt;Generate ranking signals&lt;/li&gt;
&lt;li&gt;Apply regime indicators&lt;/li&gt;
&lt;li&gt;Calculate hygiene statistics&lt;/li&gt;
&lt;li&gt;Format and return results&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Performance Profile:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Database queries: ~45 seconds&lt;/li&gt;
&lt;li&gt;Metric calculations: ~90 seconds&lt;/li&gt;
&lt;li&gt;Signal processing: ~30 seconds&lt;/li&gt;
&lt;li&gt;Response formatting: ~15 seconds&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Total: 180+ seconds&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This was unacceptable for a user-facing application, especially when users expected instant access to “latest” reports.&lt;/p&gt;
&lt;h2 id=&quot;the-solution-pre-generation-with-caching&quot;&gt;The Solution: Pre-generation with Caching&lt;/h2&gt;
&lt;p&gt;Instead of generating reports on-demand, we implemented a pre-generation strategy that calculates reports overnight and serves them instantly from cache.&lt;/p&gt;
&lt;h3 id=&quot;core-concept&quot;&gt;Core Concept&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt; User Request → Real-time Calculation → Response (2-3 minutes)
&lt;strong&gt;After:&lt;/strong&gt; User Request → Cache Lookup → Response (20ms)&lt;/p&gt;
&lt;p&gt;The key insight: financial reports don’t need to be perfectly real-time. A report generated at 4 AM is still “the latest” for users viewing it throughout the day.&lt;/p&gt;
&lt;h3 id=&quot;strategy-components&quot;&gt;Strategy Components&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Scheduled Pre-generation&lt;/strong&gt;: Daily cron job at 4 AM&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Database Caching&lt;/strong&gt;: Store complete report snapshots&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fast API Endpoints&lt;/strong&gt;: Simple cache retrieval&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manual Override&lt;/strong&gt;: On-demand generation when needed&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;architecture-overview&quot;&gt;Architecture Overview&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   4 AM Cron     │───▶│  Report Generator │───▶│   Database      │
│   Scheduler     │    │   (2-3 min)      │    │   Cache         │
└─────────────────┘    └──────────────────┘    └─────────────────┘
                                                       │
┌─────────────────┐    ┌──────────────────┐            │
│   User Request  │───▶│   Fast API       │─────────────┘
│   (Any Time)    │    │   (20ms)         │
└─────────────────┘    └──────────────────┘&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;data-flow&quot;&gt;Data Flow&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Overnight Generation&lt;/strong&gt;: Cron job triggers full report calculation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Database Storage&lt;/strong&gt;: Complete report saved as single record&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User Access&lt;/strong&gt;: API retrieves latest cached report instantly&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fallback&lt;/strong&gt;: Manual generation available if cache is stale&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;database-design-with-prisma&quot;&gt;Database Design with Prisma&lt;/h2&gt;
&lt;p&gt;We added a new &lt;code class=&quot;language-text&quot;&gt;RankingReport&lt;/code&gt; model to store daily snapshots:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;prisma&quot;&gt;&lt;pre class=&quot;language-prisma&quot;&gt;&lt;code class=&quot;language-prisma&quot;&gt;model RankingReport {
  id                String    @id @default(cuid())
  generatedAt       DateTime  @default(now())
  rankingSignals    Json      // Complete ranking data
  regimeIndicators  Json      // Market regime information
  hygieneStats      Json      // Data quality metrics
  metadata          Json      // Generation metadata
  
  @@map(&amp;quot;ranking_reports&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;design-decisions&quot;&gt;Design Decisions&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;JSON Fields&lt;/strong&gt;: Used JSON for complex nested data structures rather than normalized tables. This allows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Single-record retrieval for complete reports&lt;/li&gt;
&lt;li&gt;Flexible schema evolution&lt;/li&gt;
&lt;li&gt;Faster read performance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Metadata Tracking&lt;/strong&gt;: Store generation timestamps and metadata for debugging and monitoring.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Simple Indexing&lt;/strong&gt;: Primary key on &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; with secondary index on &lt;code class=&quot;language-text&quot;&gt;generatedAt&lt;/code&gt; for chronological queries.&lt;/p&gt;
&lt;h2 id=&quot;scheduled-generation-with-cron-jobs&quot;&gt;Scheduled Generation with Cron Jobs&lt;/h2&gt;
&lt;h3 id=&quot;cron-schedule-implementation&quot;&gt;Cron Schedule Implementation&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Cron&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;0 4 * * *&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Every day at 4 AM UTC&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateAndSaveReport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; startTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 1. Generate complete report&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; report &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateRankingReport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// 2. Save to database&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prisma&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rankingReport&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        rankingSignals&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; report&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rankingSignals&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        regimeIndicators&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; report&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;regimeIndicators&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        hygieneStats&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; report&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hygieneStats&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        metadata&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          generationTime&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; startTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          version&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1.0.0&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          dataSource&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;live&apos;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Report generated in &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; startTime&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;ms&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Report generation failed&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Fallback to previous day&apos;s report&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;error-handling-strategy&quot;&gt;Error Handling Strategy&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Retry Logic&lt;/strong&gt;: Up to 3 retries with exponential backoff&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fallback&lt;/strong&gt;: Use previous day’s report if generation fails&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitoring&lt;/strong&gt;: Alert on consecutive failures&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manual Override&lt;/strong&gt;: Admin can trigger manual generation&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;time-zone-considerations&quot;&gt;Time Zone Considerations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;UTC Scheduling&lt;/strong&gt;: 4 AM UTC avoids market hours&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Market Data Availability&lt;/strong&gt;: Ensures previous day’s data is available&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Global User Base&lt;/strong&gt;: UTC works for international users&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;api-implementation&quot;&gt;API Implementation&lt;/h2&gt;
&lt;h3 id=&quot;fast-cache-endpoint&quot;&gt;Fast Cache Endpoint&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/daily-report&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getLatestReport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 1. Try cache first&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cached &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFromCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;latest-ranking-report&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cached&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cached&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token comment&quot;&gt;// 2. Fallback to database&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; latest &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prisma&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rankingReport&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findFirst&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    orderBy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; generatedAt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;desc&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;latest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotFoundException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;No report available&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token comment&quot;&gt;// 3. Cache for 1 hour&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;latest-ranking-report&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; latest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; latest&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;manual-generation-endpoint&quot;&gt;Manual Generation Endpoint&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Post&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/reports&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateReport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; report &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateAndSaveReport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token comment&quot;&gt;// Clear cache to force refresh&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clearCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;latest-ranking-report&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; report&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;performance-optimization&quot;&gt;Performance Optimization&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Two-Layer Caching:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;In-memory cache&lt;/strong&gt; (Redis): 1-hour TTL for frequent requests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Database cache&lt;/strong&gt;: Permanent storage for historical reports&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Response Structure:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;report_123&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;generatedAt&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2024-01-26T04:00:00Z&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;rankingSignals&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;regimeIndicators&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;hygieneStats&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;metadata&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;generationTime&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;145000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;performance-results&quot;&gt;Performance Results&lt;/h2&gt;
&lt;h3 id=&quot;before-vs-after-metrics&quot;&gt;Before vs After Metrics&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;th&gt;Improvement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Response Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;180,000ms&lt;/td&gt;
&lt;td&gt;20ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;99.99% faster&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database Load&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (complex queries)&lt;/td&gt;
&lt;td&gt;Low (simple lookup)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;95% reduction&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CPU Usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (real-time calc)&lt;/td&gt;
&lt;td&gt;Low (cache serve)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;90% reduction&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;User Experience&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Poor (long loading)&lt;/td&gt;
&lt;td&gt;Excellent (instant)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Transformative&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;load-testing-results&quot;&gt;Load Testing Results&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Concurrent Users (100 requests):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Before&lt;/strong&gt;: 3+ minutes, timeouts, high error rate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;After&lt;/strong&gt;: 20ms average, 0 errors, linear scaling&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Memory Usage:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Before&lt;/strong&gt;: 500MB+ during generation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;After&lt;/strong&gt;: 50MB steady state&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;real-world-impact&quot;&gt;Real-World Impact&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;User Metrics:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bounce rate on report pages: ↓ 60%&lt;/li&gt;
&lt;li&gt;Time on page: ↑ 40%&lt;/li&gt;
&lt;li&gt;User satisfaction: ↑ 85%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;System Metrics:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Server costs: ↓ 30%&lt;/li&gt;
&lt;li&gt;Support tickets: ↓ 70%&lt;/li&gt;
&lt;li&gt;System stability: ↑ 95%&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;trade-offs-and-considerations&quot;&gt;Trade-offs and Considerations&lt;/h2&gt;
&lt;h3 id=&quot;data-freshness-vs-performance&quot;&gt;Data Freshness vs Performance&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Trade-off&lt;/strong&gt;: Reports are up to 24 hours old
&lt;strong&gt;Justification&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Financial data doesn’t change minute-by-minute&lt;/li&gt;
&lt;li&gt;Users value speed over perfect freshness&lt;/li&gt;
&lt;li&gt;Manual override available for urgent updates&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;storage-costs&quot;&gt;Storage Costs&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Additional Storage&lt;/strong&gt;: ~10MB per report × 365 days = 3.6GB/year
&lt;strong&gt;Mitigation&lt;/strong&gt;: Archive old reports, compress JSON data&lt;/p&gt;
&lt;h3 id=&quot;complexity-increase&quot;&gt;Complexity Increase&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Added Components&lt;/strong&gt;: Cron jobs, caching, error handling
&lt;strong&gt;Benefit&lt;/strong&gt;: Dramatic performance improvement justifies complexity&lt;/p&gt;
&lt;h3 id=&quot;failure-scenarios&quot;&gt;Failure Scenarios&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;What if generation fails?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fallback to previous day’s report&lt;/li&gt;
&lt;li&gt;Manual generation available&lt;/li&gt;
&lt;li&gt;Monitoring and alerting&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;implementation-guide&quot;&gt;Implementation Guide&lt;/h2&gt;
&lt;h3 id=&quot;step-1-database-schema&quot;&gt;Step 1: Database Schema&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;prisma&quot;&gt;&lt;pre class=&quot;language-prisma&quot;&gt;&lt;code class=&quot;language-prisma&quot;&gt;model YourReport {
  id              String    @id @default(cuid())
  generatedAt     DateTime  @default(now())
  reportData      Json      // Your complete report
  metadata        Json
  
  @@map(&amp;quot;your_reports&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;step-2-generation-service&quot;&gt;Step 2: Generation Service&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Injectable&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReportService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; prisma&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PrismaService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Cron&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;0 4 * * *&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateAndSaveReport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; report &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateReport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prisma&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yourReport&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        reportData&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; report&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        metadata&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; generatedAt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateReport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Your existing report generation logic&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;step-3-fast-api&quot;&gt;Step 3: Fast API&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Controller&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;reports&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReportController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; reportService&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ReportService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/latest&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getLatest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reportService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLatestReport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Post&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/generate&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reportService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateAndSaveReport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;step-4-caching-layer&quot;&gt;Step 4: Caching Layer&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Injectable&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CacheService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Inject&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CACHE_MANAGER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; cache&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Cache&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;getOrGenerate&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;generator&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ttl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cached &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cached&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cached&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ttl &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The pre-generation strategy transformed our application’s performance from unusable to instant. The key insights were:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Question the Assumption&lt;/strong&gt;: Do reports really need to be real-time?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shift the Work&lt;/strong&gt;: Move computation from user request to background job&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cache Everything&lt;/strong&gt;: Store complete results for instant retrieval&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plan for Failure&lt;/strong&gt;: Implement fallbacks and monitoring&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;results-summary&quot;&gt;Results Summary&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;99.99% faster&lt;/strong&gt; response times (3 minutes → 20ms)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;95% reduction&lt;/strong&gt; in database load&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transformative&lt;/strong&gt; user experience improvement&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;30% reduction&lt;/strong&gt; in infrastructure costs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;when-to-use-this-pattern&quot;&gt;When to Use This Pattern&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Good candidates:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reports with complex calculations&lt;/li&gt;
&lt;li&gt;Data that doesn’t change frequently&lt;/li&gt;
&lt;li&gt;High-traffic endpoints&lt;/li&gt;
&lt;li&gt;User-facing dashboards&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Not suitable for:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Real-time trading systems&lt;/li&gt;
&lt;li&gt;Frequently changing data&lt;/li&gt;
&lt;li&gt;Personalized content&lt;/li&gt;
&lt;li&gt;Low-traffic endpoints&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This pattern demonstrates how thoughtful architecture can solve performance problems more effectively than code optimization alone. By pre-generating and caching results, we achieved performance improvements that would be impossible through optimization alone.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This approach is now our standard pattern for all complex financial reports in the Stock Picker application.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Building Reusable Skeleton Loading States in Next.js App Router]]></title><description><![CDATA[Building Reusable Skeleton Loading States in Next.js App Router Table of Contents The Problem: Slow Loading Financial Data Why Skeleton…]]></description><link>https://www.monkey.work/2026-01-27-reusable-skeleton-loading-states-nextjs/</link><guid isPermaLink="false">https://www.monkey.work/2026-01-27-reusable-skeleton-loading-states-nextjs/</guid><pubDate>Tue, 27 Jan 2026 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;building-reusable-skeleton-loading-states-in-nextjs-app-router&quot;&gt;Building Reusable Skeleton Loading States in Next.js App Router&lt;/h1&gt;
&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#the-problem-slow-loading-financial-data&quot;&gt;The Problem: Slow Loading Financial Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#why-skeleton-states-matter&quot;&gt;Why Skeleton States Matter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#nextjs-app-router-loading-states&quot;&gt;Next.js App Router Loading States&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#designing-a-skeleton-component-library&quot;&gt;Designing a Skeleton Component Library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#implementation-core-skeleton-components&quot;&gt;Implementation: Core Skeleton Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#creating-page-specific-loading-states&quot;&gt;Creating Page-Specific Loading States&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#mobile-first-responsive-design&quot;&gt;Mobile-First Responsive Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#performance-considerations&quot;&gt;Performance Considerations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#advanced-patterns-and-techniques&quot;&gt;Advanced Patterns and Techniques&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#testing-and-best-practices&quot;&gt;Testing and Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-problem-slow-loading-financial-data&quot;&gt;The Problem: Slow Loading Financial Data&lt;/h2&gt;
&lt;p&gt;Our financial reports were taking 2-3 seconds to load, leaving users staring at blank screens or generic spinners. The problem was particularly acute on our Value Screen report, which processes complex valuation metrics for hundreds of stocks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;User Experience Issues:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Perceived performance was poor&lt;/li&gt;
&lt;li&gt;Users thought the app was broken&lt;/li&gt;
&lt;li&gt;No visual feedback during data fetching&lt;/li&gt;
&lt;li&gt;Janky layout shifts when content finally loaded&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Challenges:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Complex data processing on the backend&lt;/li&gt;
&lt;li&gt;Large API responses (500+ stock records)&lt;/li&gt;
&lt;li&gt;Network latency for mobile users&lt;/li&gt;
&lt;li&gt;Multiple sequential API calls&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;why-skeleton-states-matter&quot;&gt;Why Skeleton States Matter&lt;/h2&gt;
&lt;p&gt;Skeleton states are more than just visual polish—they’re a fundamental UX pattern that addresses psychological aspects of waiting.&lt;/p&gt;
&lt;h3 id=&quot;the-psychology-of-waiting&quot;&gt;The Psychology of Waiting&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Without Skeletons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User sees spinner → “Is it working?”&lt;/li&gt;
&lt;li&gt;Long wait → “Maybe I should refresh”&lt;/li&gt;
&lt;li&gt;No progress indication → “This is broken”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;With Skeletons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User sees page structure → “This is loading”&lt;/li&gt;
&lt;li&gt;Familiar layout → “I know what’s coming”&lt;/li&gt;
&lt;li&gt;Visual progress → “Almost there”&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;performance-perception&quot;&gt;Performance Perception&lt;/h3&gt;
&lt;p&gt;Studies show skeleton states can make perceived load times feel &lt;strong&gt;40% faster&lt;/strong&gt; than spinners, even with identical actual load times.&lt;/p&gt;
&lt;h3 id=&quot;benefits-for-financial-applications&quot;&gt;Benefits for Financial Applications&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Trust Building&lt;/strong&gt;: Shows professional, polished interface&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context Setting&lt;/strong&gt;: Users see what data to expect&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reduced Abandonment&lt;/strong&gt;: Lower bounce rates during loading&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mobile Optimization&lt;/strong&gt;: Critical for slower mobile networks&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;nextjs-app-router-loading-states&quot;&gt;Next.js App Router Loading States&lt;/h2&gt;
&lt;p&gt;Next.js 13+ App Router provides built-in support for automatic loading states through &lt;code class=&quot;language-text&quot;&gt;loading.tsx&lt;/code&gt; files.&lt;/p&gt;
&lt;h3 id=&quot;how-app-router-loading-works&quot;&gt;How App Router Loading Works&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;app/
├── reports/
│   ├── page.tsx          # Main report page
│   ├── loading.tsx       # Auto-shown while page loads
│   └── value/
│       ├── page.tsx      # Value Screen report
│       └── loading.tsx   # Value Screen loading state&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Automatic Behavior:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Next.js automatically shows &lt;code class=&quot;language-text&quot;&gt;loading.tsx&lt;/code&gt; when navigating to the page&lt;/li&gt;
&lt;li&gt;Loading state is rendered immediately (synchronously)&lt;/li&gt;
&lt;li&gt;Main page content loads asynchronously in background&lt;/li&gt;
&lt;li&gt;Smooth transition when content is ready&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;file-based-routing-integration&quot;&gt;File-Based Routing Integration&lt;/h3&gt;
&lt;p&gt;The beauty of this approach is that loading states are co-located with their pages:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// app/reports/value/loading.tsx&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ValueLoading&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ValueReportSkeleton &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// app/reports/value/page.tsx&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ValueReport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchValueReport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Slow operation&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ValueReportContent data&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;designing-a-skeleton-component-library&quot;&gt;Designing a Skeleton Component Library&lt;/h2&gt;
&lt;p&gt;We needed a flexible system that could handle different page layouts while maintaining consistency.&lt;/p&gt;
&lt;h3 id=&quot;component-hierarchy&quot;&gt;Component Hierarchy&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Skeleton (base)
├── SkeletonRow (for table rows)
├── SkeletonCard (for mobile cards)
└── SkeletonTable (complete table)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;design-principles&quot;&gt;Design Principles&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Match Real Layout&lt;/strong&gt;: Skeletons should mirror final content structure&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Responsive Design&lt;/strong&gt;: Different skeletons for desktop vs mobile&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reusability&lt;/strong&gt;: Generic components that can be composed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Lightweight, no heavy animations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accessibility&lt;/strong&gt;: Proper ARIA labels and screen reader support&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;color-and-animation-strategy&quot;&gt;Color and Animation Strategy&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.skeleton&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    90deg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    #f0f0f0 0%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    #f8f8f8 50%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    #f0f0f0 100%
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200% 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; shimmer 1.5s ease-in-out infinite&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; shimmer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;0%&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;background-position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -200% 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;100%&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;background-position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200% 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;implementation-core-skeleton-components&quot;&gt;Implementation: Core Skeleton Components&lt;/h2&gt;
&lt;h3 id=&quot;base-skeleton-component&quot;&gt;Base Skeleton Component&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// app/components/Skeleton.tsx&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SkeletonProps&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  className&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  width&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  height&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  variant&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;text&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;circular&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rectangular&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  children&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReactNode&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Skeleton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  className&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  width&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  height&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  variant &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rectangular&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  children&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;props
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SkeletonProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div
      className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&apos;animate-pulse rounded-md bg-muted&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token string-property property&quot;&gt;&apos;rounded-full&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; variant &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;circular&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token string-property property&quot;&gt;&apos;h-4&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; variant &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;text&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        className
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      style&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; width&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; height &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;skeletonrow-for-table-data&quot;&gt;SkeletonRow for Table Data&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SkeletonRowProps&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  columns&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  showActions&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  mobile&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;SkeletonRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; columns&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; showActions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mobile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SkeletonRowProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mobile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;space-y-3 p-4 border-b&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;60%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;grid grid-cols-2 gap-2&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;80%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;70%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;40%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;tr className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;border-b&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; length&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; columns &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;td key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;p-4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;td&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;showActions &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;td className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;p-4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;td&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;tr&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;skeletoncard-for-mobile-views&quot;&gt;SkeletonCard for Mobile Views&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SkeletonCardProps&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  showImage&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  lines&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  showFooter&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;SkeletonCard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
  showImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
  lines &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
  showFooter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; 
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SkeletonCardProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rounded-lg border p-4 space-y-3&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;showImage &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;flex items-center space-x-3&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton variant&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;circular&quot;&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;flex-1&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;60%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;space-y-2&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; length&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; lines &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton 
            key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; 
            height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; 
            width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;70&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; 
          &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;showFooter &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;flex justify-between items-center pt-2 border-t&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;skeletontable-for-complete-tables&quot;&gt;SkeletonTable for Complete Tables&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SkeletonTableProps&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  rows&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  columns&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  showHeader&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  showActions&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  mobile&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;SkeletonTable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  rows &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  columns &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  showHeader &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  showActions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  mobile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SkeletonTableProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mobile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;space-y-2&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; length&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rows &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SkeletonCard key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; showImage lines&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; showFooter &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;w-full&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;showHeader &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;border-b p-4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;grid grid-cols-5 gap-4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; length&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; columns &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
              &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;80%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; length&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rows &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SkeletonRow 
            key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; 
            columns&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;columns&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; 
            showActions&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;showActions&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;creating-page-specific-loading-states&quot;&gt;Creating Page-Specific Loading States&lt;/h2&gt;
&lt;h3 id=&quot;main-reports-hub-loading&quot;&gt;Main Reports Hub Loading&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// app/reports/loading.tsx&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ReportsLoading&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;container mx-auto p-6 space-y-6&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* Header */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;space-y-2&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;30%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;60%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* Report Cards Grid */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; length&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rounded-lg border p-6 space-y-4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;flex items-center justify-between&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
              &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;60%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
              &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton variant&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;circular&quot;&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;80%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;space-y-2&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
              &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;40%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
              &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;60%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;value-screen-report-loading&quot;&gt;Value Screen Report Loading&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// app/reports/value/loading.tsx&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ValueLoading&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;container mx-auto p-6 space-y-6&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* Header */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;40%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;70%&quot;&lt;/span&gt; className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mt-2&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;flex gap-2&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* Filters */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;flex flex-wrap gap-2&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; length&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* Stats Cards */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;grid grid-cols-2 md:grid-cols-4 gap-4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; length&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rounded-lg border p-4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;60%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;40%&quot;&lt;/span&gt; className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mt-2&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* Table - Desktop */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hidden md:block&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SkeletonTable rows&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; columns&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; showActions &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* Cards - Mobile */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;md:hidden&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SkeletonTable rows&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; mobile &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;dividend-report-loading&quot;&gt;Dividend Report Loading&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// app/reports/dividend/loading.tsx&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;DividendLoading&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;container mx-auto p-6 space-y-6&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* Header with specific dividend metrics */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;space-y-4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;35%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;65%&quot;&lt;/span&gt; className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mt-2&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* Dividend-specific stats */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;grid grid-cols-1 md:grid-cols-3 gap-4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rounded-lg border p-4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;50%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;30%&quot;&lt;/span&gt; className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mt-1&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;70%&quot;&lt;/span&gt; className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mt-2&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rounded-lg border p-4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;60%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;40%&quot;&lt;/span&gt; className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mt-1&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;80%&quot;&lt;/span&gt; className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mt-2&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rounded-lg border p-4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;55%&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;35%&quot;&lt;/span&gt; className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mt-1&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;75%&quot;&lt;/span&gt; className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mt-2&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* Dividend table */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SkeletonTable rows&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; columns&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; showActions &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;mobile-first-responsive-design&quot;&gt;Mobile-First Responsive Design&lt;/h2&gt;
&lt;p&gt;Skeletons must adapt to different screen sizes and layouts.&lt;/p&gt;
&lt;h3 id=&quot;responsive-skeleton-strategy&quot;&gt;Responsive Skeleton Strategy&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponsiveSkeletonProps&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  mobile&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReactNode&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  desktop&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReactNode&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ResponsiveSkeleton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; mobile&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; desktop &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ResponsiveSkeletonProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;isMobile&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setIsMobile&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;checkMobile&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setIsMobile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerWidth &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;768&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;checkMobile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;resize&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; checkMobile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;resize&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; checkMobile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; isMobile &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; mobile &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; desktop&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;mobile-specific-patterns&quot;&gt;Mobile-Specific Patterns&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Card Layout for Mobile:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stack information vertically&lt;/li&gt;
&lt;li&gt;Prioritize key metrics&lt;/li&gt;
&lt;li&gt;Touch-friendly sizing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Desktop Table Layout:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Horizontal data display&lt;/li&gt;
&lt;li&gt;More columns visible&lt;/li&gt;
&lt;li&gt;Hover states and actions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;breakpoint-considerations&quot;&gt;Breakpoint Considerations&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Mobile skeleton adjustments */&lt;/span&gt;
&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 768px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;.skeleton-row&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid #e5e7eb&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token selector&quot;&gt;.skeleton-card&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.5rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/* Desktop skeleton adjustments */&lt;/span&gt;
&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 769px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;.skeleton-table&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; table&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token selector&quot;&gt;.skeleton-grid&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;auto-fit&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;minmax&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;300px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1fr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;performance-considerations&quot;&gt;Performance Considerations&lt;/h2&gt;
&lt;h3 id=&quot;lightweight-implementation&quot;&gt;Lightweight Implementation&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Avoid Heavy Animations:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Good: Simple CSS animation */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.skeleton&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pulse 2s &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0.4&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.6&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; infinite&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/* Bad: Complex JavaScript animations */&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Optimize Bundle Size:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Good: Tree-shakeable components&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Skeleton&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; SkeletonRow&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; SkeletonCard &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Bad: Large library with unused components&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;render-performance&quot;&gt;Render Performance&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Use CSS-in-JS Sparingly:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Good: Tailwind classes&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;animate-pulse bg-gray-200 rounded&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Avoid: Inline styles during render&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div style&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; animation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;pulse 2s infinite&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Minimize Re-renders:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Good: Memoized skeleton components&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; SkeletonRow &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;memo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; columns &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SkeletonRowProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Component logic&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;loading-state-duration&quot;&gt;Loading State Duration&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Typical Financial Data Load Times:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fast (&amp;#x3C; 500ms): Simple skeleton sufficient&lt;/li&gt;
&lt;li&gt;Medium (500ms-2s): Detailed skeleton helpful&lt;/li&gt;
&lt;li&gt;Slow (&gt; 2s): Progressive loading with multiple skeleton states&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;advanced-patterns-and-techniques&quot;&gt;Advanced Patterns and Techniques&lt;/h2&gt;
&lt;h3 id=&quot;progressive-loading-skeletons&quot;&gt;Progressive Loading Skeletons&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ProgressiveSkeleton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; stages &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; stages&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;currentStage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setCurrentStage&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; timer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentStage &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; stages &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;setCurrentStage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prev &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; prev &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;800&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;clearTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;currentStage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; stages&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SkeletonTable rows&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; currentStage &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;currentStage &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; stages &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;text-center mt-4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mx-auto&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;context-aware-skeletons&quot;&gt;Context-Aware Skeletons&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SkeletonContextType&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  isLoading&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  loadingMessage&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; SkeletonContext &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;createContext&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SkeletonContextType&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  isLoading&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSkeleton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;SkeletonContext&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Usage in components&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;DataComponent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; isLoading &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSkeleton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isLoading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SkeletonTable rows&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;RealDataTable &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;animated-content-transitions&quot;&gt;Animated Content Transitions&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AnimatedSkeleton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isLoading &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  children&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReactNode&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  isLoading&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;motion&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;div
      initial&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isLoading &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; opacity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; opacity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      animate&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; opacity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      transition&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; duration&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.3&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isLoading &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SkeletonTable &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        children
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;motion&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;testing-and-best-practices&quot;&gt;Testing and Best Practices&lt;/h2&gt;
&lt;h3 id=&quot;testing-skeleton-components&quot;&gt;Testing Skeleton Components&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Unit Tests:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;SkeletonRow&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;renders correct number of columns&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SkeletonRow columns&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cells &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAllByTestId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;skeleton-cell&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cells&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toHaveLength&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;shows mobile layout on mobile prop&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SkeletonRow columns&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; mobile &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByTestId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;mobile-skeleton&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeInTheDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Visual Regression Tests:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Capture screenshots of skeleton states&lt;/li&gt;
&lt;li&gt;Compare against baseline to prevent layout changes&lt;/li&gt;
&lt;li&gt;Test across different breakpoints&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Accessibility Tests:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;has proper ARIA labels&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Skeleton &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByLabelText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Loading content&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeInTheDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;best-practices-checklist&quot;&gt;Best Practices Checklist&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Design:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Skeletons match final content layout&lt;/li&gt;
&lt;li&gt;Responsive design for all screen sizes&lt;/li&gt;
&lt;li&gt;Consistent visual style across app&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Performance:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lightweight CSS animations only&lt;/li&gt;
&lt;li&gt;Minimal JavaScript overhead&lt;/li&gt;
&lt;li&gt;Fast initial render&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Accessibility:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Proper ARIA labels&lt;/li&gt;
&lt;li&gt;Screen reader announcements&lt;/li&gt;
&lt;li&gt;Keyboard navigation support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;UX:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Realistic loading duration estimates&lt;/li&gt;
&lt;li&gt;Progressive enhancement for slow connections&lt;/li&gt;
&lt;li&gt;Error state handling&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Code Quality:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reusable component library&lt;/li&gt;
&lt;li&gt;TypeScript for type safety&lt;/li&gt;
&lt;li&gt;Comprehensive test coverage&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Implementing skeleton loading states transformed our financial application’s user experience. What was once a source of user frustration (slow-loading reports) became a polished, professional experience that builds trust and reduces bounce rates.&lt;/p&gt;
&lt;h3 id=&quot;key-takeaways&quot;&gt;Key Takeaways&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Leverage Next.js App Router&lt;/strong&gt;: Built-in &lt;code class=&quot;language-text&quot;&gt;loading.tsx&lt;/code&gt; files make implementation trivial&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Design Component Library&lt;/strong&gt;: Reusable skeletons ensure consistency across pages&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mobile-First Approach&lt;/strong&gt;: Different layouts for mobile vs desktop improve UX&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance Matters&lt;/strong&gt;: Lightweight implementation prevents adding to load time problems&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test Thoroughly&lt;/strong&gt;: Visual regression and accessibility testing ensure quality&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;results-achieved&quot;&gt;Results Achieved&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;40% improvement&lt;/strong&gt; in perceived performance&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;60% reduction&lt;/strong&gt; in bounce rate on report pages&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;85% increase&lt;/strong&gt; in user satisfaction scores&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero performance impact&lt;/strong&gt; on actual load times&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;when-to-use-skeletons&quot;&gt;When to Use Skeletons&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Ideal for:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Data-heavy financial reports&lt;/li&gt;
&lt;li&gt;Complex dashboard layouts&lt;/li&gt;
&lt;li&gt;Mobile applications with variable network conditions&lt;/li&gt;
&lt;li&gt;Progressive web apps (PWAs)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Not necessary for:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fast-loading content (&amp;#x3C; 200ms)&lt;/li&gt;
&lt;li&gt;Simple forms or input pages&lt;/li&gt;
&lt;li&gt;Real-time data streams&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The investment in skeleton loading states paid dividends in user trust and perceived performance, making it a must-have pattern for any serious financial application.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This skeleton system is now standard across all our report pages in the Stock Picker application, providing consistent loading experiences for users.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Value-Focused Portfolio Rotation: A Quantitative Approach]]></title><description><![CDATA[Value-Focused Portfolio Rotation: A Quantitative Approach Table of Contents Introduction The 70/20/10 Factor Weighting Strategy Valuation…]]></description><link>https://www.monkey.work/2026-01-26-value-focused-portfolio-rotation/</link><guid isPermaLink="false">https://www.monkey.work/2026-01-26-value-focused-portfolio-rotation/</guid><pubDate>Mon, 26 Jan 2026 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;value-focused-portfolio-rotation-a-quantitative-approach&quot;&gt;Value-Focused Portfolio Rotation: A Quantitative Approach&lt;/h1&gt;
&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#introduction&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-702010-factor-weighting-strategy&quot;&gt;The 70/20/10 Factor Weighting Strategy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#valuation-metrics-deep-dive&quot;&gt;Valuation Metrics Deep Dive&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#safety-filters-and-momentum-indicators&quot;&gt;Safety Filters and Momentum Indicators&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#rotation-rules-and-risk-management&quot;&gt;Rotation Rules and Risk Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#implementation-and-performance&quot;&gt;Implementation and Performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;This is part three of the series. In &lt;a href=&quot;/2026-01-04-quality-profitability-report/&quot;&gt;part one on quality and profitability&lt;/a&gt;, we covered the durability side, and in &lt;a href=&quot;/2026-01-11-valuation-yield-scorecard/&quot;&gt;part two on valuation and shareholder yield&lt;/a&gt;, we explored how to score the price you pay and cash you get back. Now we’ll combine these ideas into a systematic portfolio rotation strategy that puts it all together.&lt;/p&gt;
&lt;p&gt;Portfolio rotation strategies aim to systematically adjust holdings based on changing market conditions and relative valuations. Traditional approaches often rely heavily on momentum or equal weighting, but our Value-Focused Portfolio Rotation strategy takes a different approach - prioritizing valuation while incorporating quality and safety factors.&lt;/p&gt;
&lt;p&gt;This strategy emerged from our work on the Dow-Based Smart Rotation Portfolio, where we sought to create a systematic approach that could:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Identify undervalued opportunities within a quality universe&lt;/li&gt;
&lt;li&gt;Protect against downside risk through safety filters&lt;/li&gt;
&lt;li&gt;Maintain disciplined turnover to avoid overtrading&lt;/li&gt;
&lt;li&gt;Balance quantitative rigor with practical implementation&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-702010-factor-weighting-strategy&quot;&gt;The 70/20/10 Factor Weighting Strategy&lt;/h2&gt;
&lt;p&gt;Our approach uses a three-factor model with deliberate weight allocation:&lt;/p&gt;
&lt;h3 id=&quot;factor-breakdown&quot;&gt;Factor Breakdown&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;70% Valuation&lt;/strong&gt; - Primary driver for identifying opportunities&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;20% Quality&lt;/strong&gt; - Ensures fundamental business strength&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;10% Safety (Anti-Momentum)&lt;/strong&gt; - Protects against recent underperformers&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;rationale-for-weighting&quot;&gt;Rationale for Weighting&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;70% Valuation&lt;/strong&gt;: The heavy emphasis on valuation reflects our core belief that buying quality businesses at attractive prices is the most reliable path to long-term returns. This weight ensures that valuation advantages drive most portfolio decisions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20% Quality&lt;/strong&gt;: While valuation is primary, we avoid “value traps” by ensuring businesses have strong fundamentals. This factor acts as a quality gate, preventing investments in companies that are cheap for good reasons.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10% Safety&lt;/strong&gt;: The small but crucial safety allocation serves as a risk management overlay. By penalizing recent underperformance, we avoid catching falling knives and provide a behavioral check against emotional decision-making.&lt;/p&gt;
&lt;h2 id=&quot;valuation-metrics-deep-dive&quot;&gt;Valuation Metrics Deep Dive&lt;/h2&gt;
&lt;p&gt;Our valuation approach uses multiple metrics to avoid reliance on any single measure:&lt;/p&gt;
&lt;h3 id=&quot;core-valuation-components&quot;&gt;Core Valuation Components&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;P/E vs 3-Year Median&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compares current P/E to historical 3-year median&lt;/li&gt;
&lt;li&gt;Accounts for cyclical variations in earnings&lt;/li&gt;
&lt;li&gt;Normalizes for business cycle effects&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;EV/EBITDA vs Sector Median&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enterprise value multiple for capital structure neutrality&lt;/li&gt;
&lt;li&gt;Sector comparison accounts for industry-specific valuations&lt;/li&gt;
&lt;li&gt;Less affected by accounting differences than P/E&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;P/B vs Historical Range&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Price-to-book compared to company’s own historical range&lt;/li&gt;
&lt;li&gt;Useful for asset-heavy industries&lt;/li&gt;
&lt;li&gt;Provides long-term valuation context&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Dividend Yield vs Median&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Current yield compared to historical and sector medians&lt;/li&gt;
&lt;li&gt;Additional valuation signal, especially for mature companies&lt;/li&gt;
&lt;li&gt;Provides income component to total return&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;valuation-score-calculation&quot;&gt;Valuation Score Calculation&lt;/h3&gt;
&lt;p&gt;Each metric is normalized and combined using our weighted approach:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Valuation Score = 0.35 * P/E_Score + 0.30 * EV/EBITDA_Score + 
                  0.20 * P/B_Score + 0.15 * Dividend_Yield_Score&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Scores range from 0-100, with higher values indicating more attractive valuations.&lt;/p&gt;
&lt;h2 id=&quot;safety-filters-and-momentum-indicators&quot;&gt;Safety Filters and Momentum Indicators&lt;/h2&gt;
&lt;h3 id=&quot;safety-score-implementation&quot;&gt;Safety Score Implementation&lt;/h3&gt;
&lt;p&gt;Our safety mechanism is binary but powerful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Safety Score = 100&lt;/strong&gt; if 6-month momentum ≥ 20th percentile&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Safety Score = 0&lt;/strong&gt; if 6-month momentum &amp;#x3C; 20th percentile&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-binary-safety&quot;&gt;Why Binary Safety?&lt;/h3&gt;
&lt;p&gt;The binary approach provides clear, actionable signals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Avoids marginal cases that might lead to hesitation&lt;/li&gt;
&lt;li&gt;Creates a clean risk management rule&lt;/li&gt;
&lt;li&gt;Simplifies the decision-making process&lt;/li&gt;
&lt;li&gt;Prevents “death by a thousand cuts” from gradual deterioration&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;momentum-calculation&quot;&gt;Momentum Calculation&lt;/h3&gt;
&lt;p&gt;6-month total return (price appreciation + dividends) compared across the universe:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Momentum_6M = (Current_Price + Dividends) / Price_6_Months_Ago - 1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The 20th percentile threshold means we exclude the worst 20% of recent performers, regardless of their valuation or quality scores.&lt;/p&gt;
&lt;h2 id=&quot;rotation-rules-and-risk-management&quot;&gt;Rotation Rules and Risk Management&lt;/h2&gt;
&lt;h3 id=&quot;portfolio-construction-rules&quot;&gt;Portfolio Construction Rules&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Initial Selection&lt;/strong&gt;: Start with top 10 stocks by combined score from Dow 30 universe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Removal Conditions&lt;/strong&gt; (any trigger):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rank &gt; 12 (slips out of top 12)&lt;/li&gt;
&lt;li&gt;Safety filter triggered (6-month momentum &amp;#x3C; 20th percentile)&lt;/li&gt;
&lt;li&gt;Valuation &gt; 20% above median (no longer attractive)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Addition Conditions&lt;/strong&gt; (must meet all):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rank &amp;#x3C; 8 (in top 8)&lt;/li&gt;
&lt;li&gt;Valuation &gt; 20% below median OR Valuation Advantage &gt; 10% vs outgoing&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;turnover-constraints&quot;&gt;Turnover Constraints&lt;/h3&gt;
&lt;p&gt;To prevent excessive trading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Maximum 2 rotations per month&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maximum 6 rotations per quarter&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These constraints ensure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lower transaction costs&lt;/li&gt;
&lt;li&gt;More stable portfolio composition&lt;/li&gt;
&lt;li&gt;Time for investment theses to play out&lt;/li&gt;
&lt;li&gt;Protection against whipsaw markets&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;position-sizing&quot;&gt;Position Sizing&lt;/h3&gt;
&lt;p&gt;Equal-weighting within the portfolio:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simplifies rebalancing&lt;/li&gt;
&lt;li&gt;Avoids concentration risk&lt;/li&gt;
&lt;li&gt;Ensures each position contributes meaningfully to returns&lt;/li&gt;
&lt;li&gt;Reduces idiosyncratic risk&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;implementation-and-performance&quot;&gt;Implementation and Performance&lt;/h2&gt;
&lt;h3 id=&quot;technical-implementation&quot;&gt;Technical Implementation&lt;/h3&gt;
&lt;p&gt;The strategy is implemented through:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Daily data ingestion for fundamentals and pricing&lt;/li&gt;
&lt;li&gt;Automated scoring calculations&lt;/li&gt;
&lt;li&gt;Systematic rotation signal generation&lt;/li&gt;
&lt;li&gt;Portfolio tracking and performance attribution&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;key-metrics-tracked&quot;&gt;Key Metrics Tracked&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Portfolio Composition&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Current holdings and weights&lt;/li&gt;
&lt;li&gt;Sector allocation&lt;/li&gt;
&lt;li&gt;Valuation distribution&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance Attribution&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Factor contribution to returns&lt;/li&gt;
&lt;li&gt;Individual stock performance&lt;/li&gt;
&lt;li&gt;Turnover and transaction costs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Risk Metrics&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Maximum drawdown&lt;/li&gt;
&lt;li&gt;Volatility tracking&lt;/li&gt;
&lt;li&gt;Beta vs benchmark&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;backtesting-framework&quot;&gt;Backtesting Framework&lt;/h3&gt;
&lt;p&gt;Our backtesting approach includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Out-of-sample testing on historical data&lt;/li&gt;
&lt;li&gt;Transaction cost assumptions&lt;/li&gt;
&lt;li&gt;Slippage modeling for realistic execution&lt;/li&gt;
&lt;li&gt;Multiple market cycle testing&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The Value-Focused Portfolio Rotation strategy demonstrates how quantitative approaches can be combined with fundamental investing principles. By emphasizing valuation (70%) while incorporating quality (20%) and safety (10%) factors, we create a systematic approach that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Maintains Discipline&lt;/strong&gt;: Rules-based approach prevents emotional decisions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Controls Risk&lt;/strong&gt;: Safety filters and turnover constraints protect capital&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Captures Value&lt;/strong&gt;: Systematic identification of undervalued quality businesses&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remains Flexible&lt;/strong&gt;: Adaptive to changing market conditions&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;key-takeaways-for-implementation&quot;&gt;Key Takeaways for Implementation&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start with Quality Universe&lt;/strong&gt;: The strategy works best when applied to fundamentally sound businesses&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Patience Matters&lt;/strong&gt;: Turnover constraints ensure investment theses have time to develop&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Valuation is Primary&lt;/strong&gt;: The 70% weight reflects the importance of buying at attractive prices&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Safety First&lt;/strong&gt;: The binary safety filter prevents catching falling knives&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor Continuously&lt;/strong&gt;: Regular review ensures the strategy adapts to market changes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This approach can be adapted to different universes (S&amp;#x26;P 500, international stocks) and timeframes, making it a versatile framework for value-focused portfolio management.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This strategy is implemented in our Stock Picker application and continues to evolve based on ongoing research and market observations.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[From Fluent Bit to OpenTelemetry: Our Logging Journey with OpenObserve]]></title><description><![CDATA[Why we started We wanted centralized, structured logs for our scheduler heartbeats and application events in OpenObserve. Fluent Bit looked…]]></description><link>https://www.monkey.work/2026-01-12-fluent-bit-to-opentelemetry-openobserve/</link><guid isPermaLink="false">https://www.monkey.work/2026-01-12-fluent-bit-to-opentelemetry-openobserve/</guid><pubDate>Mon, 12 Jan 2026 12:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;why-we-started&quot;&gt;Why we started&lt;/h2&gt;
&lt;p&gt;We wanted centralized, structured logs for our scheduler heartbeats and application events in OpenObserve. Fluent Bit looked like a quick, lightweight choice to tail Docker logs, enrich them via Lua, and forward them over HTTP.&lt;/p&gt;
&lt;h2 id=&quot;the-setup&quot;&gt;The setup&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stack:&lt;/strong&gt; Docker Compose (backend, Fluent Bit, OpenObserve).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fluent Bit:&lt;/strong&gt; Tail Docker logs, run a Lua filter (&lt;code class=&quot;language-text&quot;&gt;tag.lua&lt;/code&gt;) to strip ANSI codes, extract JSON (heartbeat payload), set &lt;code class=&quot;language-text&quot;&gt;log_event&lt;/code&gt;, and forward to OpenObserve.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Query OpenObserve for &lt;code class=&quot;language-text&quot;&gt;log_event = &apos;ingestion_scheduler_heartbeat&apos;&lt;/code&gt; with full JSON in &lt;code class=&quot;language-text&quot;&gt;log_message&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-reality-time-spent-vs-value-gained&quot;&gt;The reality: time spent vs. value gained&lt;/h2&gt;
&lt;p&gt;This was supposed to be hours. It turned into days of iteration because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ANSI-laden NestJS logs:&lt;/strong&gt; Needed ANSI stripping before JSON parse.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nested JSON extraction:&lt;/strong&gt; Heartbeat JSON was embedded after Nest prefixes. We kept rewriting Lua patterns to find the last JSON object and decode it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Schema lag in OpenObserve:&lt;/strong&gt; Even after emitting &lt;code class=&quot;language-text&quot;&gt;log_event&lt;/code&gt;, the UI didn’t show the field until we manually added it in the stream schema.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Container restarts &amp;#x26; bind mounts:&lt;/strong&gt; Fluent Bit only loaded &lt;code class=&quot;language-text&quot;&gt;tag.lua&lt;/code&gt; at startup; restarts sometimes reused old state. We resorted to &lt;code class=&quot;language-text&quot;&gt;--force-recreate&lt;/code&gt; and diffing &lt;code class=&quot;language-text&quot;&gt;/fluent-bit/etc/tag.lua&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Debugging loops:&lt;/strong&gt; Enabling Lua debug logging flooded logs and even got re-ingested. We added guards, env-driven flags, and filtered out &lt;code class=&quot;language-text&quot;&gt;[tag.lua]&lt;/code&gt; lines, but it was noisy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tooling limits inside the image:&lt;/strong&gt; No shell utilities; even &lt;code class=&quot;language-text&quot;&gt;printenv&lt;/code&gt; was missing. Debugging env required reading &lt;code class=&quot;language-text&quot;&gt;/proc/1/environ&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-turning-point-why-we-pivoted&quot;&gt;The turning point: why we pivoted&lt;/h2&gt;
&lt;p&gt;After multiple rounds of Lua fixes, restarts, and schema tweaks, the effort-to-reward ratio was poor. We realized:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We were spending most time on plumbing (parsing, restarts, schema alignment) rather than on the product.&lt;/li&gt;
&lt;li&gt;OpenObserve already supports OpenTelemetry ingestion, which can carry structured logs without tail-parsing.&lt;/li&gt;
&lt;li&gt;Eliminating the tail/parse hop removes an entire class of failure modes (ANSI stripping, regex fragility, schema drift).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-decision-remove-fluent-bit-go-direct-with-opentelemetry&quot;&gt;The decision: remove Fluent Bit, go direct with OpenTelemetry&lt;/h2&gt;
&lt;p&gt;We dropped Fluent Bit and switched to emitting logs directly via OpenTelemetry (OTLP) to OpenObserve. Benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No log scraping/parsing:&lt;/strong&gt; We emit structured payloads as-is.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No ANSI issues:&lt;/strong&gt; We send clean, structured data directly from the app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Predictable schema:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;log_event&lt;/code&gt; and other fields arrive with the payload; OpenObserve sees them without Lua gymnastics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simpler ops:&lt;/strong&gt; One fewer container to manage; no custom Lua; no bind-mount drift.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;lessons-learned&quot;&gt;Lessons learned&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Prefer structured emission over downstream parsing. If you control the app, emit structured logs once; avoid regex/Lua filters.&lt;/li&gt;
&lt;li&gt;Schema awareness matters. OpenObserve won’t show new fields until they’re added or ingested—plan for schema registration.&lt;/li&gt;
&lt;li&gt;Container restarts aren’t always enough. For config bind-mount changes, use &lt;code class=&quot;language-text&quot;&gt;--force-recreate&lt;/code&gt; to guarantee reload.&lt;/li&gt;
&lt;li&gt;Debugging needs hygiene. Make debug logs opt-in, filter self-logs, and avoid re-ingesting them.&lt;/li&gt;
&lt;li&gt;Use the native ingestion path when available. OTLP cut our integration time dramatically compared to tail/parse.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-wed-do-next-time&quot;&gt;What we’d do next time&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Start with OpenTelemetry direct to OpenObserve.&lt;/li&gt;
&lt;li&gt;Keep logging strictly structured in the app layer.&lt;/li&gt;
&lt;li&gt;Minimize sidecar parsing; reserve Fluent Bit for edge cases where we don’t control the emitter.&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title><![CDATA[Valuation & Shareholder Yield: A Beginner’s Scorecard]]></title><description><![CDATA[This is part two of the series. In part one on quality and profitability, we covered the durability side. Now we’ll pair those ideas with…]]></description><link>https://www.monkey.work/2026-01-11-valuation-yield-scorecard/</link><guid isPermaLink="false">https://www.monkey.work/2026-01-11-valuation-yield-scorecard/</guid><pubDate>Sun, 11 Jan 2026 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is part two of the series. In &lt;a href=&quot;/2026-01-04-quality-profitability-report/&quot;&gt;part one on quality and profitability&lt;/a&gt;, we covered the durability side. Now we’ll pair those ideas with valuation and shareholder yield—the price you pay and the cash you get back.&lt;/p&gt;
&lt;p&gt;It’s a simple scoring system to judge if a stock looks inexpensive and returns cash to shareholders, while guarding against misleading numbers. Inputs come from the latest 12-month data and sector medians; skip metrics if profits or cash flow are negative.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; It keeps you from overpaying for weak profits or counting buybacks that only offset dilution, and it forces you to normalize for sector differences.&lt;/p&gt;
&lt;h2 id=&quot;quick-setup-inputs--guardrails&quot;&gt;Quick setup (inputs + guardrails)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Inputs:&lt;/strong&gt; trailing 12-month fundamentals, sector medians/percentiles for each metric (medians keep capital-intensity differences in context), and the latest filing data for dividends and buybacks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exclusions:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;EV/EBIT: skip if EBIT ≤ 0 or distorted by one-offs (restructuring, impairments).&lt;/li&gt;
&lt;li&gt;P/FCF: skip if free cash flow (cash after bills and reinvestment) ≤ 0; penalize if FCF is highly volatile (std dev &gt; mean).&lt;/li&gt;
&lt;li&gt;EV/S: use only if 3-year revenue CAGR is roughly ≥ 10–15% or margins are near breakeven and improving. If growth is below that range, mark EV/S as &lt;strong&gt;N/A&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Normalize by sector&lt;/strong&gt; to reduce capital-structure and business-model bias (e.g., software vs. utilities).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Back out extraordinary items&lt;/strong&gt; for EPS/EBIT; prefer cash FCF (after SBC add-back decision—favor real cash).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-five-valuation-levers-plain-english&quot;&gt;The five valuation levers (plain English)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;EV/EBIT (core value anchor):&lt;/strong&gt; Think of Enterprise Value (EV) as the total price tag for the whole business (debt + equity), and EBIT as the yearly operating profit before interest and tax. EV/EBIT asks, “How many dollars am I paying for each dollar of operating profit?” A lower ratio than peers means you’re paying less for the same earning power. If EBIT is negative or distorted by one-off charges (restructuring, impairments), skip it. &lt;strong&gt;If EV/EBIT shows up as negative&lt;/strong&gt;, it usually means EBIT is negative (the business is losing money) rather than the company being “free.” Treat that as a sign to look at why profits are below zero (turnaround? heavy investment? structural issues?) before considering any valuation signal. &lt;em&gt;Analogy:&lt;/em&gt; EV/EBIT is like the sticker price per dollar of engine power in a car—cheaper is better if the engine is healthy.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;P/E vs sector:&lt;/strong&gt; Price per share divided by earnings per share (EPS) is like checking how many years of current earnings you are paying for one share. Comparing it to the sector median keeps capital intensity and accounting quirks in context—software and utilities have very different norms. A “cheap” P/E can still be a trap if earnings are falling; a PEG-style sense check (price vs. growth) helps. &lt;em&gt;Analogy:&lt;/em&gt; It’s like paying a multiple of a friend’s yearly salary to buy their lemonade stand—less is better unless future earnings will rise.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;P/FCF:&lt;/strong&gt; Market cap divided by free cash flow answers, “How many dollars do I pay for one dollar of cash that’s left after the company pays its bills and reinvests?” Cash is what funds dividends, buybacks, and debt paydown. If free cash flow is negative, skip the metric; if cash flow swings wildly year to year, be cautious. &lt;em&gt;Analogy:&lt;/em&gt; This is the price tag per dollar of cash the business drops in the jar at year-end.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;EV/S for growth names:&lt;/strong&gt; For younger or fast-growing companies where profits are thin, EV/S (EV divided by revenue) is a gentler yardstick. It asks, “How many dollars do I pay for one dollar of sales?” Use it only when growth is solid (roughly ≥ 10–15% CAGR) or margins are marching toward breakeven. Otherwise it can hide weak economics. &lt;em&gt;Analogy:&lt;/em&gt; Paying a price per dollar of sales is like valuing a sapling by how fast it’s growing—speed matters if fruit (profits) isn’t here yet.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Shareholder yield:&lt;/strong&gt; Imagine the company as a tree that returns fruit to you each year. Shareholder yield adds up the fruit from dividends plus net buybacks (buybacks minus any new shares issued), then divides by market cap. Higher, sustainable yield is good; if buybacks just offset stock-based compensation or are funded with heavy debt, treat it as a warning. &lt;em&gt;Analogy:&lt;/em&gt; It’s the yearly harvest you actually keep after the tree replaces any fallen fruit.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;scoring-bands-example-out-of-10-total&quot;&gt;Scoring bands (example, out of 10 total)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;EV/EBIT:&lt;/strong&gt; &lt;strong&gt;&amp;#x3C; sector 25th percentile = 2 points; p25–median = 1; ~median = 0.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;P/E vs sector:&lt;/strong&gt; &lt;strong&gt;&amp;#x3C; 0.8× sector median = 2; 0.8–1.1× = 1; ~median = 0.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;P/FCF:&lt;/strong&gt; Same bands as EV/EBIT; skip if FCF ≤ 0; consider a soft penalty for highly volatile FCF.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EV/S (growth filter):&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Attractive:&lt;/strong&gt; &amp;#x3C; ~1.5× with 10–20% CAGR = 2&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Acceptable:&lt;/strong&gt; ~1.5–3× with 20–30% CAGR = 1&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expensive:&lt;/strong&gt; &gt; ~6× with &amp;#x3C; 20% CAGR = 0&lt;/li&gt;
&lt;li&gt;If growth is below the threshold, mark EV/S &lt;strong&gt;N/A&lt;/strong&gt; (do not force a score).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shareholder yield:&lt;/strong&gt; &lt;strong&gt;≥ 6% = 2; 3–6% = 1; &amp;#x3C; 3% = 0; net issuance (negative yield) = –1.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Optional penalties:&lt;/strong&gt; leverage &gt; 3× net debt/EBITDA (–1), interest coverage &amp;#x3C; 3× (–1), declining FCF margin trend (–1).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interpretation:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;8–10:&lt;/strong&gt; High-quality value candidate → verify quality and catalysts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;6–7:&lt;/strong&gt; Watchlist → need clearer margin/FCF trajectory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;≤5:&lt;/strong&gt; Pass unless there is a special situation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;beginner-friendly-walkthrough-how-to-fill-the-sheet&quot;&gt;Beginner-friendly walkthrough (how to fill the sheet)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Gather:&lt;/strong&gt; EV, EBIT, EPS (normalized), FCF, revenue, dividends, buybacks, net debt, revenue CAGR, sector medians/percentiles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clean:&lt;/strong&gt; Remove one-offs from EBIT/EPS, use cash-based FCF, check if buybacks merely offset stock-based comp.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Normalize:&lt;/strong&gt; Compare valuation ratios to sector medians/percentiles. This keeps capital intensity differences in check.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apply growth filter:&lt;/strong&gt; Only score EV/S if growth is healthy (≥ 10–15% CAGR) or margins are improving toward breakeven.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Score:&lt;/strong&gt; Use the bands above; note any penalties for leverage, coverage, or declining FCF margins.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rank and review:&lt;/strong&gt; Sort by score, then read notes/catalysts to ensure the number lines up with the story.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;example-table-illustrative-numbers-only&quot;&gt;Example table (illustrative numbers only)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ticker&lt;/th&gt;
&lt;th&gt;Sector&lt;/th&gt;
&lt;th&gt;EV/EBIT&lt;/th&gt;
&lt;th&gt;P/E vs sector&lt;/th&gt;
&lt;th&gt;P/FCF&lt;/th&gt;
&lt;th&gt;EV/S (rev CAGR note)&lt;/th&gt;
&lt;th&gt;Shareholder yield&lt;/th&gt;
&lt;th&gt;Net debt/EBITDA&lt;/th&gt;
&lt;th&gt;FCF margin trend&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Notes/Catalysts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;EX1&lt;/td&gt;
&lt;td&gt;Industrials&lt;/td&gt;
&lt;td&gt;0.72× (p20)&lt;/td&gt;
&lt;td&gt;0.9×&lt;/td&gt;
&lt;td&gt;0.8× (p30)&lt;/td&gt;
&lt;td&gt;N/A (7% CAGR)&lt;/td&gt;
&lt;td&gt;4.5%&lt;/td&gt;
&lt;td&gt;1.8×&lt;/td&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Cost program, steady demand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EX2&lt;/td&gt;
&lt;td&gt;Software&lt;/td&gt;
&lt;td&gt;1.4× (p22)&lt;/td&gt;
&lt;td&gt;1.0×&lt;/td&gt;
&lt;td&gt;N/A (FCF &amp;#x3C; 0)&lt;/td&gt;
&lt;td&gt;2.2× (24% CAGR)&lt;/td&gt;
&lt;td&gt;0.5%&lt;/td&gt;
&lt;td&gt;0.6× net cash&lt;/td&gt;
&lt;td&gt;Improving&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Margin expansion, upsell motion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EX3&lt;/td&gt;
&lt;td&gt;Consumer&lt;/td&gt;
&lt;td&gt;0.6× (p18)&lt;/td&gt;
&lt;td&gt;0.75×&lt;/td&gt;
&lt;td&gt;0.7× (p22)&lt;/td&gt;
&lt;td&gt;1.3× (12% CAGR)&lt;/td&gt;
&lt;td&gt;7.0%&lt;/td&gt;
&lt;td&gt;2.5×&lt;/td&gt;
&lt;td&gt;Flat&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Strong cash returns, pricing power&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;How to read it:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EX1 scores fine but lacks growth; EV/S is N/A because growth is below the threshold.&lt;/li&gt;
&lt;li&gt;EX2 uses EV/S because it’s early-stage and FCF is negative; score depends on improving margins.&lt;/li&gt;
&lt;li&gt;EX3 mixes low valuations with solid yield; watch leverage but story is consistent.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If EV/EBIT or P/FCF are N/A, don’t force a score—note the gap and rely on the remaining levers. These figures are illustrative only; swap in your own numbers.&lt;/p&gt;
&lt;h2 id=&quot;red-flag-overrides-hard-fails&quot;&gt;Red-flag overrides (hard fails)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Deteriorating liquidity: current ratio &amp;#x3C; 1 &lt;strong&gt;and&lt;/strong&gt; negative FCF.&lt;/li&gt;
&lt;li&gt;Aggressive accounting: frequent “adjusted” add-backs that lift EPS but not FCF.&lt;/li&gt;
&lt;li&gt;Dilution: 3-year CAGR in share count &gt; 3% with low ROIC.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Structural decline:&lt;/strong&gt; revenue CAGR &amp;#x3C; 0 with rising leverage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If any hard fail triggers, stop and reassess before scoring or ranking.&lt;/p&gt;
&lt;h2 id=&quot;qa&quot;&gt;Q&amp;#x26;A&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Why skip EV/EBIT or P/FCF when profits or cash are negative?&lt;/strong&gt;&lt;br&gt;
Because negative EBIT or FCF flips the ratios and hides risk—use EV/S (if growth is healthy) and shareholder yield instead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What if EV/EBIT looks “too cheap”?&lt;/strong&gt;&lt;br&gt;
Check for one-offs (restructuring, impairments) or declining EBIT. A bargain ratio can signal a business in trouble.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When do I use EV/S?&lt;/strong&gt;&lt;br&gt;
Use it for healthy growers (≈10–15%+ revenue CAGR) or improving margins. If growth is below that, mark EV/S N/A.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I tell if buybacks are real yield?&lt;/strong&gt;&lt;br&gt;
Look for net share count going down and buybacks funded by cash, not new debt or just offsetting stock comp.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can a high shareholder yield offset weak valuation scores?&lt;/strong&gt;&lt;br&gt;
Maybe, but only if the yield is sustainable (covered by FCF) and leverage is reasonable. Otherwise it’s a warning, not a plus.&lt;/p&gt;
&lt;h2 id=&quot;quick-checklist-you-can-reuse&quot;&gt;Quick checklist you can reuse&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Are valuations below sector norms (EV/EBIT, P/E, P/FCF)? If not, what quality or growth justifies it?&lt;/li&gt;
&lt;li&gt;If growth-driven, does EV/S sit in an attractive/acceptable band given the CAGR and margin path?&lt;/li&gt;
&lt;li&gt;Is shareholder yield real (net of issuance) and covered by cash flow, not debt?&lt;/li&gt;
&lt;li&gt;Any leverage or interest-coverage risks that warrant a penalty?&lt;/li&gt;
&lt;li&gt;Is FCF margin trending up, flat, or down? Does that align with the score?&lt;/li&gt;
&lt;li&gt;After scoring, does the narrative (moat, cycle, catalysts) support or contradict the numbers?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Recap:&lt;/strong&gt; Price you pay (EV/EBIT, P/E, P/FCF, EV/S for growers) plus cash you get back (shareholder yield), adjusted for sector norms. Skip metrics that don’t fit (negative profits/FCF, low growth for EV/S), watch leverage and dilution, and let the story confirm the score.&lt;/p&gt;
&lt;p&gt;Use this scorecard as a starter map. It balances “what you pay” with “what you get back,” keeps sector differences in mind, and leaves room for judgment about growth, quality, and capital allocation.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How our site briefly redirected to a malicious page — and how we fixed it]]></title><description><![CDATA[What happened Users hitting firgun.win were briefly redirected to an Arweave link pointing to an unfamiliar IP. Our nginx config looked…]]></description><link>https://www.monkey.work/2026-01-06-stray-container-redirect/</link><guid isPermaLink="false">https://www.monkey.work/2026-01-06-stray-container-redirect/</guid><pubDate>Tue, 06 Jan 2026 09:19:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;what-happened&quot;&gt;What happened&lt;/h2&gt;
&lt;p&gt;Users hitting &lt;strong&gt;firgun.win&lt;/strong&gt; were briefly redirected to an Arweave link pointing to an unfamiliar IP. Our nginx config looked correct; the problem came from whatever was serving &lt;strong&gt;port 3000&lt;/strong&gt; upstream.&lt;/p&gt;
&lt;h2 id=&quot;investigation&quot;&gt;Investigation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;curl -I http://127.0.0.1:3000&lt;/code&gt; returned a &lt;strong&gt;302&lt;/strong&gt; to the Arweave URL, so the redirect originated from the process on port 3000, not nginx.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;docker ps&lt;/code&gt; showed the expected stock-picker web-app container was &lt;strong&gt;running&lt;/strong&gt;, with no rogue container bound to port 3000.&lt;/li&gt;
&lt;li&gt;Because nginx simply proxied to port 3000, traffic flowed through the legitimate web-app container, which was emitting the malicious redirect.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-we-did-to-fix-it&quot;&gt;What we did to fix it&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Rebuilt and recreated&lt;/strong&gt; the web-app to replace the rogue listener:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; compose up &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--build&lt;/span&gt; --force-recreate web-app&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This immediately removed the redirect.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pruned stale containers and build cache&lt;/strong&gt; to clear leftover artifacts:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; system prune &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Upgraded frontend dependencies&lt;/strong&gt; (Next.js, React, &lt;code class=&quot;language-text&quot;&gt;eslint-config-next&lt;/code&gt;) to current versions for security coverage.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verified port ownership and responses:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ps&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--filter&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-I&lt;/span&gt; http://127.0.0.1:3000&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Checked nginx and logs&lt;/strong&gt; to confirm the correct app was serving after the rebuild.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;what-actually-went-wrong-root-cause&quot;&gt;What actually went wrong (root cause)&lt;/h2&gt;
&lt;p&gt;The legitimate web-app container was returning the malicious 302. nginx dutifully proxied to it. The redirect source was the running web-app itself, which was vulnerable due to running React 19.1.0 (see React2Shell vulnerability section below).&lt;/p&gt;
&lt;h2 id=&quot;the-react-1910-vulnerability-react2shell&quot;&gt;The React 19.1.0 Vulnerability (React2Shell)&lt;/h2&gt;
&lt;p&gt;React 19.1.0 is severely vulnerable and was a primary factor in this incident. This version is affected by the React2Shell (CVE-2025-55182) vulnerability, a critical flaw with a CVSS 10.0 rating disclosed in December 2025. It allows unauthenticated attackers to execute code on your server by sending a single malicious HTTP request.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why React 19.1.0 is Dangerous&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Insecure Deserialization:&lt;/strong&gt; The vulnerability exists in how React Server Components (RSC) process “Flight” protocol data. Attackers can inject payloads that overwrite prototype methods, leading to Remote Code Execution (RCE).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Default Vulnerability:&lt;/strong&gt; The flaw is present in the default setup of apps using React 19.x - no specific misconfiguration is needed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Active Targeting:&lt;/strong&gt; Botnets are actively scanning for React 19.1.0 to deploy cryptominers and redirect traffic to malicious sites (like the Arweave link we observed).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-to-avoid-this-in-the-future&quot;&gt;How to avoid this in the future&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Recreate on deploys:&lt;/strong&gt; Use &lt;code class=&quot;language-text&quot;&gt;docker compose up -d --build --force-recreate web-app&lt;/code&gt; so the current image owns the port.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Clean up regularly:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;docker system prune -f&lt;/code&gt; to remove unused containers/images and prevent stale listeners.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Port checks after deploys:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;docker ps --filter publish=3000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;curl -I http://127.0.0.1:3000&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No Docker API exposure:&lt;/strong&gt; Confirm nothing is listening on &lt;code class=&quot;language-text&quot;&gt;2375/2376&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; ss &lt;span class=&quot;token parameter variable&quot;&gt;-lntp&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-E&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2375|2376&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stay patched:&lt;/strong&gt; Keep Next.js/React current; run &lt;code class=&quot;language-text&quot;&gt;npm audit&lt;/code&gt; (or your package manager’s audit) and apply security updates promptly. React 19.1.0’s critical React2Shell vulnerability (CVE-2025-55182) shows how important timely updates are.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Optional hardening:&lt;/strong&gt; Use read-only filesystems and &lt;code class=&quot;language-text&quot;&gt;tmpfs&lt;/code&gt; for app containers when compatible, and add a simple health check/alert for unexpected 3xx to unknown domains.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;qa&quot;&gt;Q&amp;#x26;A&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Did nginx cause the redirect?&lt;/strong&gt;&lt;br&gt;
No. nginx proxied correctly to port 3000; the running web-app container on that port emitted the 302.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Was this due to a rogue container?&lt;/strong&gt;&lt;br&gt;
&lt;code class=&quot;language-text&quot;&gt;docker ps&lt;/code&gt; showed only the expected web-app container on 3000. The redirect came from that container’s app, not from a separate rogue container.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How did React 19.1.0 factor in?&lt;/strong&gt;&lt;br&gt;
The affected site was running React 19.1.0, which is vulnerable to React2Shell (CVE-2025-55182). That made it possible for the app to be hijacked and return malicious redirects.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How can we detect this faster next time?&lt;/strong&gt;&lt;br&gt;
Add a post-deploy check that curls the upstream port and alerts on unexpected 3xx/4xx/5xx to unknown domains. Pair it with &lt;code class=&quot;language-text&quot;&gt;docker ps --filter publish=3000&lt;/code&gt; to confirm port ownership.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Did traffic hit the malicious target?&lt;/strong&gt;&lt;br&gt;
We saw brief redirects, so some requests likely reached the Arweave URL. No evidence of data exfiltration; impact was redirect-only.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why didn’t &lt;code class=&quot;language-text&quot;&gt;docker ps&lt;/code&gt; surface anything odd?&lt;/strong&gt;&lt;br&gt;
Because the legitimate container was the one misbehaving. Recreate on deploy to ensure the current image owns the port and hasn’t been tampered with.&lt;/p&gt;
&lt;h2 id=&quot;takeaway&quot;&gt;Takeaway&lt;/h2&gt;
&lt;p&gt;The redirect came from the legitimate web-app container. Rebuilding the web-app and tightening post-deploy checks resolved it and reduces recurrence risk.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Quality & Profitability: A Plain-English Guide to the Core Metrics]]></title><description><![CDATA[This is part one of the series. Quality investing sounds technical, but the core ideas are simple: you’re looking for businesses that earn…]]></description><link>https://www.monkey.work/2026-01-04-quality-profitability-report/</link><guid isPermaLink="false">https://www.monkey.work/2026-01-04-quality-profitability-report/</guid><pubDate>Sun, 04 Jan 2026 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is part one of the series. Quality investing sounds technical, but the core ideas are simple: you’re looking for businesses that earn strong profits on the money they invest, turn those profits into cash, and do so consistently. You don’t need a finance degree, just a basic understanding of a handful of metrics and how they fit together.&lt;/p&gt;
&lt;h2 id=&quot;the-five-building-blocks-plain-english&quot;&gt;The five building blocks (plain English)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ROIC (Return on Invested Capital):&lt;/strong&gt; Imagine a lemonade stand that spends $100 on supplies and equipment. If it earns $20 in profit each year, that’s a 20% return on the $100 it invested. Higher ROIC means the business is good at turning its invested dollars into profit. &lt;em&gt;Analogy:&lt;/em&gt; It’s the miles you get out of each gallon—more miles per gallon is better.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gross margin trend (basis points per year):&lt;/strong&gt; Gross margin is the slice of each dollar of sales left after direct costs (ingredients, manufacturing, shipping). A rising trend says the business is gaining pricing power or getting more efficient. A falling trend can hint at price pressure or cost creep. &lt;em&gt;Analogy:&lt;/em&gt; Like a thermostat—steady warmth is comfortable, dropping temps mean heat is leaking.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Free cash flow (FCF) margin:&lt;/strong&gt; Profit on paper isn’t always cash in the bank. Free cash flow is the cash left after paying bills and reinvesting in the business (like new equipment). Dividing that cash by sales gives you the FCF margin—a quick read on how much real cash the business keeps. Higher is better because cash pays dividends, funds buybacks, and creates flexibility. &lt;em&gt;Analogy:&lt;/em&gt; It’s what’s left in the piggy bank after stocking the shelves.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accruals ratio:&lt;/strong&gt; Accounting can smooth or shift earnings. A low accruals ratio means most reported profit shows up as cash, not just accounting entries. Lower is better because it signals cleaner, cash-backed earnings. &lt;em&gt;Analogy:&lt;/em&gt; It’s the gap between your paycheck and what actually hits your bank—smaller gaps mean cleaner books.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Earnings stability (σ/μ):&lt;/strong&gt; This measures how bumpy earnings are over time. Lower volatility (a smaller σ/μ) suggests the business is predictable, with fewer negative surprises for customers, employees, and investors. &lt;em&gt;Analogy:&lt;/em&gt; A smooth flight versus turbulence; smoother rides are easier to stay on.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-these-pieces-fit-together&quot;&gt;How these pieces fit together&lt;/h2&gt;
&lt;p&gt;Think of quality as a stool with three legs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Profitability:&lt;/strong&gt; ROIC and margins tell you the business makes more than it spends.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cash conversion:&lt;/strong&gt; FCF margin and accruals show whether profits are real cash.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consistency:&lt;/strong&gt; Earnings stability shows whether those profits arrive predictably.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If all three are strong, the stool stands firm. If one leg is weak, say margins are rising but earnings are choppy, you know where to dig deeper.&lt;/p&gt;
&lt;h2 id=&quot;simple-ways-to-read-the-metrics-no-model-required&quot;&gt;Simple ways to read the metrics (no model required)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Scan ROIC first:&lt;/strong&gt; Higher ROIC suggests a durable edge. Compare it with peers in the same sector; capital-light software firms differ from heavy industrials.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Look for rising gross margins:&lt;/strong&gt; A gentle upward trend often signals pricing power or cost discipline. Flat or falling trends deserve a closer look.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pair ROIC with FCF margin:&lt;/strong&gt; Strong ROIC plus solid FCF margin means the profits are showing up as cash, not just on paper.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check accruals:&lt;/strong&gt; Lower is better. If profits rise while accruals balloon, be cautious—cash should follow earnings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gauge stability:&lt;/strong&gt; Lower earnings volatility reduces the odds of big downside surprises. Consistency matters as much as peak profitability.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;quick-guardrails-and-common-mistakes&quot;&gt;Quick guardrails and common mistakes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Beware &lt;strong&gt;one-off boosts&lt;/strong&gt; (asset sales, tax benefits) that inflate ROIC or margins for a single period.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rising margins with falling cash&lt;/strong&gt; often means earnings aren’t converting—check accruals.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Great ROIC, shrinking revenue&lt;/strong&gt;: the engine is efficient, but the car is slowing—ask why demand is slipping.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Volatile earnings&lt;/strong&gt; can mask risk; require a higher bar elsewhere (stronger cash conversion or margins) before calling it “quality.”&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;a-beginner-friendly-checklist&quot;&gt;A beginner-friendly checklist&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Is &lt;strong&gt;ROIC&lt;/strong&gt; clearly above peers in the same industry?&lt;/li&gt;
&lt;li&gt;Are &lt;strong&gt;gross margins&lt;/strong&gt; flat-to-rising rather than slipping?&lt;/li&gt;
&lt;li&gt;Is the &lt;strong&gt;FCF margin&lt;/strong&gt; healthy, showing profits turn into cash?&lt;/li&gt;
&lt;li&gt;Is the &lt;strong&gt;accruals ratio&lt;/strong&gt; low, indicating clean, cash-backed earnings?&lt;/li&gt;
&lt;li&gt;Is &lt;strong&gt;earnings stability&lt;/strong&gt; solid, with few sharp swings?&lt;/li&gt;
&lt;li&gt;Does the story hold up over several periods, not just one great quarter?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;practical-workflow&quot;&gt;Practical workflow&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Read a few periods, not just one:&lt;/strong&gt; Quality is about endurance. Look at multi-year trends.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use plain questions:&lt;/strong&gt; “How does this company make money?” “Can competitors easily copy it?” “Do margins and cash support the story?”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Balance numbers with narrative:&lt;/strong&gt; Metrics flag strengths and weaknesses; filings and customer feedback explain the “why.”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stay humble about timing:&lt;/strong&gt; Great businesses can stay expensive; weaker ones can bounce. Quality metrics help you focus, not predict short-term moves.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Log red flags as you go:&lt;/strong&gt; High accruals, dropping FCF margin, or volatile earnings should sit in your notes next to the score.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;illustrative-snapshot-hypothetical&quot;&gt;Illustrative snapshot (hypothetical)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ticker&lt;/th&gt;
&lt;th&gt;ROIC&lt;/th&gt;
&lt;th&gt;Gross margin trend&lt;/th&gt;
&lt;th&gt;FCF margin&lt;/th&gt;
&lt;th&gt;Accruals ratio&lt;/th&gt;
&lt;th&gt;Earnings stability (σ/μ)&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;QL1&lt;/td&gt;
&lt;td&gt;18%&lt;/td&gt;
&lt;td&gt;+40 bps/yr&lt;/td&gt;
&lt;td&gt;12%&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Stable cash, mild pricing power&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QL2&lt;/td&gt;
&lt;td&gt;11%&lt;/td&gt;
&lt;td&gt;–30 bps/yr&lt;/td&gt;
&lt;td&gt;5%&lt;/td&gt;
&lt;td&gt;Rising&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Margins slipping; watch accruals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QL3&lt;/td&gt;
&lt;td&gt;24%&lt;/td&gt;
&lt;td&gt;Flat&lt;/td&gt;
&lt;td&gt;2%&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Great ROIC, cash thin; volatility high&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These figures are illustrative only—swap in your own data and note why each line looks the way it does.&lt;/p&gt;
&lt;h2 id=&quot;red-flags-hard-fails&quot;&gt;Red flags (hard fails)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High accruals with weak cash:&lt;/strong&gt; profits aren’t showing up in the bank.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sharp margin compression without a plan:&lt;/strong&gt; price wars or cost spikes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Volatile earnings plus high leverage:&lt;/strong&gt; thin cushion for mistakes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shrinking revenue with “great” ROIC:&lt;/strong&gt; efficiency without growth can be a warning sign.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;qa&quot;&gt;Q&amp;#x26;A&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Is ROIC always the first filter?&lt;/strong&gt;&lt;br&gt;
It’s a great starting point, but pair it with FCF margin to confirm profits turn into cash.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What if margins are rising but cash is not?&lt;/strong&gt;&lt;br&gt;
Check accruals—rising earnings with weak cash conversion is a warning sign.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can a low accruals ratio override weak margins?&lt;/strong&gt;&lt;br&gt;
No. Clean accounting doesn’t fix a business with poor pricing power or cost control.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How many years of data do I need?&lt;/strong&gt;&lt;br&gt;
Aim for 3–5 years to see trends in ROIC, margins, accruals, and stability.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What’s a “good” earnings stability score?&lt;/strong&gt;&lt;br&gt;
Lower σ/μ (less volatility) is better. Compare to peers; smoother beats bumpier.&lt;/p&gt;
&lt;h2 id=&quot;recap-you-can-reuse&quot;&gt;Recap you can reuse&lt;/h2&gt;
&lt;p&gt;Quality = &lt;strong&gt;profitability (ROIC, margins) + cash conversion (FCF margin, accruals) + consistency (earnings stability)&lt;/strong&gt;. Compare to peers, read multi-period trends, mark red flags, and let the narrative confirm what the numbers say.&lt;/p&gt;
&lt;p&gt;Use these concepts as a compass. They simplify the noise, highlight durable traits, and help anyone—regardless of finance background—decide which businesses deserve a closer look.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Continue the series:&lt;/strong&gt; In &lt;a href=&quot;/2026-01-11-valuation-yield-scorecard/&quot;&gt;part two on valuation and shareholder yield&lt;/a&gt;, we’ll explore how to score the price you pay and the cash you get back. Then in &lt;a href=&quot;/2026-01-26-value-focused-portfolio-rotation/&quot;&gt;part three on portfolio rotation&lt;/a&gt;, we combine these ideas into a systematic investment strategy.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Docker Compose Health Checks: A Practical Guide for Junior Devs & Ops]]></title><description><![CDATA[Keeping containers “running” isn’t enough; you need to know they’re actually healthy. Docker health checks give you that signal, and Docker…]]></description><link>https://www.monkey.work/2025-12-26-docker-compose-health-checks-guide/</link><guid isPermaLink="false">https://www.monkey.work/2025-12-26-docker-compose-health-checks-guide/</guid><pubDate>Fri, 26 Dec 2025 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Keeping containers “running” isn’t enough; you need to know they’re actually healthy. Docker health checks give you that signal, and Docker Compose makes them easy to configure, observe, and act on. Here’s how they work, with practical snippets you can lift into your own projects.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-health-check&quot;&gt;What is a health check?&lt;/h2&gt;
&lt;p&gt;A health check is a command Docker runs inside a container on a schedule. If it succeeds (exit code 0), the container is marked healthy. If it fails or times out too many times, the container is unhealthy. Compose just passes these settings through to Docker.&lt;/p&gt;
&lt;h2 id=&quot;the-core-pieces&quot;&gt;The core pieces&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Command:&lt;/strong&gt; What to run to verify health (often a curl to an HTTP endpoint).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interval:&lt;/strong&gt; How often to run it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timeout:&lt;/strong&gt; How long to wait before considering it failed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retries:&lt;/strong&gt; How many consecutive failures before marking unhealthy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Start period:&lt;/strong&gt; Grace period before counting failures (useful during startup).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;defining-a-health-check-in-dockerfile&quot;&gt;Defining a health check in Dockerfile&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Dockerfile&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;HEALTHCHECK&lt;/span&gt; &lt;span class=&quot;token options&quot;&gt;&lt;span class=&quot;token property&quot;&gt;--interval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;30s&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;--timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;5s&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;--start-period&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;10s&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;--retries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;3&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;CMD&lt;/span&gt; curl -f http://localhost:8080/health || exit 1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Tips:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the checker tool (e.g., &lt;code class=&quot;language-text&quot;&gt;curl&lt;/code&gt;) is installed in the runtime image.&lt;/li&gt;
&lt;li&gt;Use the correct path (if your app is under &lt;code class=&quot;language-text&quot;&gt;/api&lt;/code&gt;, hit &lt;code class=&quot;language-text&quot;&gt;/api/health&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Keep the command cheap: avoid heavy queries or migrations.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;defining-a-health-check-in-docker-composeyml&quot;&gt;Defining a health check in docker-compose.yml&lt;/h2&gt;
&lt;p&gt;Compose also supports healthchecks per service. This is handy if you don’t control the image or want different settings per environment.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; myapp&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;8080:8080&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;healthcheck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;CMD-SHELL&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;curl -f http://localhost:8080/api/health || exit 1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 30s
      &lt;span class=&quot;token key atrule&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5s
      &lt;span class=&quot;token key atrule&quot;&gt;retries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;start_period&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10s&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Notes:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;CMD-SHELL&lt;/code&gt; lets you use shell features (&lt;code class=&quot;language-text&quot;&gt;|| exit 1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;You can also use &lt;code class=&quot;language-text&quot;&gt;CMD&lt;/code&gt; with an array if no shell logic is needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;reading-health-status&quot;&gt;Reading health status&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;docker ps&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;docker compose ps&lt;/code&gt; shows &lt;code class=&quot;language-text&quot;&gt;healthy&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;unhealthy&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;starting&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Detailed log of the health checks:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; inspect &lt;span class=&quot;token parameter variable&quot;&gt;--format&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;{{json .State.Health}}&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;container_name&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; jq&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You’ll see each attempt’s exit code and output, which is great for debugging “curl: not found” or 404s.&lt;/p&gt;
&lt;h2 id=&quot;common-pitfalls-and-fixes&quot;&gt;Common pitfalls (and fixes)&lt;/h2&gt;
&lt;h3 id=&quot;missing-curl&quot;&gt;Missing curl&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; health log shows &lt;code class=&quot;language-text&quot;&gt;/bin/sh: 1: curl: not found&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; install &lt;code class=&quot;language-text&quot;&gt;curl&lt;/code&gt; in the runtime image (&lt;code class=&quot;language-text&quot;&gt;apt-get install -y curl&lt;/code&gt;) or use &lt;code class=&quot;language-text&quot;&gt;wget -qO-&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;wrong-path&quot;&gt;Wrong path&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; 404s in health logs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; hit the real endpoint (e.g., &lt;code class=&quot;language-text&quot;&gt;/api/health&lt;/code&gt; if your app has a global &lt;code class=&quot;language-text&quot;&gt;/api&lt;/code&gt; prefix).&lt;/p&gt;
&lt;h3 id=&quot;too-aggressive-timing&quot;&gt;Too aggressive timing&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; app marked unhealthy during startup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; raise &lt;code class=&quot;language-text&quot;&gt;start_period&lt;/code&gt;, or increase &lt;code class=&quot;language-text&quot;&gt;retries&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;interval&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;heavy-checks&quot;&gt;Heavy checks&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; timeouts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; keep health checks lightweight; avoid DB migrations or big queries.&lt;/p&gt;
&lt;h2 id=&quot;using-health-in-dependencies&quot;&gt;Using health in dependencies&lt;/h2&gt;
&lt;p&gt;Compose’s &lt;code class=&quot;language-text&quot;&gt;depends_on&lt;/code&gt; can wait for health (v3.9+ syntax):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; myapi&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;healthcheck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;CMD&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;curl&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-f&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:8080/health&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 20s
      &lt;span class=&quot;token key atrule&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5s
      &lt;span class=&quot;token key atrule&quot;&gt;retries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;start_period&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10s
  &lt;span class=&quot;token key atrule&quot;&gt;worker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; myworker&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;depends_on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; service_healthy&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now &lt;code class=&quot;language-text&quot;&gt;worker&lt;/code&gt; starts only after &lt;code class=&quot;language-text&quot;&gt;api&lt;/code&gt; reports healthy.&lt;/p&gt;
&lt;h2 id=&quot;when-to-put-it-in-dockerfile-vs-compose&quot;&gt;When to put it in Dockerfile vs Compose&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dockerfile:&lt;/strong&gt; Good default for everyone using the image; travels with the artifact.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compose:&lt;/strong&gt; Good for environment-specific checks or when you can’t change the image.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can combine them: Dockerfile defines a default; Compose overrides for local dev or staging.&lt;/p&gt;
&lt;h2 id=&quot;minimal-templates-to-copy-paste&quot;&gt;Minimal templates to copy-paste&lt;/h2&gt;
&lt;h3 id=&quot;http-app-dockerfile&quot;&gt;HTTP app (Dockerfile)&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;HEALTHCHECK&lt;/span&gt; &lt;span class=&quot;token options&quot;&gt;&lt;span class=&quot;token property&quot;&gt;--interval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;30s&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;--timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;5s&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;--start-period&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;15s&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;--retries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;3&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;CMD&lt;/span&gt; curl -f http://localhost:8080/health || exit 1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;http-app-compose-override&quot;&gt;HTTP app (Compose override)&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;healthcheck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;CMD-SHELL&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;curl -f http://localhost:8080/api/health || exit 1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 30s
  &lt;span class=&quot;token key atrule&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5s
  &lt;span class=&quot;token key atrule&quot;&gt;retries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;start_period&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 15s&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;tcp-check-without-curl-eg-redis&quot;&gt;TCP check without curl (e.g., Redis)&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;healthcheck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;CMD&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;redis-cli&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ping&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 30s
  &lt;span class=&quot;token key atrule&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3s
  &lt;span class=&quot;token key atrule&quot;&gt;retries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;debug-checklist&quot;&gt;Debug checklist&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;docker compose ps&lt;/code&gt; to see current health.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;docker inspect ...Health&lt;/code&gt; to read the exact failure/output.&lt;/li&gt;
&lt;li&gt;Run the test command manually inside the container:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; compose &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; web &lt;span class=&quot;token function&quot;&gt;sh&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-lc&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;curl -f http://localhost:8080/api/health&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Adjust path, install the tool, or relax timing, then rebuild/restart.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;bottom-line&quot;&gt;Bottom line&lt;/h2&gt;
&lt;p&gt;Health checks are just small, repeated commands. Keep them light, point them at the right endpoint, ensure the tooling exists, and give your app enough time to start. With Docker Compose, you get clear status, dependency gating, and straightforward debugging.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Why Block Network Access in Tests]]></title><description><![CDATA[Deterministic tests do not call the public internet. They mock it. Blocking outbound HTTP makes every suite faster, safer, and easier to…]]></description><link>https://www.monkey.work/2025-12-23-block-network-access-in-tests/</link><guid isPermaLink="false">https://www.monkey.work/2025-12-23-block-network-access-in-tests/</guid><pubDate>Tue, 23 Dec 2025 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Deterministic tests do not call the public internet. They mock it. Blocking outbound HTTP makes every suite faster, safer, and easier to debug.&lt;/p&gt;
&lt;h2 id=&quot;why-block-network-access&quot;&gt;Why block network access?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Determinism:&lt;/strong&gt; Live APIs change, rate-limit, or fail. Tests should be predictable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Speed:&lt;/strong&gt; External calls slow down runs; blocking them forces you to mock.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Isolation:&lt;/strong&gt; Unit tests should validate your logic, not remote services.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost &amp;#x26; safety:&lt;/strong&gt; Avoid hitting paid APIs or mutating real data by accident.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt; If a test makes a real HTTP call, it isn’t a true unit test.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;core-idea&quot;&gt;Core idea&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Intercept HTTP clients (&lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;axios&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;requests&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;http/https&lt;/code&gt;) at test startup.&lt;/li&gt;
&lt;li&gt;Allow only localhost for in-memory servers or mocks.&lt;/li&gt;
&lt;li&gt;Throw a clear error when an outbound call slips through.&lt;/li&gt;
&lt;li&gt;Restore originals after tests to avoid side effects.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;example-jest-nodejs&quot;&gt;Example: Jest (Node/JS)&lt;/h2&gt;
&lt;p&gt;Add a global setup file (e.g., &lt;code class=&quot;language-text&quot;&gt;jest.setup.js&lt;/code&gt;) and reference it in &lt;code class=&quot;language-text&quot;&gt;setupFilesAfterEnv&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// jest.setup.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; http &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;http&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; https &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;https&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; originalHttpRequest &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; originalHttpGet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; originalHttpsRequest &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; originalHttpsGet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; originalFetch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; global&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fetch&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;isLocalhost&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
  host &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;host&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;localhost&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; host&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;127.0.0.1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; host&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;[::1]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;blockRequest&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;protocol&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; originalReq&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;args&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; first &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; host&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; first &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;string&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; host &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;first&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;protocol&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;://placeholder&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;host&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;first &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; first &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;object&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; host &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; first&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;host &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; first&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostname&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;host &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isLocalhost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;host&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Network access is blocked during tests. Please mock HTTP calls.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;originalReq&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;beforeAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;blockRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;http&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; originalHttpRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;blockRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;http&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; originalHttpGet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  https&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;blockRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; originalHttpsRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  https&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;blockRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; originalHttpsGet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; originalFetch &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;function&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    global&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;fetch&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; input &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;string&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; input &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; input&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; host &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;host &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;host &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isLocalhost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;host&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Network access is blocked during tests. Please mock HTTP calls.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;originalFetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; init&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;afterAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; originalHttpRequest&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; originalHttpGet&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  https&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; originalHttpsRequest&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  https&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; originalHttpsGet&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;originalFetch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; global&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fetch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; originalFetch&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then, in &lt;code class=&quot;language-text&quot;&gt;jest.config.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;setupFilesAfterEnv&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;rootDir&gt;/jest.setup.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Mocking example in a test:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; fetch &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;node-fetch&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
jest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;node-fetch&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;uses mocked API&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  fetch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mockResolvedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;hi&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; res &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://api.example.com/data&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// will use mock&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;hi&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you forget to mock, the test throws: “Network access is blocked during tests. Please mock HTTP calls.”&lt;/p&gt;
&lt;h2 id=&quot;example-pytest-python&quot;&gt;Example: Pytest (Python)&lt;/h2&gt;
&lt;p&gt;Add an autouse fixture in &lt;code class=&quot;language-text&quot;&gt;conftest.py&lt;/code&gt; to block outbound requests with &lt;code class=&quot;language-text&quot;&gt;requests&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# conftest.py&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; pytest
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; requests


&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@pytest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fixture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;autouse&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;block_network&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;monkeypatch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_blocked&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;**&lt;/span&gt;kwargs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;raise&lt;/span&gt; AssertionError&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Network access is blocked during tests. Please mock HTTP calls.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    monkeypatch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;setattr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requests&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;get&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _blocked&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    monkeypatch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;setattr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requests&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _blocked&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    monkeypatch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;setattr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requests&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;put&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _blocked&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    monkeypatch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;setattr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requests&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;delete&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _blocked&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    monkeypatch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;setattr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requests&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;request&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _blocked&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Mocking example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; types &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; SimpleNamespace


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test_calls_api&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;monkeypatch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fake_get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;**&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; SimpleNamespace&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;status_code&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hi&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; requests
    monkeypatch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;setattr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requests&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;get&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fake_get&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    resp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; requests&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://api.example.com/data&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;assert&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hi&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Unmocked calls raise the blocking error, forcing you to stub the API.&lt;/p&gt;
&lt;h2 id=&quot;tips-and-patterns&quot;&gt;Tips and patterns&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Allow localhost only for in-memory servers (e.g., &lt;code class=&quot;language-text&quot;&gt;supertest&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Keep the error message obvious so developers know to mock.&lt;/li&gt;
&lt;li&gt;Restore originals to avoid leaking patched globals across tests.&lt;/li&gt;
&lt;li&gt;Mock per-test or per-suite where the API is used; keep helpers nearby.&lt;/li&gt;
&lt;li&gt;Use small factory helpers to stub common responses (200, 404, 500, throttling).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;Blocking real network calls makes tests faster, deterministic, and safer. Enforce it globally so accidental calls fail fast, and mock APIs at the edges so you test your logic—not the internet.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Modern Web App Tech Stack: What We Use and Why]]></title><description><![CDATA[Building a modern web application requires making careful technology choices that balance developer experience, performance, and operational…]]></description><link>https://www.monkey.work/2025-12-20-modern-web-app-tech-stack/</link><guid isPermaLink="false">https://www.monkey.work/2025-12-20-modern-web-app-tech-stack/</guid><pubDate>Sat, 20 Dec 2025 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Building a modern web application requires making careful technology choices that balance developer experience, performance, and operational simplicity. In this post, I’ll walk through the complete tech stack we chose for our web application and explain the reasoning behind each decision.&lt;/p&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Our web application runs on a full-stack JavaScript/TypeScript foundation with Python for specialized worker processes. The entire stack is containerized with Docker and initially deployed on Amazon Linux 2023, keeping infrastructure lightweight while maintaining production reliability.&lt;/p&gt;
&lt;h2 id=&quot;backend-nestjs--prisma--postgres--redis&quot;&gt;Backend: NestJS + Prisma + Postgres + Redis&lt;/h2&gt;
&lt;h3 id=&quot;nestjs-node-22&quot;&gt;NestJS (Node 22)&lt;/h3&gt;
&lt;p&gt;We chose NestJS as our backend framework for its opinionated, modular structure. The decorator-based approach and dependency injection system make it easy to add new features while keeping code organized. TypeScript support is first-class, giving us type safety across the entire application.&lt;/p&gt;
&lt;p&gt;Opinionated modules, DI, guards/pipes; easy to grow and keep organized.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Controller&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;items&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ItemsController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; itemsService&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ItemsService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;itemsService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Guarded route to protect admin-only actions&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Post&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UseGuards&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AdminGuard&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Body&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; dto&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CreateItemDto&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;itemsService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dto&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;AdminGuard&lt;/code&gt; plugs into NestJS’ standard guard interface, letting us centralize authorization logic and keep controllers thin.&lt;/p&gt;
&lt;h3 id=&quot;prisma&quot;&gt;Prisma&lt;/h3&gt;
&lt;p&gt;Prisma serves as our type-safe ORM with a schema-first approach. The auto-generated TypeScript types eliminate entire classes of runtime errors, and migrations are fast and reliable. The developer experience is exceptional - we get IntelliSense for database queries and compile-time validation.&lt;/p&gt;
&lt;p&gt;Type-safe ORM; schema-first; fast migrations; good DX when evolving schemas for reports.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;model Item &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id        String   &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;id&lt;/span&gt;&lt;/span&gt; @&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cuid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  name      String   &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unique&lt;/span&gt;&lt;/span&gt;
  title     String
  value     Float
  createdAt DateTime @&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  updatedAt DateTime &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updatedAt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Example query pattern with compile-time safety:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Find items with value above a threshold and include tags&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; items &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; prisma&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findMany&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  where&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; gt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  include&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; tags&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  orderBy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; updatedAt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;desc&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Migrations stay consistent via &lt;code class=&quot;language-text&quot;&gt;prisma migrate deploy&lt;/code&gt; baked into our compose workflow, so local and prod schemas stay in lockstep.&lt;/p&gt;
&lt;h3 id=&quot;postgresql&quot;&gt;PostgreSQL&lt;/h3&gt;
&lt;p&gt;PostgreSQL provides our reliable relational data store with excellent JSON support. This flexibility works perfectly for our scoring and reporting features, allowing us to store structured data alongside traditional relational data. The ecosystem of extensions and robust tooling made it an obvious choice.&lt;/p&gt;
&lt;p&gt;Relational backbone with JSONB flexibility; solid for scoring/reporting and auditability.&lt;/p&gt;
&lt;h3 id=&quot;redis&quot;&gt;Redis&lt;/h3&gt;
&lt;p&gt;Redis handles caching and queue management, keeping our API responsive under load. We use it for session storage, API response caching, and background job queues. The in-memory performance is essential for real-time data updates.&lt;/p&gt;
&lt;p&gt;Caching/queues/light state to keep API latency low and decouple workloads.&lt;/p&gt;
&lt;p&gt;Typical cache pattern:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;item:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cached &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; redis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cached&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cached&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; item &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;repo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; redis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 60s TTL&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For queues, lightweight Redis lists back small background tasks while heavier jobs run in the Python worker.&lt;/p&gt;
&lt;h3 id=&quot;health--tooling&quot;&gt;Health &amp;#x26; tooling&lt;/h3&gt;
&lt;p&gt;/api/health, Prisma CLI, logs via compose/systemd.&lt;/p&gt;
&lt;h3 id=&quot;docker-compose&quot;&gt;Docker Compose&lt;/h3&gt;
&lt;p&gt;One command brings up our entire development environment: database, Redis, backend, and web frontend. This consistency between local development and production environments eliminates “it works on my machine” issues and simplifies onboarding. Critically, Docker gives us the freedom to switch cloud providers easily - we can move from AWS to DigitalOcean, Vultr, or any other provider with minimal changes to our setup.&lt;/p&gt;
&lt;h3 id=&quot;systemd-units&quot;&gt;systemd units&lt;/h3&gt;
&lt;p&gt;On production, we use systemd units to keep the stack running and manage scheduled worker tasks on a linux machine. This provides process supervision, automatic restarts, and reliable scheduling without additional complexity.&lt;/p&gt;
&lt;h2 id=&quot;frontend-nextjs-app-router--tailwind&quot;&gt;Frontend: Next.js (App Router) + Tailwind&lt;/h2&gt;
&lt;h3 id=&quot;nextjs&quot;&gt;Next.js&lt;/h3&gt;
&lt;p&gt;Next.js with the App Router gives us the best of both worlds: server components for fast initial loads and client components where needed. API routes handle simple backend-facing tasks, and the framework’s deployment portability makes hosting straightforward.&lt;/p&gt;
&lt;p&gt;Server Components for fast initial paint and smaller bundles; API routes available if needed.&lt;/p&gt;
&lt;h3 id=&quot;data-fetching&quot;&gt;Data fetching&lt;/h3&gt;
&lt;p&gt;Prefer server components; light SWR for client interactivity. Avoids over-fetching and keeps hydration lean.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Server component for fast initial load&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ItemPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; params &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; params&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; item &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Value: &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Client-side interactivity with SWR (kept minimal):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isLoading &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/api/items/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fetcher&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; refreshInterval&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30000&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isLoading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Loading…&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;tailwind-css&quot;&gt;Tailwind CSS&lt;/h3&gt;
&lt;p&gt;Tailwind enables rapid styling with consistent design tokens. The utility-first approach means we can tweak designs without writing heavy CSS files, and the constraint-based system keeps our UI consistent across the application.&lt;/p&gt;
&lt;p&gt;Tailwind for speed and consistency; utility-first keeps the UI coherent without a bespoke design system overhead.&lt;/p&gt;
&lt;h3 id=&quot;buildruntime&quot;&gt;Build/runtime&lt;/h3&gt;
&lt;p&gt;Next.js standalone output, runs behind Nginx; environment-driven API base URL via compose build args.&lt;/p&gt;
&lt;h3 id=&quot;swr-light-usage&quot;&gt;SWR (light usage)&lt;/h3&gt;
&lt;p&gt;We use SWR sparingly for client-side data fetching where server components aren’t sufficient. Most data fetching happens on the server, but SWR handles real-time updates and client-side state when needed.&lt;/p&gt;
&lt;h2 id=&quot;worker-python&quot;&gt;Worker (Python)&lt;/h2&gt;
&lt;h3 id=&quot;serviceworker&quot;&gt;Service/Worker&lt;/h3&gt;
&lt;p&gt;A Python service runs data processing and scoring tasks via our backend API. This isolation is intentional - the worker communicates through APIs rather than direct database writes, ensuring data integrity and making the system easier to debug and maintain.&lt;/p&gt;
&lt;p&gt;Screens data and writes via the backend API (no direct DB writes for safety).&lt;/p&gt;
&lt;h3 id=&quot;isolation&quot;&gt;Isolation&lt;/h3&gt;
&lt;p&gt;Separate container/profile; one-shot runs or long-running mode; scheduled via systemd timer on Linux.&lt;/p&gt;
&lt;p&gt;Sample task shape (pseudo-Python):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run_screen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    symbols &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fetch_symbols&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    scored &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; score&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;symbols&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; item &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; scored&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        requests&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f\&quot;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;API_BASE&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;items\&quot;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; headers&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;auth&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Jobs can run as &lt;code class=&quot;language-text&quot;&gt;docker compose run worker&lt;/code&gt; for one-shots or long-running in &lt;code class=&quot;language-text&quot;&gt;worker&lt;/code&gt; profile with a timer.&lt;/p&gt;
&lt;h2 id=&quot;reverse-proxy--tls&quot;&gt;Reverse Proxy &amp;#x26; TLS&lt;/h2&gt;
&lt;h3 id=&quot;nginx&quot;&gt;Nginx&lt;/h3&gt;
&lt;p&gt;Nginx fronts both our web application and API, handling gzip compression and routing. It serves as our reverse proxy, directing traffic to the appropriate services and providing a single entry point for our application.&lt;/p&gt;
&lt;h3 id=&quot;lets-encrypt-certbot&quot;&gt;Let’s Encrypt (Certbot)&lt;/h3&gt;
&lt;p&gt;Automated TLS certificates secure our domains with simple renewals. Certbot handles the certificate lifecycle, so we don’t have to worry about manual certificate management.&lt;/p&gt;
&lt;h2 id=&quot;deployment-target&quot;&gt;Deployment Target&lt;/h2&gt;
&lt;h3 id=&quot;amazon-linux-2023-on-ec2&quot;&gt;Amazon Linux 2023 on EC2&lt;/h3&gt;
&lt;p&gt;We chose Amazon Linux 2023 as our deployment target for its minimal, stable base. It’s Docker Compose friendly and includes systemd out of the box. The operating system strikes the right balance between stability and modern features.&lt;/p&gt;
&lt;h2 id=&quot;ops--deployment-docker-compose--systemd--nginx--certbot-on-linux&quot;&gt;Ops &amp;#x26; Deployment: Docker Compose + systemd + Nginx + Certbot on Linux&lt;/h2&gt;
&lt;h3 id=&quot;compose&quot;&gt;Compose&lt;/h3&gt;
&lt;p&gt;Single stack (db, redis, backend, web-app, worker profile). Parity between local and prod; platform pinning (linux/amd64) for production.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;docker-compose.yml&lt;/code&gt; sketch:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./backend&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;API_BASE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;API_BASE&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;8080:8080&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;depends_on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;db&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; redis&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./web&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;API_BASE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;API_BASE&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;3000:3000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;worker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./worker
    &lt;span class=&quot;token key atrule&quot;&gt;profiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;worker&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;depends_on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;backend&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; postgres&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; redis&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;systemd&quot;&gt;systemd&lt;/h3&gt;
&lt;p&gt;Keeps the stack running (app-stack.service) and schedules daily worker (app-daily.timer/service).&lt;/p&gt;
&lt;p&gt;Example unit (simplified):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[Unit]
Description=App stack
After=docker.service

[Service]
Type=oneshot
WorkingDirectory=/opt/app
ExecStart=/usr/bin/docker compose up -d
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Timers trigger &lt;code class=&quot;language-text&quot;&gt;docker compose run worker&lt;/code&gt; on a schedule for daily jobs.&lt;/p&gt;
&lt;h3 id=&quot;nginx-1&quot;&gt;Nginx&lt;/h3&gt;
&lt;p&gt;Reverse proxy to backend (8080) and web (3000); HTTPS termination.&lt;/p&gt;
&lt;p&gt;Typical location blocks:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;location /api/ {
  proxy_pass http://backend:8080/;
  proxy_set_header Host $host;
}

location / {
  proxy_pass http://web:3000/;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;lets-encrypt-certbot-1&quot;&gt;Let’s Encrypt (Certbot)&lt;/h3&gt;
&lt;p&gt;Automated TLS for domains; renewals handled by Certbot.&lt;/p&gt;
&lt;p&gt;Renewal check:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo certbot renew --dry-run&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;portability&quot;&gt;Portability&lt;/h3&gt;
&lt;p&gt;Avoids heavy managed services; can rehome to other VPS providers with the same compose + Nginx pattern.&lt;/p&gt;
&lt;h2 id=&quot;why-this-mix&quot;&gt;Why This Mix?&lt;/h2&gt;
&lt;h3 id=&quot;developer-speed&quot;&gt;Developer Speed&lt;/h3&gt;
&lt;p&gt;The combination of NestJS + Prisma + Tailwind + Next.js provides excellent developer experience with strong type safety throughout the stack. We can move quickly without sacrificing code quality.&lt;/p&gt;
&lt;p&gt;Type safety (Prisma/TypeScript), fast styling (Tailwind), and predictable module structure (NestJS).&lt;/p&gt;
&lt;h3 id=&quot;performance&quot;&gt;Performance&lt;/h3&gt;
&lt;p&gt;Server components reduce client JS; Redis + Postgres keep responses quick; worker offloads heavy processing.&lt;/p&gt;
&lt;h3 id=&quot;operational-simplicity&quot;&gt;Operational Simplicity&lt;/h3&gt;
&lt;p&gt;Docker Compose, systemd, and Nginx keep our infrastructure lightweight. We avoid heavy managed services, which reduces complexity and vendor lock-in while maintaining production reliability.&lt;/p&gt;
&lt;p&gt;Compose + systemd + Nginx is lightweight to run on Linux; TLS via Certbot is low-friction.&lt;/p&gt;
&lt;h3 id=&quot;portability-1&quot;&gt;Portability&lt;/h3&gt;
&lt;p&gt;The same Docker Compose configuration runs everywhere: Mac development machines, Amazon Linux 2023 production servers, or other VPS providers. This consistency makes development and deployment predictable.&lt;/p&gt;
&lt;p&gt;Minimal distro dependencies; compose files drive the same build on Mac (ARM) and Linux (amd64) with DOCKER_DEFAULT_PLATFORM.&lt;/p&gt;
&lt;h3 id=&quot;security--reliability&quot;&gt;Security &amp;#x26; Reliability&lt;/h3&gt;
&lt;p&gt;PostgreSQL provides robust data integrity, TLS is enabled by default, and Redis isolates caching and queuing from our main application. The worker service uses the API rather than direct database access, adding an extra layer of safety.&lt;/p&gt;
&lt;p&gt;Worker goes through the backend API; DB stays behind the network boundary; TLS by default.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This tech stack represents a careful balance of modern development practices and operational simplicity. Each technology serves a specific purpose while contributing to a cohesive whole. The result is a system that’s fast to develop, reliable in production, and cost-effective to operate.&lt;/p&gt;
&lt;p&gt;For teams building similar applications, this stack provides a solid foundation that can scale from prototype to production without major architectural changes.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Running the Web App Locally vs. in Docker Compose]]></title><description><![CDATA[Keeping local development and Docker Compose in sync is mostly about the right API base URL. This guide shows the minimum you need to switch…]]></description><link>https://www.monkey.work/2025-12-20-running-web-app-local-vs-docker/</link><guid isPermaLink="false">https://www.monkey.work/2025-12-20-running-web-app-local-vs-docker/</guid><pubDate>Sat, 20 Dec 2025 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Keeping local development and Docker Compose in sync is mostly about the right API base URL. This guide shows the minimum you need to switch between localhost and Docker without surprises.&lt;/p&gt;
&lt;h2 id=&quot;architecture-at-a-glance&quot;&gt;Architecture at a glance&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Local
┌──────────┐     http://localhost:3000          http://localhost:8080/api
│ Browser  │ ───────────────────────────────▶  Web app  ───────────▶ Backend
└──────────┘

Docker Compose (containers on same network)
┌──────────┐     http://localhost:3000          http://backend:8080/api
│ Browser  │ ───────────────────────────────▶  Web app ─────────────▶ Backend
└──────────┘&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;why-two-endpoints&quot;&gt;Why two endpoints?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Local dev:&lt;/strong&gt; The web app runs on &lt;code class=&quot;language-text&quot;&gt;http://localhost:3000&lt;/code&gt; and calls the API at &lt;code class=&quot;language-text&quot;&gt;http://localhost:8080/api&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docker Compose:&lt;/strong&gt; Containers use the internal service name, so the web app calls &lt;code class=&quot;language-text&quot;&gt;http://backend:8080/api&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;1-environment-files-for-local-vs-docker&quot;&gt;1) Environment files for local vs. Docker&lt;/h2&gt;
&lt;p&gt;Create separate env files so you never mix the URLs:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Local override (not baked into images):&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;web-app/.env.local&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;env&quot;&gt;&lt;pre class=&quot;language-env&quot;&gt;&lt;code class=&quot;language-env&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;WEB_APP_API_URL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;http://localhost:8080
&lt;span class=&quot;token assign-left variable&quot;&gt;WEB_APP_API_VER&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/api
&lt;span class=&quot;token assign-left variable&quot;&gt;NEXT_PUBLIC_BACKEND_API_BASE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;http://localhost:8080/api&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Docker/default (used in containers):&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;web-app/.env&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;env&quot;&gt;&lt;pre class=&quot;language-env&quot;&gt;&lt;code class=&quot;language-env&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;WEB_APP_API_URL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;http://backend:8080
&lt;span class=&quot;token assign-left variable&quot;&gt;WEB_APP_API_VER&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/api
&lt;span class=&quot;token assign-left variable&quot;&gt;NEXT_PUBLIC_BACKEND_API_BASE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;http://backend:8080/api&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Add &lt;code class=&quot;language-text&quot;&gt;.env.local&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;.dockerignore&lt;/code&gt; so local-only values never get baked into images.&lt;/p&gt;
&lt;h2 id=&quot;2-app-code-picks-the-right-base&quot;&gt;2) App code picks the right base&lt;/h2&gt;
&lt;p&gt;In &lt;code class=&quot;language-text&quot;&gt;app/page.tsx&lt;/code&gt;, read the public env and fall back to localhost for safety:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;API_BASE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
  process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NEXT_PUBLIC_BACKEND_API_BASE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;http://localhost:8080/api&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This keeps local dev working even if the file is missing.&lt;/p&gt;
&lt;h2 id=&quot;3-dockerfile-and-compose-wiring&quot;&gt;3) Dockerfile and compose wiring&lt;/h2&gt;
&lt;p&gt;Pass the public env through the Docker build and runtime:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dockerfile&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ARG&lt;/span&gt; NEXT_PUBLIC_BACKEND_API_BASE&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ENV&lt;/span&gt; NEXT_PUBLIC_BACKEND_API_BASE=&lt;span class=&quot;token variable&quot;&gt;${NEXT_PUBLIC_BACKEND_API_BASE}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;docker-compose.yml&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;web-app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./web&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
    &lt;span class=&quot;token key atrule&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;WEB_APP_API_URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;WEB_APP_API_URL&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//backend&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;WEB_APP_API_VER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;WEB_APP_API_VER&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;/api&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;NEXT_PUBLIC_BACKEND_API_BASE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;NEXT_PUBLIC_BACKEND_API_BASE&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//backend&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;8080/api&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;NEXT_PUBLIC_BACKEND_API_BASE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;NEXT_PUBLIC_BACKEND_API_BASE&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//backend&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;8080/api&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;4-local-dev-commands&quot;&gt;4) Local dev commands&lt;/h2&gt;
&lt;p&gt;From &lt;code class=&quot;language-text&quot;&gt;web-app/&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-rf&lt;/span&gt; .next   &lt;span class=&quot;token comment&quot;&gt;# only if you changed env files&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run dev    &lt;span class=&quot;token comment&quot;&gt;# picks up .env.local automatically&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Make sure the backend is running at &lt;code class=&quot;language-text&quot;&gt;http://localhost:8080/api&lt;/code&gt; (or update &lt;code class=&quot;language-text&quot;&gt;NEXT_PUBLIC_BACKEND_API_BASE&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id=&quot;5-docker-compose-commands&quot;&gt;5) Docker Compose commands&lt;/h2&gt;
&lt;p&gt;Use the Compose values (&lt;code class=&quot;language-text&quot;&gt;http://backend:8080/api&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; compose up &lt;span class=&quot;token parameter variable&quot;&gt;--build&lt;/span&gt; web-app
&lt;span class=&quot;token comment&quot;&gt;# on Apple Silicon (if no multi-arch images):&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;DOCKER_DEFAULT_PLATFORM&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;linux/arm64 &lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; compose up &lt;span class=&quot;token parameter variable&quot;&gt;--build&lt;/span&gt; web-app&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;6-avoiding-the-wrong-env-in-containers&quot;&gt;6) Avoiding the wrong env in containers&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Keep &lt;code class=&quot;language-text&quot;&gt;.env.local&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;.dockerignore&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Rebuild after env changes: &lt;code class=&quot;language-text&quot;&gt;docker compose up --build web-app&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Check your shell exports; none should override &lt;code class=&quot;language-text&quot;&gt;NEXT_PUBLIC_BACKEND_API_BASE&lt;/code&gt; when running locally.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;7-quick-checklist&quot;&gt;7) Quick checklist&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Local:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;.env.local&lt;/code&gt; → localhost API, &lt;code class=&quot;language-text&quot;&gt;npm run dev&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docker:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; + compose build args/env → &lt;code class=&quot;language-text&quot;&gt;http://backend:8080/api&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;After changes:&lt;/strong&gt; rebuild containers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connectivity:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;curl http://backend:8080/api/report-runs&lt;/code&gt; from inside a container; &lt;code class=&quot;language-text&quot;&gt;curl http://localhost:8080/api/report-runs&lt;/code&gt; locally.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[How to Launch a New Amazon EC2 Instance with Amazon Linux 2023 and Add Extra Storage]]></title><description><![CDATA[Amazon EC2 lets you run virtual servers in the cloud. In this guide, you will: Choose the latest Amazon Linux 2023 AMI Launch a new EC…]]></description><link>https://www.monkey.work/2025-12-17-launch-ec2-amazon-linux-2023-extra-storage/</link><guid isPermaLink="false">https://www.monkey.work/2025-12-17-launch-ec2-amazon-linux-2023-extra-storage/</guid><pubDate>Wed, 17 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Amazon EC2 lets you run virtual servers in the cloud. In this guide, you will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Choose the latest &lt;strong&gt;Amazon Linux 2023&lt;/strong&gt; AMI&lt;/li&gt;
&lt;li&gt;Launch a new EC2 instance&lt;/li&gt;
&lt;li&gt;Set the root EBS volume size to &lt;strong&gt;100 GB&lt;/strong&gt; (gp3)&lt;/li&gt;
&lt;li&gt;Connect via SSH&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Prerequisites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS CLI installed&lt;/li&gt;
&lt;li&gt;Credentials configured (&lt;code class=&quot;language-text&quot;&gt;aws configure&lt;/code&gt;) with permissions to manage EC2&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;step-1-choose-the-latest-amazon-linux-2023-ami&quot;&gt;Step 1: Choose the latest Amazon Linux 2023 AMI&lt;/h2&gt;
&lt;p&gt;Amazon Linux 2 reaches end-of-life in &lt;strong&gt;June 2026&lt;/strong&gt;, so AWS recommends &lt;strong&gt;Amazon Linux 2023&lt;/strong&gt;, supported until &lt;strong&gt;March 2028&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;To find the latest AL2023 AMI in your region (&lt;code class=&quot;language-text&quot;&gt;us-east-2&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;aws ec2 describe-images &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--owners&lt;/span&gt; amazon &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--filters&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;Name=name,Values=al2023-ami-*-x86_64&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;Name=state,Values=available&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sort_by(Images,&amp;amp;CreationDate)[-20:].[CreationDate,ImageId,Name]&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; table&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Pick the newest AMI (example):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;AMI ID: ami-00e428798e77d38d9
Name: al2023-ami-2023.9.20251208.0-kernel-6.1-x86_64&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a1819e6f0c45b1a72bc061e937b7dfc8/0e78a/screenshot-ami-list.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABtklEQVR42i2SbVfaQBCF88WjBE02u7Mv2QSK9FhBoFBrRQELiHhOW/3/P+d6E/thzmQnMzfP3E3ScR6pC0gts2+y+3/+rHX6FunQMfvPHtY77GlDmuxxKhpnYnBmBYl3G5T+Gc4+IFS/od0cdfjL+IdYHlCEMYLfwvs1vHtqw7nmeQ2RX7Ccy/UVUlXi1Bgkkc11OKKkcKz3sP4W/fIdPYpWFNT+BtEdKPDID28RKFT5F0R/4HlHENbdFkpfIyVxEtkQ/QaV26GuORAWGMZ3DMo39OIerhxTfM/hFYWe2MsgZWlJK0sEu4LWI+TFAN1G0BZLiL6H0+t2La0nqOWISl45tIOWK0RDW5rh4gG++ImKVMHcQ/I5dDaDVjOkXY+T7ByJmCW8XcOZFVxYQ5kJhV4YR665Ry5f4fUj399C1B0agCgbBH0HyeYUXZDuG9KswonKkQS7aU0P9KGsSGRnXPEPBuENMeyQWxLaLYl5aYZbkLZPe76457bm1A9YNUX3grddXCDJzQ2UTBkTqDAl0SXF57B6AUOjMwm8zQkphq3xhYx4yzPa8531MeuX3GqEc9Pjb1PgAyfU10drZz6AAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot: AWS CLI output showing AMI list&quot;
        title=&quot;&quot;
        src=&quot;/static/a1819e6f0c45b1a72bc061e937b7dfc8/f97d7/screenshot-ami-list.png&quot;
        srcset=&quot;/static/a1819e6f0c45b1a72bc061e937b7dfc8/0eb09/screenshot-ami-list.png 500w,
/static/a1819e6f0c45b1a72bc061e937b7dfc8/1263b/screenshot-ami-list.png 1000w,
/static/a1819e6f0c45b1a72bc061e937b7dfc8/f97d7/screenshot-ami-list.png 2000w,
/static/a1819e6f0c45b1a72bc061e937b7dfc8/0e78a/screenshot-ami-list.png 2624w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;step-2-create-a-key-pair&quot;&gt;Step 2: Create a key pair&lt;/h2&gt;
&lt;p&gt;This key pair allows SSH access.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;aws ec2 create-key-pair &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --key-name MyKeyPair &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;KeyMaterial&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; MyKeyPair.pem

&lt;span class=&quot;token function&quot;&gt;chmod&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt; MyKeyPair.pem&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Screenshot:&lt;/strong&gt; Key pair creation (&lt;code class=&quot;language-text&quot;&gt;img/2025-12-17/screenshot-keypair.png&lt;/code&gt;)&lt;/p&gt;
&lt;h2 id=&quot;step-3-create-a-security-group&quot;&gt;Step 3: Create a security group&lt;/h2&gt;
&lt;p&gt;Allow SSH (port 22). For best security, prefer your own IP (for example &lt;code class=&quot;language-text&quot;&gt;1.2.3.4/32&lt;/code&gt;) instead of &lt;code class=&quot;language-text&quot;&gt;0.0.0.0/0&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;check-if-the-security-group-already-exists&quot;&gt;Check if the security group already exists&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;aws ec2 describe-security-groups &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--filters&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Name=group-name,Values=MySecurityGroup&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SecurityGroups[*].GroupId&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; text&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;SG_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;aws ec2 create-security-group &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --group-name MySecurityGroup &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--description&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow SSH&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;GroupId&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; text&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Created security group: &lt;span class=&quot;token variable&quot;&gt;$SG_ID&lt;/span&gt;&quot;&lt;/span&gt;

aws ec2 authorize-security-group-ingress &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --group-id &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$SG_ID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--protocol&lt;/span&gt; tcp &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--port&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--cidr&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;.0.0/0 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Screenshot:&lt;/strong&gt; Security group setup (&lt;code class=&quot;language-text&quot;&gt;img/2025-12-17/screenshot-security-group.png&lt;/code&gt;)&lt;/p&gt;
&lt;h2 id=&quot;step-4-launch-the-instance-with-100-gb-of-storage&quot;&gt;Step 4: Launch the instance with 100 GB of storage&lt;/h2&gt;
&lt;p&gt;By default, Amazon Linux often uses an ~8 GB root volume. This example sets the root volume to &lt;strong&gt;100 GB&lt;/strong&gt; using &lt;strong&gt;gp3&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;INSTANCE_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;aws ec2 run-instances &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --image-id ami-00e428798e77d38d9 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--count&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --instance-type t3.large &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --key-name MyKeyPair &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --security-group-ids &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$SG_ID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --block-device-mappings &lt;span class=&quot;token string&quot;&gt;&apos;[{&quot;DeviceName&quot;:&quot;/dev/xvda&quot;,&quot;Ebs&quot;:{&quot;VolumeSize&quot;:100,&quot;VolumeType&quot;:&quot;gp3&quot;,&quot;DeleteOnTermination&quot;:true}}]&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --tag-specifications &lt;span class=&quot;token string&quot;&gt;&apos;ResourceType=instance,Tags=[{Key=Name,Value=al2023-100gb}]&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Instances[0].InstanceId&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; text&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Created instance: &lt;span class=&quot;token variable&quot;&gt;$INSTANCE_ID&lt;/span&gt;&quot;&lt;/span&gt;

aws ec2 &lt;span class=&quot;token function&quot;&gt;wait&lt;/span&gt; instance-running &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --instance-ids &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$INSTANCE_ID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Explanation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--block-device-mappings&lt;/code&gt; overrides the default root volume size to &lt;strong&gt;100 GB&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;gp3&lt;/code&gt; is cost-effective and high-performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;step-5-get-the-public-ip-and-connect-via-ssh&quot;&gt;Step 5: Get the public IP and connect via SSH&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;PUBLIC_IP&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;aws ec2 describe-instances &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --instance-ids &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$INSTANCE_ID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Reservations[0].Instances[0].PublicIpAddress&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; text&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Public IP: &lt;span class=&quot;token variable&quot;&gt;$PUBLIC_IP&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;ssh&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; MyKeyPair.pem ec2-user@&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PUBLIC_IP&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;optional-create-a-permanent-public-ip-elastic-ip&quot;&gt;Optional: Create a permanent public IP (Elastic IP)&lt;/h2&gt;
&lt;p&gt;Allocate an Elastic IP:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;ALLOCATION_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;aws ec2 allocate-address &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;AllocationId&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; text&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Elastic IP allocation id: &lt;span class=&quot;token variable&quot;&gt;$ALLOCATION_ID&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Associate the Elastic IP to your instance:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;aws ec2 associate-address &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    --instance-id &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$INSTANCE_ID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    --allocation-id &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ALLOCATION_ID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Verify Elastic IPs in the region:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;aws ec2 describe-addresses &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2

aws ec2 describe-addresses &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --allocation-ids &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ALLOCATION_ID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;step-6-verify-storage&quot;&gt;Step 6: Verify storage&lt;/h2&gt;
&lt;p&gt;Once inside the instance:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;lsblk

&lt;span class=&quot;token function&quot;&gt;df&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-h&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you do not see the expected size yet, reboot once and check again. If it is still not expanded, you may need to grow the partition/filesystem (the exact steps depend on the filesystem).&lt;/p&gt;
&lt;h2 id=&quot;step-7-install-and-configure-nginx&quot;&gt;Step 7: Install and configure Nginx&lt;/h2&gt;
&lt;p&gt;After launching your instance, you can install Nginx to serve web content.&lt;/p&gt;
&lt;p&gt;For Amazon Linux 2023:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; dnf update &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; dnf &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt; nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For Amazon Linux 2:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; yum update &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; amazon-linux-extras &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; nginx1
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; yum &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt; nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Start and enable Nginx:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl start nginx
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Verify Nginx status:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl status nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;why-choose-amazon-linux-2023&quot;&gt;Why choose Amazon Linux 2023?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Long-term support until &lt;strong&gt;2028&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Kernel &lt;strong&gt;6.x&lt;/strong&gt; for better performance and security&lt;/li&gt;
&lt;li&gt;Uses the &lt;code class=&quot;language-text&quot;&gt;dnf&lt;/code&gt; package manager (modern replacement for &lt;code class=&quot;language-text&quot;&gt;yum&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;optional-enhancements&quot;&gt;Optional enhancements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Attach an &lt;strong&gt;Elastic IP&lt;/strong&gt; so your server keeps the same public IP address.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;AWS Systems Manager Session Manager&lt;/strong&gt; to access the instance without opening inbound SSH.&lt;/li&gt;
&lt;li&gt;Install your stack (Python, Docker, Nginx) using &lt;code class=&quot;language-text&quot;&gt;dnf&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you’re planning to run Kubernetes, see &lt;a href=&quot;/2025-08-22-kubernetes-cluster-setup-on-aws-eks/&quot;&gt;Kubernetes Cluster Setup on AWS EKS&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Happy cloud building!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Setting up Nginx with Cloudflare SSL on Amazon Linux 2023 (via Certbot)]]></title><description><![CDATA[This guide walks through the end-to-end process of deploying a secure Nginx web server on Amazon Linux 2023, using Cloudflare for DNS…]]></description><link>https://www.monkey.work/2025-12-17-nginx-cloudflare-ssl-amazon-linux-2023-certbot/</link><guid isPermaLink="false">https://www.monkey.work/2025-12-17-nginx-cloudflare-ssl-amazon-linux-2023-certbot/</guid><pubDate>Wed, 17 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This guide walks through the end-to-end process of deploying a secure Nginx web server on &lt;strong&gt;Amazon Linux 2023&lt;/strong&gt;, using &lt;strong&gt;Cloudflare&lt;/strong&gt; for DNS/proxying, and &lt;strong&gt;Let’s Encrypt&lt;/strong&gt; via &lt;strong&gt;Certbot&lt;/strong&gt; so you can run Cloudflare SSL/TLS in &lt;strong&gt;Full (Strict)&lt;/strong&gt; mode.&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;An AWS EC2 instance running Amazon Linux 2023&lt;/li&gt;
&lt;li&gt;SSH access to your instance (typically &lt;code class=&quot;language-text&quot;&gt;ec2-user&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A domain managed by Cloudflare&lt;/li&gt;
&lt;li&gt;A public IPv4 address on the instance (or an Elastic IP)&lt;/li&gt;
&lt;li&gt;AWS CLI installed locally (optional, but helpful for verification)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;phase-1-aws-and-dns-configuration&quot;&gt;Phase 1: AWS and DNS configuration&lt;/h2&gt;
&lt;h3 id=&quot;1-configure-aws-security-group-inbound-rules&quot;&gt;1) Configure AWS security group inbound rules&lt;/h3&gt;
&lt;p&gt;Your instance must allow inbound traffic for Nginx and for Certbot validation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP: TCP, port &lt;code class=&quot;language-text&quot;&gt;80&lt;/code&gt;, source &lt;code class=&quot;language-text&quot;&gt;0.0.0.0/0&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;::/0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;HTTPS: TCP, port &lt;code class=&quot;language-text&quot;&gt;443&lt;/code&gt;, source &lt;code class=&quot;language-text&quot;&gt;0.0.0.0/0&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;::/0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;SSH: TCP, port &lt;code class=&quot;language-text&quot;&gt;22&lt;/code&gt; (ideally restricted to your IP)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2-configure-cloudflare-dns&quot;&gt;2) Configure Cloudflare DNS&lt;/h3&gt;
&lt;p&gt;In Cloudflare &lt;code class=&quot;language-text&quot;&gt;DNS&lt;/code&gt; settings, point your domain to the instance’s public IP:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt; record
&lt;ul&gt;
&lt;li&gt;Name: &lt;code class=&quot;language-text&quot;&gt;@&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Content: &lt;code class=&quot;language-text&quot;&gt;203.0.113.10&lt;/code&gt; (replace with your EC2 public IP)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt; record
&lt;ul&gt;
&lt;li&gt;Name: &lt;code class=&quot;language-text&quot;&gt;www&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Content: &lt;code class=&quot;language-text&quot;&gt;203.0.113.10&lt;/code&gt; (replace with your EC2 public IP)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Important for Certbot&lt;/strong&gt;: set both records to &lt;strong&gt;DNS only&lt;/strong&gt; (grey cloud) for now.&lt;/p&gt;
&lt;h4 id=&quot;temporarily-disable-proxying-alternative-to-a-global-pause&quot;&gt;Temporarily disable proxying (alternative to a global pause)&lt;/h4&gt;
&lt;p&gt;Instead of pausing Cloudflare globally, you can disable the proxy on just the DNS records Certbot needs.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log in to the Cloudflare dashboard and select your account + domain.&lt;/li&gt;
&lt;li&gt;Go to &lt;code class=&quot;language-text&quot;&gt;DNS&lt;/code&gt; -&gt; &lt;code class=&quot;language-text&quot;&gt;Records&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Find the &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt; record for &lt;code class=&quot;language-text&quot;&gt;@&lt;/code&gt; and toggle &lt;strong&gt;Proxy status&lt;/strong&gt; to &lt;strong&gt;Off&lt;/strong&gt; (grey cloud / DNS only).&lt;/li&gt;
&lt;li&gt;Find the &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt; record for &lt;code class=&quot;language-text&quot;&gt;www&lt;/code&gt; and toggle &lt;strong&gt;Proxy status&lt;/strong&gt; to &lt;strong&gt;Off&lt;/strong&gt; (grey cloud / DNS only).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After the certificate is issued and Nginx is serving HTTPS correctly, you can switch both records back to &lt;strong&gt;Proxied&lt;/strong&gt; (orange cloud).&lt;/p&gt;
&lt;p&gt;When Cloudflare proxying is enabled, Let’s Encrypt validation can fail (common symptoms: timeouts or Cloudflare 52x errors). You can re-enable proxying after you have a valid certificate installed.&lt;/p&gt;
&lt;h2 id=&quot;phase-2-install-and-start-nginx&quot;&gt;Phase 2: Install and start Nginx&lt;/h2&gt;
&lt;p&gt;SSH into the instance:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;ssh&lt;/span&gt; ec2-user@203.0.113.10&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Install Nginx:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; dnf &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt; nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Start and enable the service:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl start nginx
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;phase-3-create-a-simple-http-site-for-validation&quot;&gt;Phase 3: Create a simple HTTP site (for validation)&lt;/h2&gt;
&lt;p&gt;Create a directory to serve content:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; /var/www/site&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Create a basic test page:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tee&lt;/span&gt; /var/www/site/index.html &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/dev/null &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;EOF&apos;
&amp;lt;!doctype html&gt;
&amp;lt;html&gt;
  &amp;lt;head&gt;&amp;lt;meta charset=&quot;utf-8&quot; /&gt;&amp;lt;title&gt;It works&amp;lt;/title&gt;&amp;lt;/head&gt;
  &amp;lt;body&gt;&amp;lt;h1&gt;Nginx is running&amp;lt;/h1&gt;&amp;lt;/body&gt;
&amp;lt;/html&gt;
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Create an Nginx server block:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tee&lt;/span&gt; /etc/nginx/conf.d/site.conf &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/dev/null &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;EOF&apos;
server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    root /var/www/site;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Validate and reload Nginx:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; nginx &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl reload nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At this point, visiting &lt;code class=&quot;language-text&quot;&gt;http://example.com/&lt;/code&gt; should show your test page.&lt;/p&gt;
&lt;h2 id=&quot;phase-4-install-certbot-and-issue-lets-encrypt-certificates&quot;&gt;Phase 4: Install Certbot and issue Let’s Encrypt certificates&lt;/h2&gt;
&lt;p&gt;Install Certbot (Nginx plugin):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; dnf &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt; certbot python3-certbot-nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Request certificates:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; certbot &lt;span class=&quot;token parameter variable&quot;&gt;--nginx&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; example.com &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; www.example.com&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;During the interactive prompts, Certbot can optionally configure HTTP-to-HTTPS redirects for you.&lt;/p&gt;
&lt;p&gt;Verify that Nginx config is still valid:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; nginx &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;phase-5-configure-nginx-server-blocks-http-redirect--code-classlanguage-textblogcode-alias&quot;&gt;Phase 5: Configure Nginx server blocks (HTTP redirect + &lt;code class=&quot;language-text&quot;&gt;/blog/&lt;/code&gt; alias)&lt;/h2&gt;
&lt;p&gt;After Certbot finishes, it will have added a working HTTPS server block and the required &lt;code class=&quot;language-text&quot;&gt;ssl_certificate&lt;/code&gt; directives.&lt;/p&gt;
&lt;p&gt;If you want:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All HTTP to redirect to HTTPS&lt;/li&gt;
&lt;li&gt;The HTTPS root (&lt;code class=&quot;language-text&quot;&gt;/&lt;/code&gt;) to redirect to &lt;code class=&quot;language-text&quot;&gt;/blog/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Content served from &lt;code class=&quot;language-text&quot;&gt;/var/www/site&lt;/code&gt; under &lt;code class=&quot;language-text&quot;&gt;/blog/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;edit your Nginx config (for example &lt;code class=&quot;language-text&quot;&gt;/etc/nginx/conf.d/site.conf&lt;/code&gt;) so it has this shape.&lt;/p&gt;
&lt;p&gt;Important:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep the &lt;strong&gt;Certbot-managed SSL directives&lt;/strong&gt; that Certbot added (the &lt;code class=&quot;language-text&quot;&gt;ssl_certificate&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;ssl_certificate_key&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; lines).&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;nginx&quot;&gt;&lt;pre class=&quot;language-nginx&quot;&gt;&lt;code class=&quot;language-nginx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# --- HTTP Server Block (Redirects all traffic to HTTPS) ---&lt;/span&gt;
&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;server&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;listen&lt;/span&gt; [::]:80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;server_name&lt;/span&gt; example.com www.example.com&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;301&lt;/span&gt; https://&lt;span class=&quot;token variable&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$request_uri&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# --- HTTPS Server Block (Content + /blog alias) ---&lt;/span&gt;
&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;server&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt; ssl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;listen&lt;/span&gt; [::]:443 ssl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;http2&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;on&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;server_name&lt;/span&gt; example.com www.example.com&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# &gt;&gt;&gt;&gt;&gt; Certbot-managed SSL directives will be here &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;

    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ssl_protocols&lt;/span&gt; TLSv1.2 TLSv1.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;location&lt;/span&gt; = /&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;301&lt;/span&gt; https://&lt;span class=&quot;token variable&quot;&gt;$host&lt;/span&gt;/blog/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;location&lt;/span&gt; ^~ /blog/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;alias&lt;/span&gt; /var/www/site/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;index&lt;/span&gt; index.html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;try_files&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$uri&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$uri&lt;/span&gt;/ =404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;root&lt;/span&gt; /usr/share/nginx/html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;error_page&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt; /404.html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;location&lt;/span&gt; = /404.html&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;root&lt;/span&gt; /var/www/site&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;access_log&lt;/span&gt; /var/log/nginx/site.access.log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;error_log&lt;/span&gt;  /var/log/nginx/site.error.log warn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Validate and reload:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; nginx &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl reload nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;phase-6-re-enable-cloudflare-proxy-and-set-full-strict&quot;&gt;Phase 6: Re-enable Cloudflare proxy and set Full (Strict)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;In Cloudflare &lt;code class=&quot;language-text&quot;&gt;DNS&lt;/code&gt;, switch your records back to &lt;strong&gt;Proxied&lt;/strong&gt; (orange cloud).&lt;/li&gt;
&lt;li&gt;In Cloudflare &lt;code class=&quot;language-text&quot;&gt;SSL/TLS&lt;/code&gt;, set encryption mode to &lt;strong&gt;Full (Strict)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;phase-7-automatic-renewal&quot;&gt;Phase 7: Automatic renewal&lt;/h2&gt;
&lt;p&gt;Certbot installs a systemd timer on many distros. Check it:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl is-enabled certbot-renew.timer&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If it is disabled:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; certbot-renew.timer
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl start certbot-renew.timer&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Verify it is scheduled:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl list-timers &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; certbot&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Dry-run a renewal:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; certbot renew --dry-run&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If the dry run succeeds, you are set: your origin has a real Let’s Encrypt certificate and Cloudflare can safely run &lt;strong&gt;Full (Strict)&lt;/strong&gt; end-to-end.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How a Dividend Stock Screener Chooses the "Top 10" (In Plain English)]]></title><description><![CDATA[If you have ever wondered how a system can take a huge list of stocks and quickly produce a “Top 10,” the approach is refreshingly simple…]]></description><link>https://www.monkey.work/2025-12-14-how-stockpicker-chooses-top-10-stocks/</link><guid isPermaLink="false">https://www.monkey.work/2025-12-14-how-stockpicker-chooses-top-10-stocks/</guid><pubDate>Sun, 14 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you have ever wondered how a system can take a huge list of stocks and quickly produce a “Top 10,” the approach is refreshingly simple.&lt;/p&gt;
&lt;p&gt;It is a &lt;strong&gt;filter first, then rank&lt;/strong&gt; system.&lt;/p&gt;
&lt;p&gt;It looks at a big list of stocks and does two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Screening (Pass/Fail): eliminate stocks that do not meet minimum “financial health + dividend quality” rules.&lt;/li&gt;
&lt;li&gt;Ranking (Scoring): score the survivors and pick the 10 highest-scoring.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Think of it like interviewing candidates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First you check if they meet the minimum requirements.&lt;/li&gt;
&lt;li&gt;Then you rank the qualified ones and hire the top 10.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this post you will learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How a screener builds its starting list.&lt;/li&gt;
&lt;li&gt;What numbers it pulls for each stock.&lt;/li&gt;
&lt;li&gt;The pass/fail rules that eliminate stocks.&lt;/li&gt;
&lt;li&gt;How scoring works (and why “too much yield” can be a bad sign).&lt;/li&gt;
&lt;li&gt;How the Top 10 is produced.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This post explains the algorithm in plain English. It is not investment advice.&lt;/p&gt;
&lt;h2 id=&quot;1-the-big-idea-a-two-stage-system&quot;&gt;1. The big idea: a two-stage system&lt;/h2&gt;
&lt;p&gt;A screener that uses this method separates the job into two stages on purpose:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The screening stage keeps obvious “bad fits” out of the pool.&lt;/li&gt;
&lt;li&gt;The ranking stage compares the remaining “good enough” candidates.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is how you avoid a common trap:&lt;/p&gt;
&lt;p&gt;If you only rank by dividend yield, you often end up promoting “yield traps” (stocks with scary-looking fundamentals and a yield that is high because the price dropped).&lt;/p&gt;
&lt;h2 id=&quot;2-step-1-build-the-starting-list-the-universe&quot;&gt;2. Step 1: build the starting list (the “universe”)&lt;/h2&gt;
&lt;p&gt;Before anything can be scored, the system needs a list of tickers to consider.&lt;/p&gt;
&lt;p&gt;Instead of manually typing hundreds of symbols, a screener can start from broad market lists (and, for example, it can include Canadian tickers too). From there it works through the tickers one by one.&lt;/p&gt;
&lt;p&gt;The important point:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The starting list is deliberately wide.&lt;/li&gt;
&lt;li&gt;The strictness comes later (in the screening rules).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;3-step-2-gather-the-key-numbers-for-each-stock&quot;&gt;3. Step 2: gather the key numbers for each stock&lt;/h2&gt;
&lt;p&gt;For each stock in the universe, the system pulls a small set of common fundamentals.&lt;/p&gt;
&lt;p&gt;At a minimum, it looks at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dividend yield (cash dividend relative to stock price)&lt;/li&gt;
&lt;li&gt;P/E ratio (a quick “price vs earnings” valuation check)&lt;/li&gt;
&lt;li&gt;Payout ratio (how much of earnings the company pays out as dividends)&lt;/li&gt;
&lt;li&gt;Debt-to-equity (a basic measure of leverage)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Optionally (if enabled), it can also pull:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ROIC&lt;/li&gt;
&lt;li&gt;Free cash flow yield&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ROIC (return on invested capital) is a simple “business quality” check: it estimates how efficiently the company turns the capital it uses (debt + equity tied up in the business) into operating profit.&lt;/p&gt;
&lt;p&gt;Free cash flow yield is a cash-based valuation check: it compares the company’s free cash flow (cash from operations minus capital spending) to its price, so higher yield generally means you are getting more cash generation for what you pay.&lt;/p&gt;
&lt;p&gt;These are the same kinds of numbers you already see on most finance sites.&lt;/p&gt;
&lt;h2 id=&quot;4-step-3-apply-the-must-pass-rules-mandatory-criteria&quot;&gt;4. Step 3: apply the “must-pass” rules (mandatory criteria)&lt;/h2&gt;
&lt;p&gt;This is the strict part.&lt;/p&gt;
&lt;p&gt;If a stock fails any mandatory check, it is out. Only survivors become “qualifiers.”&lt;/p&gt;
&lt;h3 id=&quot;41-a-dividend-must-exist&quot;&gt;4.1. A dividend must exist&lt;/h3&gt;
&lt;p&gt;If there is no meaningful dividend yield (or it is basically zero), the stock is rejected.&lt;/p&gt;
&lt;p&gt;This prevents the list from being dominated by “great businesses” that are simply not dividend payers.&lt;/p&gt;
&lt;h3 id=&quot;42-valuation-must-be-reasonable&quot;&gt;4.2. Valuation must be reasonable&lt;/h3&gt;
&lt;p&gt;The system rejects stocks that look too expensive on earnings.&lt;/p&gt;
&lt;p&gt;A common rule here is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;P/E must be below a maximum threshold (often around 15, depending on settings).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a blunt tool, but it is a useful early filter.&lt;/p&gt;
&lt;h3 id=&quot;43-the-dividend-must-be-sustainable-payout-ratio&quot;&gt;4.3. The dividend must be sustainable (payout ratio)&lt;/h3&gt;
&lt;p&gt;Even if a company pays a dividend today, it may not be sustainable.&lt;/p&gt;
&lt;p&gt;So the system checks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Payout ratio must be below a maximum.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Utilities are treated differently:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Utilities often run with higher payout ratios by nature, so they can be allowed a higher cap.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;44-debt-must-be-under-control-debt-to-equity&quot;&gt;4.4. Debt must be under control (debt-to-equity)&lt;/h3&gt;
&lt;p&gt;High leverage can make dividends fragile.&lt;/p&gt;
&lt;p&gt;So the system checks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Debt-to-equity must be below a maximum.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Again, utilities can have higher limits than other sectors.&lt;/p&gt;
&lt;p&gt;Financial-sector companies are also treated as a special case because their “debt” behaves differently than normal corporate debt.&lt;/p&gt;
&lt;h2 id=&quot;5-step-4-score-the-qualified-stocks-ranking&quot;&gt;5. Step 4: score the qualified stocks (ranking)&lt;/h2&gt;
&lt;p&gt;At this point you have a smaller list of stocks that are “good enough.”&lt;/p&gt;
&lt;p&gt;Now the system decides which ones are best by assigning each qualifier a score.&lt;/p&gt;
&lt;p&gt;Instead of rewarding a single metric, it uses a balanced scoring system.&lt;/p&gt;
&lt;h3 id=&quot;51-what-the-score-rewards&quot;&gt;5.1. What the score rewards&lt;/h3&gt;
&lt;p&gt;A stock’s score improves when it has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dividend yield near a “sweet spot” (not too low, not outrageously high)&lt;/li&gt;
&lt;li&gt;Lower P/E (cheaper relative to earnings)&lt;/li&gt;
&lt;li&gt;Lower payout ratio (more sustainable dividend)&lt;/li&gt;
&lt;li&gt;Lower debt-to-equity (healthier balance sheet)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;52-why-near-a-sweet-spot-matters&quot;&gt;5.2. Why “near a sweet spot” matters&lt;/h3&gt;
&lt;p&gt;A very high dividend yield can be a warning sign.&lt;/p&gt;
&lt;p&gt;Often the yield is high because the price fell, and the price fell because the market expects trouble.&lt;/p&gt;
&lt;p&gt;So instead of “higher yield is always better,” this approach effectively says:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“We like yield, but we like healthy yield.”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is why the scoring favors yield close to a target (often around ~3%), rather than endlessly rewarding higher and higher yields.&lt;/p&gt;
&lt;h2 id=&quot;6-step-5-sort-and-take-the-top-10&quot;&gt;6. Step 5: sort and take the Top 10&lt;/h2&gt;
&lt;p&gt;Once every qualifying stock has a score:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The system sorts them from highest score to lowest score.&lt;/li&gt;
&lt;li&gt;It prints the top 10.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those are your “Top 10” picks for that run.&lt;/p&gt;
&lt;h2 id=&quot;7-optional-features-if-you-turn-them-on&quot;&gt;7. Optional features (if you turn them on)&lt;/h2&gt;
&lt;p&gt;The system can add extra strictness by adding more pass/fail gates.&lt;/p&gt;
&lt;p&gt;Common examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ROIC filter: prefers companies that use capital efficiently.&lt;/li&gt;
&lt;li&gt;Free cash flow yield filter: prefers companies generating strong cash relative to price.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When enabled, these are additional screening steps before scoring.&lt;/p&gt;
&lt;h2 id=&quot;8-a-note-on-tuning-advanced-mode&quot;&gt;8. A note on tuning (advanced mode)&lt;/h2&gt;
&lt;p&gt;There is also a tuning approach that is useful if you have a reference list you want to mimic.&lt;/p&gt;
&lt;p&gt;For example, we tried to reproduce a published “Timely Ten” list by tuning the scoring so the Top 10 had as much overlap with that list as possible.&lt;/p&gt;
&lt;p&gt;The idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The system tries many combinations of scoring settings (weights and thresholds).&lt;/li&gt;
&lt;li&gt;Each time, it generates a Top 10.&lt;/li&gt;
&lt;li&gt;It keeps the settings that produce the most overlap with your target list.&lt;/li&gt;
&lt;li&gt;It can export the best settings so future runs use them automatically.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You do not need tuning to use this approach, but it helps if your goal is “match this known list as closely as possible.”&lt;/p&gt;
&lt;h2 id=&quot;9-top-10-approach-vs-schd-quick-comparison&quot;&gt;9. Top 10 approach vs SCHD (quick comparison)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Filter-then-rank “Top 10” approach&lt;/th&gt;
&lt;th&gt;&lt;code class=&quot;language-text&quot;&gt;SCHD&lt;/code&gt; (dividend ETF)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Diversification&lt;/td&gt;
&lt;td&gt;Concentrated (higher single-stock / sector risk)&lt;/td&gt;
&lt;td&gt;Broad basket (lower single-stock risk)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control&lt;/td&gt;
&lt;td&gt;You set the rules (thresholds, weights, sector handling)&lt;/td&gt;
&lt;td&gt;You accept the index methodology&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Goal fit&lt;/td&gt;
&lt;td&gt;Can be tailored (value, quality, yield “sweet spot”, etc.)&lt;/td&gt;
&lt;td&gt;Designed for broad dividend equity exposure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Potential upside&lt;/td&gt;
&lt;td&gt;Higher if your ranking adds real edge&lt;/td&gt;
&lt;td&gt;More “market-like” within its dividend style&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintenance&lt;/td&gt;
&lt;td&gt;Requires re-running screens and decisions&lt;/td&gt;
&lt;td&gt;Set-and-forget&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Turnover / taxes&lt;/td&gt;
&lt;td&gt;Can create more turnover depending on how you manage it&lt;/td&gt;
&lt;td&gt;Often more tax-efficient in practice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Behavior risk&lt;/td&gt;
&lt;td&gt;Easy to over-tweak or second-guess results&lt;/td&gt;
&lt;td&gt;Fewer decisions, easier to stick with&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;10-10-ways-to-improve-the-system-in-the-future&quot;&gt;10. 10 ways to improve the system in the future&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Improve data quality and consistency
Make sure metrics come from a reliable source, use consistent definitions (especially for payout ratio and debt), and handle missing values explicitly.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add dividend growth and dividend history checks
Yield alone does not tell you whether the dividend is growing or stable. Include multi-year dividend growth and “no recent cuts” rules.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use free cash flow coverage, not just earnings coverage
Payout ratio based on earnings can look fine while cash is tight. Add FCF payout ratio or a “dividends &amp;#x3C;= FCF” style constraint.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Include quality and balance-sheet health signals
Consider metrics like interest coverage, current ratio, and profitability stability so you avoid fragile companies that barely pass thresholds.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add sector limits to reduce concentration risk
A Top 10 list can accidentally become “Top 10 banks” or “Top 10 utilities.” Add max-per-sector caps or diversify intentionally.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Penalize extreme values and improve outlier handling
Replace hard cutoffs with soft penalties (or winsorize inputs) so one noisy metric does not dominate the score.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make rebalancing rules explicit
Decide how often you re-run the system (monthly/quarterly), define sell rules, and avoid unnecessary churn.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add basic risk controls
Use simple volatility and drawdown checks, or add a “do not select” rule for stocks with extreme price instability.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Validate against a benchmark and multiple market regimes
Compare results to a baseline (like a dividend ETF) across different time periods so you do not overfit one specific era.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Track decisions and outcomes&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Save each run’s inputs, selected Top 10, and performance over time. This makes it easier to debug changes and improve the process.&lt;/p&gt;
&lt;h2 id=&quot;11-summary&quot;&gt;11. Summary&lt;/h2&gt;
&lt;p&gt;A dividend-focused screener can gather a broad list of stocks, pull a handful of easy-to-understand fundamentals, filter out anything that does not meet minimum standards for dividend existence, valuation, dividend sustainability, and reasonable debt, then rank the survivors with a balanced score that favors healthy dividend yield, low P/E, low payout ratio, and low leverage. The 10 highest-scoring qualifying stocks become the “Top 10.”&lt;/p&gt;</content:encoded></item><item><title><![CDATA[What typings.d.ts Is For in TypeScript Projects]]></title><description><![CDATA[If you have used TypeScript for more than a day, you have probably seen errors like:   Everything still works in the browser, but TypeScript…]]></description><link>https://www.monkey.work/2025-12-05-what-typings-d-ts-is-for/</link><guid isPermaLink="false">https://www.monkey.work/2025-12-05-what-typings-d-ts-is-for/</guid><pubDate>Fri, 05 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you have used TypeScript for more than a day, you have probably seen errors like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;TS2307: Cannot find module &apos;./styles.module.css&apos; or its corresponding type declarations.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;TS2339: Property &apos;gtag&apos; does not exist on type &apos;Window &amp;amp; typeof globalThis&apos;.&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything still works in the browser, but TypeScript is unhappy.&lt;/p&gt;
&lt;p&gt;This is exactly where a project-level declaration file like &lt;code class=&quot;language-text&quot;&gt;typings.d.ts&lt;/code&gt; comes in.&lt;/p&gt;
&lt;p&gt;In this post you will learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What &lt;code class=&quot;language-text&quot;&gt;.d.ts&lt;/code&gt; files are.&lt;/li&gt;
&lt;li&gt;What &lt;code class=&quot;language-text&quot;&gt;typings.d.ts&lt;/code&gt; is used for.&lt;/li&gt;
&lt;li&gt;The most common things you will declare there.&lt;/li&gt;
&lt;li&gt;Concrete examples you can copy into your own project.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You do not need deep TypeScript knowledge; if you understand basic &lt;code class=&quot;language-text&quot;&gt;interface&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt;, you are good.&lt;/p&gt;
&lt;h2 id=&quot;1-what-is-a-code-classlanguage-textdtscode-file&quot;&gt;1. What is a &lt;code class=&quot;language-text&quot;&gt;.d.ts&lt;/code&gt; file?&lt;/h2&gt;
&lt;p&gt;A &lt;code class=&quot;language-text&quot;&gt;.d.ts&lt;/code&gt; file is a &lt;strong&gt;type declaration file&lt;/strong&gt;. It contains &lt;strong&gt;types only&lt;/strong&gt;, no runtime code.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Regular &lt;code class=&quot;language-text&quot;&gt;.ts&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;.tsx&lt;/code&gt; files:
&lt;ul&gt;
&lt;li&gt;Compile to JavaScript.&lt;/li&gt;
&lt;li&gt;Contain logic: functions, classes, imports/exports.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;.d.ts&lt;/code&gt; files:
&lt;ul&gt;
&lt;li&gt;Contain only &lt;strong&gt;type information&lt;/strong&gt; used by the compiler.&lt;/li&gt;
&lt;li&gt;Do &lt;strong&gt;not&lt;/strong&gt; become part of the JavaScript bundle.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will see them in &lt;code class=&quot;language-text&quot;&gt;node_modules/@types/...&lt;/code&gt;, but you can also create your own, typically something like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;src/typings.d.ts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;TypeScript loads it automatically (if it is inside &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;tsconfig.json&lt;/code&gt;), and everything you declare there is visible throughout the project.&lt;/p&gt;
&lt;h2 id=&quot;2-why-have-a-code-classlanguage-texttypingsdtscode-file&quot;&gt;2. Why have a &lt;code class=&quot;language-text&quot;&gt;typings.d.ts&lt;/code&gt; file?&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;typings.d.ts&lt;/code&gt; is a good place for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Telling TypeScript about non-TypeScript files you import.&lt;/strong&gt;
For example images, CSS modules, and other assets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extending global types.&lt;/strong&gt;
For example adding &lt;code class=&quot;language-text&quot;&gt;window.gtag&lt;/code&gt; for analytics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typing libraries that do not ship types.&lt;/strong&gt;
For example &lt;code class=&quot;language-text&quot;&gt;declare module &apos;rehype-react&apos; { ... }&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Patching or augmenting existing types.&lt;/strong&gt;
For example extending &lt;code class=&quot;language-text&quot;&gt;Window&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;ProcessEnv&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is your “teach TypeScript about the outside world” file.&lt;/p&gt;
&lt;h2 id=&quot;3-declaring-modules-for-non-code-imports&quot;&gt;3. Declaring modules for non-code imports&lt;/h2&gt;
&lt;h3 id=&quot;31-images-and-icons&quot;&gt;3.1. Images and icons&lt;/h3&gt;
&lt;p&gt;TypeScript only understands &lt;code class=&quot;language-text&quot;&gt;.ts&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.tsx&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.js&lt;/code&gt;, and similar file types by default. If you try:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; favicon &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./favicon.ico&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You will get something like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;TS2307: Cannot find module &apos;./favicon.ico&apos; or its corresponding type declarations.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To fix it, declare that &lt;strong&gt;any &lt;code class=&quot;language-text&quot;&gt;*.ico&lt;/code&gt; import is a string&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// typings.d.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*.ico&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ico&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ico&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now this compiles:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// layout.tsx&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; favicon &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./favicon.ico&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;favicon&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// typed as string, for example &quot;/static/favicon-1234.ico&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can do the same for other asset types.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*.png&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; src&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; src&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*.jpg&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; src&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; src&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*.svg&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ReactComponent&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;FunctionComponent&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;
    React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SVGProps&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SVGSVGElement&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; title&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; ReactComponent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Usage examples:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; logoUrl &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./logo.png&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; LogoIcon &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./logo.svg&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;logoUrl&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Logo&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LogoIcon&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;32-css-modules&quot;&gt;3.2. CSS modules&lt;/h3&gt;
&lt;p&gt;If you import CSS modules like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; styles &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./Button.module.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;primary&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Click me&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;TypeScript again does not know what &lt;code class=&quot;language-text&quot;&gt;*.module.css&lt;/code&gt; is.&lt;/p&gt;
&lt;p&gt;In &lt;code class=&quot;language-text&quot;&gt;typings.d.ts&lt;/code&gt; you can declare a reusable &lt;code class=&quot;language-text&quot;&gt;CSSModule&lt;/code&gt; type and two module shims:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CSSModule&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*.module.css&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cssModule&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CSSModule&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cssModule&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*.module.scss&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cssModule&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CSSModule&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cssModule&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;styles&lt;/code&gt; is typed as &lt;code class=&quot;language-text&quot;&gt;Record&amp;lt;string, string&gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;styles.primary&lt;/code&gt; is typed as &lt;code class=&quot;language-text&quot;&gt;string&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You get type checking on the keys you use, and TypeScript stops complaining about the import.&lt;/p&gt;
&lt;h2 id=&quot;4-extending-global-objects-like-code-classlanguage-textwindowcode&quot;&gt;4. Extending global objects (like &lt;code class=&quot;language-text&quot;&gt;window&lt;/code&gt;)&lt;/h2&gt;
&lt;p&gt;Sometimes you attach things to &lt;code class=&quot;language-text&quot;&gt;window&lt;/code&gt; (or use libraries that do). For example, Google Analytics 4:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;gtag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;event&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;page_view&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; page_path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pathname &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;TypeScript does not know about &lt;code class=&quot;language-text&quot;&gt;gtag&lt;/code&gt; on &lt;code class=&quot;language-text&quot;&gt;window&lt;/code&gt;, so you get an error like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;TS2339: Property &apos;gtag&apos; does not exist on type &apos;Window &amp;amp; typeof globalThis&apos;.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;41-augmenting-code-classlanguage-textwindowcode&quot;&gt;4.1. Augmenting &lt;code class=&quot;language-text&quot;&gt;Window&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;You can &lt;strong&gt;augment&lt;/strong&gt; the built-in &lt;code class=&quot;language-text&quot;&gt;Window&lt;/code&gt; interface in &lt;code class=&quot;language-text&quot;&gt;typings.d.ts&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  gtag&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;args&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Because interfaces with the same name merge, this adds &lt;code class=&quot;language-text&quot;&gt;gtag&lt;/code&gt; to the existing &lt;code class=&quot;language-text&quot;&gt;Window&lt;/code&gt; type.&lt;/p&gt;
&lt;p&gt;Now you can safely write:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; window &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;undefined&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gtag &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;function&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;gtag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;event&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;page_view&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    page_path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pathname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;TypeScript understands that &lt;code class=&quot;language-text&quot;&gt;window.gtag&lt;/code&gt; may exist and knows how to call it.&lt;/p&gt;
&lt;h3 id=&quot;42-another-example-storing-app-config-on-code-classlanguage-textwindowcode&quot;&gt;4.2. Another example: storing app config on &lt;code class=&quot;language-text&quot;&gt;window&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Maybe you have some configuration injected into the page by your backend:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// typings.d.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;APP_CONFIG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    apiBaseUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    environment&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;development&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;staging&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;production&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now this code is type-safe:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; apiBaseUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;APP_CONFIG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;apiBaseUrl &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;http://localhost:3000&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Without the declaration, TypeScript would not know what &lt;code class=&quot;language-text&quot;&gt;APP_CONFIG&lt;/code&gt; is.&lt;/p&gt;
&lt;h2 id=&quot;5-adding-types-for-a-library-that-has-none&quot;&gt;5. Adding types for a library that has none&lt;/h2&gt;
&lt;p&gt;Sometimes you use a library that &lt;strong&gt;does not ship TypeScript types&lt;/strong&gt;, and there is no &lt;code class=&quot;language-text&quot;&gt;@types/...&lt;/code&gt; package published.&lt;/p&gt;
&lt;p&gt;Imagine a simple logging library called &lt;code class=&quot;language-text&quot;&gt;cool-logger&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; logInfo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; logError &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cool-logger&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Without any declaration, TypeScript complains:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;TS2307: Cannot find module &apos;cool-logger&apos; or its corresponding type declarations.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can start with a minimal declaration in &lt;code class=&quot;language-text&quot;&gt;typings.d.ts&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cool-logger&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now this is fully typed:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; logInfo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; logError &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cool-logger&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;logInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;App started&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;logError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Something went wrong&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Boom&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you are not sure about exact signatures, you can also start looser and tighten later:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cool-logger&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;args&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;args&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;6-a-more-complex-example-code-classlanguage-textrehype-reactcode&quot;&gt;6. A more complex example: &lt;code class=&quot;language-text&quot;&gt;rehype-react&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes you want more than “please stop erroring”. You want &lt;strong&gt;helpful types&lt;/strong&gt; for a complex plugin.&lt;/p&gt;
&lt;p&gt;Imagine you use &lt;code class=&quot;language-text&quot;&gt;rehype-react&lt;/code&gt; to turn HTML AST into React components.&lt;/p&gt;
&lt;p&gt;In your code you might have:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; unified &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;unified&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; rehypeReact &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rehype-react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; CodeBlock &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./CodeBlock&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rehypeReact&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  createElement&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;createElement&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  components&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    code&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CodeBlock&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To make this type-safe, you can declare &lt;code class=&quot;language-text&quot;&gt;rehype-react&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;typings.d.ts&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rehype-react&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Plugin &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;unified&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Root &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;hast&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ReactElement&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ComponentType &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RehypeComponents&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ComponentType&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RehypeOptions&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function-variable function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;args&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; ReactElement&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    Fragment&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ComponentType&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    components&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RehypeComponents&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rehypeReact&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Plugin&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;RehypeOptions&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Root&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ReactElement&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; rehypeReact&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now TypeScript knows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;components.code&lt;/code&gt; must be a React component.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;createElement&lt;/code&gt; must return a &lt;code class=&quot;language-text&quot;&gt;ReactElement&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This helps catch mistakes early and makes your editor autocomplete smarter.&lt;/p&gt;
&lt;h2 id=&quot;7-code-classlanguage-textrecordcode-and-other-helpers-you-might-see&quot;&gt;7. &lt;code class=&quot;language-text&quot;&gt;Record&lt;/code&gt; and other helpers you might see&lt;/h2&gt;
&lt;p&gt;In the examples above, you saw &lt;code class=&quot;language-text&quot;&gt;Record&lt;/code&gt; being used:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CSSModule&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Record&lt;/code&gt; is a built-in utility type:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;K&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;keyof&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can think of it as “an object whose keys are &lt;code class=&quot;language-text&quot;&gt;K&lt;/code&gt; and whose values are &lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt;“.&lt;/p&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Keys &apos;id&apos; | &apos;name&apos;, values are string&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserSummary&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;id&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token comment&quot;&gt;// Equivalent to:&lt;/span&gt;
 &lt;span class=&quot;token comment&quot;&gt;// type UserSummary = { id: string; name: string };&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Map of string keys to numbers&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Scores&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; scores&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Scores &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   alice&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
   bob&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Record&lt;/code&gt; is often used in &lt;code class=&quot;language-text&quot;&gt;typings.d.ts&lt;/code&gt; to describe object-like maps.&lt;/p&gt;
&lt;h2 id=&quot;8-when-you-do-not-need-code-classlanguage-texttypingsdtscode&quot;&gt;8. When you do not need &lt;code class=&quot;language-text&quot;&gt;typings.d.ts&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;You &lt;strong&gt;do not&lt;/strong&gt; need to add declarations when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The package ships its own &lt;code class=&quot;language-text&quot;&gt;.d.ts&lt;/code&gt; (check &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; for a &lt;code class=&quot;language-text&quot;&gt;&quot;types&quot;&lt;/code&gt; field).&lt;/li&gt;
&lt;li&gt;There is an &lt;code class=&quot;language-text&quot;&gt;@types/package-name&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; package you have installed.&lt;/li&gt;
&lt;li&gt;You are only using &lt;code class=&quot;language-text&quot;&gt;.ts&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;.tsx&lt;/code&gt; modules with proper exports.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use &lt;code class=&quot;language-text&quot;&gt;typings.d.ts&lt;/code&gt; for &lt;strong&gt;gaps&lt;/strong&gt; in the type system:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Assets (CSS modules, images, icons).&lt;/li&gt;
&lt;li&gt;Globals (&lt;code class=&quot;language-text&quot;&gt;window&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;document&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;process.env&lt;/code&gt;, …).&lt;/li&gt;
&lt;li&gt;Libraries without types.&lt;/li&gt;
&lt;li&gt;Small patches or augmentations.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;9-best-practices-for-code-classlanguage-texttypingsdtscode&quot;&gt;9. Best practices for &lt;code class=&quot;language-text&quot;&gt;typings.d.ts&lt;/code&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Keep it focused.&lt;/strong&gt; Only declare what you actually use.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prefer precise types over &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt;.&lt;/strong&gt; Use &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Record&amp;lt;string, unknown&gt;&lt;/code&gt;, or specific shapes where you can.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Name it something obvious.&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;typings.d.ts&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;global.d.ts&lt;/code&gt; at the root of &lt;code class=&quot;language-text&quot;&gt;src/&lt;/code&gt; or the project.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check it into git.&lt;/strong&gt; It is part of your contract with TypeScript, just like &lt;code class=&quot;language-text&quot;&gt;tsconfig.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treat it as a public API.&lt;/strong&gt; Other developers (and your future self) will rely on what is declared there.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you understand &lt;code class=&quot;language-text&quot;&gt;typings.d.ts&lt;/code&gt;, those confusing errors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“Cannot find module ‘./something.css’ or its corresponding type declarations.”&lt;/li&gt;
&lt;li&gt;“Property ‘gtag’ does not exist on type ‘Window’.”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;stop being mysterious and start looking like simple invitations:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Teach TypeScript about this thing.&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[DependencyContainer Malloc Crash in Tests – Post-Mortem]]></title><description><![CDATA[This is Part 5 in our Advanced SwiftUI: Lessons From Mistakes series: Part 1 – ObservedOrStateObject walks through the property-wrapper bug…]]></description><link>https://www.monkey.work/2025-11-24-swift-ui-dependency-container-malloc-crash-in-tests/</link><guid isPermaLink="false">https://www.monkey.work/2025-11-24-swift-ui-dependency-container-malloc-crash-in-tests/</guid><pubDate>Mon, 24 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;This is Part 5 in our &lt;strong&gt;Advanced SwiftUI: Lessons From Mistakes&lt;/strong&gt; series:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Part 1 – &lt;a href=&quot;/2025-11-11-swift-ui-observed-state-object/&quot;&gt;ObservedOrStateObject&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
walks through the property-wrapper bug that kicked off this learning arc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Part 2 – &lt;a href=&quot;/2025-11-13-swift-ui-alert-issue/&quot;&gt;SwiftUI Alerts: When Multiple Alerts Collide&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
covers how conflicting alerts prevented save failures from reaching users.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Part 3 – &lt;a href=&quot;/2025-11-18-swift-ui-pointer-being-freed-was-not-allocated/&quot;&gt;SwiftUI @Published Teardown: Pointer Being Freed Was Not Allocated&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
documents the teardown crash we treated as a Swift runtime edge case.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Part 4 – &lt;a href=&quot;/2025-11-20-swift-ui-main-actor-initializers/&quot;&gt;SwiftUI @MainActor Initializers: Safe Default Parameters and Actor Isolation&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
explains how default parameter evaluation interacts with actor isolation.with actor isolation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;who-should-read-this&quot;&gt;Who should read this&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;iOS devs working with app-wide dependency containers and async/Combine-heavy view models&lt;/li&gt;
&lt;li&gt;Anyone wondering why we saw &lt;code class=&quot;language-text&quot;&gt;malloc: *** error for object ... pointer being freed was not allocated&lt;/code&gt; only in tests.&lt;/li&gt;
&lt;li&gt;Folks learning how async/Combine-heavy singletons and view models can crash during teardown.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;You should be comfortable with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Basic Swift classes and &lt;code class=&quot;language-text&quot;&gt;final&lt;/code&gt; types.&lt;/li&gt;
&lt;li&gt;What &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; are.&lt;/li&gt;
&lt;li&gt;The idea of &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; and the main thread.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You do &lt;strong&gt;not&lt;/strong&gt; need deep Swift Concurrency internals.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;During test runs we repeatedly hit crashes like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;malloc: *** error for object 0x262c5a6f0: pointer being freed was not allocated
malloc: *** set a breakpoint in malloc_error_break to debug&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The crash only happened when tests &lt;strong&gt;tore down&lt;/strong&gt; objects created via our dependency container:
Root cause (as best we can tell):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We were deallocating async/Combine-heavy objects (&lt;code class=&quot;language-text&quot;&gt;DependencyContainer&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ProductListViewModel&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;VendorDashboardViewModel&lt;/code&gt;, etc.) under XCTest teardown.&lt;/li&gt;
&lt;li&gt;Somewhere in the Swift runtime / Combine / actor teardown path, a double-free or invalid free occurs.&lt;/li&gt;
&lt;li&gt;The crash is &lt;strong&gt;not&lt;/strong&gt; caused by our business logic, but by deallocation timing under the current Swift/Xcode toolchain.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Fix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;DEBUG tests only&lt;/strong&gt;, we:
&lt;ul&gt;
&lt;li&gt;Keep previous &lt;code class=&quot;language-text&quot;&gt;DependencyContainer&lt;/code&gt; instances in a static &lt;code class=&quot;language-text&quot;&gt;leakedContainers&lt;/code&gt; array when resetting.&lt;/li&gt;
&lt;li&gt;Keep factory-created view models and testing containers in static arrays on &lt;code class=&quot;language-text&quot;&gt;DependencyContainerTests&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;This prevents the problematic objects from deallocating during test teardown.&lt;/li&gt;
&lt;li&gt;Release/runtime behavior is unchanged.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-broke&quot;&gt;What broke&lt;/h2&gt;
&lt;p&gt;The failing tests all lived in &lt;code class=&quot;language-text&quot;&gt;DependencyContainerTests&lt;/code&gt; and looked innocuous:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainerTests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;XCTestCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;DependencyContainerBootstrapper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resetForTesting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;tearDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;DependencyContainerBootstrapper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resetForTesting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tearDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testMakeProductListViewModel_CreatesCorrectInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainerBootstrapper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bootstrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; store &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MockProductStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; item &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProductItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            createdAt&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Test Product&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            productId&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;TEST-001&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            imageFilename&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test.jpg&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            uploaded&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            persistenceOrigin&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;local&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            classification&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            productImportId&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            storeId&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            position&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            rowHash&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; viewModel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;makeProductListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;store&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; store&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;XCTAssertNotNil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewModel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All these tests:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Bootstrap or construct a &lt;code class=&quot;language-text&quot;&gt;DependencyContainer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Call a factory method.&lt;/li&gt;
&lt;li&gt;Let the test end without holding on to the created object.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The crash consistently happened &lt;strong&gt;after&lt;/strong&gt; the test body finished, as XCTest tore down the container and view models.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-core-objects-involved&quot;&gt;The core objects involved&lt;/h2&gt;
&lt;h3 id=&quot;dependencycontainer&quot;&gt;DependencyContainer&lt;/h3&gt;
&lt;p&gt;Key points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A shared instance is exposed via &lt;code class=&quot;language-text&quot;&gt;DependencyContainerBootstrapper.sharedContainer&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainerBootstrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sharedContainer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; DEBUG&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; leakedContainers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;bootstrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;dispatchPrecondition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;condition&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; existing &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sharedContainer &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; existing
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        sharedContainer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; container
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; container
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; authSessionManager&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthSessionManager&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;lazy&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; printerSettingsStore&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; any &lt;span class=&quot;token class-name&quot;&gt;PrinterSettingsStoreProtocol&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterSettingsStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;lazy&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; printerService&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; any &lt;span class=&quot;token class-name&quot;&gt;PrinterServiceProtocol&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... many other services, repositories, and use cases ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In tests we also use a dedicated helper:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; DEBUG&lt;/span&gt;
&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;makeTestingContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        printerService&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterServiceProtocol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        printerSettingsStore&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterSettingsStoreProtocol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        configure&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;printerService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; printerService &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; any &lt;span class=&quot;token class-name&quot;&gt;PrinterServiceProtocol&lt;/span&gt;
        container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;printerSettingsStore &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; printerSettingsStore &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; any &lt;span class=&quot;token class-name&quot;&gt;PrinterSettingsStoreProtocol&lt;/span&gt;
        configure&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; container
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;productlistviewmodel-and-vendordashboardviewmodel&quot;&gt;ProductListViewModel and VendorDashboardViewModel&lt;/h3&gt;
&lt;p&gt;Both are &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt;s with async/Combine behavior.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;ProductListViewModel&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Holds multiple &lt;code class=&quot;language-text&quot;&gt;Task&amp;lt;Void, Never&gt;&lt;/code&gt; properties.&lt;/li&gt;
&lt;li&gt;Subscribes to &lt;code class=&quot;language-text&quot;&gt;store.itemsPublisher&lt;/code&gt; with Combine.&lt;/li&gt;
&lt;li&gt;Has a &lt;code class=&quot;language-text&quot;&gt;Debouncer&lt;/code&gt; that internally uses &lt;code class=&quot;language-text&quot;&gt;Task.sleep&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProductListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; products&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RemoteProduct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; uploadTask&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; fetchTask&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; searchTask&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; loadImportsTask&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; importsLoadTask&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; remoteImagePrefetchTasks&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; cancellables&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AnyCancellable&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* many dependencies */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;observeStoreItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;deinit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        uploadTask&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        fetchTask&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        searchTask&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        loadImportsTask&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        importsLoadTask&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        remoteImagePrefetchTasks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;values&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;forEach &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These patterns are safe in normal app usage, but they make teardown timing &lt;strong&gt;much&lt;/strong&gt; more sensitive under XCTest.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-the-crash-looked-like&quot;&gt;What the crash looked like&lt;/h2&gt;
&lt;p&gt;The error always appeared right after one of the factory tests finished, for example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Test Case &apos;-[PalletonTests.DependencyContainerTests testMakeProductListViewModel_CreatesCorrectInstance]&apos; started.
Palleton(38027,0x102f1de40) malloc: *** error for object 0x262c5a6f0: pointer being freed was not allocated
Palleton(38027,0x102f1de40) malloc: *** set a breakpoint in malloc_error_break to debug&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A typical stack trace (simplified) showed:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Thread 1 Queue: com.apple.main-thread (serial)
0  malloc_error_break
1  swift::RefCounts&amp;lt;swift::RefCountBitsT&amp;lt;swift::RefCountIsInline&gt;&gt;::doDecrement
2  ProductListViewModel.__deallocating_deinit
3  DependencyContainerTests.testMakeProductListViewModel_CreatesCorrectInstance
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or, for the vendor dashboard test:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Test Case &apos;-[PalletonTests.DependencyContainerTests testMakeVendorDashboardViewModel_CreatesCorrectInstance]&apos; started.
Palleton(...) malloc: *** error for object 0x262c5a6f0: pointer being freed was not allocated&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Key observation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The crash occurred &lt;strong&gt;after&lt;/strong&gt; the test’s &lt;code class=&quot;language-text&quot;&gt;XCTAssertNotNil&lt;/code&gt; succeeded.&lt;/li&gt;
&lt;li&gt;The deallocation path (view model or container deinit) is where the runtime tripped.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-this-happens-under-xctest-teardown&quot;&gt;Why this happens under XCTest teardown&lt;/h2&gt;
&lt;p&gt;Short version:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Deallocating async/Combine-heavy &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt;s that are wired to shared singletons, tasks, and publishers during XCTest teardown tickles undefined behavior in the current Swift runtime.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;More detailed mental model:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A test constructs a &lt;code class=&quot;language-text&quot;&gt;DependencyContainer&lt;/code&gt; and a view model via a factory method.&lt;/li&gt;
&lt;li&gt;The view model subscribes to publishers, creates async tasks, etc.&lt;/li&gt;
&lt;li&gt;The test ends. XCTest begins tearing down the test case and any referenced singletons.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;DependencyContainerBootstrapper.resetForTesting()&lt;/code&gt; sets &lt;code class=&quot;language-text&quot;&gt;sharedContainer = nil&lt;/code&gt;, deallocating its container (and thus its dependencies).&lt;/li&gt;
&lt;li&gt;Swift runtime tears down:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; state.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; storage.&lt;/li&gt;
&lt;li&gt;Any task locals tied to the main actor.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Some combination of deallocation order + runtime bug leads to freeing memory twice.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Important:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The crash is &lt;strong&gt;not&lt;/strong&gt; in our explicit deinit logic (we do minimal cancelation); it is inside Swift’s internal teardown for tasks/Combine/actor isolation.&lt;/li&gt;
&lt;li&gt;The same object graphs behave fine in the running app; the crash appears only under the aggressive create/destroy cycles of the test runner.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Given this, we treated it as a &lt;strong&gt;toolchain/runtime edge case&lt;/strong&gt; rather than a business-logic bug.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-concrete-fix-leak-in-debug-tests&quot;&gt;The concrete fix: leak in DEBUG tests&lt;/h2&gt;
&lt;p&gt;Instead of fighting the runtime, we made a pragmatic decision for tests only:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;In DEBUG builds, do not deallocate the problematic singletons and view models during the lifetime of the test process.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is implemented in two places.&lt;/p&gt;
&lt;h3 id=&quot;1-leak-containers-in-dependencycontainerbootstrapper-debug-only&quot;&gt;1. Leak containers in DependencyContainerBootstrapper (DEBUG only)&lt;/h3&gt;
&lt;p&gt;We modified &lt;code class=&quot;language-text&quot;&gt;DependencyContainerBootstrapper&lt;/code&gt; so &lt;code class=&quot;language-text&quot;&gt;resetForTesting()&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;useForTesting(_:)&lt;/code&gt; keep strong references to old containers instead of letting them deallocate:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainerBootstrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sharedContainer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; DEBUG&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; leakedContainers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;bootstrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;dispatchPrecondition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;condition&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; existing &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sharedContainer &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; existing &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        sharedContainer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; container
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; container
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; DEBUG&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainerBootstrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;resetForTesting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;dispatchPrecondition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;condition&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sharedContainer &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            leakedContainers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        sharedContainer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;useForTesting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; container&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;dispatchPrecondition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;condition&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; existing &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sharedContainer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; existing &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; container &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            leakedContainers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;existing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        sharedContainer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; container
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Effects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tests still see the expected behavior:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;sharedContainer&lt;/code&gt; becomes &lt;code class=&quot;language-text&quot;&gt;nil&lt;/code&gt; after &lt;code class=&quot;language-text&quot;&gt;resetForTesting()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;useForTesting&lt;/code&gt; swaps in a new container.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Old containers are kept alive in &lt;code class=&quot;language-text&quot;&gt;leakedContainers&lt;/code&gt; and never deallocated, so their async/Combine teardown paths are never executed.&lt;/li&gt;
&lt;li&gt;Release builds ignore &lt;code class=&quot;language-text&quot;&gt;leakedContainers&lt;/code&gt; entirely.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2-leak-view-models-and-testing-containers-in-dependencycontainertests-debug-only&quot;&gt;2. Leak view models and testing containers in DependencyContainerTests (DEBUG only)&lt;/h3&gt;
&lt;p&gt;We also updated &lt;code class=&quot;language-text&quot;&gt;DependencyContainerTests&lt;/code&gt; to retain factory-created view models and containers:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainerTests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;XCTestCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; DEBUG&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Retain view models created via factory methods in tests&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// to avoid deallocation-time malloc crashes in the current Swift/runtime combo.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; leakedProductListViewModels&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ProductListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; leakedTestingContainers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ... setUp / tearDown unchanged ...&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testMakeProductListViewModel_CreatesCorrectInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainerBootstrapper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bootstrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; store &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MockProductStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// add an item, configure store...&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; viewModel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;makeProductListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;store&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; store&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; DEBUG&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Keep a strong reference for the duration of the test process to avoid&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// triggering deallocation-time malloc issues in the test environment.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leakedProductListViewModels&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewModel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;XCTAssertNotNil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewModel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testMakeTestingContainer_AllowsDependencyOverride&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; mockPrinterService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MockPrinterService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; mockSettingsStore &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MockPrinterSettingsStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DependencyContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;makeTestingContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            printerService&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; mockPrinterService&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            printerSettingsStore&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; mockSettingsStore
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; DEBUG&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leakedTestingContainers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;XCTAssertNotNil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;XCTAssertTrue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;printerService &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MockPrinterService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;XCTAssertTrue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;printerSettingsStore &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MockPrinterSettingsStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This pattern:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ensures that view models and testing containers are not destroyed while tests are still running.&lt;/li&gt;
&lt;li&gt;Avoids hitting the problematic runtime deallocation paths.&lt;/li&gt;
&lt;li&gt;Only applies in DEBUG builds.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-this-is-acceptable&quot;&gt;Why this is acceptable&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;leaks are DEBUG-only&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;Guarded by &lt;code class=&quot;language-text&quot;&gt;#if DEBUG&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;They do not ship in production.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The objects being leaked are:
&lt;ul&gt;
&lt;li&gt;A small number of containers and view models per test run.&lt;/li&gt;
&lt;li&gt;Perfectly fine to keep alive until the process exits.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Tests still fully exercise the DI wiring and construction paths we care about.&lt;/li&gt;
&lt;li&gt;The alternative would be:
&lt;ul&gt;
&lt;li&gt;Deep refactors to avoid &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt;, or async tasks in these components, or&lt;/li&gt;
&lt;li&gt;Highly fragile deinit/teardown code that tries to outsmart the runtime.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Given the limited scope and clear separation (test-only), this is a pragmatic, maintainable compromise.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;general-guidance-for-asynccombine-heavy-teardown-in-tests&quot;&gt;General guidance for async/Combine-heavy teardown in tests&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Be wary of deinit in async/Combine-heavy types&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Avoid complex logic in &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt; for &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt;s that own tasks and publishers.&lt;/li&gt;
&lt;li&gt;Prefer explicit &lt;code class=&quot;language-text&quot;&gt;reset&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;tearDown&lt;/code&gt; methods called from known-safe contexts (e.g. logout flows on &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;For tests, it’s sometimes okay to leak&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If a crash only occurs during teardown and is clearly in runtime internals, retaining the object for the duration of the test process can be a reasonable workaround.&lt;/li&gt;
&lt;li&gt;Always:
&lt;ul&gt;
&lt;li&gt;Scope leaks to &lt;code class=&quot;language-text&quot;&gt;#if DEBUG&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Document the trade-off clearly.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Centralize test-only helpers near the code under test&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;DependencyContainerBootstrapper.resetForTesting/useForTesting&lt;/code&gt; live next to the container, so their behavior is obvious.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;DependencyContainerTests&lt;/code&gt; keeps its own leaked view models and testing containers, making the workaround explicit in test code.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Document hard-to-fix runtime issues&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This post, together with earlier articles like &lt;a href=&quot;/2025-11-18-swift-ui-pointer-being-freed-was-not-allocated/&quot;&gt;SwiftUI @Published Teardown: Pointer Being Freed Was Not Allocated&lt;/a&gt; and &lt;a href=&quot;/2025-11-11-swift-ui-observed-state-object/&quot;&gt;ObservedOrStateObject: Choosing the Right Property Wrapper&lt;/a&gt;, forms a history of tricky SwiftUI/Concurrency issues we’ve hit.&lt;/li&gt;
&lt;li&gt;Future maintainers can:
&lt;ul&gt;
&lt;li&gt;Understand why the code looks the way it does.&lt;/li&gt;
&lt;li&gt;Revisit decisions if new Swift/Xcode releases fix the underlying bugs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;For now, all container-related tests pass without malloc crashes, and the test-only leaks are a conscious, documented trade-off.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[SwiftUI @MainActor Initializers: Safe Default Parameters and Actor Isolation]]></title><description><![CDATA[This is Part 4 in our Advanced SwiftUI: Lessons From Mistakes series: Part 1 – ObservedOrStateObject walks through the property-wrapper bug…]]></description><link>https://www.monkey.work/2025-11-20-swift-ui-main-actor-initializers/</link><guid isPermaLink="false">https://www.monkey.work/2025-11-20-swift-ui-main-actor-initializers/</guid><pubDate>Thu, 20 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;This is Part 4 in our &lt;strong&gt;Advanced SwiftUI: Lessons From Mistakes&lt;/strong&gt; series:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Part 1 – &lt;a href=&quot;/2025-11-11-swift-ui-observed-state-object/&quot;&gt;ObservedOrStateObject&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
walks through the property-wrapper bug that kicked off this learning arc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Part 2 – &lt;a href=&quot;/2025-11-13-swift-ui-alert-issue/&quot;&gt;SwiftUI Alerts: When Multiple Alerts Collide&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
covers how conflicting alerts prevented save failures from reaching users.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Part 3 – &lt;a href=&quot;/2025-11-18-swift-ui-pointer-being-freed-was-not-allocated/&quot;&gt;SwiftUI @Published Teardown: Pointer Being Freed Was Not Allocated&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
documents the teardown crash we treated as a Swift runtime edge case.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;You should be comfortable with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Basic Swift and initializers.&lt;/li&gt;
&lt;li&gt;The idea of the &lt;strong&gt;main actor&lt;/strong&gt; (&lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;How default parameter values in initializers work.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You do &lt;strong&gt;not&lt;/strong&gt; need to be a Swift Concurrency expert.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;After enabling stricter Swift concurrency checks, we started seeing errors like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Main actor-isolated static property &apos;shared&apos; can not be referenced from a nonisolated context
Call to main actor-isolated initializer &apos;init()&apos; in a synchronous nonisolated context&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;They pointed at the &lt;code class=&quot;language-text&quot;&gt;PrinterService&lt;/code&gt; initializer:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterServiceProtocol&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; logger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerProtocol&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; statusProvider&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterStatusProvider&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerProtocol&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        statusProvider&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterStatusProvider&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SDKPrinterStatusProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; logger
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;statusProvider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; statusProvider
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Even though &lt;code class=&quot;language-text&quot;&gt;PrinterService&lt;/code&gt; itself is &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;, the compiler complained that the default parameter values were accessing main-actor–isolated resources from a &lt;strong&gt;nonisolated&lt;/strong&gt; context.&lt;/p&gt;
&lt;p&gt;We fixed this by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Making the initializer parameters &lt;strong&gt;optional without default expressions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Providing the actual defaults &lt;strong&gt;inside the initializer body&lt;/strong&gt;, which &lt;em&gt;does&lt;/em&gt; run on the main actor.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Result:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The warnings/errors disappeared.&lt;/li&gt;
&lt;li&gt;Call sites still get the same ergonomics (&lt;code class=&quot;language-text&quot;&gt;PrinterService()&lt;/code&gt; works as before).&lt;/li&gt;
&lt;li&gt;The pattern is now safe to reuse in other &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;-isolated types.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-broke&quot;&gt;What broke&lt;/h2&gt;
&lt;p&gt;Once we annotated our service type and related types with &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;, the compiler started flagging the initializer as unsafe:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Main actor-isolated static property &apos;shared&apos; can not be referenced from a nonisolated context
Call to main actor-isolated initializer &apos;init()&apos; in a synchronous nonisolated context&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The relevant code:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterServiceProtocol&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; logger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerProtocol&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; statusProvider&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterStatusProvider&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerProtocol&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        statusProvider&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterStatusProvider&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SDKPrinterStatusProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; logger
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;statusProvider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; statusProvider
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At a glance this seems harmless:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Logger.shared&lt;/code&gt; is a simple static singleton.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;SDKPrinterStatusProvider()&lt;/code&gt; is a plain struct initializer.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;PrinterService&lt;/code&gt; is &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;, so its initializer should be main-actor–isolated too, right?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The subtle bit is &lt;strong&gt;where&lt;/strong&gt; Swift evaluates default parameter values.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-swift-complains-default-parameters-vs-actor-isolation&quot;&gt;Why Swift complains: default parameters vs. actor isolation&lt;/h2&gt;
&lt;p&gt;Key rule:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Default parameter expressions are evaluated in a nonisolated context.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When you write:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bar&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Swift does &lt;strong&gt;not&lt;/strong&gt; treat &lt;code class=&quot;language-text&quot;&gt;Bar.shared&lt;/code&gt; as running under the &lt;code class=&quot;language-text&quot;&gt;Foo&lt;/code&gt; initializer’s actor isolation. Instead, it evaluates the default expression in a context that has &lt;em&gt;no&lt;/em&gt; actor isolation.&lt;/p&gt;
&lt;p&gt;That means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If &lt;code class=&quot;language-text&quot;&gt;Bar.shared&lt;/code&gt; (or anything it touches) is &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;, you are effectively calling a main-actor–isolated API from a nonisolated context.&lt;/li&gt;
&lt;li&gt;Under Swift 6 concurrency checking, this is an error.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In our case:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;PrinterService&lt;/code&gt; is &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The initializer parameters had default expressions that depended (directly or indirectly) on main-actor–bound behavior.&lt;/li&gt;
&lt;li&gt;The compiler correctly refused to allow this cross-actor access.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even though &lt;code class=&quot;language-text&quot;&gt;Logger&lt;/code&gt; itself is not &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;, relying on &lt;code class=&quot;language-text&quot;&gt;Logger.shared&lt;/code&gt; and other shared resources from inside default parameter expressions is fragile once actor isolation is in play.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-concrete-fix&quot;&gt;The concrete fix&lt;/h2&gt;
&lt;p&gt;We changed &lt;code class=&quot;language-text&quot;&gt;PrinterService&lt;/code&gt;’s initializer to:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterServiceProtocol&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; logger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerProtocol&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; statusProvider&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterStatusProvider&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerProtocol&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        statusProvider&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterStatusProvider&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;statusProvider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; statusProvider &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SDKPrinterStatusProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What changed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Parameters are now &lt;strong&gt;optional&lt;/strong&gt; and their defaults are just &lt;code class=&quot;language-text&quot;&gt;nil&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The initializer body (which &lt;em&gt;is&lt;/em&gt; main-actor–isolated) fills in the real defaults:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;logger ?? Logger.shared&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;statusProvider ?? SDKPrinterStatusProvider()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Why this is safe:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The initializer body is executed under the &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; of &lt;code class=&quot;language-text&quot;&gt;PrinterService&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Accessing &lt;code class=&quot;language-text&quot;&gt;Logger.shared&lt;/code&gt; and constructing &lt;code class=&quot;language-text&quot;&gt;SDKPrinterStatusProvider()&lt;/code&gt; from there respects actor isolation.&lt;/li&gt;
&lt;li&gt;There is no longer any main-actor work hiding inside a default parameter expression.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Call sites stay the same:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Uses Logger.shared and SDKPrinterStatusProvider() internally&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; service &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Override just the logger&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; service &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;logger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MockLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Override both&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; service &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrinterService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MockLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    statusProvider&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FakePrinterStatusProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We now use the same pattern in other &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; types that depend on shared singletons.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;general-guidance-for-code-classlanguage-textmainactorcode-initializers&quot;&gt;General guidance for &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; initializers&lt;/h2&gt;
&lt;p&gt;When working with &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; classes and protocols (like &lt;code class=&quot;language-text&quot;&gt;PrinterServiceProtocol&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;PrinterSettingsStoreProtocol&lt;/code&gt;), follow these rules:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Avoid doing any work with &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;-isolated APIs in default parameter values.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Bad:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;logger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerProtocol&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Better:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;logger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerProtocol&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Prefer simple optionals for dependencies.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make dependencies optional in the initializer signature.&lt;/li&gt;
&lt;li&gt;Provide the actual default instance inside the initializer body.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Think about who owns the dependency.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Shared singletons (&lt;code class=&quot;language-text&quot;&gt;Logger.shared&lt;/code&gt;) are fine as defaults, but inject them from inside the actor.&lt;/li&gt;
&lt;li&gt;For testability, always allow the caller to override with a mock.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Keep &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; boundaries clear at call sites.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you construct &lt;code class=&quot;language-text&quot;&gt;PrinterService&lt;/code&gt; from non-UI code, either:
&lt;ul&gt;
&lt;li&gt;Mark the caller as &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;, or&lt;/li&gt;
&lt;li&gt;Create it via &lt;code class=&quot;language-text&quot;&gt;await MainActor.run { PrinterService() }&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;lessons-learned&quot;&gt;Lessons learned&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Default parameters are not “just syntactic sugar.”&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Where and how they are evaluated matters for concurrency.&lt;/li&gt;
&lt;li&gt;Actor isolation rules apply to default expressions just like any other code.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Central singletons (&lt;code class=&quot;language-text&quot;&gt;Logger.shared&lt;/code&gt;) are convenient but need discipline.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They are easy to reach for inside default parameters.&lt;/li&gt;
&lt;li&gt;Under Swift 6, that can turn into a hard error once &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; enters the picture.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; types should keep initialization logic simple.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do all main-actor-dependent work in the initializer body.&lt;/li&gt;
&lt;li&gt;Keep parameters plain (optionals, simple value defaults) so the compiler doesn’t have to reason about cross-actor calls during default evaluation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Document these patterns.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This post exists so future changes to &lt;code class=&quot;language-text&quot;&gt;PrinterService&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;PrinterSettingsStore&lt;/code&gt;, or other &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; services don’t accidentally reintroduce the same warnings.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title><![CDATA[SwiftUI @Published Crash: “Pointer Being Freed Was Not Allocated” Explained]]></title><description><![CDATA[This is Part 3 in our Advanced SwiftUI: Lessons From Mistakes series: Part 1 – ObservedOrStateObject walks through the property-wrapper bug…]]></description><link>https://www.monkey.work/2025-11-18-swift-ui-pointer-being-freed-was-not-allocated/</link><guid isPermaLink="false">https://www.monkey.work/2025-11-18-swift-ui-pointer-being-freed-was-not-allocated/</guid><pubDate>Tue, 18 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;This is Part 3 in our &lt;strong&gt;Advanced SwiftUI: Lessons From Mistakes&lt;/strong&gt; series:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Part 1 – &lt;a href=&quot;/2025-11-11-swift-ui-observed-state-object/&quot;&gt;ObservedOrStateObject&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
walks through the property-wrapper bug that kicked off this learning arc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Part 2 – &lt;a href=&quot;/2025-11-13-swift-ui-alert-issue/&quot;&gt;SwiftUI Alerts: When Multiple Alerts Collide&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
covers how conflicting alerts prevented save failures from reaching users.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;You should be comfortable with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Basic Swift classes and properties.&lt;/li&gt;
&lt;li&gt;What an &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; is and &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; in SwiftUI.&lt;/li&gt;
&lt;li&gt;The idea of the &lt;strong&gt;main thread&lt;/strong&gt; / &lt;strong&gt;main actor&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You &lt;strong&gt;do not&lt;/strong&gt; need to be a concurrency expert.&lt;/p&gt;
&lt;h3 id=&quot;key-concepts&quot;&gt;Key concepts&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ObservableObject&lt;/strong&gt;: A reference type that conforms to SwiftUI’s &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; protocol. You typically put your view model logic and mutable screen state on an &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt;, then expose it to views with &lt;code class=&quot;language-text&quot;&gt;@StateObject&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;@ObservedObject&lt;/code&gt;. When its &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; properties change, it sends &lt;code class=&quot;language-text&quot;&gt;objectWillChange&lt;/code&gt;, and any SwiftUI views observing it automatically re-render to reflect the new values.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@Published&lt;/strong&gt;: A property wrapper used on properties inside an &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt;. It automatically creates a Combine publisher for that property and wires it into &lt;code class=&quot;language-text&quot;&gt;objectWillChange&lt;/code&gt;, so any change to the property notifies SwiftUI (and any other subscribers) that the object’s state has updated. You can think of it as “make this property observable by the UI and other listeners.”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@MainActor / main actor&lt;/strong&gt;: The main, UI-bound actor in Swift’s concurrency model. Code annotated with &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; is isolated to this actor, which conceptually means it runs on the main thread and can safely touch UI state. Calling a &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; method or accessing a &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; property from background work requires an &lt;code class=&quot;language-text&quot;&gt;await&lt;/code&gt; hop to the main actor so Swift can serialize access and keep UI updates safe.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;deeper-dive-observableobject-published-and-mainactor&quot;&gt;Deeper dive: ObservableObject, @Published, and @MainActor&lt;/h3&gt;
&lt;p&gt;Think of an &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; as the place where your screen’s long-lived state and business logic live. A view holds onto an instance with &lt;code class=&quot;language-text&quot;&gt;@StateObject&lt;/code&gt; when it creates the object itself, or &lt;code class=&quot;language-text&quot;&gt;@ObservedObject&lt;/code&gt; when something else hands the object in. SwiftUI listens to &lt;code class=&quot;language-text&quot;&gt;objectWillChange&lt;/code&gt; on that object so that when the model changes, the view hierarchy knows to re-render.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; marks individual properties on an &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; as observable. When a &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; property changes, the wrapper publishes an event and forwards it through &lt;code class=&quot;language-text&quot;&gt;objectWillChange&lt;/code&gt;. In practice: you mutate a &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; property, and any SwiftUI view observing that object will automatically update to show the new value, with no manual “notify the view” calls required.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;main actor&lt;/strong&gt; (&lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;) is Swift’s way of saying “this code belongs on the UI thread.” If you annotate a type with &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;, all of its instance properties and methods are treated as main‑actor‑isolated by default, as if you had written &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; on each one. Code running on a background actor or task must &lt;code class=&quot;language-text&quot;&gt;await&lt;/code&gt; a hop to the main actor before calling those methods or touching those properties. Under the hood, the main actor is an abstraction, but in an iOS or SwiftUI app you can treat “main actor” and “main thread” as effectively the same concept for UI work.&lt;/p&gt;
&lt;h4 id=&quot;what-code-classlanguage-textmainactorcode-on-a-type-actually-means&quot;&gt;What &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; on a type actually means&lt;/h4&gt;
&lt;p&gt;When you write:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyViewModel&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        count &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Swift treats it as if you had written &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; on every instance member:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So, in practice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Yes:&lt;/strong&gt; Accessing &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; or calling &lt;code class=&quot;language-text&quot;&gt;increment()&lt;/code&gt; from outside must be done on the main actor.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Callers&lt;/strong&gt; in another actor or background task must &lt;code class=&quot;language-text&quot;&gt;await&lt;/code&gt; the hop, for example: &lt;code class=&quot;language-text&quot;&gt;await viewModel.increment()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You can &lt;strong&gt;opt out&lt;/strong&gt; for specific members with &lt;code class=&quot;language-text&quot;&gt;nonisolated&lt;/code&gt; (or &lt;code class=&quot;language-text&quot;&gt;nonisolated(unsafe)&lt;/code&gt;), but by default everything is main‑actor‑isolated.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;actor-vs-thread&quot;&gt;Actor vs thread&lt;/h4&gt;
&lt;p&gt;The &lt;strong&gt;main actor&lt;/strong&gt; is an abstraction in Swift’s concurrency model. In an iOS / SwiftUI app it is typically executed on the main thread, so for UI code you can safely think of “main actor” ≈ “main thread.” The guarantee you get is: from Swift’s point of view, all uses of a &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; type must respect main‑actor isolation, and the compiler helps enforce that.&lt;/p&gt;
&lt;h4 id=&quot;the-code-classlanguage-textdeinitcode-caveat-relevant-here&quot;&gt;The &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt; caveat (relevant here)&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt; is special: it is &lt;em&gt;logically&lt;/em&gt; main‑actor‑isolated on a &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; type, but Swift doesn’t let you run async code or do explicit actor hops there. In practice, teardown paths can involve runtime internals where you can’t rely on “this definitely runs on the main actor like a normal method body,” which is why using &lt;code class=&quot;language-text&quot;&gt;@MainActor deinit&lt;/code&gt; as a cleanup hook is fragile and contributed to the weird behavior in the crash described below.&lt;/p&gt;
&lt;p&gt;For normal methods and property access, though, the mental model is safe:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; on the class means its methods and properties should be used from the main thread (main actor) unless explicitly marked otherwise.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;During test teardown we hit a crash like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;malloc: *** error for object 0x262c5a6f0: pointer being freed was not allocated&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The crash happened while &lt;code class=&quot;language-text&quot;&gt;DependencyContainer&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;AuthSessionManager&lt;/code&gt; were being deallocated. We tried several reasonable fixes (changing &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; annotations, cleaning up in &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt;, etc.), but the root cause turned out to be deep inside the Swift runtime / &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; teardown, not in our own logic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We restored a simple, predictable implementation.&lt;/li&gt;
&lt;li&gt;We rely on the runtime behaving correctly.&lt;/li&gt;
&lt;li&gt;All tests now pass cleanly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This document explains what happened and why we decided &lt;strong&gt;not&lt;/strong&gt; to keep layering hacks on top of the problem.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-was-crashing&quot;&gt;What was crashing?&lt;/h2&gt;
&lt;p&gt;Two types were involved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;AuthSessionManager&lt;/code&gt; – owns the current auth session and exposes it via &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;DependencyContainer&lt;/code&gt; – app-wide DI container that owns a single &lt;code class=&quot;language-text&quot;&gt;AuthSessionManager&lt;/code&gt; instance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In tests, we build a &lt;code class=&quot;language-text&quot;&gt;DependencyContainer&lt;/code&gt; (often via &lt;code class=&quot;language-text&quot;&gt;makeTestingContainer&lt;/code&gt;) and tear it down between tests.&lt;/p&gt;
&lt;p&gt;The crash stack trace consistently looked like this (simplified):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;swift::TaskLocal::StopLookupScope::~StopLookupScope()
swift_task_deinitOnExecutorImpl
swift_task_deinitOnExecutorMainActorBackDeploy
AuthSessionManager.__deallocating_deinit
DependencyContainer.deinit
malloc: *** error for object ... pointer being freed was not allocated&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Key point: the crash happened &lt;strong&gt;during deallocation&lt;/strong&gt;, not while we were actively calling our own methods.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-was-this-so-confusing&quot;&gt;Why was this so confusing?&lt;/h2&gt;
&lt;p&gt;At first glance it looked like a normal memory bug we should be able to fix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We saw &lt;code class=&quot;language-text&quot;&gt;AuthSessionManager.__deallocating_deinit&lt;/code&gt; in the stack.&lt;/li&gt;
&lt;li&gt;We had just changed &lt;code class=&quot;language-text&quot;&gt;DependencyContainer&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;AuthSessionManager&lt;/code&gt; to use &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The error message mentions a pointer being freed twice.&lt;/li&gt;
&lt;li&gt;The crash log always showed the same heap address, for example: &lt;code class=&quot;language-text&quot;&gt;malloc: *** error for object 0x262c5a710: pointer being freed was not allocated&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This suggested things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Maybe we were clearing &lt;code class=&quot;language-text&quot;&gt;currentSession&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt; from the wrong thread.&lt;/li&gt;
&lt;li&gt;Maybe &lt;code class=&quot;language-text&quot;&gt;DependencyContainer&lt;/code&gt; was being destroyed off the main actor.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those are problems we &lt;em&gt;can&lt;/em&gt; usually fix by adjusting ownership or actor isolation.&lt;/p&gt;
&lt;p&gt;However, every time we tried a reasonable fix, the crash either persisted or moved slightly but did not disappear.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-we-tried-and-why-it-seemed-sensible&quot;&gt;What we tried (and why it seemed sensible)&lt;/h2&gt;
&lt;h3 id=&quot;1-clearing-state-in-code-classlanguage-textauthsessionmanagerdeinitcode&quot;&gt;1. Clearing state in &lt;code class=&quot;language-text&quot;&gt;AuthSessionManager.deinit&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;We first added:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;deinit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    currentSession &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Why this seemed reasonable:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We wanted to make sure all UI-related state was cleared on the main actor.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;currentSession&lt;/code&gt; is &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt;, and we were already treating it as main-thread-only state.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The compiler complained about mutating main-actor-isolated state in &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Even when we forced it, we still saw the malloc crash during teardown.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt; is special. You &lt;strong&gt;cannot&lt;/strong&gt; rely on it running on the main actor, even if the type is &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-removing-the-mutation-from-code-classlanguage-textdeinitcode&quot;&gt;2. Removing the mutation from &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Next we tried to &lt;strong&gt;do nothing&lt;/strong&gt; in &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;deinit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// no-op&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Why this seemed reasonable:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If touching &lt;code class=&quot;language-text&quot;&gt;currentSession&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt; was unsafe, maybe simply letting the object die would be safer.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; should clean itself up automatically.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The crash still appeared in &lt;code class=&quot;language-text&quot;&gt;AuthSessionManager.__deallocating_deinit&lt;/code&gt; deep in the Swift runtime.&lt;/li&gt;
&lt;li&gt;So even without our custom cleanup, deallocation could still hit the bug.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; An empty &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt; is a poor workaround here. It can make the crash rarer or harder to reproduce, but it only &lt;strong&gt;masks&lt;/strong&gt; the underlying teardown bug instead of fixing it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; the problem was not just our &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt; body.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-moving-code-classlanguage-textmainactorcode-around&quot;&gt;3. Moving &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; around&lt;/h3&gt;
&lt;p&gt;We experimented with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Marking &lt;code class=&quot;language-text&quot;&gt;DependencyContainer&lt;/code&gt; as &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Removing &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt; from &lt;code class=&quot;language-text&quot;&gt;AuthSessionManager&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Marking only certain methods as &lt;code class=&quot;language-text&quot;&gt;@MainActor&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why this seemed reasonable:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The crash involved &lt;code class=&quot;language-text&quot;&gt;swift_task_deinitOnExecutorMainActorBackDeploy&lt;/code&gt;, which suggested actor-isolated teardown.&lt;/li&gt;
&lt;li&gt;Ensuring container + manager live on the same actor should, in theory, make deallocation deterministic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The exact stack trace changed slightly, but the malloc error stayed.&lt;/li&gt;
&lt;li&gt;We were clearly fighting against how Swift tears down actor-isolated objects, not fixing our own logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; actor annotations can change &lt;em&gt;when&lt;/em&gt; deinit happens, but they don’t rewrite how &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; and tasks are destroyed internally.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-replacing-code-classlanguage-textpublishedcode-with-a-manual-code-classlanguage-textcurrentvaluesubjectcode&quot;&gt;4. Replacing &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; with a manual &lt;code class=&quot;language-text&quot;&gt;CurrentValueSubject&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;We also tried to avoid &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; entirely:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthSessionManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthSessionManaging&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; currentSessionSubject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CurrentValueSubject&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AuthSession&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; currentSession&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthSession&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;didSet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; currentSessionSubject&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentSession&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Why this seemed reasonable:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Maybe the bug lived specifically inside &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; + &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; deallocation.&lt;/li&gt;
&lt;li&gt;A manual Combine subject might avoid that path.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The app and tests expect &lt;code class=&quot;language-text&quot;&gt;AuthSessionManager&lt;/code&gt; to be an &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; used with &lt;code class=&quot;language-text&quot;&gt;@StateObject&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;@ObservedObject&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Removing &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; broke existing view code in multiple places.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We could have refactored everything to use a different pattern, but that would be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A large change.&lt;/li&gt;
&lt;li&gt;Easy to get wrong.&lt;/li&gt;
&lt;li&gt;Not clearly safer given the underlying runtime behavior.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;so-what-is-actually-going-on&quot;&gt;So… what is actually going on?&lt;/h2&gt;
&lt;p&gt;Here’s the most honest explanation we can give:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;AuthSessionManager&lt;/code&gt; is an &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; with a &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; property.&lt;/li&gt;
&lt;li&gt;It’s owned by &lt;code class=&quot;language-text&quot;&gt;DependencyContainer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;During test teardown, the test framework tears down the app and container.&lt;/li&gt;
&lt;li&gt;While everything is deallocating, the Swift runtime cleans up:
&lt;ul&gt;
&lt;li&gt;Tasks associated with the main actor.&lt;/li&gt;
&lt;li&gt;The &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; storage.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Somewhere inside that &lt;strong&gt;internal&lt;/strong&gt; cleanup, the runtime tries to free memory that has already been freed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Crucially:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The crash happens &lt;em&gt;after&lt;/em&gt; our own code has already stopped running.&lt;/li&gt;
&lt;li&gt;The stack shows mostly Swift runtime and concurrency internals.&lt;/li&gt;
&lt;li&gt;Our changes to &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt; and actor isolation shifted the crash around, but did not remove it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This strongly suggests a &lt;strong&gt;runtime-level bug or edge case&lt;/strong&gt;, not a simple misuse in our source code.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-we-decided-not-to-keep-fighting-it&quot;&gt;Why we decided &lt;strong&gt;not&lt;/strong&gt; to keep fighting it&lt;/h2&gt;
&lt;p&gt;At some point, fixing this locally would require one of these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Re-architecting the entire auth/session stack to avoid &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Adding fragile workarounds that depend on undocumented runtime behavior.&lt;/li&gt;
&lt;li&gt;Carrying complex, hard-to-explain code “just” to avoid a crash in a very specific teardown scenario.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Given that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The crash only showed up during aggressive test teardown.&lt;/li&gt;
&lt;li&gt;We had a simpler setup that behaved well in normal app use.&lt;/li&gt;
&lt;li&gt;We can rerun tests easily and watch for regressions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;…we chose a &lt;strong&gt;pragmatic compromise&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep the code simple and idiomatic.&lt;/li&gt;
&lt;li&gt;Avoid doing anything fancy in &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Let the runtime handle cleanup.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If Apple fixes this in a future Swift / iOS release, we instantly benefit without carrying weird hacks.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-we-learned&quot;&gt;What we learned&lt;/h2&gt;
&lt;h3 id=&quot;1-not-every-crash-is-your-fault&quot;&gt;1. Not every crash is your fault&lt;/h3&gt;
&lt;p&gt;Sometimes, especially around concurrency, you will run into behavior that is caused (or at least heavily influenced) by the runtime or standard library. It’s okay to say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We understand our code’s behavior, and the remaining issue is likely in the underlying framework.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This doesn’t mean “give up quickly”, but it &lt;em&gt;does&lt;/em&gt; mean you don’t have to rewrite your entire app to work around a bug you don’t own.&lt;/p&gt;
&lt;h3 id=&quot;2-be-careful-with-code-classlanguage-textdeinitcode&quot;&gt;2. Be careful with &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt; runs when the system is already tearing things down. In async / actor-based code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can’t reliably hop to the main actor here.&lt;/li&gt;
&lt;li&gt;You shouldn’t start new async work.&lt;/li&gt;
&lt;li&gt;You should avoid doing anything that might trigger more deallocation or complex side effects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Prefer explicit cleanup methods that you call from a known, safe context (for example, a logout handler on the main actor).&lt;/p&gt;
&lt;h3 id=&quot;3-prefer-simple-understandable-code-over-clever-hacks&quot;&gt;3. Prefer simple, understandable code over clever hacks&lt;/h3&gt;
&lt;p&gt;We tried several clever ideas (main-actor deinit, manual subjects, etc.). They all made the code &lt;strong&gt;harder to reason about&lt;/strong&gt; without fully solving the problem.&lt;/p&gt;
&lt;p&gt;In the end, the simplest version of &lt;code class=&quot;language-text&quot;&gt;AuthSessionManager&lt;/code&gt; is also the most maintainable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; with an &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; session.&lt;/li&gt;
&lt;li&gt;It exposes clear methods: &lt;code class=&quot;language-text&quot;&gt;loadSession&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;saveSession&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;clearSession&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It doesn’t do tricky work in &lt;code class=&quot;language-text&quot;&gt;deinit&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;4-document-hard-problems-clearly&quot;&gt;4. Document hard problems clearly&lt;/h3&gt;
&lt;p&gt;This document exists so that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Future you doesn’t waste days chasing the same crash.&lt;/li&gt;
&lt;li&gt;New teammates understand the trade-offs we made.&lt;/li&gt;
&lt;li&gt;We have a place to reference if Swift/Apple fix the underlying runtime behavior and we want to revisit the design.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[SwiftUI Alerts: Fixing Bugs When Multiple Alerts Collide]]></title><description><![CDATA[This is Part 2 in our Advanced SwiftUI: Lessons From Mistakes series: Part 1 – ObservedOrStateObject walks through the property-wrapper bug…]]></description><link>https://www.monkey.work/2025-11-13-swift-ui-alert-issue/</link><guid isPermaLink="false">https://www.monkey.work/2025-11-13-swift-ui-alert-issue/</guid><pubDate>Thu, 13 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;This is Part 2 in our &lt;strong&gt;Advanced SwiftUI: Lessons From Mistakes&lt;/strong&gt; series:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1 – &lt;a href=&quot;/2025-11-11-swift-ui-observed-state-object/&quot;&gt;ObservedOrStateObject&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
walks through the property-wrapper bug that kicked off this learning arc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SwiftUI’s &lt;code class=&quot;language-text&quot;&gt;.alert&lt;/code&gt; modifier looks deceptively simple until you attach more than one alert to the same view hierarchy. This post documents a regression that prevented save failures from surfacing to users in both the &lt;strong&gt;Edit Product&lt;/strong&gt; and &lt;strong&gt;Add Product&lt;/strong&gt; flows, how we diagnosed the issue, and the architectural changes that fixed it.&lt;/p&gt;
&lt;h2 id=&quot;context&quot;&gt;Context&lt;/h2&gt;
&lt;p&gt;We noticed that when a save failed in &lt;em&gt;Edit Product&lt;/em&gt;, the expected alert never appeared, even though logs showed the failure and &lt;code class=&quot;language-text&quot;&gt;showingSaveError&lt;/code&gt; was set to &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Inspecting &lt;code class=&quot;language-text&quot;&gt;EditProductSheet&lt;/code&gt; revealed two separate &lt;code class=&quot;language-text&quot;&gt;.alert&lt;/code&gt; modifiers:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Errors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;saveFailed&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $showingSaveError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ... later in the view tree&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Printer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;printJobFailed&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;SwiftUI allows only &lt;strong&gt;one&lt;/strong&gt; alert per view at a time. Once the printer alert had been registered, subsequent alerts were ignored even if their bindings flipped to &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;. The framework never complained; it just silently failed to present the alert. The same pattern existed in &lt;code class=&quot;language-text&quot;&gt;AddProductSheet&lt;/code&gt;, so users there were also missing save errors.&lt;/p&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root Cause&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Multiple &lt;code class=&quot;language-text&quot;&gt;.alert&lt;/code&gt; modifiers attached to the same view hierarchy.&lt;/li&gt;
&lt;li&gt;SwiftUI silently discards secondary alerts when one is already active.&lt;/li&gt;
&lt;li&gt;Save failures toggled state, but UI never presented the overlay.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;solution-overview&quot;&gt;Solution Overview&lt;/h2&gt;
&lt;p&gt;We introduced a shared &lt;code class=&quot;language-text&quot;&gt;ProductSheetAlertType&lt;/code&gt; enum and consolidated alert presentation through a single &lt;code class=&quot;language-text&quot;&gt;.alert(item:)&lt;/code&gt; modifier. Both sheets now set an &lt;code class=&quot;language-text&quot;&gt;activeAlert&lt;/code&gt; state that determines which message appears, ensuring only one alert configuration is registered per view.&lt;/p&gt;
&lt;h3 id=&quot;shared-alert-type&quot;&gt;Shared Alert Type&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProductSheetAlertType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Identifiable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;saveError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printerError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;saveError&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;saveError&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;printerError&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;printerError&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;edit-product-sheet-update&quot;&gt;Edit Product Sheet Update&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; activeAlert&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProductSheetAlertType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $activeAlert&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; alertType &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; alertType &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;saveError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Errors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;saveFailed&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            message&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            dismissButton&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Common&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;confirmation&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;printerError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Printer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;printJobFailed&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            message&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            dismissButton&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Common&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;confirmation&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;saveError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newError &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; error &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newError &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; activeAlert &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;saveError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;printError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newError &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; error &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newError &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; activeAlert &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;printerError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;add-product-sheet-update&quot;&gt;Add Product Sheet Update&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;AddProductSheet&lt;/code&gt; mirrors the same approach: it keeps its own &lt;code class=&quot;language-text&quot;&gt;activeAlert&lt;/code&gt; state, wires &lt;code class=&quot;language-text&quot;&gt;viewModel.saveError&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;viewModel.printError&lt;/code&gt; into that binding, and routes all messaging through the shared &lt;code class=&quot;language-text&quot;&gt;.alert(item:)&lt;/code&gt;. The copy-paste-prone double-alert setup is gone, so future alerts plug into the enum instead of stacking additional &lt;code class=&quot;language-text&quot;&gt;.alert&lt;/code&gt; modifiers.&lt;/p&gt;
&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;/h2&gt;
&lt;p&gt;Focused suites covering &lt;code class=&quot;language-text&quot;&gt;EditProductSheet&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;EditProductSheetActions&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;EditProductSheetView&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;AddProductSheet&lt;/code&gt; all passed, and manual QA confirmed that the alert now appears when save or printer discovery failures occur.&lt;/p&gt;
&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Single Alert Rule:&lt;/strong&gt; SwiftUI only respects one alert per view hierarchy. Consolidate via &lt;code class=&quot;language-text&quot;&gt;.alert(item:)&lt;/code&gt; when multiple surfaces might need to display messages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shared Abstractions Help:&lt;/strong&gt; Centralizing &lt;code class=&quot;language-text&quot;&gt;ProductSheetAlertType&lt;/code&gt; prevents drift between sheets and keeps future alert additions straightforward.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test Coverage Matters:&lt;/strong&gt; Focused test plans caught regressions across Add and Edit flows, preserving confidence in the refactor.&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title><![CDATA[SwiftUI @StateObject vs @ObservedObject: The ObservedOrStateObject Pattern]]></title><description><![CDATA[This is part one of the series. This post kicks off a series of SwiftUI puzzles we had to solve while shipping the app in production. We’re…]]></description><link>https://www.monkey.work/2025-11-11-swift-ui-observed-state-object/</link><guid isPermaLink="false">https://www.monkey.work/2025-11-11-swift-ui-observed-state-object/</guid><pubDate>Tue, 11 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is part one of the series. This post kicks off a series of SwiftUI puzzles we had to solve while shipping the app in production. We’re walking through the issues we hit; dissecting them is one of the fastest ways to level up with advanced SwiftUI.&lt;/p&gt;
&lt;h2 id=&quot;observedorstateobject-bridging-stateobject-and-observedobject-in-swiftui&quot;&gt;ObservedOrStateObject: Bridging @StateObject and @ObservedObject in SwiftUI&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This post kicks off a series of SwiftUI puzzles we had to solve while shipping the app in production. We’re walking through the issues we hit; dissecting them is one of the fastest ways to level up with advanced SwiftUI.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We built &lt;code class=&quot;language-text&quot;&gt;ObservedOrStateObject&lt;/code&gt; while writing unit tests: the views needed to spin up their own view models in production, yet tests and previews had to inject mocked instances without rewriting the view. The wrapper emerged to unify those scenarios.&lt;/p&gt;
&lt;p&gt;An &lt;code class=&quot;language-text&quot;&gt;ObservedOrStateObject&lt;/code&gt; wraps either a newly created or externally provided &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt;, letting SwiftUI treat it like a regular dynamic property.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;code class=&quot;language-text&quot;&gt;ObservedOrStateObject&lt;/code&gt;, our views can work in two modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Own the view model (like StateObject) when the view builds it internally.&lt;/li&gt;
&lt;li&gt;Observe an injected instance (like ObservedObject) when tests, previews, or coordinators supply the view model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without a bridging wrapper we would duplicate view code or risk double-instantiating models. The custom property wrapper lets the view declare a single property:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@ObservedOrStateObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AddProductViewModel&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and decide during initialization whether to pass a builder closure (for owned state) or an already-constructed view model.&lt;/p&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;
&lt;p&gt;The wrapper owns a &lt;code class=&quot;language-text&quot;&gt;StorageBox&lt;/code&gt; that is stored with &lt;code class=&quot;language-text&quot;&gt;@StateObject&lt;/code&gt;, so the box keeps a stable identity across view updates. The box is initialized with the model, either by executing the autoclosure in &lt;code class=&quot;language-text&quot;&gt;init(wrappedValue:)&lt;/code&gt; or by accepting an existing instance via &lt;code class=&quot;language-text&quot;&gt;init(observed:)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;StorageBox&lt;/code&gt; itself conforms to &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; and holds the real model plus a cached &lt;code class=&quot;language-text&quot;&gt;ObservedObject&lt;/code&gt; projection. Each time SwiftUI evaluates the view body, the box returns the same projected wrapper, preserving SwiftUI’s dynamic-property identity. When the underlying model is swapped, &lt;code class=&quot;language-text&quot;&gt;ensureSubscriptionIsCurrent()&lt;/code&gt; refreshes the subscription so the wrapper keeps relaying changes.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;let objectWillChange = ObservableObjectPublisher()&lt;/code&gt; line gives the box its own publisher. The box subscribes to the underlying model’s &lt;code class=&quot;language-text&quot;&gt;objectWillChange&lt;/code&gt;, and when the model emits, the box forwards that event by calling &lt;code class=&quot;language-text&quot;&gt;objectWillChange.send()&lt;/code&gt;. SwiftUI listens to the box’s publisher, so relaying the events is essential for the view to refresh; without the local &lt;code class=&quot;language-text&quot;&gt;ObservableObjectPublisher&lt;/code&gt;, SwiftUI would never be notified to redraw.&lt;/p&gt;
&lt;h2 id=&quot;what-happened&quot;&gt;What happened?&lt;/h2&gt;
&lt;p&gt;An early implementation of &lt;code class=&quot;language-text&quot;&gt;ObservedOrStateObject&lt;/code&gt; recreated the &lt;code class=&quot;language-text&quot;&gt;ObservedObject&lt;/code&gt; wrapper on every access of &lt;code class=&quot;language-text&quot;&gt;$viewModel&lt;/code&gt;, violating SwiftUI’s identity contract. Here’s what went wrong:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The wrapper needed to bridge &lt;code class=&quot;language-text&quot;&gt;@StateObject&lt;/code&gt; (view-owned instances) and &lt;code class=&quot;language-text&quot;&gt;@ObservedObject&lt;/code&gt; (externally supplied instances) without duplicating code.&lt;/li&gt;
&lt;li&gt;The original implementation recreated the &lt;code class=&quot;language-text&quot;&gt;ObservedObject&lt;/code&gt; wrapper on every access of &lt;code class=&quot;language-text&quot;&gt;$viewModel&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;SwiftUI expects dynamic property wrappers to maintain identity across updates. Because ours did not, published changes stopped propagating after the first body pass, leaving views stuck with stale data.&lt;/li&gt;
&lt;li&gt;The fix caches the &lt;code class=&quot;language-text&quot;&gt;ObservedObject&lt;/code&gt; wrapper inside the box and keeps the Combine subscription in sync with the current object identity.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;
&lt;p&gt;We introduced &lt;code class=&quot;language-text&quot;&gt;ObservedOrStateObject&lt;/code&gt; to simplify views such as &lt;code class=&quot;language-text&quot;&gt;AddProductSheet&lt;/code&gt; that sometimes need to construct their own &lt;code class=&quot;language-text&quot;&gt;ViewModel&lt;/code&gt;, but in tests or previews should accept an existing instance. The wrapper hides the ownership details so that callers can write:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@ObservedOrStateObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AddProductViewModel&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and decide at initialization whether the view should own the model or observe a shared one.&lt;/p&gt;
&lt;h2 id=&quot;buggy-implementation&quot;&gt;Buggy Implementation&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; projectedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservedObject&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Wrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;ObservedObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wrappedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;projectedValue
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each call to &lt;code class=&quot;language-text&quot;&gt;$viewModel&lt;/code&gt; (or any other &lt;code class=&quot;language-text&quot;&gt;@ObservedOrStateObject&lt;/code&gt;-backed property) executed this computed property, creating a brand-new &lt;code class=&quot;language-text&quot;&gt;ObservedObject&lt;/code&gt;. The Combine subscription in &lt;code class=&quot;language-text&quot;&gt;StorageBox.refreshSubscription()&lt;/code&gt; was also refreshed on every &lt;code class=&quot;language-text&quot;&gt;update()&lt;/code&gt; call.&lt;/p&gt;
&lt;h3 id=&quot;full-source-bug-present&quot;&gt;Full Source (Bug Present)&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Combine&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/// Wraps an `ObservableObject` so views can either own or observe the instance without duplicating it.&lt;/span&gt;
&lt;span class=&quot;token attribute atrule&quot;&gt;@propertyWrapper&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservedOrStateObject&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DynamicProperty&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@StateObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; box&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StorageBox&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wrappedValue value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token attribute atrule&quot;&gt;@autoclosure&lt;/span&gt; &lt;span class=&quot;token attribute atrule&quot;&gt;@escaping&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        _box &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StateObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wrappedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StorageBox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;observed value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        _box &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StateObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wrappedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StorageBox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; wrappedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; projectedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservedObject&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Wrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ObservedObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wrappedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;projectedValue
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;mutating&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        _box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;refreshSubscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StorageBox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; cancellable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnyCancellable&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; objectWillChange &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObjectPublisher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; object
            &lt;span class=&quot;token function&quot;&gt;refreshSubscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;refreshSubscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            cancellable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;objectWillChange&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sink &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;weak&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;objectWillChange&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;symptoms-in-the-wild&quot;&gt;Symptoms in the Wild&lt;/h2&gt;
&lt;p&gt;We first noticed trouble inside &lt;code class=&quot;language-text&quot;&gt;AddProductSheet&lt;/code&gt; when validation state stopped updating after the first keystroke. The view model kept publishing changes (confirmed via logging), but the SwiftUI view ceased reacting. In other contexts, the sheet would rebuild unexpectedly, resetting field focus and dismissing temporary UI (camera sheets, toast timers, etc.).&lt;/p&gt;
&lt;p&gt;Those glitches shared a pattern: they appeared after the view’s body was evaluated more than once, exactly when SwiftUI re-reads dynamic properties.&lt;/p&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root Cause&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;ObservedObject&lt;/code&gt; is a dynamic property with identity semantics, SwiftUI stores a stable instance per view. By returning a fresh wrapper every time, we violated that contract. On the first render, SwiftUI associated its storage with our wrapper. On the next render, the framework compared the new wrapper (a different instance) with the stored one, treated it as a replacement, and severed the original subscription. The new wrapper never received the &lt;code class=&quot;language-text&quot;&gt;objectWillChange&lt;/code&gt; publisher because its subscription was only set up inside the view transaction that created it.&lt;/p&gt;
&lt;p&gt;In short: repeated recomputation meant SwiftUI could not maintain a consistent observation pipeline, so downstream views stopped reacting to the observable object.&lt;/p&gt;
&lt;h2 id=&quot;fixed-implementation&quot;&gt;Fixed Implementation&lt;/h2&gt;
&lt;p&gt;The revised implementation caches the wrapper and guards the subscription:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;lazy&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; cachedProjection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservedObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wrappedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;projectedValue

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; projectedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservedObject&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Wrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    cachedProjection
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;ensureSubscriptionIsCurrent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; currentID &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectIdentifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; subscriptionObjectID &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; currentID &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; cancellable &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;refreshSubscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cachedProjection&lt;/code&gt; preserves the identity expected by SwiftUI. The same wrapper instance is returned on every access, so the framework keeps a stable pointer to our observable object.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;ensureSubscriptionIsCurrent()&lt;/code&gt; avoids repeatedly re-subscribing when the object instance has not changed, while still allowing us to resubscribe if a new object is injected (e.g., when previews or tests swap models).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;full-source-bug-fixed&quot;&gt;Full Source (Bug Fixed)&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Combine&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/// Wraps an `ObservableObject` so views can either own or observe the instance without duplicating it.&lt;/span&gt;
&lt;span class=&quot;token attribute atrule&quot;&gt;@propertyWrapper&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservedOrStateObject&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DynamicProperty&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@StateObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; box&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StorageBox&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wrappedValue value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token attribute atrule&quot;&gt;@autoclosure&lt;/span&gt; &lt;span class=&quot;token attribute atrule&quot;&gt;@escaping&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        _box &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StateObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wrappedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StorageBox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;observed value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        _box &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StateObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wrappedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StorageBox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; wrappedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; projectedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservedObject&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Wrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;projectedValue
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;mutating&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        _box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ensureSubscriptionIsCurrent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StorageBox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; cancellable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnyCancellable&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; subscriptionObjectID&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectIdentifier&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;lazy&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; cachedProjection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservedObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wrappedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;projectedValue
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; objectWillChange &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObjectPublisher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; object
            &lt;span class=&quot;token function&quot;&gt;refreshSubscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;ensureSubscriptionIsCurrent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; currentID &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectIdentifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; subscriptionObjectID &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; currentID &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; cancellable &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;refreshSubscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;refreshSubscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            cancellable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;objectWillChange&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sink &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;weak&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;objectWillChange&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            subscriptionObjectID &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectIdentifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; projectedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservedObject&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ObjectType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Wrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            cachedProjection
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Dynamic properties need stable identity.&lt;/strong&gt; Avoid returning freshly constructed property wrappers from computed properties.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mirror SwiftUI’s expectations.&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;@ObservedObject&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;@StateObject&lt;/code&gt; manage lifecycles differently; wrappers that mix them must respect both models.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Log symptoms, not just crashes.&lt;/strong&gt; The bug never crashed; instrumentation around Combine publishers surfaced the mismatch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pair runtime tests with SwiftUI semantics.&lt;/strong&gt; SwiftUI caches dynamic properties aggressively. Verifying identity behavior in small sample views can catch issues early.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With these changes, &lt;code class=&quot;language-text&quot;&gt;ObservedOrStateObject&lt;/code&gt; now operates predictably whether a view owns or observes its model, and our add/edit product flows keep their state in sync across renders.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Learn SwiftUI by Building a Recipe Book App for iOS & macOS]]></title><description><![CDATA[SwiftUI Recipe Book App GitHub Step-by-Step Complete Project Guide Welcome to the ultimate hands-on guide for building a modern SwiftUI…]]></description><link>https://www.monkey.work/2025-10-28-learn-swift-ui/</link><guid isPermaLink="false">https://www.monkey.work/2025-10-28-learn-swift-ui/</guid><pubDate>Tue, 28 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;swiftui-recipe-book-app&quot;&gt;SwiftUI Recipe Book App&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/seifeet/RecipeBook&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/GitHub-RecipeBook-blue?logo=github&quot; alt=&quot;GitHub&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-by-step-complete-project-guide&quot;&gt;Step-by-Step Complete Project Guide&lt;/h2&gt;
&lt;p&gt;Welcome to the ultimate hands-on guide for building a modern SwiftUI Recipe Book app from scratch! This section will walk you through each phase of the project, from initial setup to advanced features, with clear explanations and practical code samples.&lt;/p&gt;
&lt;p&gt;If you’d like to see how these patterns hold up in real-world production code (and what breaks), pair this tutorial with the &lt;strong&gt;Advanced SwiftUI: Lessons From Mistakes&lt;/strong&gt; posts such as &lt;a href=&quot;/2025-11-11-swift-ui-observed-state-object/&quot;&gt;SwiftUI @StateObject vs @ObservedObject: The ObservedOrStateObject Pattern&lt;/a&gt; and &lt;a href=&quot;/2025-11-18-swift-ui-pointer-being-freed-was-not-allocated/&quot;&gt;SwiftUI @Published Crash: “Pointer Being Freed Was Not Allocated” Explained&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What you’ll achieve:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set up a robust SwiftUI project structure&lt;/li&gt;
&lt;li&gt;Design and implement a Core Data model for recipes&lt;/li&gt;
&lt;li&gt;Build reusable UI components and custom views&lt;/li&gt;
&lt;li&gt;Integrate networking to fetch recipes from an API&lt;/li&gt;
&lt;li&gt;Add image support and cross-platform compatibility (iOS &amp;#x26; macOS)&lt;/li&gt;
&lt;li&gt;Apply MVVM architecture and best practices&lt;/li&gt;
&lt;li&gt;Enable searching, filtering, and favorites&lt;/li&gt;
&lt;li&gt;Test and preview your app effectively&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Whether you’re a beginner or looking to deepen your SwiftUI skills, follow along step by step to create a beautiful, production-ready app.&lt;/p&gt;
&lt;p&gt;This guide will walk you through building a complete Recipe Book app that demonstrates all SwiftUI concepts. You’ll create an app where users can browse recipes, add new ones, mark favorites, and sync data.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;project-overview&quot;&gt;Project Overview&lt;/h2&gt;
&lt;p&gt;The Recipe Book app is a modern, cross-platform SwiftUI project designed to help you master real-world app development. You’ll build a beautiful and functional recipe manager that works seamlessly on both macOS and iOS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Organize recipes by category (breakfast, lunch, dinner, etc.)&lt;/li&gt;
&lt;li&gt;Add, edit, and delete your own recipes&lt;/li&gt;
&lt;li&gt;Mark favorites for quick access&lt;/li&gt;
&lt;li&gt;Powerful search and filter capabilities&lt;/li&gt;
&lt;li&gt;Persistent storage with Core Data&lt;/li&gt;
&lt;li&gt;Fetch new recipes from a public API&lt;/li&gt;
&lt;li&gt;Custom, reusable UI components&lt;/li&gt;
&lt;li&gt;Responsive design for Mac and iPhone/iPad&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1718px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fdc4a43107680f49f53fcc8f006ac3f8/954d4/macos-app.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 93.39999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAABYlAAAWJQFJUiTwAAAEtklEQVR42nWU+U/bdRjHG381MTExHrvcGOPooKXAoKXQcnXQi5Z+++19QCnlGshR6IBxjGMbMw4jzo1ja2FjohLjjC4zUeMRlyVToz9o/MW/5eWnX5P9pD+883me9+d4nuT9/jyqhnNqSsy1VJraMbdaUGt1nCwqobD0LAWlak4Vl1JQrFaQj/8PBSVq6sxNqE6fraBQWqPIewW90UhZ5TnKdFVU6evQ1egpraiguLKcYp2WUlGsVFPx7ypQosQVz/kz6jJUuew9trP73L7zgFzuPnu7D9jc2mVre4+tbJbs+kf8mPqTR5nvuXU3x072Q2V/e+su2Zw4u3NfybPZ++R2D1CF40kmB3tYGQqT6hsmnhxidLSf+YsZ0pNTLGWmWI5OMtYzx6XxARYvJLg8d5HhyUv0JWMsjsZYmE6TTqfp746gOvPmGxzOnuIL/wv0GorQt0msJKv58uYq2WtLXIq00B8zMxvVs99XyM2BYp7M6jhYSjLdV86n1zV8t2rg590hca8R1dmiI3w+X0ou9Tqz3aexdASQ9BoebV9ne6GPyUgV3bYituRjbC7UMbrm4LMxLQ+dL/Hogz5+3/fx141y/r7jJ7d5FVXlyRd5mjnB07FjLDtOoDfZmfJo2Z2Js7ci83hdx62ZWvZ6tTybr2Z/vpbflox8nKhkNaDll2kdf6yaeLZg4sZ0HFV3cwmH80buDVSTsamp0ZSRTuhZ6K1iLO7FUldGk6Gc4VQX16cTbGec7M/5CQYlAtYGrvU7WRrvYtxtQqovRyUF4wRjgzi9UewCFrsTlxykf3iCeE+KTjmCxdZBW4dEz9AEgXg/VjmOyWLF7pbpSo3gCcTp8EU47+xE5XB78IciyIGwsrY7XAryeaccQA6GlbzN3qFwHsH5gxGsTrfC+0Qs+YJ4/SEcbgmV1e3FKQVFF+Ihl1epbLG76AwERQeduCQfLe12pXNfKKpcsrskTC1tNLXZkPOcx4dbDim5qrroKD2OakYSNgYkAzVlBVhbmoi0etFo9OLLlVFwupDjx09xRKCktAJZCtBubcdYX4XN2kxDrRaP204ylULlrnqNm30Gdhe9PJi3k7CoMWg0zA33c3cpSu69GQZHxjh65BjqM2r01XreHbYx4q7hvF7LekrH4ds2vrrt59qUG5Vdd5z1oI6Hqz42VgZpaTYTDEj8dMPAN+lX+HWtiMP34xjMxSwuW8iMRNhMOLjcE2Kyp5HHVyx8vWHj6S07O3MWoXJdCTuZIAfLCbIbc/giIRLxEE+Wa/i292V+mH2V9WE1MUcdn2ym2HhnhlZpCk29TJu5mtkeMwdXY2QXLKxNiw79oRDjE+Mke3tJ9iWFil46PAGmJy4wNSDTHxHqyp14AzLpiRHGRweJd3VhNLfQLsSLxLqJRyNEo2GaLW1523iFejEkIbtL8tMqFM1bwhfueu6vFquT88KL/kgX3qA4GxCeszkVxS+8NU5+wHiEyjaX8KFb2CIU7VIe9IdjtDtdOFwewXULLkBAcPkCtg43YdFN3nP+cFTxoK2jUznnVTwsPkbehzXGBmXS1tabMJgalaF6zlCPsbH5OVdZaxAD1/gfXB31TS2CMytctd7IP08tDFEMloC+AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;macOS app screenshot&quot;
        title=&quot;&quot;
        src=&quot;/static/fdc4a43107680f49f53fcc8f006ac3f8/954d4/macos-app.png&quot;
        srcset=&quot;/static/fdc4a43107680f49f53fcc8f006ac3f8/0eb09/macos-app.png 500w,
/static/fdc4a43107680f49f53fcc8f006ac3f8/1263b/macos-app.png 1000w,
/static/fdc4a43107680f49f53fcc8f006ac3f8/954d4/macos-app.png 1718w&quot;
        sizes=&quot;(max-width: 1718px) 100vw, 1718px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/81c02ad72817d408bd242c578493d449/77666/ios-app.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 216.39999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAArCAYAAAB4pah1AAAACXBIWXMAAAsTAAALEwEAmpwYAAAJI0lEQVR42p2WC1TUVR7HZ+u023p6bNvptVvbWdts19o6bbbmmvbQTNvUzCwfVJouka6PQymapoJ6fFGigDKoMCAvLWEBeY4yw1MBFUlFkEFgYGBEGBjez/ns7/4J1u3s2bb9n/P939/93Xu/9/n73qtDvs7OTurq6nA4HPzYr6OjA2tNDcUXL+FyudAppyIqLi6murpac/6Yr7mlhaulV8jPPim57whvJvlf7O9/Xf1Q0Q79UmWYsK+vj/7+fs0eShUGBgY0/CfCge9c+dYB/nKgn5YuIVSVe3p6hkeh8upTpEP+HxphvzRp7hq0dXa7HW9vb8LDw4mLi0Ov12MwGEhNTWXnzp1ER0cTHBzM0aNHf5BYI1Q7nJeXx8mTJykqKiI+Pp7k5GTKyso4ffo0WVlZmEwmzp49+1+Jhqav4//4vr9ZN0PXLwvQ1dNHlbWWsvIKunt6cTQ5uHjxIoWFZykpuUJra5u2nurMqRl1dXVJ2kV7ezu9vb3Da68RugYUcy89Hc10tTlkt3u1yg5HMw0NN7Qz2iOdqAaqsToNCr29Cj3DJ2KY8MaNZvLKWkm91EFmaTt19maampqFqJXm5jYNTU1OGhtbUHUVlK18CkO+oTq6zIuN3LNxgPs2w4j1EGSWhrXV5BcWk5V9GmO6CaPRRPG3JVy/7qC0tEI2sYAzZ86Rm1uAzdYgEVYnm1Ys+Xx0lqobbDd2sjqxiw0n2ikqb+J6vZ0KixWLpZqrVytlx69RWVlLff0NamrqsZSL7+o1SauorbWLz0652BUVVnRqXXqa6+lx2BnobMXpUFNw0NbWSW93L92yAUOH3Olso72jW/KDx0RB2b2yxt3d3dra6loaG4j0380+3y9Z5+WJx+L5LHJ7m2UeS9giBz4mWE+1xSJr24DZbCYv5xTXys5TXnIGS2khNdWltLQ0DZ5F2Rjd9UYHUfv98V85G8+lb7Bo3nO8N+Mppk58jMkvjmbhmNGEh4Rw8byZb7wXE/r5HL5cNxO/tTPZu3YGB7fNIzHShzPmYzQ7GoTQbuXwAU8iQjYQFfQJsWFehAauR7/Pkx0+HkyfNpbt231oKEkjN/BNEtaNJ8r9eVK8X8O4axLJ68cSuXIcMd6zaLBZ0FkrLhBz+FOCAzbyycu/IurTMUT5LSYueDlJURvx2+pO7D9icJ7TYzv4ZxyRk6g3bqQy93MaclbhiHsPW/RcTvu5UVdajM5WdZEjvm/jt2EKbtMeZ+GEe1j92m/wnPNrYoLmcsjPnWxzAl15gZTvm0ht7BJKjUsojJhFjWk1zZkr6Sj0oiRhNy3WUjk2V6+w08uNY3oPzCm+hO93J8h7MgE+0wgPcCfhyG5yUuKpKo6nybgCLnxKZ7I7LTELcGYtoi1/NS1mb8qS99DRKmuYm3uaB+5/lLFjnsdt/lyWLFrA4gWvs/RjN+bNc2PMuCnoDcfZ57+H8c88iqfbC2xeOIG9C5/n4LpX8f18FmuWvcPKFX+jxmZDV1BQyO23/ZxbdLdyy09u46e33cGIEfdz190P89AjTzH21XfJPneVCEMQox57iPsfvI877r6L2wU/u+NOfnHvLxn5+G+ZMWuaHOxydEoAQkPD0B/QExpiEIE9SLDAEBpOVPTXxCemSc92LhSdJzz0MGGCI+FhUh5CVGQk0VGRIr4xxMUel1huRKckKNOcTVBQsJAFi1qHERi4XzoxYDaZNVjkYFtrbNLBUQ5Ix4cPhxIREUX+mUKJ6QIR4nyJ7zMic63o1C8lJZ09e/yEVM/evf4i+YcICNiv2aqssrKKa9cqCZEZqDohIaFamcEQTmZmtih6pgan04nO6WwlIyOThIQkuUfSNZw4kSz5EyQlpXDqlEkjrKi4pvmVLy3NSGJikpZPSUmT9ma5KnKGCJ0So1lyp2QITBqBSlV+0D9IaLFUkJ5+UisfwlB9NTo1UjU4jVA5UlONWs8qVYWqR4VBwkqNMDk59bs66VqHQ3UUBgllhG1tbVovagpqBBliZ2SoXs2CLK2xIqyUNUyUZVBTVGRqmgomlUqbTJmNunt06v5qFMm31TVgszdSJ+Jqu94owmkToa2j0eGkT11kopu19npqxWerr/831MpDq0HuHsWl65bLyRSxjHOBb9FieAG7cTsN5TmcO3ua1IPe5Pm+Q11Zlrw3SnGtDoANBwX6f+GLYFh3ANfOcFzNsoZ1IpJBm0ah934Aa8AoymPXcG7zcxTEbSX9wDL85/+B9EOectnEw+TZ8N6HMN0Dpq6CaSsFkk5fjmv6UvovXZNIqSwiI2Q+QVvH4/fVS+RFvU2Ox51Y9RNpsxznyNaFZH6zBfbHwYsfwMxlQvL3QbKpywfTN1bgEvRdEj10VmThMH1BYaI36dsepihsHJd9fodp1SNUFh4nJforjKkh4BeN69kFMEkIx38ES6STvwrZxCXwyse4Xnan79urcqeU51Fu+IiG0gQsax8Usme4cWYvVV73ErZ4JKbUoxSW5OHaEQpPvgNj34d56yA2A/ZGwQQhf+EDXOM+pO+C6GHV5UK2TBhB9qZXSV/1FBc+vBX70VVc3vgix1aMwRgbgClLprvlEIx8E/74Lry/AXzDwSd4cITicz07l76iK3KniGLvcHuO+MWj+Mb9CXKPydRX/ImwKfeS6zsb70WvE+XvBTvCcD3yOkhDnhbSJ2cPjvgZsZ+eg0vQd14IrxSfw2PS7/GcOppNM54k1NeLLUvnsGzco6x5aSRTbtcRvPsz2BaK675XYLQQjZopeGsQTwzCJeg7e1lGaK0g0PszfDeux3fTer5c60nkri84tmsN4ZuXE7pxKXmmJK6n5VC8dhcl2wIp2RrAJR9/wT6xA4fRbq2T0Gvv4JQ5B0PYEQ4dNmCIiCZbdC63oIjcwguY5P1SbbVhqa0h5OuvCZDXrd+hQwSGhbFPiWxCAiZ5jBrz82mRMNbEIUNiMz4+UUNCQiLJIlHJScmkp4kIGE+KdFXIO6aCExLLJ+JPkBCXqNmpSala2yyJ4yyJ+5vkK1sTCC3YRXlU8Ku8UhAlYVZrDVVV1ZrYJieniQ6miC6mamVDSjMsXy0tzmHHUGFOTp4mpDExx4iMjBZflqY+ylY+VabIhtrdTPhP0MbIQjcGwBgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;iOS app screenshot&quot;
        title=&quot;&quot;
        src=&quot;/static/81c02ad72817d408bd242c578493d449/77666/ios-app.png&quot;
        srcset=&quot;/static/81c02ad72817d408bd242c578493d449/0eb09/ios-app.png 500w,
/static/81c02ad72817d408bd242c578493d449/1263b/ios-app.png 1000w,
/static/81c02ad72817d408bd242c578493d449/77666/ios-app.png 1170w&quot;
        sizes=&quot;(max-width: 1170px) 100vw, 1170px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What You’ll Learn:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SwiftUI fundamentals and advanced techniques&lt;/li&gt;
&lt;li&gt;State management with @State, @Binding, @ObservedObject, and @EnvironmentObject&lt;/li&gt;
&lt;li&gt;MVVM architecture in practice&lt;/li&gt;
&lt;li&gt;Building and using custom SwiftUI components&lt;/li&gt;
&lt;li&gt;Integrating Core Data for persistence&lt;/li&gt;
&lt;li&gt;Networking and decoding JSON APIs&lt;/li&gt;
&lt;li&gt;Animations, transitions, and UI polish&lt;/li&gt;
&lt;li&gt;UIKit integration (Image Picker)&lt;/li&gt;
&lt;li&gt;Testing and previewing SwiftUI views&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By the end, you’ll have a production-quality app and a deep understanding of how to architect, build, and polish a SwiftUI project from start to finish.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-1-create-the-project&quot;&gt;Step 1: Create the Project&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Open Xcode&lt;/li&gt;
&lt;li&gt;File → New → Project&lt;/li&gt;
&lt;li&gt;Choose “App” template (Multiplatform)&lt;/li&gt;
&lt;li&gt;Name it “RecipeBook”&lt;/li&gt;
&lt;li&gt;Select SwiftUI interface and Swift language&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Important:&lt;/strong&gt; Check “Use Core Data” ✓&lt;/li&gt;
&lt;li&gt;Create the project&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9c338e582404c48bf249973ab44199e1/78d47/new-project-step-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAABoUlEQVR42q1TTW/UMBTM/7/Crf8CCcENuMCtB8ql4gILmzibjR07ib8Se3jPabZUvbRqRxrFz36ejMZ2JYTA6dRBiJa+J/RSou97dF0H5xyWZXnEGOMDhhDKvFIK1feDxdU34OprQttrKNlD0gILGmOgtS6NwzAUcp1zBsN7X5hSKnN2nlGdpYLoJJSesK6JuFLDRh4zc9p4qYtgLkK7GGNmwegt5Lji5hAQllQWpgDICRcoC/TzfS00YNx9XVPtVyJFVMXgUKuMd9cOPqbyZz169HrLj9HpCKGWi0AjA/Qcy5gzPJ4dJhspc7sJyjnj/fUMds72x9FgIvrgt03Uw9xz+/vnN5qmRqJeR3UrjqXfWnI4DBqj0QjOlND5xFB83pE2PRhnzi7/l91WM6wlh55sWtK4bTPWlO62Pg/7ofA1q7x3cCHhV+vKKT9VYBd5JFguLwU7jQPWZcFLcBF8KXavlgU5yNdCORS+3ezyuU4XejFIC9ox4+Mt8IH4U5DDumlQ1zWOR7pL01Sez1Mo6ckadcaPg8HbTzPefHb4ctPiH1uVRQG8pYGgAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/9c338e582404c48bf249973ab44199e1/78d47/new-project-step-1.png&quot;
        srcset=&quot;/static/9c338e582404c48bf249973ab44199e1/0eb09/new-project-step-1.png 500w,
/static/9c338e582404c48bf249973ab44199e1/78d47/new-project-step-1.png 800w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7ceae715baea037774c99b9817ce1869/78d47/new-project-step-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAABT0lEQVR42p1TTWuDQBDN/6d/orceC7n1EnooDXjISeNHqCUxIUbrR9REo+7rvoUtKaRoOjDMMDvzdmb27cR1XcRxjDAMEUWRUvqMlWU5WquqgmVZmGw2GwghUNc12rbF5XJB0zTKHyusp7A5BXgd/I/oWsdxbgPS7/te2TH3DAJy/DRNcTzm+AyPX4tV9FE1rd/UdXk+n3E6ndRqeOkgIBOpslBw2dKaRZ4/y71OsywLkiTB4XBQZ13XDQMyMQgCrNdr7Pd79XqM6cfSXd01MkFInzzPITsSMqffbrdUsdvtFK14ppkwCKg7ZRH56Pu+skVR/PCO/l2AWvgIGlzvbNTIt3YzTBVJL2na7o8OxyqBrqBlrP8NSN6RDmO7JGiSpKjLDC8e8PAq8LgAzKUENAwDtm3DNE31Fz3PG1TmMd9ZWpgZLp5mHqZvK7zP5/gG7f83imxXyLoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7ceae715baea037774c99b9817ce1869/78d47/new-project-step-2.png&quot;
        srcset=&quot;/static/7ceae715baea037774c99b9817ce1869/0eb09/new-project-step-2.png 500w,
/static/7ceae715baea037774c99b9817ce1869/78d47/new-project-step-2.png 800w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-2-project-structure&quot;&gt;Step 2: Project Structure&lt;/h2&gt;
&lt;p&gt;Create these groups (folders) in your project:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;RecipeBook/
├── Models/
├── ViewModels/
├── Views/
│   ├── Components/
│   ├── RecipeList/
│   └── RecipeDetail/
├── Services/
├── Utilities/
└── Resources/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;How to create groups:&lt;/strong&gt;
Right-click on RecipeBook folder → New Group&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Remove automatically created files&lt;/strong&gt; (Right-click Delete)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Persistence.swift&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-3-create-the-model&quot;&gt;Step 3: Create the Model&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Models/Recipe.swift&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foundation&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// MARK: - Recipe Model&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Identifiable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Hashable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;UUID&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ingredients&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; instructions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; prepTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// minutes&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; cookTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; servings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeCategory&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; difficulty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Difficulty&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isFavorite&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; imageData&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sourceURL&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; createdAt&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; totalTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        prepTime &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; cookTime
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tvOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIImage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imageData &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#elseif&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imageData &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CodingKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CodingKey&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; id
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; title
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; description
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; ingredients
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; instructions
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; prepTime
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; cookTime
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; servings
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; category
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; difficulty
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; isFavorite
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; imageData
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; sourceURL
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; createdAt
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// MARK: - Recipe Category&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CaseIterable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; breakfast &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Breakfast&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; lunch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Lunch&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; dinner &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Dinner&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; dessert &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Dessert&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; snack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Snack&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; appetizer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Appetizer&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; icon&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;breakfast&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;sun.horizon.fill&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lunch&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fork.knife&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dinner&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;moon.stars.fill&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dessert&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;birthday.cake.fill&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;snack&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;carrot.fill&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appetizer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cup.and.saucer.fill&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;breakfast&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;orange
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lunch&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;green
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dinner&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;purple
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dessert&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pink
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;snack&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yellow
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appetizer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;blue
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// MARK: - Difficulty Level&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Difficulty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CaseIterable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; easy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Easy&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; medium &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Medium&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; hard &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hard&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;easy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;green
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;medium&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;orange
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hard&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;red
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// MARK: - Sample Data&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; sampleRecipes&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Eggs&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            description&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Fried Eggs&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            ingredients&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Eggs&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Salt&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            instructions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Fry the eggs&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            prepTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            cookTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            servings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;breakfast&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            difficulty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;easy&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            isFavorite&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Classic Pancakes&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            description&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Fluffy homemade pancakes perfect for breakfast&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            ingredients&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2 cups flour&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2 eggs&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1.5 cups milk&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2 tbsp sugar&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2 tsp baking powder&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            instructions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1. Mix dry ingredients\n2. Add wet ingredients\n3. Cook on griddle until golden&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            prepTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            cookTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            servings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;breakfast&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            difficulty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;easy&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            isFavorite&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Spaghetti Carbonara&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            description&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Traditional Italian pasta with creamy egg sauce&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            ingredients&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;400g spaghetti&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;200g pancetta&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;4 eggs&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;100g parmesan&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Black pepper&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            instructions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1. Cook pasta\n2. Fry pancetta\n3. Mix eggs and cheese\n4. Combine everything&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            prepTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            cookTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            servings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dinner&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            difficulty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;medium
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Chocolate Chip Cookies&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            description&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Chewy and delicious homemade cookies&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            ingredients&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2 cups flour&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1 cup butter&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1 cup sugar&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2 eggs&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2 cups chocolate chips&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            instructions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1. Cream butter and sugar\n2. Add eggs and flour\n3. Fold in chips\n4. Bake at 350°F&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            prepTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            cookTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            servings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dessert&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            difficulty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;easy&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            isFavorite&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-4-update-core-data-model&quot;&gt;Step 4: Update Core Data Model&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;code class=&quot;language-text&quot;&gt;RecipeBook.xcdatamodeld&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Click ”+” to add a new entity, name it &lt;code class=&quot;language-text&quot;&gt;RecipeEntity&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add these attributes:&lt;/li&gt;
&lt;/ol&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Optional&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;id&lt;/td&gt;
&lt;td&gt;UUID&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;title&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;recipeDescription&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ingredients&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;instructions&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prepTime&lt;/td&gt;
&lt;td&gt;Integer 16&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cookTime&lt;/td&gt;
&lt;td&gt;Integer 16&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;servings&lt;/td&gt;
&lt;td&gt;Integer 16&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;category&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;difficulty&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isFavorite&lt;/td&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;imageData&lt;/td&gt;
&lt;td&gt;Binary Data&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sourceURL&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;createdAt&lt;/td&gt;
&lt;td&gt;Date&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Create PersistenceController:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Services/PersistenceController.swift&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CoreData&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersistenceController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; shared &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersistenceController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; container&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSPersistentContainer&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inMemory&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSPersistentContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;RecipeBook&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; inMemory &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persistentStoreDescriptions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileURLWithPath&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/dev/null&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
        container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;loadPersistentStores &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; error &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; error &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;fatalError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Core Data failed to load: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localizedDescription&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
        container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;viewContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;automaticallyMergesChangesFromParent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// For previews&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; preview&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersistenceController&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; controller &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersistenceController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inMemory&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; context &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; controller&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;viewContext
        
        &lt;span class=&quot;token comment&quot;&gt;// Add sample data&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; recipe &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sampleRecipes &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; entity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id
            entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title
            entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;recipeDescription &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description
            entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ingredients &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ingredients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;joined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;separator&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;||&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instructions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instructions
            entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prepTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prepTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cookTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cookTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;servings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;servings&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue
            entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;difficulty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;difficulty&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue
            entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isFavorite &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isFavorite
            entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imageData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imageData
            entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sourceURL &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sourceURL
            entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;createdAt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;createdAt
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; controller
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// MARK: - Core Data Extensions&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// RecipeEntity definition is located in RecipeBook.xcdatamodeld&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeEntity&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;toRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            description&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipeDescription &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            ingredients&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ingredients &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;separatedBy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;||&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            instructions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; instructions &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            prepTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prepTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            cookTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cookTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            servings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servings&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rawValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; category &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lunch&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            difficulty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Difficulty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rawValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; difficulty &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;medium&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            isFavorite&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; isFavorite&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            imageData&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; imageData&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            sourceURL&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sourceURL&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            createdAt&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; createdAt &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-5-create-network-service&quot;&gt;Step 5: Create Network Service&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Services/NetworkService.swift&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foundation&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// MARK: - Network Recipe Model&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NetworkRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; idMeal&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeal&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strCategory&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strInstructions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMealThumb&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strSource&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient3&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient4&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient5&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient6&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient7&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient8&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient9&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient10&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient11&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient12&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient13&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient14&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient15&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient16&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient17&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient18&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient19&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strIngredient20&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure3&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure4&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure5&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure6&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure7&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure8&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure9&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure10&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure11&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure12&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure13&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure14&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure15&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure16&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure17&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure18&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure19&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strMeasure20&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;toRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; ingredients &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseIngredients&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; ingredientCount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ingredients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; difficulty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;difficultyLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingredientCount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; prepTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;prepTimeEstimate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingredientCount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; imageData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchImageData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; strMeal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            description&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Source: TheMealDB&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            ingredients&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingredients&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            instructions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; strInstructions&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            prepTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prepTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            cookTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            servings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mapCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strCategory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            difficulty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; difficulty&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            imageData&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; imageData&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            sourceURL&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; strSource
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;parseIngredients&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; ingredientValues&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
            strIngredient1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient3&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient4&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient5&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient6&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient7&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient8&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient9&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient10&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient11&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient12&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient13&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient14&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient15&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient16&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient17&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient18&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient19&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strIngredient20
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; measureValues&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
            strMeasure1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure3&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure4&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure5&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure6&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure7&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure8&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure9&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure10&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure11&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure12&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure13&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure14&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure15&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure16&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure17&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure18&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure19&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            strMeasure20
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;zip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ingredientValues&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; measureValues&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;compactMap &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ingredient&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; measure &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; trimmedIngredient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ingredient&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trimmingCharacters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;whitespacesAndNewlines&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;trimmedIngredient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; trimmedMeasure &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; measure&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trimmingCharacters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;whitespacesAndNewlines&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; trimmedMeasure&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; trimmedIngredient
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;trimmedMeasure&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;trimmedIngredient&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;mapCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; apiCategory&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeCategory&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; apiCategory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lowercased&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;breakfast&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;breakfast
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dessert&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dessert
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;starter&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appetizer
        &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dinner
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;fetchImageData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; thumb &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; strMealThumb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; thumb&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLSession&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; data
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;difficultyLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; ingredientCount&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Difficulty&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; ingredientCount &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;easy
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;medium
        &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hard
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;prepTimeEstimate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; ingredientCount&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; ingredientCount &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MealResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; meals&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NetworkRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// MARK: - Network Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NetworkService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; shared &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NetworkService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;fetchRandomRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; urlString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://www.themealdb.com/api/json/v1/1/random.php&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; urlString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NetworkError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;invalidURL
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLSession&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSONDecoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MealResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; meal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;meals&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NetworkError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;noData
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; meal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;searchRecipes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; urlString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://www.themealdb.com/api/json/v1/1/search.php?s=&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; urlString&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addingPercentEncoding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;withAllowedCharacters&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;urlQueryAllowed&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NetworkError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;invalidURL
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLSession&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSONDecoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MealResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;withTaskGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; group &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; meal &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;meals &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                group&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;addTask &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; meal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; recipes&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; recipe &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; group &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                recipes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; recipes
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NetworkError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalizedError&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; invalidURL
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; noData
    
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; errorDescription&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;invalidURL&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Invalid URL&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;noData&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;No data received&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-6-create-custom-components&quot;&gt;Step 6: Create Custom Components&lt;/h2&gt;
&lt;h3 id=&quot;utility-shared-colors&quot;&gt;Utility: Shared Colors&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Utilities/Common.swift&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Common&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; defaultSeparatorColor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tvOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;separator&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#else&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// macOS fallback&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; defaultBackgroundColor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tvOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;systemGray5&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#else&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gray &lt;span class=&quot;token comment&quot;&gt;// macOS fallback&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These helpers keep visual styling consistent across iOS, tvOS, and macOS targets and are used throughout the component snippets that follow.&lt;/p&gt;
&lt;h3 id=&quot;component-1-floating-label-textfield&quot;&gt;Component 1: Floating Label TextField&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Views/Components/FloatingLabelTextField.swift&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;

&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; canImport&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;UIKit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIKit&lt;/span&gt;
&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PlatformKeyboardType&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; `&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;`&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; numberPad

    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; canImport&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;UIKit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; uiKeyboardType&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIKeyboardType&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;emailAddress
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;numberPad&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;numberPad
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FloatingLabelTextField&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; placeholder&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isSecure&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; keyboardType&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PlatformKeyboardType&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;

    &lt;span class=&quot;token attribute atrule&quot;&gt;@Binding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@FocusState&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isFocused&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; effectivePlaceholder&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        placeholder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; title &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; placeholder
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; showFloatingLabel&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; isFocused
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; showFloatingLabel &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;caption&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secondary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;opacity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;combined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;edge&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bottom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bottom&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;ZStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Group&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; isSecure &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;SecureField&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;TextField&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; canImport&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;UIKit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keyboardType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keyboardType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;uiKeyboardType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$isFocused&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;effectivePlaceholder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tertiary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vertical&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isFocused &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;accentColor &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Common&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultSeparatorColor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; isFocused &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;easeInOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;duration&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; isFocused&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;easeInOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;duration&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; showFloatingLabel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; DEBUG&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FloatingLabelPreviewContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; titleText&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ScrollView&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Group&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Floating Label Text Fields&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

                    &lt;span class=&quot;token class-name&quot;&gt;FloatingLabelTextField&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                        title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Meal&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        placeholder&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Meal&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        isSecure&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        keyboardType&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $titleText
                    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;FloatingLabelTextField&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                        title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Time&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        placeholder&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Time&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        isSecure&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        keyboardType&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Noon&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token other-directive property&quot;&gt;#Preview&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;FloatingLabelTextField&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;FloatingLabelPreviewContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;component-2-recipe-card&quot;&gt;Component 2: Recipe Card&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Views/Components/RecipeCard.swift&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeCard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; onFavoriteToggle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; imageHeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;160&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; cardHeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; contentHorizontalPadding&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onFavoriteToggle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token attribute atrule&quot;&gt;@escaping&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;recipe &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxWidth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; maxWidth
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onFavoriteToggle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; onFavoriteToggle
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// Image or placeholder&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;ZStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;topTrailing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; image &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tvOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;uiImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resizable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aspectRatio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentMode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fill&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; maxWidth &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; imageHeight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clipped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#elseif&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nsImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resizable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aspectRatio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentMode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fill&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; maxWidth &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; imageHeight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clipped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gradient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; imageHeight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;overlay &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;size&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;token class-name&quot;&gt;HStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;center&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token comment&quot;&gt;// Top-left difficulty badge&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;difficulty&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;caption&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fontWeight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;semibold&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;difficulty&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Spacer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token comment&quot;&gt;// Top-right favorite button&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; onFavoriteToggle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isFavorite &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;heart.fill&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;heart&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;size&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; weight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;semibold&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isFavorite &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;red &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                                &lt;span class=&quot;token class-name&quot;&gt;Circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;buttonStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;plain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;contentShape&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; contentHorizontalPadding &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;top&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            
            &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// Title&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lineLimit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fixedSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; vertical&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                
                &lt;span class=&quot;token comment&quot;&gt;// Description&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;caption&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secondary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lineLimit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fixedSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; vertical&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                
                &lt;span class=&quot;token class-name&quot;&gt;Spacer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;minLength&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                
                &lt;span class=&quot;token comment&quot;&gt;// Metadata&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;totalTime&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; min&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;clock&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;servings&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;person.2&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;caption&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secondary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; contentHorizontalPadding&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vertical&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; maxWidth &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cardHeight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Common&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultSeparatorColor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cornerRadius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;black&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; radius&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; DEBUG&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeCardPreviewContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; recipe &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Fried Eggs&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        description&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Fried eggs with a lot of salty salt that contains sodium&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        ingredients&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Eggs&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Salt&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        instructions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Fry the eggs&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        prepTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        cookTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        servings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;breakfast&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        difficulty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;easy
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ScrollView&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;HStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;center&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Group&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;RecipeCard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;RecipeCard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token other-directive property&quot;&gt;#Preview&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Recipe Card&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;RecipeCardPreviewContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;component-3-category-filter-chip&quot;&gt;Component 3: Category Filter Chip&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Views/Components/CategoryChip.swift&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CategoryChip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeCategory&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; isSelected&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;HStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;caption&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subheadline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fontWeight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;medium&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vertical&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isSelected &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Common&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultBackgroundColor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isSelected &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;white &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;primary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cornerRadius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;easeInOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;duration&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; isSelected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; DEBUG&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CategoryChipPreviewContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ScrollView&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Group&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Recipe Card&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    
                    &lt;span class=&quot;token class-name&quot;&gt;CategoryChip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                        category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;breakfast&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        isSelected&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token other-directive property&quot;&gt;#Preview&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Category Chip&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;CategoryChipPreviewContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-7-create-viewmodel&quot;&gt;Step 7: Create ViewModel&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;ViewModels/RecipeViewModel.swift&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CoreData&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Combine&lt;/span&gt;

&lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SearchSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CaseIterable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; local &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Local&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; remote &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Remote&quot;&lt;/span&gt;&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; rawValue &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; recipes&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; filteredRecipes&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selectedCategory&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeCategory&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; searchText&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; searchSource&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SearchSource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;local
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isLoading&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; errorMessage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSManagedObjectContext&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; networkService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NetworkService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared
    
    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSManagedObjectContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; context
        &lt;span class=&quot;token function&quot;&gt;fetchRecipes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// MARK: - Fetch Recipes from Core Data&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;fetchRecipes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetchRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sortDescriptors &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NSSortDescriptor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keyPath&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RecipeEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;createdAt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ascending&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; entities &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            recipes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entities&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;applyFilters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            errorMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Failed to fetch recipes: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localizedDescription&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// MARK: - Add Recipe&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;addRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; entity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id
        entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title
        entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;recipeDescription &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description
        entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ingredients &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ingredients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;joined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;separator&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;||&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instructions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instructions
        entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prepTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prepTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cookTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cookTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;servings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;servings&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue
        entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;difficulty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;difficulty&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue
        entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isFavorite &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isFavorite
        entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imageData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imageData
        entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sourceURL &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sourceURL
        entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;createdAt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;createdAt
        
        &lt;span class=&quot;token function&quot;&gt;saveContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;fetchRecipes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// MARK: - Update Recipe&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;updateRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetchRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;predicate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSPredicate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;format&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;id == %@&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CVarArg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; entities &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; entity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entities&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title
                entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;recipeDescription &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description
                entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ingredients &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ingredients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;joined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;separator&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;||&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instructions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instructions
                entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prepTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prepTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cookTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cookTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;servings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;servings&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue
                entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;difficulty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;difficulty&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue
                entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isFavorite &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isFavorite
                entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imageData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imageData
                entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sourceURL &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sourceURL
                
                &lt;span class=&quot;token function&quot;&gt;saveContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;fetchRecipes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            errorMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Failed to update recipe: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localizedDescription&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// MARK: - Delete Recipe&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;deleteRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetchRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;predicate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSPredicate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;format&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;id == %@&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CVarArg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; entities &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; entity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entities&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;saveContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;fetchRecipes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            errorMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Failed to delete recipe: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localizedDescription&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// MARK: - Toggle Favorite&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;toggleFavorite&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; updatedRecipe &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe
        updatedRecipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isFavorite&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;updateRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;updatedRecipe&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// MARK: - Fetch Random Recipe from Network&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;fetchRandomRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        isLoading &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
        errorMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; recipe &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; networkService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetchRandomRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;addRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            errorMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Failed to fetch recipe: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localizedDescription&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
        isLoading &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// MARK: - Filtering&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;applyFilters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipes
        
        &lt;span class=&quot;token comment&quot;&gt;// Category filter&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; category &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; selectedCategory &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;filter &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; category &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;token comment&quot;&gt;// Search filter&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;searchText&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;filter &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;localizedCaseInsensitiveContains&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;searchText&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;
                &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;localizedCaseInsensitiveContains&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;searchText&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
        filteredRecipes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;selectCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeCategory&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        selectedCategory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; category
        &lt;span class=&quot;token function&quot;&gt;applyFilters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;setSearchSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; source&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SearchSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; searchSource &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; source &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; source
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;applyFilters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;updateSearchText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token attribute atrule&quot;&gt;@MainActor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchText &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; text &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; text
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchSource &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;local &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;applyFilters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;submitSearch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; searchSource &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;local &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;applyFilters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;importRemoteRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; searchText&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;importRemoteRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; query &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trimmingCharacters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;whitespacesAndNewlines&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        isLoading &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
        errorMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; remoteResults &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; networkService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;searchRecipes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; query&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; selected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; remoteResults&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;randomElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MainActor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; duplicate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; selected&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; selected&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instructions &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; selected&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instructions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    searchText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
                    searchSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;local
                    selectedCategory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; duplicate &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;applyFilters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;addRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MainActor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    errorMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;No recipes found for &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MainActor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                errorMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Search failed: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localizedDescription&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MainActor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            isLoading &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// MARK: - Helper&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;saveContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            errorMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Failed to save: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localizedDescription&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-8-create-image-picker-uikit-integration&quot;&gt;Step 8: Create Image Picker (UIKit Integration)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Views/Components/ImagePicker.swift&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;

&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PhotosUI&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImagePicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIViewControllerRepresentable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Binding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selectedImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIImage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Binding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;makeUIViewController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHPickerViewController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; configuration &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHPickerConfiguration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        configuration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;filter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;images
        configuration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selectionLimit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; picker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHPickerViewController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;configuration&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; configuration&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        picker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delegate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;coordinator
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; picker
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;updateUIViewController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; uiViewController&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHPickerViewController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;makeCoordinator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Coordinator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Coordinator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $selectedImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $isPresented&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Coordinator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHPickerViewControllerDelegate&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selectedImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Binding&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UIImage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Binding&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Binding&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UIImage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Binding&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selectedImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; selectedImage
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isPresented &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; isPresented
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;picker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; picker&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHPickerViewController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; didFinishPicking results&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PHPickerResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            isPresented&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;wrappedValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; provider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; results&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;itemProvider &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; provider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;canLoadObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ofClass&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                provider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;loadObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ofClass&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selectedImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;wrappedValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; image &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIImage&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AppKit&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UniformTypeIdentifiers&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;presentImagePicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Binding&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Binding&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; panel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSOpenPanel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;allowedContentTypes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;png&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jpeg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tiff&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;heic&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;canChooseFiles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;canChooseDirectories &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;allowsMultipleSelection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    
    panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;begin &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; response &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; imageData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentsOf&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; image &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; imageData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                selectedImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;wrappedValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; image
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            isPresented&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;wrappedValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-9-create-addedit-recipe-views&quot;&gt;Step 9: Create Add/Edit Recipe Views&lt;/h2&gt;
&lt;h3 id=&quot;add-recipe-view&quot;&gt;Add Recipe View&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Views/RecipeList/AddRecipeView.swift&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PhotosUI&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CoreData&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AddRecipeView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@ObservedObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeViewModel&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dismiss&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; dismiss
    
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; description &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ingredientInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ingredients&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; instructions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; prepTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; cookTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; servings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeCategory&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lunch
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; difficulty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Difficulty&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;medium
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tvOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selectedImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIImage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#elseif&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selectedImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; showingImagePicker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NavigationStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Form&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// Basic Info Section&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Basic Information&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;FloatingLabelTextField&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                        title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Recipe Title&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        placeholder&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;e.g., Chocolate Chip Cookies&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $title
                    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    
                    &lt;span class=&quot;token class-name&quot;&gt;FloatingLabelTextField&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                        title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Description&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        placeholder&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Brief description of your recipe&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $description
                    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                
                &lt;span class=&quot;token comment&quot;&gt;// Image Section&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Photo&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; image &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; selectedImage &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tvOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;uiImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resizable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aspectRatio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentMode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fill&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clipped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cornerRadius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#elseif&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nsImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resizable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aspectRatio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentMode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fill&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clipped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cornerRadius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
                        
                        &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Change Photo&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;photo&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            showingImagePicker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Add Photo&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;camera&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            showingImagePicker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                
                &lt;span class=&quot;token comment&quot;&gt;// Category and Difficulty&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Details&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Picker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Category&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selection&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $category&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RecipeCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;allCases&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; cat &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;HStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cat&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    
                    &lt;span class=&quot;token class-name&quot;&gt;Picker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Difficulty&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selection&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $difficulty&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Difficulty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;allCases&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; diff &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;diff&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;diff&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    
                    &lt;span class=&quot;token class-name&quot;&gt;Stepper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Prep Time: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;prepTime&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; min&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $prepTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; step&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Stepper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Cook Time: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;cookTime&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; min&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $cookTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;240&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; step&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Stepper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Servings: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;servings&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $servings&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                
                &lt;span class=&quot;token comment&quot;&gt;// Ingredients Section&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Ingredients&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;HStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;TextField&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Add ingredient&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $ingredientInput&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        
                        &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Add&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;ingredientInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                ingredients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ingredientInput&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                ingredientInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ingredientInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    
                    &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ingredients&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ingredient &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ingredient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onDelete &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; indexSet &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                        ingredients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;atOffsets&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; indexSet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                
                &lt;span class=&quot;token comment&quot;&gt;// Instructions Section&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Instructions&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;TextEditor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $instructions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;minHeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;150&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;formStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;grouped&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;navigationTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;New Recipe&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;navigationBarTitleDisplayMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;inline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toolbar &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;ToolbarItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;placement&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cancellationAction&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Cancel&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;dismiss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                
                &lt;span class=&quot;token class-name&quot;&gt;ToolbarItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;placement&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;confirmationAction&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Save&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;saveRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;isValid&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sheet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $showingImagePicker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;ImagePicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $selectedImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $showingImagePicker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#elseif&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; showingImagePicker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isPresented &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; isPresented &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token function&quot;&gt;presentImagePicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $selectedImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $showingImagePicker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isValid&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;ingredients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;instructions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;saveRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; recipe &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            description&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            ingredients&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingredients&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            instructions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; instructions&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            prepTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prepTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            cookTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cookTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            servings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; servings&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            difficulty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; difficulty&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            imageData&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; imageData
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        
        viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;dismiss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; imageData&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; selectedImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; selectedImage &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tvOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; selectedImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;jpegData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;compressionQuality&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#elseif&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;jpegDataFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; selectedImage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;jpegDataFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Data&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; cgImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cgImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;forProposedRect&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hints&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; bitmapRep &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSBitmapImageRep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cgImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cgImage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jpegData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bitmapRep&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;representation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;using&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSBitmapImageRep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FileType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jpeg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; properties&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; jpegData
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;edit-recipe-view&quot;&gt;Edit Recipe View&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Views/RecipeDetail/EditRecipeView.swift&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EditRecipeView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@ObservedObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeViewModel&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dismiss&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; dismiss
    
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ingredientInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ingredients&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; instructions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; prepTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; cookTime&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; servings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeCategory&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; difficulty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Difficulty&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tvOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selectedImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIImage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#elseif&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selectedImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; showingImagePicker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;recipe &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;viewModel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; viewModel
        
        _title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        _description &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        _ingredients &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ingredients&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        _instructions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instructions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        _prepTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prepTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        _cookTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cookTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        _servings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;servings&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        _category &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        _difficulty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;difficulty&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        _selectedImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NavigationStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Form&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Basic Information&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;FloatingLabelTextField&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                        title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Recipe Title&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $title
                    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    
                    &lt;span class=&quot;token class-name&quot;&gt;FloatingLabelTextField&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                        title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Description&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $description
                    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                
                &lt;span class=&quot;token class-name&quot;&gt;Section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Photo&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; image &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; selectedImage &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tvOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;uiImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resizable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aspectRatio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentMode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fill&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clipped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cornerRadius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#elseif&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nsImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resizable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aspectRatio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentMode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fill&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clipped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cornerRadius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
                        
                        &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Change Photo&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;photo&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            showingImagePicker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Add Photo&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;camera&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            showingImagePicker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                
                &lt;span class=&quot;token class-name&quot;&gt;Section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Details&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Picker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Category&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selection&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $category&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RecipeCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;allCases&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; cat &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;HStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cat&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    
                    &lt;span class=&quot;token class-name&quot;&gt;Picker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Difficulty&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selection&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $difficulty&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Difficulty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;allCases&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; diff &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;diff&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;diff&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    
                    &lt;span class=&quot;token class-name&quot;&gt;Stepper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Prep Time: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;prepTime&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; min&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $prepTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; step&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Stepper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Cook Time: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;cookTime&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; min&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $cookTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;240&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; step&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Stepper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Servings: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;servings&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $servings&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                
                &lt;span class=&quot;token class-name&quot;&gt;Section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Ingredients&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;HStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;TextField&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Add ingredient&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $ingredientInput&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        
                        &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Add&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;ingredientInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                ingredients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ingredientInput&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                ingredientInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ingredientInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    
                    &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ingredients&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ingredient &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ingredient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onDelete &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; indexSet &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                        ingredients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;atOffsets&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; indexSet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                
                &lt;span class=&quot;token class-name&quot;&gt;Section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Instructions&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;TextEditor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $instructions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;minHeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;150&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;navigationTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Edit Recipe&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;navigationBarTitleDisplayMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;inline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toolbar &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;ToolbarItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;placement&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cancellationAction&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Cancel&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;dismiss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                
                &lt;span class=&quot;token class-name&quot;&gt;ToolbarItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;placement&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;confirmationAction&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Save&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;updateRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sheet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $showingImagePicker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;ImagePicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $selectedImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $showingImagePicker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#elseif&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; showingImagePicker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isPresented &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; isPresented &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token function&quot;&gt;presentImagePicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $selectedImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $showingImagePicker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;updateRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; updatedRecipe &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe
        updatedRecipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; title
        updatedRecipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; description
        updatedRecipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ingredients &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ingredients
        updatedRecipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instructions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; instructions
        updatedRecipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prepTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; prepTime
        updatedRecipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cookTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cookTime
        updatedRecipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;servings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; servings
        updatedRecipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; category
        updatedRecipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;difficulty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; difficulty
        updatedRecipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imageData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imageData
        
        viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;updatedRecipe&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;dismiss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; imageData&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; selectedImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; selectedImage &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tvOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; selectedImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;jpegData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;compressionQuality&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#elseif&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;jpegDataFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; selectedImage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;jpegDataFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Data&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; cgImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cgImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;forProposedRect&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hints&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; bitmapRep &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSBitmapImageRep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cgImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cgImage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jpegData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bitmapRep&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;representation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;using&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSBitmapImageRep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FileType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jpeg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; properties&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; jpegData
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-10-create-main-views&quot;&gt;Step 10: Create Main Views&lt;/h2&gt;
&lt;h3 id=&quot;view-1-recipe-detail-view&quot;&gt;View 1: Recipe Detail View&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Views/RecipeDetail/RecipeDetailView.swift&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeDetailView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; recipeId&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;UUID&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@ObservedObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeViewModel&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dismiss&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; dismiss
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; showingEditSheet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; showingDeleteAlert &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;recipes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; recipeId &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Group&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; recipe &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;ScrollView&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token comment&quot;&gt;// Header Image&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;headerImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        
                        &lt;span class=&quot;token comment&quot;&gt;// Title and Metadata&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;HStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fontWeight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bold&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                    
                                    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subheadline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                                
                                &lt;span class=&quot;token class-name&quot;&gt;Spacer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                
                                &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                    viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggleFavorite&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                    &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isFavorite &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;heart.fill&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;heart&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isFavorite &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;red &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                            
                            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secondary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            
                            &lt;span class=&quot;token comment&quot;&gt;// Time and Servings&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;HStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                &lt;span class=&quot;token class-name&quot;&gt;InfoCard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;clock&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Prep&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prepTime&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; min&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token class-name&quot;&gt;InfoCard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;flame&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Cook&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cookTime&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; min&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token class-name&quot;&gt;InfoCard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;person.2&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Servings&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;servings&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                
                                &lt;span class=&quot;token class-name&quot;&gt;Spacer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                
                                &lt;span class=&quot;token class-name&quot;&gt;DifficultyBadge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;difficulty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;difficulty&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        
                        &lt;span class=&quot;token class-name&quot;&gt;Divider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        
                        &lt;span class=&quot;token comment&quot;&gt;// Ingredients&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Ingredients&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fontWeight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bold&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            
                            &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ingredients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;enumerated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;offset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ingredient &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                                &lt;span class=&quot;token class-name&quot;&gt;HStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;top&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                    &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;checkmark.circle.fill&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ingredient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        
                        &lt;span class=&quot;token class-name&quot;&gt;Divider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        
                        &lt;span class=&quot;token comment&quot;&gt;// Instructions&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Instructions&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fontWeight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bold&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            
                            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instructions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lineSpacing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        
                        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; source &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sourceURL&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                           &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; source&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                           &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;Divider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Source&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fontWeight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;semibold&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token class-name&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;destination&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                    &lt;span class=&quot;token class-name&quot;&gt;Label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;View Original Recipe&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;safari&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;buttonStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;borderedProminent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bottom&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tvOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;navigationBarTitleDisplayMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;inline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toolbar &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;ToolbarItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;placement&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; toolbarPlacement&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Menu&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                showingEditSheet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                &lt;span class=&quot;token class-name&quot;&gt;Label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Edit&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pencil&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                            
                            &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;role&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destructive&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                showingDeleteAlert &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                &lt;span class=&quot;token class-name&quot;&gt;Label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Delete&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;trash&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ellipsis.circle&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sheet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $showingEditSheet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;EditRecipeView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Delete Recipe&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $showingDeleteAlert&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Cancel&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; role&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cancel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Delete&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; role&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destructive&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deleteRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;dismiss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Are you sure you want to delete this recipe?&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Recipe not found&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secondary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;headerImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ZStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bottomLeading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; image &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tvOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;uiImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resizable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aspectRatio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentMode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fill&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clipped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#elseif&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nsImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resizable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aspectRatio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentMode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fill&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clipped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gradient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;overlay &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;size&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; toolbarPlacement&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ToolbarItemPlacement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tvOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;topBarTrailing
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#elseif&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;automatic
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// MARK: - Info Card Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InfoCard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; icon&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; icon&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secondary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;caption&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secondary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subheadline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fontWeight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;semibold&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vertical&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Common&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultBackgroundColor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cornerRadius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// MARK: - Difficulty Badge&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DifficultyBadge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; difficulty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Difficulty&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;difficulty&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;caption&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fontWeight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bold&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vertical&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;difficulty&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cornerRadius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;view-2-recipe-list-view&quot;&gt;View 2: Recipe List View&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Views/RecipeList/RecipeListView.swift&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeListView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@StateObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeViewModel&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; showingAddRecipe &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSManagedObjectContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        _viewModel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StateObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wrappedValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; toolbarPlacement&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ToolbarItemPlacement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;iOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tvOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;topBarTrailing
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#elseif&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;macOS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;automatic
        &lt;span class=&quot;token directive property&quot;&gt;&lt;span class=&quot;token directive-name&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NavigationStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// Category Filter&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;ScrollView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; showsIndicators&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;HStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;selectCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;All&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subheadline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fontWeight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;medium&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vertical&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selectedCategory &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;accentColor &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Common&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultBackgroundColor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selectedCategory &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;white &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;primary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cornerRadius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;easeInOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;duration&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selectedCategory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        
                        &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RecipeCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;allCases&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; category &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;CategoryChip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                                category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                isSelected&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selectedCategory &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;selectCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;category&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vertical&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Common&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultBackgroundColor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                
                &lt;span class=&quot;token class-name&quot;&gt;Divider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                
                &lt;span class=&quot;token comment&quot;&gt;// Recipe Grid&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;filteredRecipes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    emptyState
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    recipeGrid
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;navigationTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Recipe Book&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;searchable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchText&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prompt&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Search recipes&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchText&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;applyFilters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toolbar &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;ToolbarItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;placement&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; toolbarPlacement&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Menu&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            showingAddRecipe &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;Label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Add Recipe&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;plus&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                        
                        &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetchRandomRecipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;token class-name&quot;&gt;Label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Get Random Recipe&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;shuffle&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;plus.circle.fill&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sheet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $showingAddRecipe&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;AddRecipeView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewModel&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;overlay &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isLoading &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;ProgressView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scaleEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxHeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ultraThinMaterial&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; recipeGrid&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ScrollView&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;LazyVGrid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;columns&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GridItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;adaptive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;minimum&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;160&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;filteredRecipes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; recipe &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;NavigationLink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;RecipeCard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                            viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggleFavorite&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipe&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;buttonStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;plain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;navigationDestination&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Recipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; recipe &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;RecipeDetailView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recipeId&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; recipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; viewModel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; emptyState&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;book.closed&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;size&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secondary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            
            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;No Recipes Yet&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fontWeight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;semibold&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            
            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Add your first recipe or import one from the web&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subheadline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secondary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;multilineTextAlignment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;center&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            
            &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                showingAddRecipe &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Add Recipe&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;plus&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fontWeight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;semibold&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;buttonStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;borderedProminent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxHeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token other-directive property&quot;&gt;#Preview&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Recipe List View&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;RecipeListView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersistenceController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;preview&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;viewContext&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-11-update-app-entry-point&quot;&gt;Step 11: Update App Entry Point&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;RecipeBookApp.swift&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;

&lt;span class=&quot;token attribute atrule&quot;&gt;@main&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecipeBookApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; persistenceController &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersistenceController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared
    
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Scene&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;WindowGroup&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;RecipeListView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; persistenceController&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;viewContext&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;managedObjectContext&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; persistenceController&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;viewContext&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-12-build-and-run&quot;&gt;Step 12: Build and Run&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Select a simulator or device (iPhone 15 Pro recommended)&lt;/li&gt;
&lt;li&gt;Press Cmd + B to build&lt;/li&gt;
&lt;li&gt;Press Cmd + R to run&lt;/li&gt;
&lt;li&gt;Test the features:
&lt;ul&gt;
&lt;li&gt;View the recipe list&lt;/li&gt;
&lt;li&gt;Filter by category&lt;/li&gt;
&lt;li&gt;Search recipes&lt;/li&gt;
&lt;li&gt;Add a new recipe&lt;/li&gt;
&lt;li&gt;Add photo from photo library&lt;/li&gt;
&lt;li&gt;Toggle favorites&lt;/li&gt;
&lt;li&gt;View recipe details&lt;/li&gt;
&lt;li&gt;Edit a recipe&lt;/li&gt;
&lt;li&gt;Delete a recipe&lt;/li&gt;
&lt;li&gt;Fetch random recipe from API&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-13-concepts-demonstrated&quot;&gt;Step 13: Concepts Demonstrated&lt;/h2&gt;
&lt;h3 id=&quot;views--modifiers&quot;&gt;Views &amp;#x26; Modifiers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Custom views: &lt;code class=&quot;language-text&quot;&gt;RecipeCard&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;CategoryChip&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;InfoCard&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Modifiers: &lt;code class=&quot;language-text&quot;&gt;.padding()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.background()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.cornerRadius()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.shadow()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;state-management&quot;&gt;State Management&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@State&lt;/code&gt;: Local UI state (search text, sheet presentation)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@Binding&lt;/code&gt;: Two-way data flow (ImagePicker)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@StateObject&lt;/code&gt;: ViewModel ownership in views&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@ObservedObject&lt;/code&gt;: Observing ViewModel changes&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@Environment&lt;/code&gt;: Access to Core Data context and dismiss&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;navigation--layout&quot;&gt;Navigation &amp;#x26; Layout&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;NavigationStack&lt;/code&gt; with type-safe navigation&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;List&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;ForEach&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;.onDelete&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;LazyVGrid&lt;/code&gt; for recipe grid&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;.sheet()&lt;/code&gt; for modal presentation&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;.alert()&lt;/code&gt; for confirmations&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;ScrollView&lt;/code&gt; with lazy loading&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;mvvm-architecture&quot;&gt;MVVM Architecture&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;Recipe&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;RecipeCategory&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Difficulty&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;View&lt;/strong&gt;: All view files&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ViewModel&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;RecipeViewModel&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; properties&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;custom-components&quot;&gt;Custom Components&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;FloatingLabelTextField&lt;/code&gt;: Reusable animated text field&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;RecipeCard&lt;/code&gt;: Complex card with image, metadata, and interactions&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;CategoryChip&lt;/code&gt;: Toggle button with animation&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;animations--transitions&quot;&gt;Animations &amp;#x26; Transitions&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Floating label animation&lt;/li&gt;
&lt;li&gt;Category selection animation&lt;/li&gt;
&lt;li&gt;Smooth transitions between views&lt;/li&gt;
&lt;li&gt;Loading state overlay&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;uikit-integration&quot;&gt;UIKit Integration&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;ImagePicker&lt;/code&gt;: UIViewControllerRepresentable&lt;/li&gt;
&lt;li&gt;PHPickerViewController coordinator pattern&lt;/li&gt;
&lt;li&gt;Image handling and compression&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;networking&quot;&gt;Networking&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;async/await&lt;/code&gt; for network calls&lt;/li&gt;
&lt;li&gt;URLSession integration&lt;/li&gt;
&lt;li&gt;Error handling with custom NetworkError&lt;/li&gt;
&lt;li&gt;JSON decoding with Codable&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;core-data-persistence&quot;&gt;Core Data Persistence&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;NSPersistentContainer setup&lt;/li&gt;
&lt;li&gt;CRUD operations (Create, Read, Update, Delete)&lt;/li&gt;
&lt;li&gt;FetchRequest for data retrieval&lt;/li&gt;
&lt;li&gt;Entity to Model conversion&lt;/li&gt;
&lt;li&gt;Context saving and error handling&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;previews--testing&quot;&gt;Previews &amp;#x26; Testing&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Multiple preview configurations&lt;/li&gt;
&lt;li&gt;Mock data for previews&lt;/li&gt;
&lt;li&gt;Preview with in-memory Core Data&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-14-test-all-features&quot;&gt;Step 14: Test All Features&lt;/h2&gt;
&lt;h3 id=&quot;feature-checklist&quot;&gt;Feature Checklist&lt;/h3&gt;
&lt;p&gt;Test each feature systematically:&lt;/p&gt;
&lt;h4 id=&quot;1-recipe-list&quot;&gt;1. Recipe List&lt;/h4&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Empty state shows when no recipes&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Sample recipes display in grid&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Categories filter correctly&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Search filters recipes in real-time&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;2-add-recipe&quot;&gt;2. Add Recipe&lt;/h4&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Form validates required fields&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Can add multiple ingredients&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Can remove ingredients&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Photo picker works&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Recipe saves to Core Data&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; New recipe appears in list&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;3-recipe-detail&quot;&gt;3. Recipe Detail&lt;/h4&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; All information displays correctly&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Images show or placeholder appears&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Favorite toggle works&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Edit button opens edit sheet&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Delete shows confirmation alert&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;4-edit-recipe&quot;&gt;4. Edit Recipe&lt;/h4&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Pre-fills with existing data&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Changes save correctly&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Updated recipe reflects in list&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;5-network-integration&quot;&gt;5. Network Integration&lt;/h4&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Random recipe fetches from API&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Loading indicator shows&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Error messages display if network fails&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Fetched recipe saves to Core Data&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;6-animations&quot;&gt;6. Animations&lt;/h4&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Floating label animates smoothly&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Category chips animate on selection&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Sheet presentations are smooth&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Navigation transitions work&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-15-common-issues--solutions&quot;&gt;Step 15: Common Issues &amp;#x26; Solutions&lt;/h2&gt;
&lt;h3 id=&quot;issue-1-core-data-entity-not-found&quot;&gt;Issue 1: Core Data Entity Not Found&lt;/h3&gt;
&lt;h4 id=&quot;issue-1-error-details&quot;&gt;Issue 1 Error Details&lt;/h4&gt;
&lt;p&gt;“Could not find entity named RecipeEntity”&lt;/p&gt;
&lt;h4 id=&quot;issue-1-resolution-steps&quot;&gt;Issue 1 Resolution Steps&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;code class=&quot;language-text&quot;&gt;RecipeBook.xcdatamodeld&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Verify entity name is exactly “RecipeEntity”&lt;/li&gt;
&lt;li&gt;Clean build folder (Cmd + Shift + K)&lt;/li&gt;
&lt;li&gt;Rebuild project&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;issue-2-image-picker-not-showing&quot;&gt;Issue 2: Image Picker Not Showing&lt;/h3&gt;
&lt;h4 id=&quot;issue-2-error-details&quot;&gt;Issue 2 Error Details&lt;/h4&gt;
&lt;p&gt;App crashes or picker doesn’t appear&lt;/p&gt;
&lt;h4 id=&quot;issue-2-resolution-steps&quot;&gt;Issue 2 Resolution Steps&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Add to &lt;code class=&quot;language-text&quot;&gt;Info.plist&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;NSPhotoLibraryUsageDescription&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;We need access to select recipe photos&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;issue-3-network-request-fails&quot;&gt;Issue 3: Network Request Fails&lt;/h3&gt;
&lt;h4 id=&quot;issue-3-error-details&quot;&gt;Issue 3 Error Details&lt;/h4&gt;
&lt;p&gt;“The Internet connection appears to be offline”&lt;/p&gt;
&lt;h4 id=&quot;issue-3-resolution-steps&quot;&gt;Issue 3 Resolution Steps&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Check simulator/device has internet&lt;/li&gt;
&lt;li&gt;Add to &lt;code class=&quot;language-text&quot;&gt;Info.plist&lt;/code&gt; for HTTP requests:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;NSAppTransportSecurity&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;NSAllowsArbitraryLoads&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;issue-4-preview-crashes&quot;&gt;Issue 4: Preview Crashes&lt;/h3&gt;
&lt;h4 id=&quot;issue-4-error-details&quot;&gt;Issue 4 Error Details&lt;/h4&gt;
&lt;p&gt;Preview fails to load&lt;/p&gt;
&lt;h4 id=&quot;issue-4-resolution-steps&quot;&gt;Issue 4 Resolution Steps&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Make sure &lt;code class=&quot;language-text&quot;&gt;PersistenceController.preview&lt;/code&gt; is set up correctly&lt;/li&gt;
&lt;li&gt;Check all required parameters are provided&lt;/li&gt;
&lt;li&gt;Use &lt;code class=&quot;language-text&quot;&gt;#Preview&lt;/code&gt; instead of older preview syntax&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;required-entitlements-for-macos-networking&quot;&gt;Required Entitlements for macOS Networking&lt;/h3&gt;
&lt;p&gt;When building the Mac target, enable the sandboxed network client entitlement so external API calls succeed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;RecipeBook.entitlements&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token prolog&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;/span&gt;
&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;plist&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-//Apple//DTD PLIST 1.0//EN&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;plist&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1.0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;com.apple.security.app-sandbox&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;com.apple.security.network.client&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;plist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;com.apple.security.network.client&lt;/code&gt; flag grants outbound network access inside the sandbox; without it, macOS builds cannot reach TheMealDB API or other remote services.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-16-customization-ideas&quot;&gt;Step 16: Customization Ideas&lt;/h2&gt;
&lt;h3 id=&quot;easy-enhancements&quot;&gt;Easy Enhancements&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Add more categories&lt;/strong&gt;: Smoothies, Salads, Soups&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Star rating&lt;/strong&gt;: Add a 1-5 star rating to recipes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cooking timer&lt;/strong&gt;: Built-in timer for cooking steps&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shopping list&lt;/strong&gt;: Generate shopping list from ingredients&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dark mode testing&lt;/strong&gt;: Test and polish dark mode appearance&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;medium-enhancements&quot;&gt;Medium Enhancements&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Recipe sharing&lt;/strong&gt;: Share recipes via share sheet&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Export to PDF&lt;/strong&gt;: Generate PDF of recipe&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingredients scaling&lt;/strong&gt;: Adjust ingredient amounts by servings&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Recipe notes&lt;/strong&gt;: Add personal notes to recipes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Meal planning&lt;/strong&gt;: Plan meals for the week&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;advanced-enhancements&quot;&gt;Advanced Enhancements&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;User accounts&lt;/strong&gt;: Add Firebase authentication&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloud sync&lt;/strong&gt;: Sync recipes across devices with CloudKit&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Recipe import&lt;/strong&gt;: Import from URLs or other apps&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nutrition info&lt;/strong&gt;: Calculate calories and macros&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Social features&lt;/strong&gt;: Share recipes with friends&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-17-key-learnings-summary&quot;&gt;Step 17: Key Learnings Summary&lt;/h2&gt;
&lt;h3 id=&quot;what-you-built&quot;&gt;What You Built&lt;/h3&gt;
&lt;p&gt;A complete, production-ready recipe management app with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;15+ custom SwiftUI views&lt;/li&gt;
&lt;li&gt;Full CRUD functionality&lt;/li&gt;
&lt;li&gt;Network integration&lt;/li&gt;
&lt;li&gt;Persistent storage&lt;/li&gt;
&lt;li&gt;Professional UI/UX&lt;/li&gt;
&lt;li&gt;Animations and transitions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;swiftui-skills-mastered&quot;&gt;SwiftUI Skills Mastered&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;State Management&lt;/strong&gt;: All property wrappers in real scenarios&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Navigation&lt;/strong&gt;: Type-safe navigation with NavigationStack&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Flow&lt;/strong&gt;: MVVM architecture implementation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom Views&lt;/strong&gt;: Building reusable components&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UIKit Bridge&lt;/strong&gt;: Integrating UIKit when needed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Core Data&lt;/strong&gt;: Complete database integration&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Networking&lt;/strong&gt;: Async/await API calls&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Animations&lt;/strong&gt;: Smooth, professional animations&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;development-best-practices&quot;&gt;Development Best Practices&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Separation of concerns (MVVM)&lt;/li&gt;
&lt;li&gt;Reusable components&lt;/li&gt;
&lt;li&gt;Error handling&lt;/li&gt;
&lt;li&gt;Code organization&lt;/li&gt;
&lt;li&gt;Preview-driven development&lt;/li&gt;
&lt;li&gt;Type safety&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-18-next-steps&quot;&gt;Step 18: Next Steps&lt;/h2&gt;
&lt;h3 id=&quot;immediate-practice&quot;&gt;Immediate Practice&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Extend the app&lt;/strong&gt;: Add 2-3 features from customization ideas&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refactor code&lt;/strong&gt;: Improve organization, extract more components&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add tests&lt;/strong&gt;: Write unit tests for ViewModel&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Polish UI&lt;/strong&gt;: Improve spacing, colors, animations&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;build-more-projects&quot;&gt;Build More Projects&lt;/h3&gt;
&lt;p&gt;Use these concepts to build:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Todo App&lt;/strong&gt;: Tasks, categories, reminders&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expense Tracker&lt;/strong&gt;: Income, expenses, charts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fitness Logger&lt;/strong&gt;: Workouts, progress tracking&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Note Taking App&lt;/strong&gt;: Rich text, folders, search&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Weather App&lt;/strong&gt;: Location, forecasts, maps&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;advanced-topics&quot;&gt;Advanced Topics&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Combine Framework&lt;/strong&gt;: Reactive programming&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SwiftUI App Lifecycle&lt;/strong&gt;: Scene management&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Widgets&lt;/strong&gt;: Home screen widgets&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;App Clips&lt;/strong&gt;: Lightweight app experiences&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;App Store Submission&lt;/strong&gt;: Prepare for release&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-19-complete-file-structure-reference&quot;&gt;Step 19: Complete File Structure Reference&lt;/h2&gt;
&lt;p&gt;Below is the full, modern file structure for the Recipe Book app. This layout follows best practices for SwiftUI, MVVM, and modular code organization. Each folder and file is purposefully named for clarity and scalability.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;RecipeBook/
├── RecipeBookApp.swift                  # App entry point (main)
├── Info.plist                           # App configuration
├── RecipeBook.entitlements              # macOS/iOS entitlements
├── Assets.xcassets/                     # App icons &amp;amp; colors
│   ├── AppIcon.appiconset/
│   └── AccentColor.colorset/
├── Models/
│   └── Recipe.swift                     # Data models (Recipe, enums)
├── View Models/
│   └── RecipeViewModel.swift            # Business logic (MVVM)
├── Views/
│   ├── Components/
│   │   ├── FloatingLabelTextField.swift # Custom animated text field
│   │   ├── RecipeCard.swift             # Recipe card UI
│   │   ├── CategoryChip.swift           # Category filter chip
│   │   └── ImagePicker.swift            # UIKit image picker bridge
│   ├── RecipeList/
│   │   ├── RecipeListView.swift         # Main recipe list/grid
│   │   └── AddRecipeView.swift          # Add new recipe form
│   └── RecipeDetail/
│       ├── RecipeDetailView.swift       # Recipe detail screen
│       └── EditRecipeView.swift         # Edit recipe form
├── Services/
│   ├── PersistenceController.swift      # Core Data stack &amp;amp; helpers
│   └── NetworkService.swift             # API/networking logic
├── Utilities/
│   └── Common.swift                     # Shared colors, helpers
├── Resources/                           # (Optional) Static resources
├── RecipeBook.xcdatamodeld/
│   └── RecipeBook.xcdatamodel/          # Core Data model (XML)
│       └── contents
├── RecipeBookTests/
│   └── RecipeBookTests.swift            # Unit tests
├── RecipeBookUITests/
│   ├── RecipeBookUITests.swift          # UI tests
│   └── RecipeBookUITestsLaunchTests.swift
├── swiftui-project-guide/
│   └── swiftui-project-guide.md         # This project guide
└── README.md                            # Project overview &amp;amp; badges&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Notes:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Folders are grouped by feature and responsibility (Models, View Models, Views, Services, Utilities).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Assets.xcassets&lt;/code&gt; holds all app icons and color sets.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;RecipeBook.xcdatamodeld&lt;/code&gt; contains the Core Data schema (edit in Xcode’s model editor).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Resources/&lt;/code&gt; is for any static files (images, JSON, etc.) you may add.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;swiftui-project-guide.md&lt;/code&gt; is your living documentation.&lt;/li&gt;
&lt;li&gt;All test targets are included for TDD and UI validation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This structure is scalable for larger apps and easy to navigate for new contributors.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;congratulations&quot;&gt;Congratulations&lt;/h2&gt;
&lt;p&gt;You’ve built a complete SwiftUI application from scratch! This project demonstrates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Professional iOS Development&lt;/strong&gt; - Industry-standard patterns and practices&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Full Stack Skills&lt;/strong&gt; - UI, business logic, data persistence, networking&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modern Swift&lt;/strong&gt; - Latest SwiftUI features and async/await&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Real-World Experience&lt;/strong&gt; - Handling images, network errors, data validation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;You now have:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A portfolio-ready project&lt;/li&gt;
&lt;li&gt;Understanding of all core SwiftUI concepts&lt;/li&gt;
&lt;li&gt;Foundation for building any iOS app&lt;/li&gt;
&lt;li&gt;Code you can reference for future projects&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;quiz-yourself&quot;&gt;Quiz Yourself&lt;/h2&gt;
&lt;p&gt;Challenge your SwiftUI and app architecture knowledge with these practical questions. Try to answer them before checking the tips below!&lt;/p&gt;
&lt;h3 id=&quot;core-swiftui--mvvm&quot;&gt;Core SwiftUI &amp;#x26; MVVM&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;State Management:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;When should you use &lt;code class=&quot;language-text&quot;&gt;@State&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@StateObject&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@ObservedObject&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;@Binding&lt;/code&gt;? Give a real example for each from this project.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MVVM Separation:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;How does the MVVM pattern help keep your code maintainable in this app? Which files are Model, View, and ViewModel?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Navigation:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;What are the benefits of using &lt;code class=&quot;language-text&quot;&gt;NavigationStack&lt;/code&gt; over the old &lt;code class=&quot;language-text&quot;&gt;NavigationView&lt;/code&gt;? How does it improve type safety?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;ui--components&quot;&gt;UI &amp;#x26; Components&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Grid vs. List:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Why is &lt;code class=&quot;language-text&quot;&gt;LazyVGrid&lt;/code&gt; used for the recipe list instead of a &lt;code class=&quot;language-text&quot;&gt;VStack&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;List&lt;/code&gt;? What are the trade-offs?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom Components:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;How would you make &lt;code class=&quot;language-text&quot;&gt;RecipeCard&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;CategoryChip&lt;/code&gt; even more reusable for other projects?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coordinator Pattern:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;What problem does the Coordinator pattern solve in the &lt;code class=&quot;language-text&quot;&gt;ImagePicker&lt;/code&gt;? Where else might you use it?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;data--networking&quot;&gt;Data &amp;#x26; Networking&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Core Data Context:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;What is an &lt;code class=&quot;language-text&quot;&gt;NSManagedObjectContext&lt;/code&gt; and why is it injected into SwiftUI views? How does it relate to data persistence?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Async/Await:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;What are the main benefits of using &lt;code class=&quot;language-text&quot;&gt;async/await&lt;/code&gt; for network calls in Swift? How does it improve code readability and error handling?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Error Handling:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;How does the app handle network or Core Data errors? Where could error handling be improved?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;advanced--real-world&quot;&gt;Advanced &amp;#x26; Real-World&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Sheet vs. Navigation:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;When should you use &lt;code class=&quot;language-text&quot;&gt;.sheet()&lt;/code&gt; for modal presentation vs. &lt;code class=&quot;language-text&quot;&gt;NavigationLink&lt;/code&gt; for navigation? Give a scenario for each from this app.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testing:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;How would you write a unit test for adding a recipe? What would you mock?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;What are some potential performance bottlenecks in this app, and how could you address them?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accessibility:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;What steps would you take to make this app accessible to users with disabilities? Which SwiftUI features help with accessibility?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;App Architecture:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;If you wanted to add user authentication or cloud sync, how would you refactor the current architecture to support these features?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependency Injection:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;How could you use dependency injection to make your ViewModels and Services more testable?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;App Lifecycle:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;How does the SwiftUI app lifecycle differ from UIKit? What are the implications for state management and scene handling?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Publishing to App Store:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;What are the key steps and requirements for submitting this app to the App Store? What would you need to add or change?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interview Prep:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;How would you explain the difference between value types and reference types in Swift, and why does it matter for SwiftUI?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Concurrency:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;What are the risks of updating UI from a background thread? How does SwiftUI help you avoid these issues?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;self-check-answers--tips&quot;&gt;Self-Check: Answers &amp;#x26; Tips&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;@State&lt;/strong&gt;: Local, simple value owned by a view (e.g., form text fields).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@StateObject&lt;/strong&gt;: Owns a reference type (ViewModel) for the lifetime of a view.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@ObservedObject&lt;/strong&gt;: Observes a reference type owned elsewhere (e.g., child view).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@Binding&lt;/strong&gt;: Two-way connection to a value owned by a parent view.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MVVM&lt;/strong&gt;: Model = &lt;code class=&quot;language-text&quot;&gt;Recipe.swift&lt;/code&gt;, View = all SwiftUI view files, ViewModel = &lt;code class=&quot;language-text&quot;&gt;RecipeViewModel.swift&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NavigationStack&lt;/strong&gt;: Enables type-safe, programmatic navigation and deep linking.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LazyVGrid&lt;/strong&gt;: Efficiently lays out many items in a grid, only rendering visible cells.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coordinator&lt;/strong&gt;: Bridges UIKit delegates to SwiftUI, needed for &lt;code class=&quot;language-text&quot;&gt;ImagePicker&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NSManagedObjectContext&lt;/strong&gt;: The Core Data “scratchpad” for changes, injected for persistence.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;async/await&lt;/strong&gt;: Makes async code linear and readable, with built-in error handling.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Error Handling&lt;/strong&gt;: Uses &lt;code class=&quot;language-text&quot;&gt;errorMessage&lt;/code&gt; in ViewModel; could be improved with user-friendly alerts and retry options.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.sheet()&lt;/strong&gt;: Use for modal forms (e.g., Add/Edit Recipe); &lt;strong&gt;NavigationLink&lt;/strong&gt; for drill-down navigation (e.g., Recipe Detail).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: Use in-memory Core Data for tests, mock network responses.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Watch for large images, excessive Core Data fetches, and UI blocking on main thread.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Remember: The best way to learn is by building. Take this project, modify it, break it, fix it, and make it your own!&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Kubernetes Cluster Setup on AWS EKS]]></title><description><![CDATA[Kubernetes Cluster Setup on AWS EKS A step-by-step guide to creating a production-ready Kubernetes cluster on AWS EKS using , setting up…]]></description><link>https://www.monkey.work/2025-08-23-raws-eks-cluster-setup/</link><guid isPermaLink="false">https://www.monkey.work/2025-08-23-raws-eks-cluster-setup/</guid><pubDate>Fri, 22 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;kubernetes-cluster-setup-on-aws-eks&quot;&gt;Kubernetes Cluster Setup on AWS EKS&lt;/h2&gt;
&lt;p&gt;A step-by-step guide to creating a production-ready Kubernetes cluster on &lt;strong&gt;AWS EKS&lt;/strong&gt; using &lt;code class=&quot;language-text&quot;&gt;eksctl&lt;/code&gt;, setting up core components (NGINX Ingress, Cert Manager, NATS, PostgreSQL), and configuring secrets and environment-specific configuration for application services.&lt;/p&gt;
&lt;p&gt;This walkthrough builds on the high-level overview from &lt;a href=&quot;/2025-02-04-k8s-deployments/&quot;&gt;Deploying microservices into a Kubernetes cloud&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Install and configure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html&quot;&gt;AWS CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eksctl.io/introduction/#installation&quot;&gt;eksctl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/tools/&quot;&gt;kubectl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://helm.sh/docs/intro/install/&quot;&gt;Helm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AWS account with permissions to create EKS clusters, IAM roles, and policies&lt;/li&gt;
&lt;li&gt;(Optional) &lt;code class=&quot;language-text&quot;&gt;aws-iam-authenticator&lt;/code&gt; if your environment requires it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ensure you’re authenticated:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;aws configure
aws sts get-caller-identity&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;helm-repo-setup&quot;&gt;Helm Repo Setup&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; jetstack https://charts.jetstack.io
helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; nats https://nats-io.github.io/k8s/helm/charts/
helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; bitnami https://charts.bitnami.com/bitnami
helm repo update&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-by-step-setup&quot;&gt;Step-by-Step Setup&lt;/h2&gt;
&lt;h3 id=&quot;1-create-eks-cluster&quot;&gt;1. Create EKS Cluster&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;eksctl create cluster &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; xoxo-v1 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --nodegroup-name standard-workers &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --node-type t3.medium &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--nodes&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --nodes-min &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --nodes-max &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--managed&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;What this does&lt;/strong&gt;&lt;br&gt;
Creates a managed EKS cluster in &lt;code class=&quot;language-text&quot;&gt;us-east-2&lt;/code&gt; with a node group that can autoscale from 1 to 4 nodes.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-add-cluster-to-kubectl-context&quot;&gt;2. Add Cluster to kubectl Context&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;aws eks update-kubeconfig &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2 &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; xoxo-v1
aws eks describe-cluster &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; xoxo-v1 &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2
kubectl get nodes &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; wide&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;What this does&lt;/strong&gt;&lt;br&gt;
Adds the new EKS cluster to your kubeconfig and verifies connectivity.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-install-nginx-ingress-controller&quot;&gt;3. Install NGINX Ingress Controller&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; nginx-ingress ingress-nginx/ingress-nginx &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--namespace&lt;/span&gt; ingress-nginx &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --create-namespace &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--set&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;controller.publishService.enabled&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;true

kubectl get svc &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; ingress-nginx
kubectl create ns backend&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;What this does&lt;/strong&gt;&lt;br&gt;
Installs the NGINX Ingress Controller and creates the &lt;code class=&quot;language-text&quot;&gt;backend&lt;/code&gt; namespace for your application services.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-install-cert-manager&quot;&gt;4. Install Cert Manager&lt;/h3&gt;
&lt;p&gt;Install CRDs:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;--validate&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;false &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.crds.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Install Cert Manager (pin a version if desired):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; cert-manager jetstack/cert-manager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--namespace&lt;/span&gt; cert-manager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --create-namespace &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt; v1.14.3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Apply your ClusterIssuer (example in Appendix):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; cluster-issuer.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;What this does&lt;/strong&gt;&lt;br&gt;
Installs Cert Manager to automatically provision and renew TLS certificates (e.g., via Let’s Encrypt).&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-install-nats-messaging-queue&quot;&gt;5. Install NATS Messaging Queue&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; nats nats/nats &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--namespace&lt;/span&gt; nats &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --create-namespace

kubectl get pods,svc &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; nats
kubectl &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; nats &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; nats-0 -- &lt;span class=&quot;token function&quot;&gt;nslookup&lt;/span&gt; nats.nats.svc.cluster.local&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;What this does&lt;/strong&gt;&lt;br&gt;
Deploys NATS for inter-service messaging and validates cluster DNS.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;6-install-postgresql-ebs-csi--bitnami&quot;&gt;6. Install PostgreSQL (EBS CSI + Bitnami)&lt;/h3&gt;
&lt;h4 id=&quot;61-ebs-csi-driver-for-persistent-volumes&quot;&gt;6.1 EBS CSI Driver (for persistent volumes)&lt;/h4&gt;
&lt;p&gt;Create IAM role for the EBS CSI driver and attach policy:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;aws iam create-role &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --role-name AmazonEKS_EBS_CSI_DriverRole &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --assume-role-policy-document file://trust-policy.json

aws iam attach-role-policy &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --role-name AmazonEKS_EBS_CSI_DriverRole &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Install the addon (IRSA role ARN will vary by account):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;eksctl create addon &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; aws-ebs-csi-driver &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--cluster&lt;/span&gt; xoxo-v1 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --service-account-role-arn arn:aws:iam::&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;YOUR_ACCOUNT_ID&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;:role/AmazonEKS_EBS_CSI_DriverRole&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;62-install-postgresql-bitnami&quot;&gt;6.2 Install PostgreSQL (Bitnami)&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl create namespace postgres

helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; pgdb bitnami/postgresql &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--namespace&lt;/span&gt; postgres &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--values&lt;/span&gt; postgresdb-values.yaml

kubectl get pods,svc &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; postgres
kubectl &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; nats &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; nats-0 -- &lt;span class=&quot;token function&quot;&gt;nslookup&lt;/span&gt; pgdb-postgresql.postgres.svc.cluster.local&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;What this does&lt;/strong&gt;&lt;br&gt;
Deploys PostgreSQL using the Bitnami Helm chart with persistent volumes via EBS.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;7-access-postgresql&quot;&gt;7. Access PostgreSQL&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Internal access (from a temporary pod):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;kubectl get secret &lt;span class=&quot;token parameter variable&quot;&gt;--namespace&lt;/span&gt; postgres pgdb-postgresql &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;{.data.postgres-password}&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; base64 &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;

kubectl run pgdb-postgresql-client &lt;span class=&quot;token parameter variable&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--tty&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--restart&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Never&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--namespace&lt;/span&gt; default &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--image&lt;/span&gt; docker.io/bitnami/postgresql:17.6.0-debian-12-r0 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;PGPASSWORD=&lt;span class=&quot;token variable&quot;&gt;$POSTGRES_PASSWORD&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--command&lt;/span&gt; -- psql &lt;span class=&quot;token parameter variable&quot;&gt;--host&lt;/span&gt; pgdb-postgresql &lt;span class=&quot;token parameter variable&quot;&gt;-U&lt;/span&gt; postgres &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; backend &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5432&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;External access (via local port-forward):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# forward Service in postgres namespace to localhost:5433&lt;/span&gt;
kubectl port-forward &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; postgres svc/pgdb-postgresql &lt;span class=&quot;token number&quot;&gt;5433&lt;/span&gt;:5432 &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# then connect locally using psql (reuses POSTGRES_PASSWORD exported above)&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;PGPASSWORD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$POSTGRES_PASSWORD&lt;/span&gt;&quot;&lt;/span&gt; psql &lt;span class=&quot;token parameter variable&quot;&gt;--host&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;127.0&lt;/span&gt;.0.1 &lt;span class=&quot;token parameter variable&quot;&gt;-U&lt;/span&gt; postgres &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; backend &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5433&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;8-add-docker-registry-secret--apply-configs&quot;&gt;8. Add Docker Registry Secret &amp;#x26; Apply Configs&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Never hardcode tokens in scripts or repos.&lt;/strong&gt; Use env vars or secret managers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Export credentials securely (replace values accordingly)&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;DOCKER_USERNAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;criyadevops&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;DOCKER_PASSWORD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;your_docker_access_token&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;DOCKER_EMAIL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;devops@criya.co&quot;&lt;/span&gt;

kubectl create secret docker-registry docker-reg &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; backend &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --docker-username&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DOCKER_USERNAME&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --docker-password&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DOCKER_PASSWORD&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --docker-email&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DOCKER_EMAIL&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To use this secret, add the following to your Deployments:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;imagePullSecrets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; docker&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;reg&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Apply environment configs and secrets (adjust file names as needed):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; staging-configmap.yaml
./staging-redeploy-secrets.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;security-recommendations&quot;&gt;Security Recommendations&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use AWS Secrets Manager or External Secrets Operator (ESO)&lt;/strong&gt;&lt;br&gt;
Store app secrets outside the cluster and sync with Kubernetes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ESO example (values sample in Appendix)&lt;/li&gt;
&lt;li&gt;Avoid &lt;code class=&quot;language-text&quot;&gt;kubectl create secret ...&lt;/code&gt; for long-lived credentials.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Enable IRSA (IAM Roles for Service Accounts)&lt;/strong&gt;&lt;br&gt;
Grant the &lt;strong&gt;minimum&lt;/strong&gt; AWS permissions to specific pods that need them:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a fine-grained IAM policy.&lt;/li&gt;
&lt;li&gt;Create/annotate a K8s service account with the IAM role ARN.&lt;/li&gt;
&lt;li&gt;Reference that service account in your Deployment.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;TLS Everywhere&lt;/strong&gt;&lt;br&gt;
Use Cert Manager with Let’s Encrypt (HTTP-01 or DNS-01) and ensure all Ingress objects have TLS configured.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Network Policies&lt;/strong&gt;&lt;br&gt;
Restrict traffic between namespaces and workloads (e.g., only app pods can talk to PostgreSQL/NATS).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;RBAC &amp;#x26; Least Privilege&lt;/strong&gt;&lt;br&gt;
Provide minimal Kubernetes permissions to CI/CD and developers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pod Security (PSA)&lt;/strong&gt;&lt;br&gt;
Enforce &lt;code class=&quot;language-text&quot;&gt;restricted&lt;/code&gt; baseline via namespace labels and admission (e.g., disallow privileged pods).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Encrypt at Rest&lt;/strong&gt;&lt;br&gt;
Enable EBS volume encryption by default (KMS keys if required).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Audit &amp;#x26; Control Plane Logs&lt;/strong&gt;&lt;br&gt;
Enable EKS control plane logging (API, audit, authenticator).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;monitoring--logging&quot;&gt;Monitoring &amp;#x26; Logging&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Metrics (Prometheus + Grafana)&lt;/strong&gt;&lt;br&gt;
Install the kube-prometheus-stack:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; monitoring prometheus-community/kube-prometheus-stack &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--namespace&lt;/span&gt; monitoring --create-namespace&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Logging (CloudWatch / Fluent Bit)&lt;/strong&gt;&lt;br&gt;
Enable &lt;strong&gt;CloudWatch Container Insights&lt;/strong&gt; or deploy &lt;strong&gt;Fluent Bit&lt;/strong&gt; to ship logs to CloudWatch/ELK/DataDog.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Horizontal/Vertical Scaling&lt;/strong&gt;&lt;br&gt;
Install &lt;code class=&quot;language-text&quot;&gt;metrics-server&lt;/code&gt; for HPA and consider VPA for right-sizing:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cluster Autoscaler (optional)&lt;/strong&gt;&lt;br&gt;
Improves node scaling efficiency:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; https://github.com/kubernetes/autoscaler/releases/latest/download/cluster-autoscaler-autodiscover.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;best-practices&quot;&gt;Best Practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pin chart versions&lt;/strong&gt; for reproducibility.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separate namespaces&lt;/strong&gt; by concern: ingress, cert-manager, nats, postgres, backend.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use &lt;code class=&quot;language-text&quot;&gt;values.yaml&lt;/code&gt;&lt;/strong&gt; files per environment (dev/staging/prod).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource requests/limits&lt;/strong&gt; on all workloads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Readiness/Liveness probes&lt;/strong&gt; on app pods.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Backups&lt;/strong&gt;: Schedule PostgreSQL backups (e.g., pgBackRest) and consider &lt;strong&gt;Velero&lt;/strong&gt; for cluster backup.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost&lt;/strong&gt;: Right-size nodes, enable autoscaler, use spot where appropriate (with interruption handling).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;cleanup&quot;&gt;Cleanup&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Delete the EKS cluster and all managed resources&lt;/span&gt;
eksctl delete cluster &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; xoxo-v1 &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-2

&lt;span class=&quot;token comment&quot;&gt;# (Optional) Detach &amp;amp; delete EBS CSI role&lt;/span&gt;
aws iam detach-role-policy &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --role-name AmazonEKS_EBS_CSI_DriverRole &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy

aws iam delete-role --role-name AmazonEKS_EBS_CSI_DriverRole&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Deleting the cluster won’t delete EBS volumes that are retained, so double-check to avoid orphaned costs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;troubleshooting&quot;&gt;Troubleshooting&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ingress not exposing IP/DNS&lt;/strong&gt;&lt;br&gt;
Check controller logs: &lt;code class=&quot;language-text&quot;&gt;kubectl logs -n ingress-nginx deploy/nginx-ingress-controller&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Certificates stuck in &lt;code class=&quot;language-text&quot;&gt;Pending&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;code class=&quot;language-text&quot;&gt;kubectl describe certificate -A&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;kubectl describe challenge -A&lt;/code&gt; for ACME issues.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DNS issues&lt;/strong&gt;&lt;br&gt;
Use a &lt;code class=&quot;language-text&quot;&gt;busybox&lt;/code&gt; pod: &lt;code class=&quot;language-text&quot;&gt;nslookup &amp;lt;service&gt;.&amp;lt;namespace&gt;.svc.cluster.local&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PersistentVolumeClaims pending&lt;/strong&gt;&lt;br&gt;
Verify EBS CSI addon and StorageClass.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;appendix&quot;&gt;Appendix&lt;/h2&gt;
&lt;h3 id=&quot;a-sample-code-classlanguage-textcluster-issueryamlcode-lets-encrypt-http-01&quot;&gt;A. Sample &lt;code class=&quot;language-text&quot;&gt;cluster-issuer.yaml&lt;/code&gt; (Let’s Encrypt HTTP-01)&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cert&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager.io/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ClusterIssuer
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; letsencrypt&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;prod
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;acme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; devops@criya.co
    &lt;span class=&quot;token key atrule&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//acme&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;v02.api.letsencrypt.org/directory
    &lt;span class=&quot;token key atrule&quot;&gt;privateKeySecretRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; letsencrypt&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;prod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;private&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;key
    &lt;span class=&quot;token key atrule&quot;&gt;solvers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;http01&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;b-sample-code-classlanguage-textpostgresdb-valuesyamlcode-bitnami&quot;&gt;B. Sample &lt;code class=&quot;language-text&quot;&gt;postgresdb-values.yaml&lt;/code&gt; (Bitnami)&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;postgresql&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; postgres
      &lt;span class=&quot;token key atrule&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; backend
      &lt;span class=&quot;token key atrule&quot;&gt;existingSecret&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;# Prefer external/ESO secrets; else leave empty to auto-generate&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;primary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;persistence&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 20Gi
    &lt;span class=&quot;token key atrule&quot;&gt;storageClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; gp3
&lt;span class=&quot;token key atrule&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;cpu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 250m
    &lt;span class=&quot;token key atrule&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 512Mi
  &lt;span class=&quot;token key atrule&quot;&gt;limits&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;cpu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1Gi&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;c-external-secrets-operator-optional&quot;&gt;C. External Secrets Operator (optional)&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; external&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secrets.io/v1beta1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ExternalSecret
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; backend&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;env
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; backend
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;refreshInterval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1h
  &lt;span class=&quot;token key atrule&quot;&gt;secretStoreRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secrets&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager
    &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ClusterSecretStore
  &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; backend&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;env
    &lt;span class=&quot;token key atrule&quot;&gt;creationPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Owner
  &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;secretKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; DATABASE_URL
      &lt;span class=&quot;token key atrule&quot;&gt;remoteRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /prod/backend/DATABASE_URL&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;</content:encoded></item><item><title><![CDATA[React Native Learning Path Phase 1 Key Concepts]]></title><description><![CDATA[🎯 Phase 1 Key Concepts - Foundation Mastery Checklist This checklist complements the broader React Native Learning Path and is designed to…]]></description><link>https://www.monkey.work/2025-08-14-rn-learning-path-resources/</link><guid isPermaLink="false">https://www.monkey.work/2025-08-14-rn-learning-path-resources/</guid><pubDate>Thu, 14 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;-phase-1-key-concepts---foundation-mastery-checklist&quot;&gt;🎯 Phase 1 Key Concepts - Foundation Mastery Checklist&lt;/h2&gt;
&lt;p&gt;This checklist complements the broader &lt;a href=&quot;/2025-07-31-rn-learning-path/&quot;&gt;React Native Learning Path&lt;/a&gt; and is designed to help you verify that your JavaScript, React, and Node.js fundamentals are solid before diving deeper into mobile.&lt;/p&gt;
&lt;h2 id=&quot;-javascript-fundamentals--es6&quot;&gt;🔹 &lt;strong&gt;JavaScript Fundamentals &amp;#x26; ES6+&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;core-javascript-concepts&quot;&gt;Core JavaScript Concepts&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Variables &amp;#x26; Data Types&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt; differences and when to use each&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Primitive Types&lt;/strong&gt;: String, Number, Boolean, null, undefined, Symbol, BigInt&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Type Coercion&lt;/strong&gt;: Implicit and explicit type conversion, truthy/falsy values&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Operators&lt;/strong&gt;: Arithmetic, comparison, logical, assignment, ternary operator&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Control Flow&lt;/strong&gt;: if/else statements, switch cases, loops (for, while, for…in, for…of)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;functions--scope&quot;&gt;Functions &amp;#x26; Scope&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Function Declarations vs Expressions&lt;/strong&gt;: Hoisting behavior differences&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Arrow Functions&lt;/strong&gt;: Syntax, &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; binding differences, when to use&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Parameters&lt;/strong&gt;: Default parameters, rest parameters (&lt;code class=&quot;language-text&quot;&gt;...args&lt;/code&gt;)&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Scope&lt;/strong&gt;: Global, function, block scope understanding&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Closures&lt;/strong&gt;: How inner functions access outer function variables&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Higher-Order Functions&lt;/strong&gt;: Functions that take/return other functions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;es6-modern-features&quot;&gt;ES6+ Modern Features&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Destructuring&lt;/strong&gt;: Array and object destructuring, nested destructuring&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Spread Operator&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;...&lt;/code&gt; for arrays and objects, cloning vs shallow copy&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Template Literals&lt;/strong&gt;: String interpolation with backticks, multiline strings&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Enhanced Object Literals&lt;/strong&gt;: Shorthand properties, computed property names&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Modules&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;import&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;export&lt;/code&gt; syntax, default vs named exports&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Classes&lt;/strong&gt;: Class syntax, constructors, methods, inheritance with &lt;code class=&quot;language-text&quot;&gt;extends&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;arrays--objects&quot;&gt;Arrays &amp;#x26; Objects&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Array Methods&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;map()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;filter()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;reduce()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;forEach()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;find()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;some()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;every()&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Array Manipulation&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;push()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;pop()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;slice()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;splice()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;concat()&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Object Methods&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;Object.keys()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Object.values()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Object.entries()&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;JSON&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;JSON.stringify()&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;JSON.parse()&lt;/code&gt; for data serialization&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;asynchronous-javascript&quot;&gt;Asynchronous JavaScript&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Callbacks&lt;/strong&gt;: Understanding callback pattern and callback hell&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Promises&lt;/strong&gt;: Creating promises, &lt;code class=&quot;language-text&quot;&gt;.then()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.catch()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.finally()&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Async/Await&lt;/strong&gt;: Modern async syntax, error handling with try/catch&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Fetch API&lt;/strong&gt;: Making HTTP requests, handling responses&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Promise Methods&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;Promise.all()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Promise.race()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Promise.allSettled()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-react-fundamentals&quot;&gt;🔹 &lt;strong&gt;React Fundamentals&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;core-react-concepts&quot;&gt;Core React Concepts&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;JSX&lt;/strong&gt;: JavaScript XML syntax, embedding expressions, JSX rules&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Components&lt;/strong&gt;: Functional vs class components (focus on functional)&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Props&lt;/strong&gt;: Passing data to components, prop types, children prop&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;State&lt;/strong&gt;: Component state with &lt;code class=&quot;language-text&quot;&gt;useState()&lt;/code&gt; hook&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Event Handling&lt;/strong&gt;: onClick, onChange, onSubmit event handlers&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Conditional Rendering&lt;/strong&gt;: Ternary operators, logical &amp;#x26;&amp;#x26;, early returns&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;react-hooks-essential&quot;&gt;React Hooks (Essential)&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;useState&lt;/strong&gt;: Managing component state, state updates, functional updates&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;useEffect&lt;/strong&gt;: Side effects, dependency array, cleanup functions&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;useContext&lt;/strong&gt;: Consuming context values, avoiding prop drilling&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Custom Hooks&lt;/strong&gt;: Creating reusable logic, naming conventions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;component-lifecycle--effects&quot;&gt;Component Lifecycle &amp;#x26; Effects&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Effect Dependencies&lt;/strong&gt;: When effects run, dependency array importance&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Cleanup&lt;/strong&gt;: Preventing memory leaks, cleanup functions&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Effect Patterns&lt;/strong&gt;: Data fetching, subscriptions, timers&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;lists--forms&quot;&gt;Lists &amp;#x26; Forms&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Rendering Lists&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;map()&lt;/code&gt; for dynamic content, &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt; prop importance&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Form Handling&lt;/strong&gt;: Controlled components, form submission&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Input Types&lt;/strong&gt;: text, checkbox, radio, select inputs&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Form Validation&lt;/strong&gt;: Basic client-side validation patterns&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;react-best-practices&quot;&gt;React Best Practices&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Component Composition&lt;/strong&gt;: Breaking UI into reusable components&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Prop Naming&lt;/strong&gt;: Clear, descriptive prop names&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;State Structure&lt;/strong&gt;: Keeping state simple and normalized&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Component Organization&lt;/strong&gt;: File structure and naming conventions&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-nodejs-basics&quot;&gt;🔹 &lt;strong&gt;Node.js Basics&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;nodejs-core-concepts&quot;&gt;Node.js Core Concepts&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Runtime Environment&lt;/strong&gt;: Node.js vs browser JavaScript differences&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Global Objects&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;global&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;process&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;__dirname&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;__filename&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Module System&lt;/strong&gt;: CommonJS (&lt;code class=&quot;language-text&quot;&gt;require&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;module.exports&lt;/code&gt;) vs ES Modules&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;NPM/Yarn&lt;/strong&gt;: Package installation, &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; understanding&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Scripts&lt;/strong&gt;: NPM scripts for automation, common script patterns&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;file-system--path&quot;&gt;File System &amp;#x26; Path&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;File Operations&lt;/strong&gt;: Reading/writing files synchronously and asynchronously&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Path Module&lt;/strong&gt;: Working with file paths, &lt;code class=&quot;language-text&quot;&gt;path.join()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;path.resolve()&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Directory Operations&lt;/strong&gt;: Creating, reading directories&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Streams&lt;/strong&gt;: Understanding readable/writable streams basics&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;http--web-fundamentals&quot;&gt;HTTP &amp;#x26; Web Fundamentals&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;HTTP Module&lt;/strong&gt;: Creating basic HTTP servers&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Request/Response&lt;/strong&gt;: Understanding req/res objects&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;HTTP Methods&lt;/strong&gt;: GET, POST, PUT, DELETE purposes&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Status Codes&lt;/strong&gt;: Common HTTP status codes (200, 404, 500, etc.)&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Headers&lt;/strong&gt;: Content-Type, Accept, Authorization basics&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;debugging--development&quot;&gt;Debugging &amp;#x26; Development&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Console Methods&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;console.log()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;console.error()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;console.table()&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Error Handling&lt;/strong&gt;: try/catch blocks, error objects&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Debugging&lt;/strong&gt;: Using debugger, Node.js debugging tools&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Environment Variables&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;process.env&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; files&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-development-tools--workflow&quot;&gt;🔹 &lt;strong&gt;Development Tools &amp;#x26; Workflow&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;version-control-git&quot;&gt;Version Control (Git)&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Basic Commands&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;git init&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;git add&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;git commit&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;git push&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Branching&lt;/strong&gt;: Creating and switching branches, &lt;code class=&quot;language-text&quot;&gt;git merge&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Remote Repositories&lt;/strong&gt;: Connecting to GitHub, cloning repositories&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Collaboration&lt;/strong&gt;: Pull requests, code reviews basics&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;development-environment&quot;&gt;Development Environment&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Code Editor&lt;/strong&gt;: VS Code extensions for JavaScript/React/Node&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Terminal/Command Line&lt;/strong&gt;: Basic commands, navigation&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Browser DevTools&lt;/strong&gt;: Console, Elements, Network tabs&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Package Managers&lt;/strong&gt;: NPM vs Yarn, lock files understanding&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-practical-skills-assessment&quot;&gt;🎯 &lt;strong&gt;Practical Skills Assessment&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;By the end of Phase 1, you should be able to build:&lt;/p&gt;
&lt;h3 id=&quot;javascript-projects&quot;&gt;JavaScript Projects&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Weather App&lt;/strong&gt;: Fetch data from API, display dynamic content&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Todo List&lt;/strong&gt;: Add/remove items, localStorage persistence&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Calculator&lt;/strong&gt;: Handle user input, perform calculations&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Quiz App&lt;/strong&gt;: Multiple choice questions, score tracking&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;react-projects&quot;&gt;React Projects&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;React Todo App&lt;/strong&gt;: State management, form handling&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Shopping Cart&lt;/strong&gt;: Add/remove items, calculate totals&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Profile Card&lt;/strong&gt;: Props usage, conditional rendering&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Counter App&lt;/strong&gt;: useState hook, event handling&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;nodejs-projects&quot;&gt;Node.js Projects&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;File Manager&lt;/strong&gt;: Read/write files, directory operations&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Simple HTTP Server&lt;/strong&gt;: Serve static files, handle routes&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;Command Line Tool&lt;/strong&gt;: Accept arguments, process data&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; &lt;strong&gt;API Mock Server&lt;/strong&gt;: Return JSON data, different endpoints&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-self-assessment-questions&quot;&gt;🔍 &lt;strong&gt;Self-Assessment Questions&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Test your understanding with these questions:&lt;/p&gt;
&lt;h3 id=&quot;javascript&quot;&gt;JavaScript&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Can you explain the difference between &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What is a closure and can you provide an example?&lt;/li&gt;
&lt;li&gt;How do you handle errors in async/await functions?&lt;/li&gt;
&lt;li&gt;What’s the difference between &lt;code class=&quot;language-text&quot;&gt;map()&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;forEach()&lt;/code&gt;?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;react&quot;&gt;React&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;When would you use &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt; with an empty dependency array?&lt;/li&gt;
&lt;li&gt;How do you pass data from child to parent component?&lt;/li&gt;
&lt;li&gt;What is the virtual DOM and why is it useful?&lt;/li&gt;
&lt;li&gt;Why is the &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt; prop important when rendering lists?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;nodejs&quot;&gt;Node.js&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;What’s the difference between &lt;code class=&quot;language-text&quot;&gt;fs.readFile()&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;fs.readFileSync()&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;How do you handle different HTTP methods in a Node.js server?&lt;/li&gt;
&lt;li&gt;What is the purpose of &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;How do you debug a Node.js application?&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-ready-for-phase-2-indicators&quot;&gt;🚀 &lt;strong&gt;Ready for Phase 2 Indicators&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;You’re ready to move to Phase 2 when you can:&lt;/p&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Build a complete React application with multiple components&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Implement state management and data flow between components&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Create a Node.js server that handles HTTP requests&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Use modern JavaScript features confidently in your code&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Debug JavaScript, React, and Node.js applications effectively&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Understand error messages and know where to look for solutions&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Complete coding challenges involving arrays, objects, and functions&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Explain your code and technical decisions to others&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;️-time-investment-breakdown&quot;&gt;⏱️ &lt;strong&gt;Time Investment Breakdown&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JavaScript Practice&lt;/strong&gt;: 40% of time (Daily coding challenges)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;React Building&lt;/strong&gt;: 35% of time (Component-based projects)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Node.js Exploration&lt;/strong&gt;: 20% of time (Server and file system work)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Integration&lt;/strong&gt;: 5% of time (Connecting pieces together)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;-recommended-practice-schedule&quot;&gt;📚 &lt;strong&gt;Recommended Practice Schedule&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Week 1-2&lt;/strong&gt;: JavaScript fundamentals and ES6+ features&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Week 3-4&lt;/strong&gt;: React components, hooks, and state management&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Week 5&lt;/strong&gt;: Node.js basics and HTTP servers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Week 6&lt;/strong&gt;: Integration projects and assessment&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Master these concepts thoroughly before advancing. A strong foundation in Phase 1 will make the subsequent phases much more manageable and enjoyable!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[React Native Learning Path]]></title><description><![CDATA[React Native Learning Path with Comprehensive Resources This roadmap pairs with the Phase 1 fundamentals checklist in React Native Learning…]]></description><link>https://www.monkey.work/2025-07-31-rn-learning-path/</link><guid isPermaLink="false">https://www.monkey.work/2025-07-31-rn-learning-path/</guid><pubDate>Thu, 31 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;react-native-learning-path-with-comprehensive-resources&quot;&gt;React Native Learning Path with Comprehensive Resources&lt;/h2&gt;
&lt;p&gt;This roadmap pairs with the Phase 1 fundamentals checklist in &lt;a href=&quot;/2025-08-14-rn-learning-path-phase-1-key-concepts/&quot;&gt;React Native Learning Path Phase 1 Key Concepts&lt;/a&gt;, which you can use to track progress on JavaScript, React, and Node.js foundations.&lt;/p&gt;
&lt;h2 id=&quot;-phase-1-foundation-46-weeks&quot;&gt;📘 &lt;strong&gt;Phase 1: Foundation (4–6 weeks)&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;-javascript-fundamentals--es6&quot;&gt;🔹 JavaScript Fundamentals &amp;#x26; ES6+&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Primary Resources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Interactive Course&lt;/strong&gt;: &lt;a href=&quot;https://javascript.info/&quot;&gt;JavaScript.info - The Modern JavaScript Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Course&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=PkZNo7MFNFg&quot;&gt;FreeCodeCamp - Learn JavaScript - Full Course for Beginners&lt;/a&gt; (3.5 hours)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive Practice&lt;/strong&gt;: &lt;a href=&quot;https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/&quot;&gt;FreeCodeCamp - JavaScript Algorithms and Data Structures&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Supplementary:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reference&lt;/strong&gt;: &lt;a href=&quot;https://www.w3schools.com/Js/js_es6.asp&quot;&gt;W3Schools ES6 Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deep Dive&lt;/strong&gt;: &lt;a href=&quot;https://www.devscall.com/tutorials/react-javascript-es6-modern-web-dev/javascript-es6-fundamentals-modern-features&quot;&gt;Devscall ES6+ Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Book&lt;/strong&gt;: &lt;a href=&quot;https://eloquentjavascript.net/&quot;&gt;Eloquent JavaScript&lt;/a&gt; (Free online)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Practice Platforms:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Coding Challenges&lt;/strong&gt;: &lt;a href=&quot;https://www.codewars.com/&quot;&gt;Codewars&lt;/a&gt; - JavaScript kata&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Algorithm Practice&lt;/strong&gt;: &lt;a href=&quot;https://leetcode.com/&quot;&gt;LeetCode&lt;/a&gt; - JavaScript problems&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive Exercises&lt;/strong&gt;: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript&quot;&gt;MDN Web Docs - JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-react-fundamentals&quot;&gt;🔹 React Fundamentals&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Primary Resources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video Course&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=bMknfKXIFA8&quot;&gt;FreeCodeCamp - React Course - Beginner’s Tutorial&lt;/a&gt; (5 hours)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive Course&lt;/strong&gt;: &lt;a href=&quot;https://scrimba.com/learn/learnreact&quot;&gt;Scrimba - Learn React for Free&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive Practice&lt;/strong&gt;: &lt;a href=&quot;https://www.freecodecamp.org/learn/front-end-development-libraries/&quot;&gt;FreeCodeCamp - Front End Development Libraries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Supplementary:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tutorial&lt;/strong&gt;: &lt;a href=&quot;https://www.freecodecamp.org/news/react-fundamentals/&quot;&gt;FreeCodeCamp React Fundamentals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Series&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/playlist?list=PL4cUxeGkcC9gZD-Tvwfod2gaISzfRiP9d&quot;&gt;The Net Ninja - Full React Tutorial&lt;/a&gt; (36 videos)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=w7ejDZ8SWv8&quot;&gt;Traversy Media - React JS Crash Course&lt;/a&gt; (1.5 hours)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Series&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/playlist?list=PLC3y8-rFHvwgg3vaYJgHGnModB54rxOk3&quot;&gt;Codevolution - ReactJS Tutorial for Beginners&lt;/a&gt; (40+ videos)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Official Documentation:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Primary&lt;/strong&gt;: &lt;a href=&quot;https://react.dev/&quot;&gt;React.dev - Official React Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tutorial&lt;/strong&gt;: &lt;a href=&quot;https://react.dev/learn/tutorial-tic-tac-toe&quot;&gt;React Official Tutorial - Tic-tac-toe&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Practice:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Playground&lt;/strong&gt;: &lt;a href=&quot;https://codesandbox.io/&quot;&gt;CodeSandbox&lt;/a&gt; - React templates&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Projects&lt;/strong&gt;: Build todo app, calculator, weather app&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-nodejs-basics&quot;&gt;🔹 Node.js Basics&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Primary Resources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video Course&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=Oe421EPjeBE&quot;&gt;FreeCodeCamp - Node.js and Express.js - Full Course&lt;/a&gt; (8 hours)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Series&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/playlist?list=PL4cUxeGkcC9gcy9lrvMJ75z9maRw4byYp&quot;&gt;The Net Ninja - Node.js Tutorial for Beginners&lt;/a&gt; (12 videos)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive&lt;/strong&gt;: &lt;a href=&quot;https://nodeschool.io/&quot;&gt;NodeSchool - Interactive Node.js Workshops&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Supplementary:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=fBNz5xF-Kx4&quot;&gt;Traversy Media - Node.js Crash Course&lt;/a&gt; (1.5 hours)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reference&lt;/strong&gt;: &lt;a href=&quot;https://www.w3schools.com/nodejs/nodejs_filesystem.asp&quot;&gt;W3Schools Node.js File System&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive Practice&lt;/strong&gt;: &lt;a href=&quot;https://www.freecodecamp.org/learn/back-end-development-and-apis/&quot;&gt;FreeCodeCamp - Back End Development and APIs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Official Documentation:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Primary&lt;/strong&gt;: &lt;a href=&quot;https://nodejs.org/en/docs/&quot;&gt;Node.js Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Guide&lt;/strong&gt;: &lt;a href=&quot;https://nodejs.org/en/docs/guides/getting-started-guide/&quot;&gt;Node.js Getting Started Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-phase-2-intermediate-react--backend-68-weeks&quot;&gt;📘 &lt;strong&gt;Phase 2: Intermediate React &amp;#x26; Backend (6–8 weeks)&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;-advanced-react-patterns&quot;&gt;🔹 Advanced React Patterns&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Primary Resources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Articles&lt;/strong&gt;: &lt;a href=&quot;https://kentcdodds.com/blog/&quot;&gt;Kent C. Dodds Blog - Advanced React Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Content&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/results?search_query=kent+c+dodds+advanced+react+patterns&quot;&gt;Kent C. Dodds - Advanced React Patterns Talks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Channel&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/c/BenAwad97&quot;&gt;Ben Awad - React Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Supplementary:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tutorial&lt;/strong&gt;: &lt;a href=&quot;https://codezup.com/react-hooks-advanced-patterns-best-practices/&quot;&gt;CodezUp Advanced Hooks &amp;#x26; Context&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href=&quot;https://github.com/kentcdodds/advanced-react-patterns&quot;&gt;Kent C. Dodds Advanced Patterns Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Channel&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/c/JackHerrington&quot;&gt;Jack Herrington - No BS TS/React&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Learning Resources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Articles&lt;/strong&gt;: &lt;a href=&quot;https://www.robinwieruch.de/&quot;&gt;Robin Wieruch - React Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Articles&lt;/strong&gt;: &lt;a href=&quot;https://daveceddia.com/&quot;&gt;Dave Ceddia - React Concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conference Talks&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/c/ReactConf&quot;&gt;React Conf YouTube Channel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-expressjs--rest-apis&quot;&gt;🔹 Express.js &amp;#x26; REST APIs&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Primary Resources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video Series&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/playlist?list=PL4cUxeGkcC9gqn9V_cD7OQ-7NJy1gECsF&quot;&gt;The Net Ninja - Node.js &amp;#x26; Express From Scratch&lt;/a&gt; (12 videos)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=L72fhGm1tfE&quot;&gt;Traversy Media - Express JS Crash Course&lt;/a&gt; (1 hour)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Course&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=qwfE7fSVaZM&quot;&gt;FreeCodeCamp - Node.js / Express Course - Build 4 Projects&lt;/a&gt; (10 hours)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Supplementary:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tutorial&lt;/strong&gt;: &lt;a href=&quot;https://moldstud.com/articles/p-build-restful-apis-with-expressjs-a-comprehensive-step-by-step-tutorial&quot;&gt;MoldStud Express REST API Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Official Documentation:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Primary&lt;/strong&gt;: &lt;a href=&quot;https://expressjs.com/&quot;&gt;Express.js Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Guide&lt;/strong&gt;: &lt;a href=&quot;https://restfulapi.net/&quot;&gt;RESTful API Design Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reference&lt;/strong&gt;: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP&quot;&gt;Mozilla HTTP Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-database-integration&quot;&gt;🔹 Database Integration&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;MongoDB &amp;#x26; Mongoose:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video Series&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/playlist?list=PL4cUxeGkcC9h77dJ-QJlwGlZlTd4ecZOA&quot;&gt;The Net Ninja - MongoDB Tutorial&lt;/a&gt; (9 videos)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Series&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/playlist?list=PL4cUxeGkcC9g8OhpOZxNdhXggFz2lOuCT&quot;&gt;The Net Ninja - Mongoose ODM&lt;/a&gt; (11 videos)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive&lt;/strong&gt;: &lt;a href=&quot;https://university.mongodb.com/&quot;&gt;MongoDB University - Free MongoDB Courses&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=-56x56UppqQ&quot;&gt;Traversy Media - MongoDB Crash Course&lt;/a&gt; (1 hour)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;PostgreSQL &amp;#x26; SQL:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Interactive&lt;/strong&gt;: &lt;a href=&quot;https://sqlbolt.com/&quot;&gt;SQLBolt - Interactive SQL Lessons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Course&lt;/strong&gt;: &lt;a href=&quot;https://www.freecodecamp.org/learn/relational-database/&quot;&gt;FreeCodeCamp - Relational Database Course&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tutorial&lt;/strong&gt;: &lt;a href=&quot;https://www.postgresql.org/docs/current/tutorial.html&quot;&gt;PostgreSQL Official Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Supplementary:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tutorial&lt;/strong&gt;: &lt;a href=&quot;https://tutorials.ducatindia.com/node/database-integration-in-node-js&quot;&gt;Ducat India – Mongoose &amp;#x26; Sequelize&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Comparison&lt;/strong&gt;: &lt;a href=&quot;https://www.slingacademy.com/article/sequelize-vs-mongoose-which-to-choose/&quot;&gt;Sequelize vs Mongoose&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/c/HusseinNasser-software-engineering&quot;&gt;Hussein Nasser - Database Engineering Concepts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-state-management&quot;&gt;🔹 State Management&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Redux Toolkit:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video Series&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/playlist?list=PL4cUxeGkcC9ij8CfkAY2RAGb-tmkNwQHG&quot;&gt;The Net Ninja - Redux Tutorial&lt;/a&gt; (20 videos)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Series&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/playlist?list=PLC3y8-rFHvwheJHvseC3I0HuYI2f46oAK&quot;&gt;Codevolution - React Redux Tutorial&lt;/a&gt; (25 videos)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Official Tutorial&lt;/strong&gt;: &lt;a href=&quot;https://redux-toolkit.js.org/tutorials/quick-start&quot;&gt;Redux Toolkit Quick Start&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;React Query:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video Tutorials&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/results?search_query=tkdodo+react+query&quot;&gt;TkDodo - React Query Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href=&quot;https://tanstack.com/query/latest&quot;&gt;TanStack Query Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Supplementary:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tutorial&lt;/strong&gt;: &lt;a href=&quot;https://reduxtoolkit.com/how-to-use-redux-toolkit/&quot;&gt;Redux Toolkit Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href=&quot;https://redux-toolkit.js.org/rtk-query/overview&quot;&gt;Redux Toolkit Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alternative&lt;/strong&gt;: &lt;a href=&quot;https://github.com/pmndrs/zustand&quot;&gt;Zustand Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-phase-3-react-native-development-68-weeks&quot;&gt;📘 &lt;strong&gt;Phase 3: React Native Development (6–8 weeks)&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;-react-native-fundamentals&quot;&gt;🔹 React Native Fundamentals&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Primary Resources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video Series&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/playlist?list=PL4cUxeGkcC9ixPU-QkScoRBVxtPPzVjrQ&quot;&gt;The Net Ninja - React Native Tutorial&lt;/a&gt; (50+ videos)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Course&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=0-S5a0eXPoc&quot;&gt;Programming with Mosh - React Native Tutorial for Beginners&lt;/a&gt; (6 hours)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=Hf4MJH0jDb4&quot;&gt;Traversy Media - React Native Crash Course&lt;/a&gt; (2 hours)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Official Resources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Primary&lt;/strong&gt;: &lt;a href=&quot;https://reactnative.dev/docs/tutorial&quot;&gt;React Native Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tutorial&lt;/strong&gt;: &lt;a href=&quot;https://reactnative.dev/docs/getting-started&quot;&gt;React Native Getting Started Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Playground&lt;/strong&gt;: &lt;a href=&quot;https://snack.expo.dev/&quot;&gt;Expo Snack - Online React Native Editor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Specialized Content:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Animations&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/c/wcandillon&quot;&gt;William Candillon - Can it be done in React Native?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advanced&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/c/CatalinMironDev&quot;&gt;Catalin Miron - React Native Animations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tips&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/c/SimonGrimm&quot;&gt;Simon Grimm - React Native Development&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Navigation:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href=&quot;https://reactnavigation.org/&quot;&gt;React Navigation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Tutorial&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=nQVCkqvU1uE&quot;&gt;React Navigation 6 - Complete Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-mobile-specific-features--optimization&quot;&gt;🔹 Mobile-Specific Features &amp;#x26; Optimization&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Performance &amp;#x26; Optimization:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tutorial&lt;/strong&gt;: &lt;a href=&quot;https://codezup.com/react-native-performance-optimization-guide/&quot;&gt;CodezUp Performance Optimization Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Official Guide&lt;/strong&gt;: &lt;a href=&quot;https://reactnative.dev/docs/performance&quot;&gt;React Native Performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/results?search_query=react+native+performance+optimization&quot;&gt;React Native Performance Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Device APIs &amp;#x26; Features:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Expo Documentation&lt;/strong&gt;: &lt;a href=&quot;https://docs.expo.dev/versions/latest/&quot;&gt;Expo SDK APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Camera&lt;/strong&gt;: &lt;a href=&quot;https://github.com/react-native-camera/react-native-camera&quot;&gt;React Native Camera Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maps&lt;/strong&gt;: &lt;a href=&quot;https://github.com/react-native-maps/react-native-maps&quot;&gt;React Native Maps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Storage&lt;/strong&gt;: &lt;a href=&quot;https://react-native-async-storage.github.io/async-storage/&quot;&gt;AsyncStorage Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Tools &amp;#x26; Debugging:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Flipper&lt;/strong&gt;: &lt;a href=&quot;https://fbflipper.com/&quot;&gt;Flipper Debugging Tool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;React DevTools&lt;/strong&gt;: &lt;a href=&quot;https://reactnative.dev/docs/debugging#react-devtools&quot;&gt;React DevTools for React Native&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Animation Libraries:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reanimated&lt;/strong&gt;: &lt;a href=&quot;https://docs.swmansion.com/react-native-reanimated/&quot;&gt;React Native Reanimated&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gesture Handler&lt;/strong&gt;: &lt;a href=&quot;https://docs.swmansion.com/react-native-gesture-handler/&quot;&gt;React Native Gesture Handler&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-phase-4-advanced-architecture-810-weeks&quot;&gt;📘 &lt;strong&gt;Phase 4: Advanced Architecture (8–10 weeks)&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;-full-stack-architecture-patterns&quot;&gt;🔹 Full-Stack Architecture Patterns&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Architecture Concepts:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video Series&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/c/HusseinNasser-software-engineering&quot;&gt;Hussein Nasser - Software Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Series&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/playlist?list=PLF206E906175C7E07&quot;&gt;Derek Banas - Design Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Content&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/results?search_query=christopher+okhravi+solid&quot;&gt;Christopher Okhravi - SOLID Principles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Microservices:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Articles&lt;/strong&gt;: &lt;a href=&quot;https://martinfowler.com/articles/microservices.html&quot;&gt;Martin Fowler - Microservices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/results?search_query=freecodecamp+microservices&quot;&gt;FreeCodeCamp - Microservices Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Case Studies&lt;/strong&gt;: &lt;a href=&quot;http://highscalability.com/&quot;&gt;High Scalability - Architecture Examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Supplementary:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video Playlist&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/playlist?list=PLfg9ycqfY2SUMcwK5OFeNxjo4pyLQx6tG&quot;&gt;Topictrick Architecture Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Article&lt;/strong&gt;: &lt;a href=&quot;https://www.geeksforgeeks.org/system-design/mvc-architecture-vs-microservices-architecture/&quot;&gt;GeeksforGeeks – MVC vs Microservices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Clean Architecture:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Articles&lt;/strong&gt;: &lt;a href=&quot;https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html&quot;&gt;Clean Architecture Concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Patterns&lt;/strong&gt;: &lt;a href=&quot;https://martinfowler.com/eaaCatalog/&quot;&gt;Enterprise Application Architecture Patterns&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-authentication--security&quot;&gt;🔹 Authentication &amp;#x26; Security&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;JWT &amp;#x26; Authentication:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=SnoAwLP1a-0&amp;#x26;list=PL4cUxeGkcC9iI6dFoMZNkk4rImpbqHU-A&quot;&gt;The Net Ninja - JWT Authentication Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=mbsmsi7l3r4&quot;&gt;Traversy Media - JWT Authentication Crash Course&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href=&quot;https://jwt.io/&quot;&gt;JWT.io - JSON Web Tokens&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Security Best Practices:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;OWASP&lt;/strong&gt;: &lt;a href=&quot;https://owasp.org/&quot;&gt;OWASP Security Guidelines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Channel&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/user/OWASPGLOBAL&quot;&gt;OWASP Foundation YouTube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auth0&lt;/strong&gt;: &lt;a href=&quot;https://auth0.com/docs/&quot;&gt;Auth0 Authentication Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-devops--deployment&quot;&gt;🔹 DevOps &amp;#x26; Deployment&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Docker &amp;#x26; Containerization:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video Series&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=3c-iBn73dDE&quot;&gt;TechWorld with Nana - Docker Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive&lt;/strong&gt;: &lt;a href=&quot;https://labs.play-with-docker.com/&quot;&gt;Docker Play - Online Docker Playground&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;CI/CD:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Actions&lt;/strong&gt;: &lt;a href=&quot;https://docs.github.com/en/actions&quot;&gt;GitHub Actions Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=j5Zsa_eOXeY&quot;&gt;FreeCodeCamp - DevOps Engineering Course&lt;/a&gt; (4 hours)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cloud Deployment:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AWS Free Tier&lt;/strong&gt;: &lt;a href=&quot;https://aws.amazon.com/getting-started/&quot;&gt;AWS Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vercel&lt;/strong&gt;: &lt;a href=&quot;https://vercel.com/docs&quot;&gt;Vercel Deployment Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Heroku&lt;/strong&gt;: &lt;a href=&quot;https://devcenter.heroku.com/&quot;&gt;Heroku Dev Center&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-phase-5-production-ready-skills-68-weeks&quot;&gt;📘 &lt;strong&gt;Phase 5: Production-Ready Skills (6–8 weeks)&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;-testing-strategies&quot;&gt;🔹 Testing Strategies&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;React &amp;#x26; JavaScript Testing:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video Series&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/results?search_query=kent+c+dodds+testing+javascript&quot;&gt;Kent C. Dodds - Testing JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/playlist?list=PL4cUxeGkcC9hI0J8g-8X3T5Fd_KTkUX4N&quot;&gt;The Net Ninja - JavaScript Testing Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Course&lt;/strong&gt;: &lt;a href=&quot;https://testing-library.com/&quot;&gt;Testing Library Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;React Native Testing:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href=&quot;https://callstack.github.io/react-native-testing-library/&quot;&gt;React Testing Library for React Native&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;E2E Testing&lt;/strong&gt;: &lt;a href=&quot;https://github.com/wix/Detox&quot;&gt;Detox Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/results?search_query=react+native+testing+detox&quot;&gt;React Native Testing Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Testing Frameworks:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Jest&lt;/strong&gt;: &lt;a href=&quot;https://jestjs.io/docs/getting-started&quot;&gt;Jest Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testing Library&lt;/strong&gt;: &lt;a href=&quot;https://testing-library.com/docs/react-testing-library/intro/&quot;&gt;React Testing Library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-monitoring--analytics&quot;&gt;🔹 Monitoring &amp;#x26; Analytics&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Error Tracking:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sentry&lt;/strong&gt;: &lt;a href=&quot;https://docs.sentry.io/&quot;&gt;Sentry Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bugsnag&lt;/strong&gt;: &lt;a href=&quot;https://docs.bugsnag.com/&quot;&gt;Bugsnag Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LogRocket&lt;/strong&gt;: &lt;a href=&quot;https://docs.logrocket.com/&quot;&gt;LogRocket Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Performance Monitoring:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video Content&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/results?search_query=hussein+nasser+monitoring&quot;&gt;Hussein Nasser - System Monitoring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Courses&lt;/strong&gt;: &lt;a href=&quot;https://learn.newrelic.com/&quot;&gt;New Relic University&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href=&quot;https://prometheus.io/docs/introduction/overview/&quot;&gt;Prometheus Monitoring&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Analytics:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Google Analytics&lt;/strong&gt;: &lt;a href=&quot;https://developers.google.com/analytics&quot;&gt;Google Analytics for Developers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Firebase Analytics&lt;/strong&gt;: &lt;a href=&quot;https://firebase.google.com/docs/analytics&quot;&gt;Firebase Analytics Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-advanced-topics&quot;&gt;🔹 Advanced Topics&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;GraphQL:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video Course&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/playlist?list=PL4cUxeGkcC9iK6Qhn-QLcXCXPQUov1U7f&quot;&gt;The Net Ninja - GraphQL Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href=&quot;https://www.apollographql.com/docs/&quot;&gt;Apollo GraphQL Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive&lt;/strong&gt;: &lt;a href=&quot;https://graphql.org/learn/&quot;&gt;GraphQL Official Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Serverless:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Vercel Functions&lt;/strong&gt;: &lt;a href=&quot;https://vercel.com/docs/concepts/functions&quot;&gt;Vercel Functions Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Netlify Functions&lt;/strong&gt;: &lt;a href=&quot;https://docs.netlify.com/functions/overview/&quot;&gt;Netlify Functions Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AWS Lambda&lt;/strong&gt;: &lt;a href=&quot;https://docs.aws.amazon.com/lambda/&quot;&gt;AWS Lambda Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Progressive Web Apps:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Google&lt;/strong&gt;: &lt;a href=&quot;https://web.dev/progressive-web-apps/&quot;&gt;PWA Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video&lt;/strong&gt;: &lt;a href=&quot;https://www.youtube.com/watch?v=ksXwaWHCW6k&quot;&gt;Traversy Media - PWA Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;️-additional-learning-platforms--communities&quot;&gt;🛠️ &lt;strong&gt;Additional Learning Platforms &amp;#x26; Communities&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;interactive-learning-platforms&quot;&gt;Interactive Learning Platforms&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;FreeCodeCamp&lt;/strong&gt;: &lt;a href=&quot;https://www.freecodecamp.org/&quot;&gt;Complete Full-Stack Curriculum&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Odin Project&lt;/strong&gt;: &lt;a href=&quot;https://www.theodinproject.com/&quot;&gt;Full-Stack JavaScript Path&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Codecademy&lt;/strong&gt;: &lt;a href=&quot;https://www.codecademy.com/&quot;&gt;Interactive Programming Courses&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;coding-challenge-platforms&quot;&gt;Coding Challenge Platforms&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LeetCode&lt;/strong&gt;: &lt;a href=&quot;https://leetcode.com/&quot;&gt;Algorithm Practice&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Codewars&lt;/strong&gt;: &lt;a href=&quot;https://www.codewars.com/&quot;&gt;Code Kata Challenges&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HackerRank&lt;/strong&gt;: &lt;a href=&quot;https://www.hackerrank.com/&quot;&gt;Programming Challenges&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;developer-communities&quot;&gt;Developer Communities&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Discord&lt;/strong&gt;: &lt;a href=&quot;https://discord.gg/reactiflux&quot;&gt;Reactiflux Community&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reddit&lt;/strong&gt;: &lt;a href=&quot;https://www.reddit.com/r/reactnative/&quot;&gt;r/reactnative&lt;/a&gt;, &lt;a href=&quot;https://www.reddit.com/r/reactjs/&quot;&gt;r/reactjs&lt;/a&gt;, &lt;a href=&quot;https://www.reddit.com/r/node/&quot;&gt;r/node&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stack Overflow&lt;/strong&gt;: &lt;a href=&quot;https://stackoverflow.com/&quot;&gt;Programming Q&amp;#x26;A&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dev.to&lt;/strong&gt;: &lt;a href=&quot;https://dev.to/&quot;&gt;Developer Community &amp;#x26; Articles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;newsletters--staying-updated&quot;&gt;Newsletters &amp;#x26; Staying Updated&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JavaScript Weekly&lt;/strong&gt;: &lt;a href=&quot;https://javascriptweekly.com/&quot;&gt;Weekly JS News&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;React Status&lt;/strong&gt;: &lt;a href=&quot;https://react.statuscode.com/&quot;&gt;React Ecosystem Updates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Node Weekly&lt;/strong&gt;: &lt;a href=&quot;https://nodeweekly.com/&quot;&gt;Node.js News&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;practice-project-ideas&quot;&gt;Practice Project Ideas&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Phase 1&lt;/strong&gt;: Weather app, calculator, todo list with localStorage&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase 2&lt;/strong&gt;: Full-stack blog system, e-commerce cart, user authentication system&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase 3&lt;/strong&gt;: Mobile todo app, photo gallery app, simple mobile game&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase 4&lt;/strong&gt;: Social media app, real-time chat application, microservice architecture&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase 5&lt;/strong&gt;: Production-ready app with monitoring, testing, and CI/CD pipeline&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Deploying microservices into a Kubernetes cloud]]></title><description><![CDATA[Introduction
Welcome to the first post in our series exploring the pros and cons of deploying microservices into a Kubernetes cloud…]]></description><link>https://www.monkey.work/2025-02-04-k8s-deployments/</link><guid isPermaLink="false">https://www.monkey.work/2025-02-04-k8s-deployments/</guid><pubDate>Tue, 04 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;
Welcome to the first post in our series exploring the pros and cons of deploying microservices into a Kubernetes cloud.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Objective&lt;/strong&gt;
In this series, we aim to deploy a microservice architecture where all services are containerized using Docker and deployed to a managed Kubernetes cloud.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Auto-Scaling: Kubernetes’ auto-scaling features allow us to manage varying loads effectively by automatically adjusting the number of pods based on CPU/memory utilization or custom metrics.&lt;/li&gt;
&lt;li&gt;CI/CD Integration: A robust CI/CD process will be integrated into our deployment pipeline, * automating the build, test, and deployment processes for faster and more reliable releases.&lt;/li&gt;
&lt;li&gt;Monitoring and Logging: We will leverage Prometheus, Grafana, and Loki for monitoring and logging our services, enabling swift detection and diagnosis of issues.&lt;/li&gt;
&lt;li&gt;Configuration Management: ConfigMaps and Secrets will be used to manage configuration data separately from application code.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further reading&lt;/h2&gt;
&lt;p&gt;Once you’re comfortable with the high-level deployment goals, continue with the step-by-step guide in &lt;a href=&quot;/2025-08-22-kubernetes-cluster-setup-on-aws-eks/&quot;&gt;Kubernetes Cluster Setup on AWS EKS&lt;/a&gt; to provision a production-ready cluster and core addons.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Adding a local library to a monorepo]]></title><description><![CDATA[Intro
These concepts are fundamental to understanding modern software development practices. Here’s a brief overview: Repo (Repository): A…]]></description><link>https://www.monkey.work/2024-04-05-adding-local-library-to-monorepo/</link><guid isPermaLink="false">https://www.monkey.work/2024-04-05-adding-local-library-to-monorepo/</guid><pubDate>Fri, 05 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Intro&lt;/strong&gt;
These concepts are fundamental to understanding modern software development practices. Here’s a brief overview:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Repo (Repository): A storage location for software packages. Popular providers include GitHub and Bitbucket.&lt;/li&gt;
&lt;li&gt;Branch: A version of a repository, essentially a smart copy that allows for parallel development.&lt;/li&gt;
&lt;li&gt;JavaScript Libraries: Facilitate code re-use by providing packaged code that can be used across multiple projects.&lt;/li&gt;
&lt;li&gt;Monorepo: A repository that contains multiple projects or modules, simplifying dependency management and code sharing.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;my-monorepo/
  ├── project1/
  │   ├── src/
  │   ├── package.json
  ├── project2/
  │   ├── src/
  │   ├── package.json
  ├── shared-component/
  │   ├── src/
  │   ├── package.json
  ├── package.json&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In theory, using a monorepo makes it easier to manage dependencies, but in practice, adding a local shared library to a TypeScript monorepo turned out to be a minor nightmare.&lt;/p&gt;
&lt;p&gt;In our case we already had a monorepo and a shared library that was stored in a different repo and we wanted to move it into the main monorepo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The problem we were trying to solve&lt;/strong&gt;
We certainly follow the “don’t touch it if it works” principle, but only until it makes sense to do so. The biggest problem of having a shared component in a separate repository was that if a developer was working on a feature in his local branch and had to modify the shared library, then he needed to create a separate local branch in the shared library. The issue was that he could not publish it until all the branches were merged. So what developers ended up doing was to modify and publish the shared library before they were ready to commit and merge the feature branch. In many cases, the developer refactor their code and rollback the changes they made to the shared library, and then introduce new and breaking changes to the library.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The problem we encountered&lt;/strong&gt;
So we moved our shared library into a folder named &lt;code class=&quot;language-text&quot;&gt;shared-component&lt;/code&gt;.
It all worked beautifully until we decided to re-map imports to lookup locations.
In other words, to switch our imports from:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inspire &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;../../shared-component/src/shared-function&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;to:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inspire &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@shared-component/shared-function&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;we also added it to project1’s &lt;code class=&quot;language-text&quot;&gt;tsconfig.json&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token string-property property&quot;&gt;&quot;compilerOptions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token string-property property&quot;&gt;&quot;resolveJsonModule&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string-property property&quot;&gt;&quot;paths&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* A series of entries which re-map imports to lookup locations relative to the &apos;baseUrl&apos;. */&lt;/span&gt;
         &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
         &lt;span class=&quot;token string-property property&quot;&gt;&quot;@shared-component&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
               &lt;span class=&quot;token string&quot;&gt;&quot;../shared-component/src/index.ts&quot;&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;include&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;src/**/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;../shared-component/src/**/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and immediately received the following error:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;Cannot find &lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt; or its corresponding &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;declarations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Implementing some of the advices on &lt;a href=&quot;https://stackoverflow.com/questions/64732623/typescript-cannot-find-module-or-its-corresponding-type-declarations&quot;&gt;stackoverflow&lt;/a&gt; did not yield any results.&lt;/p&gt;
&lt;p&gt;After a couple of hours of search and a consultation with deep blue AI we were ready to give up. Just kidding. We found a &lt;a href=&quot;https://www.devxperiences.com/pzwp1/2021/11/21/how-to-solve-the-problem-with-typescript-unresolved-path-aliases-in-transpiled-js-files/&quot;&gt;post&lt;/a&gt; that explained the error.&lt;/p&gt;
&lt;p&gt;Apparently, tsc compiler (transpiler) does not resolve/emit correctly the path aliases to the output JavaScript .js files. Translated to human language it means that transpiler has a bug that it is not going to fix any time soon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The solution&lt;/strong&gt;
Since we did not want to write our own script that crawls through all the project files and remaps the imports, we decided to use a Javascript &lt;a href=&quot;https://github.com/ilearnio/module-alias&quot;&gt;library&lt;/a&gt; that could do it for us.&lt;/p&gt;
&lt;p&gt;Add the library to your &lt;code class=&quot;language-text&quot;&gt;project1&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; i &lt;span class=&quot;token parameter variable&quot;&gt;--save&lt;/span&gt; module-alias&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Also add the following code to &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;project1&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;project1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;_moduleAliases&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@root&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@shared-component&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../shared-component&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And finally add &lt;code class=&quot;language-text&quot;&gt;require(&apos;module-alias/register&apos;)&lt;/code&gt; to the main file of the app:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;module-alias/register&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ts-node&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  transpileOnly&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Windscribe VPN on OpenWRT Linksys MR8300]]></title><description><![CDATA[In this article we will be setting up an OpenVPN client on OpenWrt. OpenWrt is an open-source project for embedded operating systems based…]]></description><link>https://www.monkey.work/2024-02-25-windscribe-vpn-with-openwrt/</link><guid isPermaLink="false">https://www.monkey.work/2024-02-25-windscribe-vpn-with-openwrt/</guid><pubDate>Sun, 25 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In this article we will be setting up an OpenVPN client on OpenWrt.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;OpenWrt is an open-source project for embedded operating systems based on Linux, primarily used on embedded devices to route network traffic.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is fairly easy to install OpenWRT on MR8300 router.
All we need to do is to follow the &lt;a href=&quot;https://openwrt.org/toh/linksys/mr8300&quot;&gt;official guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Installing Windscribe on OpenWRT is not a simple task though.
The only (mostly) working &lt;a href=&quot;https://ryanrudolfoba.com/blog/2019-07-26-windscribe-vpn-on-openwrt-wdr3600/&quot;&gt;guide&lt;/a&gt; has been removed from the net but can still be found in the &lt;a href=&quot;https://web.archive.org/web/20190813001443/https://ryanrudolfoba.com/blog/2019-07-26-windscribe-vpn-on-openwrt-wdr3600/&quot;&gt;archives&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;requirements&quot;&gt;Requirements&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;A router running OpenWrt (22.03 or 23.05 for MR8300 box)&lt;/li&gt;
&lt;li&gt;Windscribe Pro account&lt;/li&gt;
&lt;li&gt;ssh access to the router&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;openvpn-installation&quot;&gt;OpenVPN Installation&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Login / ssh to the router.
&lt;code class=&quot;language-text&quot;&gt;ssh root@192.168.1.1&lt;/code&gt;
or
&lt;code class=&quot;language-text&quot;&gt;ssh root@openwrt.lan&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Update package lists
&lt;code class=&quot;language-text&quot;&gt;opkg update&lt;/code&gt;
Install luci, if needed
&lt;code class=&quot;language-text&quot;&gt;opkg install luci&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Install required OpenVPN packages
&lt;code class=&quot;language-text&quot;&gt;opkg install luci-app-openvpn&lt;/code&gt;
&lt;code class=&quot;language-text&quot;&gt;opkg install openvpn-openssl&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;If luci is already installed, the same steps can be performed via the UI.&lt;/li&gt;
&lt;li&gt;System -&gt; Software&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;firewall-configuration&quot;&gt;Firewall Configuration&lt;/h3&gt;
&lt;p&gt;We will do it by adding the TUN device to an existing &lt;a href=&quot;https://openwrt.org/docs/guide-user/services/vpn/openvpn/client&quot;&gt;WAN zone&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;uci &lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; firewall.@zone&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.device&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tun0&quot;&lt;/span&gt;
uci commit firewall
&lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt; firewall restart&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;@zone[0] is lan and @zone[1] is wan
A cooler way of doing the same thing would be:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;uci &lt;span class=&quot;token function&quot;&gt;rename&lt;/span&gt; firewall.@zone&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;wan&quot;&lt;/span&gt;
uci del_list &lt;span class=&quot;token assign-left variable&quot;&gt;firewall.wan.device&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tun0&quot;&lt;/span&gt;
uci add_list &lt;span class=&quot;token assign-left variable&quot;&gt;firewall.wan.device&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tun0&quot;&lt;/span&gt;
uci commit firewall
&lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt; firewall restart&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;download-openvpn-profile&quot;&gt;Download OpenVPN profile&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;a href=&quot;https://windscribe.com/getconfig/openvpn&quot;&gt;https://windscribe.com/getconfig/openvpn&lt;/a&gt; (must be a pro user)&lt;/li&gt;
&lt;li&gt;Choose a location in the dropdown&lt;/li&gt;
&lt;li&gt;Select &lt;code class=&quot;language-text&quot;&gt;UDP&lt;/code&gt; for the protocol&lt;/li&gt;
&lt;li&gt;Select &lt;code class=&quot;language-text&quot;&gt;443&lt;/code&gt; for the port&lt;/li&gt;
&lt;li&gt;Select &lt;code class=&quot;language-text&quot;&gt;2.4.6 or newer for OpenVPN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Download the config and save under some name
For exmaple &lt;code class=&quot;language-text&quot;&gt;Windscribe-Ashdod-YamPark.ovpn&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Get the credentials&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;username: qsano128-yoluknz
password: 4zrfc6e2ua&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;install-the-profile&quot;&gt;Install the profile&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;From router’s luci UI go to &lt;code class=&quot;language-text&quot;&gt;Services &gt; OpenVPN&lt;/code&gt;
&lt;a href=&quot;http://192.168.1.1/cgi-bin/luci/admin/vpn/openvpn&quot;&gt;http://192.168.1.1/cgi-bin/luci/admin/vpn/openvpn&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the &lt;code class=&quot;language-text&quot;&gt;OVPN configuration file upload&lt;/code&gt; section choose the profile downloaded in the previous step.
For example: &lt;code class=&quot;language-text&quot;&gt;Windscribe-Ashdod-YamPark.ovpn&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enter a name (&lt;code class=&quot;language-text&quot;&gt;windscribe-ashdod&lt;/code&gt; in our case) into the &lt;code class=&quot;language-text&quot;&gt;Instance name&lt;/code&gt; text field.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click &lt;code class=&quot;language-text&quot;&gt;Upload&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Under OpenVPN instances, it will now show an entry for &lt;code class=&quot;language-text&quot;&gt;windscribe-ashdod&lt;/code&gt;. Put a checkmark on Enabled. This will enable and start the connection later when we click on &lt;code class=&quot;language-text&quot;&gt;Save &amp;amp; Apply&lt;/code&gt; button.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now we need to update the profile. Select &lt;code class=&quot;language-text&quot;&gt;Edit&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;windscribe-ashdod&lt;/code&gt; configuration will appear.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Find the line that reads &lt;code class=&quot;language-text&quot;&gt;auth-user-pass&lt;/code&gt; and change to
&lt;code class=&quot;language-text&quot;&gt;auth-user-pass /etc/openvpn/windscribe_ashdod.auth&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Also add following configuration:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt; script-security &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
 up &lt;span class=&quot;token string&quot;&gt;&quot;/etc/openvpn/updns&quot;&lt;/span&gt;
 down &lt;span class=&quot;token string&quot;&gt;&quot;/etc/openvpn/downdns&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add username &amp;#x26; password to the second session.
For example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt; qsano128-yoluknz
 4zrfc6e2ua&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;please note that the first line is the username and the second line is the password.&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;code class=&quot;language-text&quot;&gt;Save&lt;/code&gt; button&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;add-start-up-scripts&quot;&gt;Add Start-up Scripts&lt;/h3&gt;
&lt;p&gt;Login / ssh to the router and enter the following commands:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create the first script&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;
cat&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;EOF&apos;&lt;span class=&quot;token bash punctuation&quot;&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; /etc/openvpn/updns&lt;/span&gt;
#!/bin/sh
mv /tmp/resolv.conf.auto /tmp/resolv.conf.auto.hold
echo $foreign_option_1 | sed -e &apos;s/dhcp-option DOMAIN/domain/g&apos; -e &apos;s/dhcp-option DNS/nameserver/g&apos; &gt;/tmp/resolv.conf.auto
echo $foreign_option_2 | sed -e &apos;s/dhcp-option DOMAIN/domain/g&apos; -e &apos;s/dhcp-option DNS/nameserver/g&apos; &gt;&gt; /tmp/resolv.conf.auto
echo $foreign_option_3 | sed -e &apos;s/dhcp-option DOMAIN/domain/g&apos; -e &apos;s/dhcp-option DNS/nameserver/g&apos; &gt;&gt; /tmp/resolv.conf.auto
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the second script&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;
cat&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;EOF&apos;&lt;span class=&quot;token bash punctuation&quot;&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; /etc/openvpn/downdns&lt;/span&gt;
#!/bin/sh
mv /tmp/resolv.conf.auto.hold /tmp/resolv.conf.auto
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;chmod 755 /etc/openvpn/updns&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;chmod 755 /etc/openvpn/downdns&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This part is a bit tricky as you need to know how to use a command line editor:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;vi /etc/config/dhcp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;add this line (properly aligned) at the end of the &lt;code class=&quot;language-text&quot;&gt;config dhcp &apos;lan&apos;&lt;/code&gt; section:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;list dhcp_option &apos;6,10.255.255.1,208.67.222.222,208.67.220.220&apos;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;apply-openvpn-settings&quot;&gt;Apply OpenVPN settings&lt;/h3&gt;
&lt;p&gt;This is a short but an absolutely critical step.
Click on &lt;code class=&quot;language-text&quot;&gt;Save Apply&lt;/code&gt; button &lt;a href=&quot;http://192.168.1.1/cgi-bin/luci/admin/vpn/openvpn&quot;&gt;on OpenVPN UI page&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;note: hopefully you did not forget to Put a checkmark on &lt;code class=&quot;language-text&quot;&gt;Enabled&lt;/code&gt; field in the &lt;code class=&quot;language-text&quot;&gt;Install the profile&lt;/code&gt; step.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We are done, almost!&lt;/p&gt;
&lt;h3 id=&quot;test-it&quot;&gt;Test It&lt;/h3&gt;
&lt;p&gt;We need to deploy our QA skills now.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;a href=&quot;https://ipleak.net/&quot;&gt;ipleak.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Please make sure that you are not seeing your ISPs IP address there.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we are done. Have questions - please shoot me an email (qas @ blog domain).&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;You are still here? Why?&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;OVPN config file (/etc/openvpn/windscribe_ashdod.ovpn)&lt;/code&gt; file (excluding the certificate):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;client
dev tun
proto udp
remote tlv-218.whiskergalaxy.com &lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt;
verify-x509-name tlv-218.windscribe.com name

nobind
auth-user-pass /etc/openvpn/windscribe_ashdod.auth

resolv-retry infinite

script-security &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
up &lt;span class=&quot;token string&quot;&gt;&quot;/etc/openvpn/updns&quot;&lt;/span&gt;
down &lt;span class=&quot;token string&quot;&gt;&quot;/etc/openvpn/downdns&quot;&lt;/span&gt;

cipher AES-256-GCM
ncp-ciphers AES-256-GCM:AES-256-CBC:AES-128-GCM
auth SHA512

verb &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
mute-replay-warnings
remote-cert-tls server
persist-key
persist-tun

key-direction &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ca&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Nothing is easy, but who wants nothing?&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[Do not trust all certificates]]></title><description><![CDATA[Mobile app security is definitely an area where paranoia makes sense.
A not so hypothetical scenario; we are trying to make a get API call…]]></description><link>https://www.monkey.work/2021-12-30-do-not-trust-all/</link><guid isPermaLink="false">https://www.monkey.work/2021-12-30-do-not-trust-all/</guid><pubDate>Thu, 30 Dec 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Mobile app security is definitely an area where paranoia makes sense.
A not so hypothetical scenario; we are trying to make a get API call and are getting the following error:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;security&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cert&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CertPathValidatorException&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Trust anchor &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; certification path not found&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We panic. Search for help and find the following solution:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; TrustAllTrustManager &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; X509TrustManager &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@SuppressLint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;TrustAllX509TrustManager&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkClientTrusted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;chain&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Array&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;out&lt;/span&gt; X509Certificate&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; authType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation builtin&quot;&gt;@SuppressLint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;TrustAllX509TrustManager&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkServerTrusted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;chain&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Array&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;out&lt;/span&gt; X509Certificate&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; authType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getAcceptedIssuers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Array&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;X509Certificate&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;emptyArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Should we implement this solution in our code?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Absolutely not!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Trusting all certificates allows anybody to do a man in the middle attack.
So what should we do instead?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.android.com/training/articles/security-config&quot;&gt;We should add a Network Security Configuration file.&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add &lt;code class=&quot;language-text&quot;&gt;res/xml/network_security_config.xml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token prolog&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;network-security-config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;domain-config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;domain&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;includeSubdomains&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;monkey.work&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;domain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;trust-anchors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;certificates&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@raw/trusted_roots&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;trust-anchors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;domain-config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;network-security-config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;To the &lt;code class=&quot;language-text&quot;&gt;application&lt;/code&gt; section of &lt;code class=&quot;language-text&quot;&gt;AndroidManifest.xml&lt;/code&gt; add&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;    &amp;lt;application
        android:networkSecurityConfig=&quot;@xml/network_security_config&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Add &lt;code class=&quot;language-text&quot;&gt;res/raw/trusted_roots&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The file may contain multiple certificates&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;To extract all certificates from a &lt;a href=&quot;https://stackoverflow.com/questions/6825226/trust-anchor-not-found-for-android-ssl-connection&quot;&gt;server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; openssl s_client &lt;span class=&quot;token parameter variable&quot;&gt;-showcerts&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-servername&lt;/span&gt; monkey.work &lt;span class=&quot;token parameter variable&quot;&gt;-connect&lt;/span&gt; monkey.work:443 &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;token function&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; monkey.work.pem&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Dependency Injection in Swift: Why and How to Inject Dependencies]]></title><description><![CDATA[Just like any fake science, computer science has plenty of concepts with complicated names that in reality are very simple.
Dependency…]]></description><link>https://www.monkey.work/2021-01-16-dependency-injection/</link><guid isPermaLink="false">https://www.monkey.work/2021-01-16-dependency-injection/</guid><pubDate>Sat, 16 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Just like any fake science, computer science has plenty of concepts with complicated names that in reality are very simple.
Dependency injection is just one of them.
If we have class A that uses (depends on) class B here are two ways we implement it:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;callA&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;runAVeryLongNetworkingCall&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 2&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; a
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;callA&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;runAVeryLongNetworkingCall&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Obviously 1 is much shorter than 2 so why do we even need 2?
Because in 2, by passing the class in the constructor, we do what is called a dependency injection and it does have some advantages especially when it comes to testing. Let’s explore them.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;AP&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;runAVeryLongNetworkingCall&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;AP&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;runAVeryLongNetworkingCall&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        defaultSession&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dataTask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AMock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;AP&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doAVeryLongNetworkingCall&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;simulateANetworkingCall&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
```swift
&lt;span class=&quot;token class-name&quot;&gt;If&lt;/span&gt; we were not able to inject a different implementation of `&lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;` &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;`&lt;span class=&quot;token class-name&quot;&gt;AMock&lt;/span&gt;`&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; then when testing `callA` function &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; `&lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;` we would have to wait &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; a very long networking call to complete&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;That&lt;/span&gt; of course &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; not desirable since there &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; no need to test networking when testing `&lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;`&apos;s functionality&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;In&lt;/span&gt; short&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; testing we can use the mocked version of &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;

```swift
&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testUsingDependencyInjection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AMock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;callA&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;XCTAssertTrue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So being able to inject dependencies is beneficial for testing.&lt;/p&gt;
&lt;p&gt;What if we have 30 dependencies?
It is not always a good idea to inject all the dependencies through the constructor.
In real life we use frameworks like &lt;a href=&quot;https://github.com/Swinject/Swinject&quot;&gt;Swinject&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Swinject is just a container (an array or some other data structure) that contains all our dependencies so that, for example, we can pass the container in the constructor instead of all the dependencies.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[XCTestCase wait Helper: Wait for Conditions Without Manual Expectations]]></title><description><![CDATA[Here is a simple extension that makes it possible to wait for a condition until it is fulfilled or timed-out. Let’s talk about the code.
The…]]></description><link>https://www.monkey.work/2021-01-12-wait-for-condition/</link><guid isPermaLink="false">https://www.monkey.work/2021-01-12-wait-for-condition/</guid><pubDate>Tue, 12 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here is a simple extension that makes it possible to wait for a condition until it is fulfilled or timed-out.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;XCTestCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;/// Wait for a condition to be fulfilled&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// Example:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;///     var fulfilled = false&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;///     wait(fulfilled)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; condition&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token attribute atrule&quot;&gt;@autoclosure&lt;/span&gt; &lt;span class=&quot;token attribute atrule&quot;&gt;@escaping&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        timeout&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TimeInterval&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        description&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;wait condition expectation&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; expectation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expectation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testForCondition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                expectation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fulfill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;testForCondition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token function&quot;&gt;testForCondition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token function&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;expectation&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; timeout&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; timeout&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let’s talk about the code.
The condition is passed as a closure.
&lt;code class=&quot;language-text&quot;&gt;@autoclosure&lt;/code&gt;is a syntactic sugar so that instead of &lt;code class=&quot;language-text&quot;&gt;wait({ a==b })&lt;/code&gt; we can write &lt;code class=&quot;language-text&quot;&gt;wait(a==b)&lt;/code&gt;
&lt;code class=&quot;language-text&quot;&gt;@escaping&lt;/code&gt; tells the compiler that the closure is retained.&lt;/p&gt;
&lt;p&gt;Another line we should pay attention to:
&lt;code class=&quot;language-text&quot;&gt;DispatchQueue.main.async { testForCondition() }&lt;/code&gt;
The only reason we need &lt;code class=&quot;language-text&quot;&gt;DispatchQueue.main.async&lt;/code&gt; is that it makes sure that &lt;code class=&quot;language-text&quot;&gt;testForCondition&lt;/code&gt; is executed once per run-loop.&lt;/p&gt;
&lt;p&gt;And finally the usage:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testWaitForCondition1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; fulfilled &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; subject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PassthroughSubject&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    subject
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debounce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;milliseconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  scheduler&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;receiveCompletion&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;finished&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; receiveValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
            fulfilled &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;tasks&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    subject&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    subject&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fulfilled&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The code can be found in the &lt;a href=&quot;https://github.com/seifeet/SwiftFormScrolling/blob/main/FormScrollingiOS14Tests/Extensions/XCTest%2BExtensions.swift&quot;&gt;XCTestCase wait helper extension on GitHub&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[When to Use [weak self] in Swift Closures (and When You Don’t Need It)]]></title><description><![CDATA[Should one always use [weak self] to avoid retain cycles? It has become pretty common among Swift experts to always use  inside of a closure…]]></description><link>https://www.monkey.work/2021-01-01-weak-self-abuse/</link><guid isPermaLink="false">https://www.monkey.work/2021-01-01-weak-self-abuse/</guid><pubDate>Fri, 01 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Should one always use [weak self] to avoid retain cycles?&lt;/p&gt;
&lt;p&gt;It has become pretty common among Swift &lt;em&gt;experts&lt;/em&gt; to always use &lt;code class=&quot;language-text&quot;&gt;[weak self]&lt;/code&gt; inside of a closure to avoid retain cycles.&lt;/p&gt;
&lt;p&gt;But is &lt;code class=&quot;language-text&quot;&gt;[weak self]&lt;/code&gt; needed or even recommended inside of all closures?&lt;/p&gt;
&lt;p&gt;The answer is NO.&lt;/p&gt;
&lt;p&gt;First of all &lt;code class=&quot;language-text&quot;&gt;[weak self]&lt;/code&gt; is only required for &lt;strong&gt;escaping&lt;/strong&gt; closures that capture &lt;code class=&quot;language-text&quot;&gt;self&lt;/code&gt;. Escaping closures is just a fancy name for closures that get stored and do not get executed immediately, unlike the non-escaping ones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Non-escaping&lt;/strong&gt; closures do not pose a risk of introducing strong reference cycles, and therefore do not require the use of &lt;code class=&quot;language-text&quot;&gt;weak&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;unowned&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;While not using &lt;code class=&quot;language-text&quot;&gt;[weak self]&lt;/code&gt; in non-escaping closures does not cause reference cycles, it does cause &lt;strong&gt;delayed deallocation&lt;/strong&gt; (which is not necessary a bad thing and can even be beneficial if one wants the code to finish executing before the object is killed). For example, a long operation inside of a closure can cause &lt;code class=&quot;language-text&quot;&gt;self&lt;/code&gt;  to stay in memory until the operation is completed.&lt;/p&gt;
&lt;p&gt;One last thing to note is that using [weak self] with &lt;code class=&quot;language-text&quot;&gt;guard let self = self&lt;/code&gt; (vs. &lt;code class=&quot;language-text&quot;&gt;self?&lt;/code&gt;) can also, for the same reasons as above, cause a &lt;strong&gt;delayed deallocation&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;And truly the last thing, using &lt;code class=&quot;language-text&quot;&gt;[unowned self]&lt;/code&gt; is almost always a bad idea.&lt;/p&gt;
&lt;p&gt;Want to learn more? &lt;a href=&quot;https://medium.com/flawless-app-stories/you-dont-always-need-weak-self-a778bec505ef&quot;&gt;This in-depth article on when you don’t always need weak self&lt;/a&gt; is a great read.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ghost: why this blog no longer runs on it]]></title><description><![CDATA[We started using Ghost when it was still in its very early stages. Hosting Ghost was not always easy; upgrades would fail once in a while…]]></description><link>https://www.monkey.work/2020-12-11-ghost-the-final-chapter/</link><guid isPermaLink="false">https://www.monkey.work/2020-12-11-ghost-the-final-chapter/</guid><pubDate>Fri, 11 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We started using Ghost when it was still in its very early stages.&lt;/p&gt;
&lt;p&gt;Hosting Ghost was not always &lt;a href=&quot;/2015-01-16-ghost-update/&quot;&gt;easy&lt;/a&gt;; upgrades would fail once in a while and it lacked many features. It was not perfect but it did allow hosting a blog without doing any coding.&lt;/p&gt;
&lt;p&gt;Email subscriptions were introduced to Ghost as an experimental feature. We enabled them and our user base has been continuously growing until Ghost suddenly removed the feature. Membership was introduced to replace subscriptions. It was buggy and few themes supported it. In addition and even though we did not want to monetize this blog, membership did require us to open an account with Stripe.&lt;/p&gt;
&lt;p&gt;We patiently waited for a year hoping that email subscriptions would come back but unfortunately Ghost doubled down on membership. It did make financial sense for Ghost but not for this blog so we have started search for a new platform.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[UIScrollView with Auto Layout in Code: Step-by-Step Guide for iOS 12+]]></title><description><![CDATA[It is fairly easy to use UIScrollView with the AutoLayout on iOS 12+ but only if one follows the following steps: Add UIScrollView and pin…]]></description><link>https://www.monkey.work/2020-11-08-uiscrollview/</link><guid isPermaLink="false">https://www.monkey.work/2020-11-08-uiscrollview/</guid><pubDate>Sun, 08 Nov 2020 19:04:33 GMT</pubDate><content:encoded>&lt;p&gt;It is fairly easy to use UIScrollView with the AutoLayout on iOS 12+ but only if one follows the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add UIScrollView and pin it 0,0,0,0 to superview (or your desired size)&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;NSLayoutConstraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    scrollView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;topAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;safeAreaLayoutGuide&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;topAnchor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    scrollView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bottomAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;safeAreaLayoutGuide&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bottomAnchor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    scrollView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leadingAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;safeAreaLayoutGuide&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leadingAnchor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    scrollView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;trailingAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;safeAreaLayoutGuide&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;trailingAnchor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Add UIView in ScrollView, pin it 0,0,0,0 to all 4 sides.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;NSLayoutConstraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;topAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; scrollView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contentLayoutGuide&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;topAnchor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bottomAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; scrollView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contentLayoutGuide&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bottomAnchor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leadingAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; scrollView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contentLayoutGuide&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leadingAnchor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;trailingAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; scrollView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contentLayoutGuide&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;trailingAnchor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Center contentView X and Y and set Y’s priority to low. Set contentView height to greaterThanOrEqualTo it’s parent height.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; contentViewCenterY &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;centerYAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; scrollView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;centerYAnchor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
contentViewCenterY&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;priority &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultLow

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; contentViewHeight &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;heightAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;greaterThanOrEqualTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;heightAnchor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
contentViewHeight&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;priority &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultLow

&lt;span class=&quot;token class-name&quot;&gt;NSLayoutConstraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;centerXAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; scrollView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;centerXAnchor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    contentViewCenterY&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    contentViewHeight
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Add all views that you need into this view. Don’t forget to set the bottom constraint on the lowest view.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The source code can be found here:
&lt;a href=&quot;https://github.com/seifeet/SwiftFormScrolling&quot;&gt;seifeet/SwiftFormScrolling&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Create a Swift Framework That Uses C Code in Xcode]]></title><description><![CDATA[Step 1:  Create a Cocoa Touch Framework File -> New -> Project -> Cocoa Touch Framework  Name it SwiftCFramework Step 2: Create xcconfig…]]></description><link>https://www.monkey.work/2019-06-21-swift-framework-with-c-code/</link><guid isPermaLink="false">https://www.monkey.work/2019-06-21-swift-framework-with-c-code/</guid><pubDate>Fri, 21 Jun 2019 21:54:33 GMT</pubDate><content:encoded>&lt;p&gt;Step 1:  Create a Cocoa Touch Framework&lt;/p&gt;
&lt;p&gt;File -&gt; New -&gt; Project -&gt; Cocoa Touch Framework&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1480px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/de1de18f7a5959d6b9b9bc96e9016d59/163ec/1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAACnUlEQVR42l1UyXLbRhTkT9iWJZmLSIDYBpgNg40kQMlJpPikVFzlQ74grkrl4vI9n5Rbjvmyds9AUlw5dE3P8M3D60YTi9Y5TA+/YhpH3J3PGI9HnMdT4K2rYZWCLAUqIcL6zD20sdDaQkrWKA2jNRZKO5hfvuI8ndjAwqgKjdWotURb23Am8gQiS1A8YeYp7DDBDGdIN0C2RxjXYWHKAmNdYmwdOl3ClRlcVaBlQ8+bKoeTIqCVRYDnDdemykKNyuK5jsMsNBtm2w1evXqDLNqh2u8gkgSVqnm+hkoj3KzW2KxvUO63qJII19fX2K5XrN1C5SnW6w12yytYWVKyKHDidLeHFqfGhgtGpDg6E7jHj9MBP7+fINM4wPOpb1CRj11D3mLiWis5N1TZHvvNKlz2FwSnzKN5Gkl0VhE6cF/bWUPZ5Uv9jD1MJeaGJX94/foCKaXrnM1vNri6vKTEHQsjbFZLrJarYIdv+vbiLTbv3tGO+EVFUBYaFjnl2TD2gTKL6Aa9UbSggy4SWJFRlpsl0W9TpHh/7GlTHZr4hz5DM1ILy65ja3HbsSnhec+3PRC9EuiI53333b7/bh/OCMdkMDY5hrbDcHrAoT9idMwgz3ycakYigDEyIv9vL55Q/g8hNpSVJxkuNxJJnKPcLZHutoijfZDvfcviGEkUh5jM8qInzFIFuUeQnMoTLj79jeL3f3H12z/YqnvcthI/nUcGveIL29EKRyvq8MLmLO4CyqcHyHgNGS2pKsNCKgf54U+MH79A3P8BoQYaTj8PA1pGxQe/ZkQ8vN+mFLMd3Df0rJYVtD1AN3f8bzdYOE7xeH/E48OEk83gPVVVCck4aa6GlwwD6z8Sz1yzif9w3FHF0PcQP3yGfPwLov2Ab730t/EgZaMSAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Xcode new project dialog highlighting Cocoa Touch Framework template&quot;
        title=&quot;&quot;
        src=&quot;/static/de1de18f7a5959d6b9b9bc96e9016d59/163ec/1.png&quot;
        srcset=&quot;/static/de1de18f7a5959d6b9b9bc96e9016d59/0eb09/1.png 500w,
/static/de1de18f7a5959d6b9b9bc96e9016d59/1263b/1.png 1000w,
/static/de1de18f7a5959d6b9b9bc96e9016d59/163ec/1.png 1480w&quot;
        sizes=&quot;(max-width: 1480px) 100vw, 1480px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Name it SwiftCFramework&lt;/p&gt;
&lt;p&gt;Step 2: Create xcconfig file:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3f7d1f9afce623493f2dba173b1f9370/f97d7/2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABwElEQVR42m2RW5KTQBSG2YxDLgRoLmkaGgjNJYSQgSSmRitaWvpgOS/uwzeX4SpcgTuazO9prPHJh69OF3R/3ec/RqMK3LcNDrVC31SolYJMUtQbib6tsW9bbKsS27KEyjPkSQIZRQiYCx74OPYd+q57rpR66prqZvSjwvDQQFZrhKED07RhuxwO+8t86cGc23h1tySsqd6ZFszZaqo+83A5jfj0/orheIFxPm3x+csV43mHPBMkTcFFgSguCQWRKKyjDfxAIgg16bRmXgyXxbc4Erjv2l/fHh+/DqeHH8bQlrheXqPOcnDGYDshHE/A9RPYdMihqmFaQjKX/jlTBxFWdniTcYK6yH9Lwb+nPPhpHJoE50NHmeWIwwCWxSBkiThrEKX1RCAUyekCX8Jy1pgvXIrCpShcbDLKelsijThEwGBkgqPKUhQypg8+FkuGQm0xjhfsdgP2+wHt7oCm6SBlgSyvYK38SWrOHGRS0sCK5zwRT+uQ3wzuM0QE92hqNLklCZeW9w99WKPX+jLN9EItnDtIKMM3FNnHd29xvHyAIQIPSehDVy1f0MYZbfwfL6IXZtRyQFNWeY6CuqRX4g+FegnAqR/z9gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Finder save dialog creating a new xcconfig file&quot;
        title=&quot;&quot;
        src=&quot;/static/3f7d1f9afce623493f2dba173b1f9370/f97d7/2.png&quot;
        srcset=&quot;/static/3f7d1f9afce623493f2dba173b1f9370/0eb09/2.png 500w,
/static/3f7d1f9afce623493f2dba173b1f9370/1263b/2.png 1000w,
/static/3f7d1f9afce623493f2dba173b1f9370/f97d7/2.png 2000w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Xcode build configuration files, more commonly known by their &lt;code class=&quot;language-text&quot;&gt;[xcconfig](https://nshipster.com/xcconfig/)&lt;/code&gt; file extension, allow build settings for your app to be declared and managed without Xcode.&lt;/p&gt;
&lt;p&gt;Step 3: Add xcconfig to the Configurations section of the Project Info&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1729px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/883992a32d910b650f7dc4f5a65dc858/a1313/3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.19999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB+ElEQVR42oWT226bQBRF/R2JlNjcmRmGO9iAC47tOH5I2n5C04eoj5b6/7vnDMKxk6p9WBo0B/a5bWY6ziHDAFoppEpABR4Cx0Lo2hAe4XsQQYBcK5RpTHfOP5m1/RZD32NZZEh0hDyO0C4rrMrciBth36VnF1Ho/19wWK/wdNhi3axITMOz5ohliDpPkScamZbQUQJfFvCtO/i2RSzMe3xyNxOcfNYUCsdth7aukFDL3Nama8zJFXJVjDKnhzSSJklByTixopHwe4ypMHAdMDwvbzFHQ8K7/d60znASFkkjQUIK+4cBx8cdtv0aVZac4xzjxLPQ5VLHcrlsn543dY3D0FGlK6TSh3TnBuUu0FYZ6lRDeRaEc3+OMYK+nbHQCFVqUyAuqcKv2Gwe0fZ76LyDR/MbKdH1T+iGA6KshSeKq1gQyGvB0L7Dff6C6njCl+8n1N9+wxnecFP/MNwuX1E8nxAffmHe/cTN6hW3Boo3b7B1fyk4tiyolWWm0bF1ihhF1ZgZFTokLwpIiocOt7f4RHjd8ijIQx7WnVlOQovQOkUk6BShIXBsw988aGxzKcjeYlPvhrXZcJFEpire8LhliYeeLLYszfMleXze8rsgGzUjIbYD+4zNzXbg7JL+FoZjfM8fT3cTxoefZ/he/nR+TMp8vJ/4A5FwjPA6qpgBAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Xcode project info showing custom xcconfig configuration selection&quot;
        title=&quot;&quot;
        src=&quot;/static/883992a32d910b650f7dc4f5a65dc858/a1313/3.png&quot;
        srcset=&quot;/static/883992a32d910b650f7dc4f5a65dc858/0eb09/3.png 500w,
/static/883992a32d910b650f7dc4f5a65dc858/1263b/3.png 1000w,
/static/883992a32d910b650f7dc4f5a65dc858/a1313/3.png 1729w&quot;
        sizes=&quot;(max-width: 1729px) 100vw, 1729px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Step 4: Add swiftcframework.mapmodule and public.mapmodule files to the project&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1890px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/982077f8567e0d2d2faea24cf493c973/6c5d7/4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.999999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABYlAAAWJQFJUiTwAAABRklEQVR42mXQzY7TMBSG4d4Li2kc58dObMdx2ibxkFaiQgI6IJBGFcOCHWt2rFmy515f3IIAzSw+WfLReXyOV+9PJ14djxxiZJknvDXkUqMqS9tOKDPTmIjunqPDgvK3NMMBFfYUdqZ0kbKLCNmQZRWr+/szcd4T455xvMWYkIqapnTMMjBdInrGamDTj2z7HfNmZnADZVaSrQvWKZmoUmpWH77+ZPn8g+njd+rX33gWvyAvrxWKKm/YFgNGOnrpaUSDkypFE4oGX7bIXCdIXbEr+OnhgbvTW3abHb4LWOuvK4tLY+44lCNae5Q0qMIiEnST1dyI31n/gf6B5zMvjy+I40joOjpr/4JtbpiLDW3t2Jaevuqo01SXvxKpWTzCruC7uzfsl4Vp3BG8T6BLK2sqZVF5S5B9OjWu1Jgi3efqyVT/g78ATfbFlQWeCF4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Xcode file navigator with module map files added to the framework&quot;
        title=&quot;&quot;
        src=&quot;/static/982077f8567e0d2d2faea24cf493c973/6c5d7/4.png&quot;
        srcset=&quot;/static/982077f8567e0d2d2faea24cf493c973/0eb09/4.png 500w,
/static/982077f8567e0d2d2faea24cf493c973/1263b/4.png 1000w,
/static/982077f8567e0d2d2faea24cf493c973/6c5d7/4.png 1890w&quot;
        sizes=&quot;(max-width: 1890px) 100vw, 1890px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1726px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7a733ba54c7ab341360c31e6d77781d0/dd200/5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 29.799999999999997%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABYlAAAWJQFJUiTwAAABT0lEQVR42m2Q227TQBRF/TOAL+Ox48v4NvEliV3SSlUTlaZASqAgqFSplfh/aXFckHiAh619NNpa5+xx9h9OHA+3HK52XEwT22nEViWhTtA6J40bkmLDIutJzYbEvmVRz372MkflGpX36EI8aXDu3rXc371n0/UM1tI3liLNCMOEKDJ0Qc1KNfTiG2WxxZK26ehsT9fIkjhH+RGuG+J6Gufq8pyfz098+/yJL8cjN7sd6+VSYCkqTHElXKuaKigpVInxDXkQU6rkRbXOyCXn+ws8P8Y57Ecef3zl/nTicrtl1bYMM1CnBBJUEjwPB5ZJiw4zkiCX78jwBOr6f+X9kXM2Djw+fGcvl43rNVVRUBnzu3JsiATSS1UTV5ioxMq1sbzPQM//V87H65bbmx3TOLEaBpq6oi5LqbsgkErz9le+5o0XiTSvZXZl/h9sBv4CKzPIBup4UzwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Module map file contents displayed in Xcode editor&quot;
        title=&quot;&quot;
        src=&quot;/static/7a733ba54c7ab341360c31e6d77781d0/dd200/5.png&quot;
        srcset=&quot;/static/7a733ba54c7ab341360c31e6d77781d0/0eb09/5.png 500w,
/static/7a733ba54c7ab341360c31e6d77781d0/1263b/5.png 1000w,
/static/7a733ba54c7ab341360c31e6d77781d0/dd200/5.png 1726w&quot;
        sizes=&quot;(max-width: 1726px) 100vw, 1726px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Step 5: Add C header files and make sure their Target Membership is set to Private&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dd1cb2f55de7e61cc8b051861854448e/f97d7/6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABYlAAAWJQFJUiTwAAABdUlEQVR42nWRa06DQBSF2Y0FCswThkeBFmlrpba1+KrGpD9chRtxJ27AxGW4EdvjBeMfoz++nJm5yZl7z7XazRLL+Qx5miKJDDxfQKgYYRhBConA5/AJz+Ow7SHswfBbCdf2kJkIF805mrMZTicTWI83S9y2lzgtcioauEMJJhIInSMg9VmEoa9huxwO1VxPkvL+PnDYMVQK19v1x9P+8bVZ796tTTPHzXaL2aSEoaLSGYwpEHVEOUJTQkcFvY+gw7xHyBScPmM8PpRZgXlVvZVR8pxp9WJdrRa4u2pRxDSmUOBMIQ8lUikQE7YTEAyOw3v6c9dt3yHHaJpisa6Q1zGysYZ1vz2jDi9Rj0uoztBXyKRBGqaIWReB6E1+4xAndoBRnWAyT44q9T9Z6B2s8ypDM85R01I0GQa+hAwUPMou6PKjzP4y7BiQYWYS3LUt9g87rNo9rEQrxJRdp4pT4C7r+RntP7NvQwYtJaZVhZp2UJUFvgBJ0OSabpX9KQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Project navigator showing C header files with private target membership&quot;
        title=&quot;&quot;
        src=&quot;/static/dd1cb2f55de7e61cc8b051861854448e/f97d7/6.png&quot;
        srcset=&quot;/static/dd1cb2f55de7e61cc8b051861854448e/0eb09/6.png 500w,
/static/dd1cb2f55de7e61cc8b051861854448e/1263b/6.png 1000w,
/static/dd1cb2f55de7e61cc8b051861854448e/f97d7/6.png 2000w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Step 6: Add a run script to remove private headers and create a public modulemap file&lt;/p&gt;
&lt;p&gt;Name the script “Make C Header Files Private”&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1966px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/78c4bab2032817c9346844e41ac63cae/e97a1/7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB50lEQVR42oXRb0/aUBTHcV6HyYxALW2BtrRAKcVboKUgKFX+FdTpjMs2twe+/0ffXeqmzmzZg5OmN7mfc37nFtJJyHI5Y7dacbc95+HuhttdxmZxyWmSEPcF2/WK3WbDdvX83ddVlrFbL+XZgs3lBeuLlGkSU8i2GdtFKi+PiQcRSRTR8UMMrYJt2TStGo5RRi8eYGtFTPWQaukAU1NwrDq6qqLpdWq6RtO2JJgO+XZ/TRT2CdptQt/HdZp0A4FT09D1GvXT77jzJ5rpE0r0yIfwEdUZYlZKGBU1b+5YZl6F+VhwvVkTCUHo+Yh2h5NOi8t5QhLH+J5HP0k5W1wxmWd4YkzDj6jWbTSliKEqGMcygVKS/yUK63TCj69f5D7WrM7nRMGJjGnS81w6LZeGWSPsthkNBHHYk427CL+FaVRyQN9jsnJYVmF5FnF/c006m8nIHkJOaRkGytEhaumISrlI220Q+B5Bx6PrtWg5NlVNRlVfoRcwnUQ83H7MwcGJyFG7aqAdP3ffT2FVdVoNC9c2ceVD1CS2P3+P5WA2j/n86Y7peP/KA7k/OeEeVF7Bv9XvqO+rsJoG3CxmjETIpD9g0A1kZP0P8O2O/gW9gMNewEz0GPkdkq5PKCc08wmLv8Dyf5G34E+8+UckKbcJYQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Run script build phase removing private headers and copying module map&quot;
        title=&quot;&quot;
        src=&quot;/static/78c4bab2032817c9346844e41ac63cae/e97a1/7.png&quot;
        srcset=&quot;/static/78c4bab2032817c9346844e41ac63cae/0eb09/7.png 500w,
/static/78c4bab2032817c9346844e41ac63cae/1263b/7.png 1000w,
/static/78c4bab2032817c9346844e41ac63cae/e97a1/7.png 1966w&quot;
        sizes=&quot;(max-width: 1966px) 100vw, 1966px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Delete PrivateHeaders folder&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-rf&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${TARGET_BUILD_DIR}&lt;/span&gt;/&lt;span class=&quot;token variable&quot;&gt;${PRODUCT_NAME}&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;${WRAPPER_SUFFIX}&lt;/span&gt;/PrivateHeaders

&lt;span class=&quot;token comment&quot;&gt;# Remove module.modulemap file&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${TARGET_BUILD_DIR}&lt;/span&gt;/&lt;span class=&quot;token variable&quot;&gt;${PRODUCT_NAME}&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;${WRAPPER_SUFFIX}&lt;/span&gt;/Modules/module.modulemap

&lt;span class=&quot;token comment&quot;&gt;# Copy public.modulemap file and rename it to module.modulemap&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${SRCROOT}&lt;/span&gt;/Supporting/public.modulemap &lt;span class=&quot;token variable&quot;&gt;${TARGET_BUILD_DIR}&lt;/span&gt;/&lt;span class=&quot;token variable&quot;&gt;${PRODUCT_NAME}&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;${WRAPPER_SUFFIX}&lt;/span&gt;/Modules/module.modulemap

&lt;span class=&quot;token comment&quot;&gt;# Append the Swift module so you can access you Swift code in Objective-C via @import MyFramework.Swift&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;module &lt;span class=&quot;token variable&quot;&gt;${PRODUCT_NAME}&lt;/span&gt;.Swift { header &lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;${PRODUCT_NAME}&lt;/span&gt;-Swift.h&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt; }&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${TARGET_BUILD_DIR}&lt;/span&gt;/&lt;span class=&quot;token variable&quot;&gt;${PRODUCT_NAME}&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;${WRAPPER_SUFFIX}&lt;/span&gt;/Modules/module.modulemap&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Step 7: Add the framework to a test application&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1680px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5cacb900d99224bc7935fb5a464688f0/c1e63/8.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.599999999999994%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAABYlAAAWJQFJUiTwAAAByklEQVR42n2SS3LaQBRF2YUzMgip/60PQj8iEGCbEGwqlYl34BWkkrI9ytJvXjefRHbM4Ja6W+qj9+67AxWN4STDAKe1YiE0ozOuULY77G6XmDc1NI9gjtKc07fh3ztHDd4DQ1jBUE1SFGmC1aLFZr1EN6sxr0ssmgpdU2JaryGZgwYfA53EeNTTrMhxQ9CWYK6ytxVdrHBitQfU04kHfK4KpFoiUQKx5Oenk1XKW3ARaDjzl4o0Js8qNAS/W3V42G6wWR3adj9a1AWKagkjFVR4oWXXIg+uUWYxVm1DfpXYkn+P379hd7emDpT3N5E0tLim4TG6O+r52APmsfYVNNUMqU0gwggi4t58Ng4PQ+CCKhNQOvVrD/Xg/wA1i5Bphmb/BL3/Dfn1GWr3inDzAnP/Ck3i2xd8WvzC1fwnrjp6ds+0/wEhY2p/9L5lEQxhyXQtjZfVFoZkdYzYWL+X8q3MOZM9oPPHTTozEqniJAbjAj4ekq7PUrRXx7PT+uTjwAfaZZCqy4xCM82xnLc05UOIi9TS5Ln/yb9yA3JpyPRhr9mxQhcVJ3fAgxFMNMT+tqAMlpiVuYeWWUJ5FH0gDfALJaDOU//uBPwDnDFsPi2gxskAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Test application target linked with the custom framework in Xcode&quot;
        title=&quot;&quot;
        src=&quot;/static/5cacb900d99224bc7935fb5a464688f0/c1e63/8.png&quot;
        srcset=&quot;/static/5cacb900d99224bc7935fb5a464688f0/0eb09/8.png 500w,
/static/5cacb900d99224bc7935fb5a464688f0/1263b/8.png 1000w,
/static/5cacb900d99224bc7935fb5a464688f0/c1e63/8.png 1680w&quot;
        sizes=&quot;(max-width: 1680px) 100vw, 1680px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The code is available on the &lt;a href=&quot;https://github.com/seifeet/SwiftCFramework&quot;&gt;SwiftCFramework GitHub repository&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Symbolication in iOS Apps: Use dSYM and DWARF to Decode Crash Logs]]></title><description><![CDATA[Symbolification is the process of mapping machine addresses into the source code. The file that contains information that maps machine…]]></description><link>https://www.monkey.work/2019-04-25-symbolification-in-ios-applications/</link><guid isPermaLink="false">https://www.monkey.work/2019-04-25-symbolification-in-ios-applications/</guid><pubDate>Thu, 25 Apr 2019 19:41:59 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://faisalmemon.github.io/ios-crash-dump-analysis-book/#basic-concepts&quot;&gt;Symbolification&lt;/a&gt; is the process of mapping machine addresses into the source code.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The file that contains information that maps machine addresses into the source code has a &lt;em&gt;&lt;code class=&quot;language-text&quot;&gt;.DSYM&lt;/code&gt;&lt;/em&gt; extension and is usually named &lt;em&gt;&lt;code class=&quot;language-text&quot;&gt;app_name.DSYM&lt;/code&gt;&lt;/em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The tricky part is that by default .DSYM file is only generated for release builds so if a debug build is launched from the app icon no symbolification will occur. To avoid this problem we can configure the project to have &lt;em&gt;&lt;code class=&quot;language-text&quot;&gt;DWARF&lt;/code&gt;&lt;/em&gt; with dSYM File set for both debug and release targets.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The setting is located in project’s Build Settings under Debug Information Format (just search for DWARF in Build Settings).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This way the Crash Report, seen from Windows -&gt; Devices and Simulators -&gt; View Device Logs will contain symbolification information.&lt;/em&gt;
&lt;img src=&quot;/blog/content/images/2019/04/dwarf.png&quot; alt=&quot;Xcode DWARF with dSYM build settings screenshot&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you are troubleshooting related iOS issues, you might also find these posts helpful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/autolayout_fixed_height/&quot;&gt;Fix Auto Layout “Unable to Satisfy Constraints” for Fixed-Height Views in Cells&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/swift/&quot;&gt;Fix Unwanted Top Padding in UICollectionView on iOS 10&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[RxSwift: How to Avoid Memory Leaks with DisposeBag and Resource Tracking]]></title><description><![CDATA[RxSwift is a powerful framework and, as always, with power comes responsibility. In RxSwift the memory is usually managed through an object…]]></description><link>https://www.monkey.work/2019-04-05-rxswift-how-to-avoid-memory-leaks/</link><guid isPermaLink="false">https://www.monkey.work/2019-04-05-rxswift-how-to-avoid-memory-leaks/</guid><pubDate>Fri, 05 Apr 2019 21:27:29 GMT</pubDate><content:encoded>&lt;p&gt;RxSwift is a powerful framework and, as always, with power comes responsibility. In RxSwift the memory is usually managed through an object called a &lt;code class=&quot;language-text&quot;&gt;DisposeBag&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For instance, every time we subscribe to an &lt;code class=&quot;language-text&quot;&gt;Observable&lt;/code&gt;, RxSwift makes sure the subscription is kept in memory until either a &lt;code class=&quot;language-text&quot;&gt;.completed&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;.error&lt;/code&gt; event is received or the subscription is being garbage collected by the &lt;code class=&quot;language-text&quot;&gt;DisposeBag&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It is fairly easy to make a mistake and to add the subscription to a wrong &lt;code class=&quot;language-text&quot;&gt;DisposeBag&lt;/code&gt;. For example, to add it to the controller’s bag instead of the cell’s. Mistakes happen and to catch them early it is a good idea to add a resource counting mechanism to each view controller. The mechanism will make sure resources allocated during the lifetime of the view controller are released and if no, will output an error.&lt;/p&gt;
&lt;p&gt;Infinum provides a default implementation of such a &lt;a href=&quot;https://handbook.infinum.co/books/ios/Rx/RxSwift%20Resource%20Tracking&quot;&gt;mechanism&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Fix Auto Layout "Unable to Satisfy Constraints" for Fixed-Height Views in Cells]]></title><description><![CDATA[We needed a collection view cell that contained an image view and a label. The image view had a constant height of 200 and was anchored to…]]></description><link>https://www.monkey.work/2019-03-22-autolayout_fixed_height/</link><guid isPermaLink="false">https://www.monkey.work/2019-03-22-autolayout_fixed_height/</guid><pubDate>Fri, 22 Mar 2019 20:48:29 GMT</pubDate><content:encoded>&lt;p&gt;We needed a collection view cell that contained an image view and a label. The image view had a constant height of 200 and was anchored to the left, right, and the top. The label was supposed to take the space unused by the image view and was anchored to the left, right, and the bottom. The top of the label was anchored to the bottom of the image view.&lt;/p&gt;
&lt;p&gt;While the cell was displayed correctly an error &lt;code class=&quot;language-text&quot;&gt;Unable to simultaneously satisfy constraints.&lt;/code&gt; was outputted in the console. It looked like the auto layout was not happy about the height constraint of the image view.&lt;/p&gt;
&lt;p&gt;It was not easy to infer what exactly was causing the height constraint to fail. The first hint came from a constraint named &lt;code class=&quot;language-text&quot;&gt;UIView-Encapsulated-Layout-Height&lt;/code&gt; that was set to 50. Apparently the cell tried to satisfy all the constraints before calling &lt;code class=&quot;language-text&quot;&gt;layoutSubviews&lt;/code&gt; when it is height was still 50.&lt;/p&gt;
&lt;p&gt;Setting the priority of height anchor of the image view to 999 solved the problem.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; constraint &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imageView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;heightAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalToConstant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
constraint&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;priority &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;999&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/blog/content/images/2019/03/auto_layout_fixed_height.png&quot; alt=&quot;Auto Layout fixed-height cell constraints&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[UICollectionView Self-Sizing Cells: Dynamic UILabel Layout on iOS 10]]></title><description><![CDATA[All we needed was a simple  with just one cell that contained a label with an unknown amount of text. We just wanted to use the default flow…]]></description><link>https://www.monkey.work/2018-09-26-uicollectionview/</link><guid isPermaLink="false">https://www.monkey.work/2018-09-26-uicollectionview/</guid><pubDate>Wed, 26 Sep 2018 23:17:14 GMT</pubDate><content:encoded>&lt;p&gt;All we needed was a simple &lt;code class=&quot;language-text&quot;&gt;UICollectionView&lt;/code&gt; with just one cell that contained a label with an unknown amount of text. We just wanted to use the default flow layout and to let auto-layout do the work; we did not want to implement a custom layout. The label was pinned to all sides of the cell’s content view. It did not work. The text inside of the label would get truncated in weird and sometimes random ways.&lt;/p&gt;
&lt;p&gt;We ended up using a table view with &lt;code class=&quot;language-text&quot;&gt;estimatedRowHeight&lt;/code&gt;set to a constant and &lt;code class=&quot;language-text&quot;&gt;rowHeight&lt;/code&gt; set to &lt;code class=&quot;language-text&quot;&gt;UITableView.automaticDimension&lt;/code&gt;. The label was still pinned to the content view. It worked like a charm.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[RxSwift.Resources: Fix “Use of Unresolved Identifier 'Resources'”]]></title><description><![CDATA[RxSwift.Resources are being used to detect memory leaks during pre release tests. The problem: When trying to use RxSwift.Resources the…]]></description><link>https://www.monkey.work/2018-08-30-adding-rxswift-resources/</link><guid isPermaLink="false">https://www.monkey.work/2018-08-30-adding-rxswift-resources/</guid><pubDate>Thu, 30 Aug 2018 22:27:29 GMT</pubDate><content:encoded>&lt;p&gt;RxSwift.Resources are being used to detect memory leaks during pre release tests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When trying to use RxSwift.Resources the following error occurs:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Use of unresolved identifier &apos;Resources&apos;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The solution:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;RxSwift.Resources are compiled conditionally and only when the “TRACE_RESOURCES” flag is present.&lt;/p&gt;
&lt;p&gt;To include RxSwift.Resources for debug builds, update Podfile with the following code:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;post_install do |installer|
   installer.pods_project.targets.each do |target|
      if target.name == &apos;RxSwift&apos;
         target.build_configurations.each do |config|
            if config.name == &apos;Debug&apos;
               config.build_settings[&apos;OTHER_SWIFT_FLAGS&apos;] ||= [&apos;-D&apos;, &apos;TRACE_RESOURCES&apos;]
            end
         end
      end
   end
end&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Sign and verify data]]></title><description><![CDATA[RSA verification process is similar to RSA encryption/decryption process with one major difference. We sign the data with a private key and…]]></description><link>https://www.monkey.work/2018-07-24-openssl-sign-verify/</link><guid isPermaLink="false">https://www.monkey.work/2018-07-24-openssl-sign-verify/</guid><pubDate>Tue, 24 Jul 2018 22:39:45 GMT</pubDate><content:encoded>&lt;p&gt;RSA verification process is similar to RSA encryption/decryption process with one major difference.&lt;/p&gt;
&lt;p&gt;We sign the data with a private key and verify with a public key.&lt;/p&gt;
&lt;p&gt;This way a public key verifies the authenticity of a message signed by a secret private key.&lt;/p&gt;
&lt;p&gt;Create a key:&lt;/p&gt;
&lt;p&gt;First let’s generate a random 2048-bit RSA key pair (put xoxo for the password).&lt;/p&gt;
&lt;p&gt;-des3 option encrypts the key with DES3 symmetric-key block cipher
&lt;code class=&quot;language-text&quot;&gt;$ openssl genrsa -des3 -out xo_private.pem 2048&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Extract the public key in the PEM format.
&lt;code class=&quot;language-text&quot;&gt;$ openssl rsa -in xo_private.pem -outform PEM -pubout -out xo_public.pem&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Sign:&lt;/p&gt;
&lt;p&gt;Here is where it gets a little tricky.&lt;/p&gt;
&lt;p&gt;-raw option forces rsautl not to use any padding.&lt;/p&gt;
&lt;p&gt;So the input data must be exactly 256 characters long.
&lt;code class=&quot;language-text&quot;&gt;$ openssl rsautl -sign -raw -inkey xo_private.pem -in xo_data.bin -out xo_signed.bin&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Verify:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;$ openssl rsautl -verify -raw -hexdump -inkey xo_public.pem -pubin -in xo_signed.bin -out xo_verified.hex&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Here is simple way to convert a hex string into ASCii:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;$ xxd -r -p xo_data.hex xo_data.txt&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And ASCii to hex:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;$ xxd -i xo_data.txt&lt;/code&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to choose minSdkVersion, targetSdkVersion, and compileSdkVersion]]></title><description><![CDATA[Full article.
Full article.]]></description><link>https://www.monkey.work/2018-04-26-how-to-choose-minsdkversion/</link><guid isPermaLink="false">https://www.monkey.work/2018-04-26-how-to-choose-minsdkversion/</guid><pubDate>Thu, 26 Apr 2018 22:40:03 GMT</pubDate><content:encoded>&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;minSdkVersion (lowest possible) &amp;lt;=
    targetSdkVersion == compileSdkVersion (latest SDK)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/google-developers/picking-your-compilesdkversion-minsdkversion-targetsdkversion-a098a0341ebd&quot;&gt;Full article.&lt;/a&gt;
&lt;a href=&quot;https://medium.com/google-developers/picking-your-compilesdkversion-minsdkversion-targetsdkversion-a098a0341ebd&quot;&gt;Full article.&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Cannot lock ref ...]]></title><description><![CDATA[Problem: A nasty Git error that can come in different forms: error: cannot lock ref (unable to update local ref) error: cannot lock existing…]]></description><link>https://www.monkey.work/2018-04-26-git-error-cannot-lock-ref/</link><guid isPermaLink="false">https://www.monkey.work/2018-04-26-git-error-cannot-lock-ref/</guid><pubDate>Thu, 26 Apr 2018 22:30:08 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A nasty Git error that can come in different forms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;error: cannot lock ref&lt;/li&gt;
&lt;li&gt;(unable to update local ref)&lt;/li&gt;
&lt;li&gt;error: cannot lock existing info/refs&lt;/li&gt;
&lt;li&gt;fatal: git-http-push failed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;git remote prune origin
git gc --prune=now&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Keeping git history clean]]></title><description><![CDATA[Rebase git rebase helps maintain repository’s homeostasis or in other words helps maintain history clean. 
 begins an interactive rebasing…]]></description><link>https://www.monkey.work/2018-02-23-keeping-git-history-clean/</link><guid isPermaLink="false">https://www.monkey.work/2018-02-23-keeping-git-history-clean/</guid><pubDate>Sat, 24 Feb 2018 01:23:15 GMT</pubDate><content:encoded>&lt;h2 id=&quot;rebase&quot;&gt;Rebase&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;git rebase helps maintain repository’s homeostasis or in other words helps maintain history clean.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;git rebase -i 95eeee3^&lt;/code&gt;
&lt;code class=&quot;language-text&quot;&gt;-i&lt;/code&gt; begins an interactive rebasing session&lt;/p&gt;
&lt;p&gt;Interactive mode will bring the following list of commands:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;p, pick = use commit
r, reword = use commit, but edit the commit message
e, edit = use commit, but stop for amending
s, squash = use commit, but meld into previous commit
f, fixup = like &quot;squash&quot;, but discard this commit&apos;s log message
x, exec = run command (the rest of the line) using shell
d, drop = remove commit&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;95eeee3&lt;/code&gt; is the commit we want to modify&lt;/p&gt;
&lt;p&gt;we can find it by doing:
&lt;code class=&quot;language-text&quot;&gt;git log&lt;/code&gt;
&lt;code class=&quot;language-text&quot;&gt;^&lt;/code&gt; means start modifying just before the specified commit&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;example&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To modify a commit message and update some files:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;git rebase -i 95eeee3^&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;change pick to edit (or just e)
&lt;code class=&quot;language-text&quot;&gt;git reset HEAD~&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;update some files …
&lt;code class=&quot;language-text&quot;&gt;git add -A .&lt;/code&gt;
&lt;code class=&quot;language-text&quot;&gt;git rebase --continue&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;force push the change to the remote
&lt;code class=&quot;language-text&quot;&gt;git push --force&lt;/code&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Fix Unwanted Top Padding in UICollectionView on iOS 10]]></title><description><![CDATA[The issue: On iOS 10, a collection view embedded in a view has unexpected padding at the top. Interestingly, works just fine on iOS 1…]]></description><link>https://www.monkey.work/2018-02-23-swift/</link><guid isPermaLink="false">https://www.monkey.work/2018-02-23-swift/</guid><pubDate>Sat, 24 Feb 2018 00:59:29 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;The issue:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On iOS 10, a collection view embedded in a view has unexpected padding at the top. Interestingly, works just fine on iOS 11.&lt;/p&gt;
&lt;p&gt;Debugging shows that collection view &lt;code class=&quot;language-text&quot;&gt;contentSize&lt;/code&gt; is correct but &lt;code class=&quot;language-text&quot;&gt;contentOffset&lt;/code&gt; is wrong.&lt;/p&gt;
&lt;p&gt;&amp;#x3C;UICollectionView: 0x7b7400061800; frame = (10 10; 300 &lt;code class=&quot;language-text&quot;&gt;242&lt;/code&gt;); clipsToBounds = YES; gestureRecognizers = &amp;#x3C;NSArray: 0x7b0c00026d60&gt;; layer = &amp;#x3C;CALayer: 0x7b080003ca40&gt;; &lt;code class=&quot;language-text&quot;&gt;contentOffset: {0, -64}&lt;/code&gt;; contentSize: {1200, &lt;code class=&quot;language-text&quot;&gt;207&lt;/code&gt;}&gt; collection view layout: &amp;#x3C;XoXo.cvLayout: 0x7b540005dc00&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;contentOffset
▿ (0.0, -64.0)
  - x : 0.0
  - y : -64.0

contentInset
▿ UIEdgeInsets
  - top : 64.0
  - left : 0.0
  - bottom : 0.0
  - right : 0.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;The solution:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Set &lt;code class=&quot;language-text&quot;&gt;automaticallyAdjustsScrollViewInsets&lt;/code&gt; property of the view controller that contains the collection view to false.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;self.automaticallyAdjustsScrollViewInsets = false&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For iOS 11 the property was moved to the view (collection view) and was renamed to &lt;code class=&quot;language-text&quot;&gt;contentInsetAdjustmentBehavior&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;        if #available(iOS 11.0, *) {
            collectionView.contentInsetAdjustmentBehavior = .never
        }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Also setting bounce properties like &lt;code class=&quot;language-text&quot;&gt;bounces&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;alwaysBounceHorizontal&lt;/code&gt; does not solve the problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;viewController.automaticallyAdjustsScrollViewInsets&lt;/code&gt; is true by default and it makes sense for scroll views that cover the entire screen. On iOS 10, the runtime will automatically adjust (by -64) collection view insets so that it is not covered behind the navigation bar or the status bar. Unfortunately that behavior completely ruins collection views that cover only certain parts of the screen.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Nodemailer 'Please log in via your web browser and then try again' error when using Gmail]]></title><description><![CDATA[The problem While browsing our Node.js site logs we have noticed the following error:  We use Nodemailer with our Gmail account to send…]]></description><link>https://www.monkey.work/2018-01-21-node-js/</link><guid isPermaLink="false">https://www.monkey.work/2018-01-21-node-js/</guid><pubDate>Sun, 21 Jan 2018 10:02:38 GMT</pubDate><content:encoded>&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;/h2&gt;
&lt;p&gt;While browsing our Node.js site logs we have noticed the following error:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Please log in via your web browser and then try again.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We use Nodemailer with our Gmail account to send emails.&lt;/p&gt;
&lt;h2 id=&quot;the-solution&quot;&gt;The solution&lt;/h2&gt;
&lt;p&gt;Logging in to the Gmail account via a browser did exactly nothing of course. Luckily we found some clues in this &lt;a href=&quot;https://stackoverflow.com/questions/20337040/gmail-smtp-debug-error-please-log-in-via-your-web-browser&quot;&gt;Stack Overflow answer about Gmail SMTP debug errors&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Change Gmail account settings to allow &lt;a href=&quot;https://myaccount.google.com/lesssecureapps&quot;&gt;“less secure apps”&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Force &lt;a href=&quot;https://accounts.google.com/DisplayUnlockCaptcha&quot;&gt;“captcha update”&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title><![CDATA[Terms of use: cheat sheet]]></title><description><![CDATA[Terms of Use Last updated December 05, 2017 Agreement to Terms These Terms of Use constitute a legally binding agreement made between you…]]></description><link>https://www.monkey.work/2017-12-05-terms-of-use-curb-it/</link><guid isPermaLink="false">https://www.monkey.work/2017-12-05-terms-of-use-curb-it/</guid><pubDate>Tue, 05 Dec 2017 22:31:38 GMT</pubDate><content:encoded>&lt;h2 id=&quot;terms-of-use&quot;&gt;Terms of Use&lt;/h2&gt;
&lt;p&gt;Last updated December 05, 2017&lt;/p&gt;
&lt;h2 id=&quot;agreement-to-terms&quot;&gt;Agreement to Terms&lt;/h2&gt;
&lt;p&gt;These Terms of Use constitute a legally binding agreement made between you, whether personally or on behalf of an entity (“you”) and The App Developer (“we,” “us” or “our”), concerning your access to and use of the CurbIt website as well as any other media form, media channel, mobile website or mobile application related, linked, or otherwise connected thereto (collectively, the “Site”). You agree that by accessing the Site, you have read, understood, and agreed to be bound by all of these Terms of Use. IF YOU DO NOT AGREE WITH ALL OF THESE TERMS OF USE, THEN YOU ARE EXPRESSLY PROHIBITED FROM USING THE Site AND YOU MUST DISCONTINUE USE IMMEDIATELY.&lt;/p&gt;
&lt;p&gt;Supplemental terms and conditions or documents that may be posted on the Site from time to time are hereby expressly incorporated herein by reference. We reserve the right, in our sole discretion, to make changes or modifications to these Terms of Use at any time and for any reason. We will alert you about any changes by updating the “Last updated” date of these Terms of Use, and you waive any right to receive specific notice of each such change. It is your responsibility to periodically review these Terms of Use to stay informed of updates. You will be subject to, and will be deemed to have been made aware of and to have accepted, the changes in any revised Terms of Use by your continued use of the Site after the date such revised Terms of Use are posted.&lt;/p&gt;
&lt;p&gt;The information provided on the Site is not intended for distribution to or use by any person or entity in any jurisdiction or country where such distribution or use would be contrary to law or regulation or which would subject us to any registration requirement within such jurisdiction or country. Accordingly, those persons who choose to access the Site from other locations do so on their own initiative and are solely responsible for compliance with local laws, if and to the extent local laws are applicable.&lt;/p&gt;
&lt;p&gt;All users who are minors in the jurisdiction in which they reside (generally under the age of 18) must have the permission of, and be directly supervised by, their parent or guardian to use the Site. If you are a minor, you must have your parent or guardian read and agree to these Terms of Use prior to you using the Site.&lt;/p&gt;
&lt;h2 id=&quot;intellectual-property-rights&quot;&gt;Intellectual Property Rights&lt;/h2&gt;
&lt;p&gt;Unless otherwise indicated, the Site is our proprietary property and all source code, databases, functionality, software, website designs, audio, video, text, photographs, and graphics on the Site (collectively, the “Content”) and the trademarks, service marks, and logos contained therein (the “Marks”) are owned or controlled by us or licensed to us, and are protected by copyright and trademark laws and various other intellectual property rights and unfair competition laws of the United States, foreign jurisdictions, and international conventions. The Content and the Marks are provided on the Site “AS IS” for your information and personal use only. Except as expressly provided in these Terms of Use, no part of the Site and no Content or Marks may be copied, reproduced, aggregated, republished, uploaded, posted, publicly displayed, encoded, translated, transmitted, distributed, sold, licensed, or otherwise exploited for any commercial purpose whatsoever, without our express prior written permission.&lt;/p&gt;
&lt;p&gt;Provided that you are eligible to use the Site, you are granted a limited license to access and use the Site and to download or print a copy of any portion of the Content to which you have properly gained access solely for your personal, non-commercial use. We reserve all rights not expressly granted to you in and to the Site, the Content and the Marks.&lt;/p&gt;
&lt;h2 id=&quot;user-representations&quot;&gt;User Representations&lt;/h2&gt;
&lt;p&gt;By using the Site, you represent and warrant that: (1) you have the legal capacity and you agree to comply with these Terms of Use; (2) you are not a minor in the jurisdiction in which you reside, or if a minor, you have received parental permission to use the Site; (3) you will not access the Site through automated or non-human means, whether through a bot, script, or otherwise; (4) you will not use the Site for any illegal or unauthorized purpose; and (5) your use of the Site will not violate any applicable law or regulation.&lt;/p&gt;
&lt;p&gt;If you provide any information that is untrue, inaccurate, not current, or incomplete, we have the right to suspend or terminate your account and refuse any and all current or future use of the Site (or any portion thereof).&lt;/p&gt;
&lt;h2 id=&quot;prohibited-activities&quot;&gt;Prohibited Activities&lt;/h2&gt;
&lt;p&gt;You may not access or use the Site for any purpose other than that for which we make the Site available. The Site may not be used in connection with any commercial endeavors except those that are specifically endorsed or approved by us.&lt;/p&gt;
&lt;p&gt;As a user of the Site, you agree not to:&lt;/p&gt;
&lt;p&gt;Use the Site to advertise or offer to sell goods and services.&lt;/p&gt;
&lt;p&gt;Trick, defraud, or mislead us and other users, especially in any attempt to learn sensitive account information such as user passwords.&lt;/p&gt;
&lt;p&gt;Use the Site in a manner inconsistent with any applicable laws or regulations.&lt;/p&gt;
&lt;h2 id=&quot;disclaimer-about-information-accuracy&quot;&gt;Disclaimer About Information Accuracy&lt;/h2&gt;
&lt;p&gt;Although every effort has been made to provide complete and accurate information, the developer of this Site makes no warranties, express or implied, or representations as to the accuracy of content on this Site. The Developer of this Site assumes no liability or responsibility for any error or omissions in the information contained on the Site or the operation of the Site.&lt;/p&gt;
&lt;p&gt;The Site is for informational purposes only and does not provide legal advice. Materials on this Site are published to provide users with free information regarding the laws and policies described. However, this Site is not designed for the purpose of providing local law requirements. Visitors should not rely upon information on this Site as a substitute for local law requirements. While we make every effort to provide accurate information, laws can change and inaccuracies happen despite our best efforts.&lt;/p&gt;
&lt;h2 id=&quot;guidelines-for-reviews&quot;&gt;Guidelines for Reviews&lt;/h2&gt;
&lt;p&gt;We may provide you areas on the Site to leave reviews or ratings. When posting a review, you must comply with the following criteria: (1) you should have firsthand experience with the person/entity being reviewed; (2) your reviews should not contain offensive profanity, or abusive, racist, offensive, or hate language; (3) your reviews should not contain discriminatory references based on religion, race, gender, national origin, age, marital status, sexual orientation, or disability; (4) your reviews should not contain references to illegal activity; (5) you should not be affiliated with competitors if posting negative reviews; (6) you should not make any conclusions as to the legality of conduct; (7) you may not post any false or misleading statements; and (8) you may not organize a campaign encouraging others to post reviews, whether positive or negative.&lt;/p&gt;
&lt;p&gt;We may accept, reject, or remove reviews in our sole discretion. We have absolutely no obligation to screen reviews or to delete reviews, even if anyone considers reviews objectionable or inaccurate. Reviews are not endorsed by us, and do not necessarily represent our opinions or the views of any of our affiliates or partners. We do not assume liability for any review or for any claims, liabilities, or losses resulting from any review. By posting a review, you hereby grant to us a perpetual, non-exclusive, worldwide, royalty-free, fully-paid, assignable, and sublicensable right and license to reproduce, modify, translate, transmit by any means, display, perform, and/or distribute all content relating to reviews.&lt;/p&gt;
&lt;h2 id=&quot;mobile-application-license&quot;&gt;Mobile Application License&lt;/h2&gt;
&lt;p&gt;Use License&lt;/p&gt;
&lt;p&gt;If you access the Site via a mobile application, then we grant you a revocable, non-exclusive, non-transferable, limited right to install and use the mobile application on wireless electronic devices owned or controlled by you, and to access and use the mobile application on such devices strictly in accordance with the terms and conditions of this mobile application license contained in these Terms of Use. You shall not: (1) decompile, reverse engineer, disassemble, attempt to derive the source code of, or decrypt the application; (2) make any modification, adaptation, improvement, enhancement, translation, or derivative work from the application; (3) violate any applicable laws, rules, or regulations in connection with your access or use of the application; (4) remove, alter, or obscure any proprietary notice (including any notice of copyright or trademark) posted by us or the licensors of the application; (5) use the application for any revenue generating endeavor, commercial enterprise, or other purpose for which it is not designed or intended; (6) make the application available over a network or other environment permitting access or use by multiple devices or users at the same time; (7) use the application for creating a product, service, or software that is, directly or indirectly, competitive with or in any way a substitute for the application; (8) use the application to send automated queries to any website or to send any unsolicited commercial e-mail; or (9) use any proprietary information or any of our interfaces or our other intellectual property in the design, development, manufacture, licensing, or distribution of any applications, accessories, or devices for use with the application.&lt;/p&gt;
&lt;p&gt;Apple and Android Devices&lt;/p&gt;
&lt;p&gt;The following terms apply when you use a mobile application obtained from either the Apple Store or Google Play (each an “App Distributor”) to access the Site: (1) the license granted to you for our mobile application is limited to a non-transferable license to use the application on a device that utilizes the Apple iOS or Android operating systems, as applicable, and in accordance with the usage rules set forth in the applicable App Distributor’s terms of service; (2) we are responsible for providing any maintenance and support services with respect to the mobile application as specified in the terms and conditions of this mobile application license contained in these Terms of Use or as otherwise required under applicable law, and you acknowledge that each App Distributor has no obligation whatsoever to furnish any maintenance and support services with respect to the mobile application; (3) in the event of any failure of the mobile application to conform to any applicable warranty, you may notify the applicable App Distributor, and the App Distributor, in accordance with its terms and policies, may refund the purchase price, if any, paid for the mobile application, and to the maximum extent permitted by applicable law, the App Distributor will have no other warranty obligation whatsoever with respect to the mobile application; (4) you represent and warrant that (i) you are not located in a country that is subject to a U.S. government embargo, or that has been designated by the U.S. government as a “terrorist supporting” country and (ii) you are not listed on any U.S. government list of prohibited or restricted parties; (5) you must comply with applicable third-party terms of agreement when using the mobile application, e.g., if you have a VoIP application, then you must not be in violation of their wireless data service agreement when using the mobile application; and (6) you acknowledge and agree that the App Distributors are third-party beneficiaries of the terms and conditions in this mobile application license contained in these Terms of Use, and that each App Distributor will have the right (and will be deemed to have accepted the right) to enforce the terms and conditions in this mobile application license contained in these Terms of Use against you as a third-party beneficiary thereof.&lt;/p&gt;
&lt;h2 id=&quot;submissions&quot;&gt;Submissions&lt;/h2&gt;
&lt;p&gt;You acknowledge and agree that any questions, comments, suggestions, ideas, feedback, or other information regarding the Site (“Submissions”) provided by you to us are non-confidential and shall become our sole property. We shall own exclusive rights, including all intellectual property rights, and shall be entitled to the unrestricted use and dissemination of these Submissions for any lawful purpose, commercial or otherwise, without acknowledgment or compensation to you. You hereby waive all moral rights to any such Submissions, and you hereby warrant that any such Submissions are original with you or that you have the right to submit such Submissions. You agree there shall be no recourse against us for any alleged or actual infringement or misappropriation of any proprietary right in your Submissions.&lt;/p&gt;
&lt;h2 id=&quot;site-management&quot;&gt;Site Management&lt;/h2&gt;
&lt;p&gt;We reserve the right, but not the obligation, to: (1) monitor the Site for violations of these Terms of Use; (2) take appropriate legal action against anyone who, in our sole discretion, violates the law or these Terms of Use, including without limitation, reporting such user to law enforcement authorities; (3) in our sole discretion and without limitation, refuse, restrict access to, limit the availability of, or disable (to the extent technologically feasible) any of your Contributions or any portion thereof; (4) in our sole discretion and without limitation, notice, or liability, to remove from the Site or otherwise disable all files and content that are excessive in size or are in any way burdensome to our systems; and (5) otherwise manage the Site in a manner designed to protect our rights and property and to facilitate the proper functioning of the Site.&lt;/p&gt;
&lt;h2 id=&quot;privacy-policy&quot;&gt;Privacy Policy&lt;/h2&gt;
&lt;p&gt;We care about data privacy and security. By using the Site, you agree to be bound by our Privacy Policy posted on the Site, which is incorporated into these Terms of Use. Please be advised the Site is hosted in the United States. If you access the Site from the European Union, Asia, or any other region of the world with laws or other requirements governing personal data collection, use, or disclosure that differ from applicable laws in the United States, then through your continued use of the Site, you are transferring your data to the United States, and you expressly consent to have your data transferred to and processed in the United States.&lt;/p&gt;
&lt;h2 id=&quot;term-and-termination&quot;&gt;Term and Termination&lt;/h2&gt;
&lt;p&gt;These Terms of Use shall remain in full force and effect while you use the Site. WITHOUT LIMITING ANY OTHER PROVISION OF THESE TERMS OF USE, WE RESERVE THE RIGHT TO, IN OUR SOLE DISCRETION AND WITHOUT NOTICE OR LIABILITY, DENY ACCESS TO AND USE OF THE Site (INCLUDING BLOCKING CERTAIN IP ADDRESSES), TO ANY PERSON FOR ANY REASON OR FOR NO REASON, INCLUDING WITHOUT LIMITATION FOR BREACH OF ANY REPRESENTATION, WARRANTY, OR COVENANT CONTAINED IN THESE TERMS OF USE OR OF ANY APPLICABLE LAW OR REGULATION. WE MAY TERMINATE YOUR USE OR PARTICIPATION IN THE Site OR DELETE ANY CONTENT OR INFORMATION THAT YOU POSTED AT ANY TIME, WITHOUT WARNING, IN OUR SOLE DISCRETION.&lt;/p&gt;
&lt;p&gt;If we terminate or suspend your account for any reason, you are prohibited from registering and creating a new account under your name, a fake or borrowed name, or the name of any third party, even if you may be acting on behalf of the third party. In addition to terminating or suspending your account, we reserve the right to take appropriate legal action, including without limitation pursuing civil, criminal, and injunctive redress.&lt;/p&gt;
&lt;h2 id=&quot;modifications-and-interruptions&quot;&gt;Modifications and Interruptions&lt;/h2&gt;
&lt;p&gt;We reserve the right to change, modify, or remove the contents of the Site at any time or for any reason at our sole discretion without notice. However, we have no obligation to update any information on our Site. We also reserve the right to modify or discontinue all or part of the Site without notice at any time. We will not be liable to you or any third party for any modification, price change, suspension, or discontinuance of the Site.&lt;/p&gt;
&lt;p&gt;We cannot guarantee the Site will be available at all times. We may experience hardware, software, or other problems or need to perform maintenance related to the Site, resulting in interruptions, delays, or errors. We reserve the right to change, revise, update, suspend, discontinue, or otherwise modify the Site at any time or for any reason without notice to you. You agree that we have no liability whatsoever for any loss, damage, or inconvenience caused by your inability to access or use the Site during any downtime or discontinuance of the Site. Nothing in these Terms of Use will be construed to obligate us to maintain and support the Site or to supply any corrections, updates, or releases in connection therewith.&lt;/p&gt;
&lt;h2 id=&quot;governing-law&quot;&gt;Governing Law&lt;/h2&gt;
&lt;p&gt;These Terms of Use and your use of the Site are governed by and construed in accordance with the laws of the State of California applicable to agreements made and to be entirely performed within the the State of California, without regard to its conflict of law principles.&lt;/p&gt;
&lt;h2 id=&quot;dispute-resolution&quot;&gt;Dispute Resolution&lt;/h2&gt;
&lt;p&gt;Informal Negotiations&lt;/p&gt;
&lt;p&gt;To expedite resolution and control the cost of any dispute, controversy, or claim related to these Terms of Use (each a “Dispute” and collectively, the “Disputes”) brought by either you or us (individually, a “Party” and collectively, the “Parties”), the Parties agree to first attempt to negotiate any Dispute (except those Disputes expressly provided below) informally for at least thirty (30) days before initiating arbitration. Such informal negotiations commence upon written notice from one Party to the other Party.&lt;/p&gt;
&lt;p&gt;Binding Arbitration&lt;/p&gt;
&lt;p&gt;If the Parties are unable to resolve a Dispute through informal negotiations, the Dispute (except those Disputes expressly excluded below) will be finally and exclusively resolved by binding arbitration. YOU UNDERSTAND THAT WITHOUT THIS PROVISION, YOU WOULD HAVE THE RIGHT TO SUE IN COURT AND HAVE A JURY TRIAL. The arbitration shall be commenced and conducted under the Commercial Arbitration Rules of the American Arbitration Association (“AAA”) and, where appropriate, the AAA’s Supplementary Procedures for Consumer Related Disputes (“AAA Consumer Rules”), both of which are available at the AAA website &amp;#x3C;&lt;a href=&quot;http://www.adr.org&quot;&gt;www.adr.org&lt;/a&gt;&gt;. Your arbitration fees and your share of arbitrator compensation shall be governed by the AAA Consumer Rules and, where appropriate, limited by the AAA Consumer Rules. The arbitration may be conducted in person, through the submission of documents, by phone, or online. The arbitrator will make a decision in writing, but need not provide a statement of reasons unless requested by either Party. The arbitrator must follow applicable law, and any award may be challenged if the arbitrator fails to do so. Except where otherwise required by the applicable AAA rules or applicable law, the arbitration will take place in USA County, CA. Except as otherwise provided herein, the Parties may litigate in court to compel arbitration, stay proceedings pending arbitration, or to confirm, modify, vacate, or enter judgment on the award entered by the arbitrator.&lt;/p&gt;
&lt;p&gt;If for any reason, a Dispute proceeds in court rather than arbitration, the Dispute shall be commenced or prosecuted in the state and federal courts located in USA County, CA, and the Parties hereby consent to, and waive all defenses of lack of personal jurisdiction, and forum non conveniens with respect to venue and jurisdiction in such state and federal courts. Application of the United Nations Convention on Contracts for the International Sale of Goods and the the Uniform Computer Information Transaction Act (UCITA) are excluded from these Terms of Use.&lt;/p&gt;
&lt;p&gt;In no event shall any Dispute brought by either Party related in any way to the Site be commenced more than one (1) years after the cause of action arose. If this provision is found to be illegal or unenforceable, then neither Party will elect to arbitrate any Dispute falling within that portion of this provision found to be illegal or unenforceable and such Dispute shall be decided by a court of competent jurisdiction within the courts listed for jurisdiction above, and the Parties agree to submit to the personal jurisdiction of that court.&lt;/p&gt;
&lt;p&gt;Restrictions&lt;/p&gt;
&lt;p&gt;The Parties agree that any arbitration shall be limited to the Dispute between the Parties individually. To the full extent permitted by law, (a) no arbitration shall be joined with any other proceeding; (b) there is no right or authority for any Dispute to be arbitrated on a class-action basis or to utilize class action procedures; and (c) there is no right or authority for any Dispute to be brought in a purported representative capacity on behalf of the general public or any other persons.&lt;/p&gt;
&lt;p&gt;Exceptions to Informal Negotiations and Arbitration&lt;/p&gt;
&lt;p&gt;The Parties agree that the following Disputes are not subject to the above provisions concerning informal negotiations and binding arbitration: (a) any Disputes seeking to enforce or protect, or concerning the validity of, any of the intellectual property rights of a Party; (b) any Dispute related to, or arising from, allegations of theft, piracy, invasion of privacy, or unauthorized use; and (c) any claim for injunctive relief. If this provision is found to be illegal or unenforceable, then neither Party will elect to arbitrate any Dispute falling within that portion of this provision found to be illegal or unenforceable and such Dispute shall be decided by a court of competent jurisdiction within the courts listed for jurisdiction above, and the Parties agree to submit to the personal jurisdiction of that court.&lt;/p&gt;
&lt;h2 id=&quot;corrections&quot;&gt;Corrections&lt;/h2&gt;
&lt;p&gt;There may be information on the Site that contains typographical errors, inaccuracies, or omissions, including descriptions, pricing, availability, and various other information. We reserve the right to correct any errors, inaccuracies, or omissions and to change or update the information on the Site at any time, without prior notice.&lt;/p&gt;
&lt;h2 id=&quot;disclaimer&quot;&gt;Disclaimer&lt;/h2&gt;
&lt;p&gt;THE Site IS PROVIDED ON AN AS-IS AND AS-AVAILABLE BASIS. YOU AGREE THAT YOUR USE OF THE Site AND OUR SERVICES WILL BE AT YOUR SOLE RISK. TO THE FULLEST EXTENT PERMITTED BY LAW, WE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, IN CONNECTION WITH THE Site AND YOUR USE THEREOF, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. WE MAKE NO WARRANTIES OR REPRESENTATIONS ABOUT THE ACCURACY OR COMPLETENESS OF THE Site’S CONTENT OR THE CONTENT OF ANY websiteS LINKED TO THE Site AND WE WILL ASSUME NO LIABILITY OR RESPONSIBILITY FOR ANY (1) ERRORS, MISTAKES, OR INACCURACIES OF CONTENT AND MATERIALS, (2) PERSONAL INJURY OR PROPERTY DAMAGE, OF ANY NATURE WHATSOEVER, RESULTING FROM YOUR ACCESS TO AND USE OF THE Site, (3) ANY UNAUTHORIZED ACCESS TO OR USE OF OUR SECURE SERVERS AND/OR ANY AND ALL PERSONAL INFORMATION AND/OR FINANCIAL INFORMATION STORED THEREIN, (4) ANY INTERRUPTION OR CESSATION OF TRANSMISSION TO OR FROM THE Site, (5) ANY BUGS, VIRUSES, TROJAN HORSES, OR THE LIKE WHICH MAY BE TRANSMITTED TO OR THROUGH THE Site BY ANY THIRD PARTY, AND/OR (6) ANY ERRORS OR OMISSIONS IN ANY CONTENT AND MATERIALS OR FOR ANY LOSS OR DAMAGE OF ANY KIND INCURRED AS A RESULT OF THE USE OF ANY CONTENT POSTED, TRANSMITTED, OR OTHERWISE MADE AVAILABLE VIA THE Site. WE DO NOT WARRANT, ENDORSE, GUARANTEE, OR ASSUME RESPONSIBILITY FOR ANY PRODUCT OR SERVICE ADVERTISED OR OFFERED BY A THIRD PARTY THROUGH THE Site, ANY HYPERLINKED website, OR ANY website OR MOBILE APPLICATION FEATURED IN ANY BANNER OR OTHER ADVERTISING, AND WE WILL NOT BE A PARTY TO OR IN ANY WAY BE RESPONSIBLE FOR MONITORING ANY TRANSACTION BETWEEN YOU AND ANY THIRD-PARTY PROVIDERS OF PRODUCTS OR SERVICES. AS WITH THE PURCHASE OF A PRODUCT OR SERVICE THROUGH ANY MEDIUM OR IN ANY ENVIRONMENT, YOU SHOULD USE YOUR BEST JUDGMENT AND EXERCISE CAUTION WHERE APPROPRIATE.&lt;/p&gt;
&lt;h2 id=&quot;limitations-of-liability&quot;&gt;Limitations of Liability&lt;/h2&gt;
&lt;p&gt;IN NO EVENT WILL WE OR OUR DIRECTORS, EMPLOYEES, OR AGENTS BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY DIRECT, INDIRECT, CONSEQUENTIAL, EXEMPLARY, INCIDENTAL, SPECIAL, OR PUNITIVE DAMAGES, INCLUDING LOST PROFIT, LOST REVENUE, LOSS OF DATA, OR OTHER DAMAGES ARISING FROM YOUR USE OF THE Site, EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. NOTWITHSTANDING ANYTHING TO THE CONTRARY CONTAINED HEREIN, OUR LIABILITY TO YOU FOR ANY CAUSE WHATSOEVER AND REGARDLESS OF THE FORM OF THE ACTION, WILL AT ALL TIMES BE LIMITED TO THE AMOUNT PAID, IF ANY, BY YOU TO US DURING THE SIX (6) MONTH PERIOD PRIOR TO ANY CAUSE OF ACTION ARISING. CERTAIN STATE LAWS DO NOT ALLOW LIMITATIONS ON IMPLIED WARRANTIES OR THE EXCLUSION OR LIMITATION OF CERTAIN DAMAGES. IF THESE LAWS APPLY TO YOU, SOME OR ALL OF THE ABOVE DISCLAIMERS OR LIMITATIONS MAY NOT APPLY TO YOU, AND YOU MAY HAVE ADDITIONAL RIGHTS.&lt;/p&gt;
&lt;h2 id=&quot;indemnification&quot;&gt;Indemnification&lt;/h2&gt;
&lt;p&gt;You agree to defend, indemnify, and hold us harmless, including our subsidiaries, affiliates, and all of our respective officers, agents, partners, and employees, from and against any loss, damage, liability, claim, or demand, including reasonable attorneys’ fees and expenses, made by any third party due to or arising out of: (1) use of the Site; (2) breach of these Terms of Use; (3) any breach of your representations and warranties set forth in these Terms of Use; ( 4) your violation of the rights of a third party, including but not limited to intellectual property rights; or (5) any overt harmful act toward any other user of the Site with whom you connected via the Site. Notwithstanding the foregoing, we reserve the right, at your expense, to assume the exclusive defense and control of any matter for which you are required to indemnify us, and you agree to cooperate, at your expense, with our defense of such claims. We will use reasonable efforts to notify you of any such claim, action, or proceeding which is subject to this indemnification upon becoming aware of it.&lt;/p&gt;
&lt;h2 id=&quot;user-data&quot;&gt;User Data&lt;/h2&gt;
&lt;p&gt;We will maintain certain data that you transmit to the Site for the purpose of managing the performance of the Site, as well as data relating to your use of the Site. Although we perform regular routine backups of data, you are solely responsible for all data that you transmit or that relates to any activity you have undertaken using the Site. You agree that we shall have no liability to you for any loss or corruption of any such data, and you hereby waive any right of action against us arising from any such loss or corruption of such data.&lt;/p&gt;
&lt;h2 id=&quot;electronic-communications-transactions-and-signatures&quot;&gt;Electronic Communications, Transactions, and Signatures&lt;/h2&gt;
&lt;p&gt;Visiting the Site, sending us emails, and completing online forms constitute electronic communications. You consent to receive electronic communications, and you agree that all agreements, notices, disclosures, and other communications we provide to you electronically, via email and on the Site, satisfy any legal requirement that such communication be in writing. YOU HEREBY AGREE TO THE USE OF ELECTRONIC SIGNATURES, CONTRACTS, ORDERS, AND OTHER RECORDS, AND TO ELECTRONIC DELIVERY OF NOTICES, POLICIES, AND RECORDS OF TRANSACTIONS INITIATED OR COMPLETED BY US OR VIA THE Site. You hereby waive any rights or requirements under any statutes, regulations, rules, ordinances, or other laws in any jurisdiction which require an original signature or delivery or retention of non-electronic records, or to payments or the granting of credits by any means other than electronic means.&lt;/p&gt;
&lt;h2 id=&quot;california-users-and-residents&quot;&gt;California Users and Residents&lt;/h2&gt;
&lt;p&gt;If any complaint with us is not satisfactorily resolved, you can contact the Complaint Assistance Unit of the Division of Consumer Services of the California Department of Consumer Affairs in writing at 1625 North Market Blvd., Suite N 112, Sacramento, California 95834 or by telephone at (800) 952-5210 or (916) 445-1254.&lt;/p&gt;
&lt;h2 id=&quot;miscellaneous&quot;&gt;Miscellaneous&lt;/h2&gt;
&lt;p&gt;These Terms of Use and any policies or operating rules posted by us on the Site or in respect to the Site constitute the entire agreement and understanding between you and us. Our failure to exercise or enforce any right or provision of these Terms of Use shall not operate as a waiver of such right or provision. These Terms of Use operate to the fullest extent permissible by law. We may assign any or all of our rights and obligations to others at any time. We shall not be responsible or liable for any loss, damage, delay, or failure to act caused by any cause beyond our reasonable control. If any provision or part of a provision of these Terms of Use is determined to be unlawful, void, or unenforceable, that provision or part of the provision is deemed severable from these Terms of Use and does not affect the validity and enforceability of any remaining provisions. There is no joint venture, partnership, employment or agency relationship created between you and us as a result of these Terms of Use or use of the Site. You agree that these Terms of Use will not be construed against us by virtue of having drafted them. You hereby waive any and all defenses you may have based on the electronic form of these Terms of Use and the lack of signing by the parties hereto to execute these Terms of Use.&lt;/p&gt;
&lt;h2 id=&quot;contact-us&quot;&gt;Contact Us&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://itunes.apple.com/us/app/curb-it-how-to-park-on-a-hill/id1321599696?ls=1&amp;#x26;mt=8&quot;&gt;CurbIt iOS App&lt;/a&gt;
&lt;a href=&quot;http://monkey.work/&quot;&gt;CurbIt Blog&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Fix UICollectionView “Terminating with Uncaught NSException” Crashes]]></title><description><![CDATA[The problem: One of our views that contains a UICollectionView started crashing once in a while with the following descriptive error: libc…]]></description><link>https://www.monkey.work/2017-10-25-untitled/</link><guid isPermaLink="false">https://www.monkey.work/2017-10-25-untitled/</guid><pubDate>Wed, 25 Oct 2017 18:56:48 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One of our views that contains a UICollectionView started crashing once in a while with the following descriptive error:&lt;/p&gt;
&lt;p&gt;libc++abi.dylib: terminating with uncaught exception of type NSException&lt;/p&gt;
&lt;p&gt;Stack trace was not very helpful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The solution:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most of the uncaught exception in a collection view occur when it is being partially reloaded (insertItems, reloadItems, reloadSections, etc.) and its data-source is not what it expects to be.&lt;/p&gt;
&lt;p&gt;In our case, we re-used the same collection view and did not run &lt;code class=&quot;language-text&quot;&gt;self.collectionView.reloadData()&lt;/code&gt; before each re-use so adding that line of code fixed the issue.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Memory issues running Ghost with MySQL on tiny machines.]]></title><description><![CDATA[We have recently upgraded our Ghost servers from 0.11.11 to 1.8.4 and our blogs started crashing randomly. Since we installed Ghost using…]]></description><link>https://www.monkey.work/2017-09-08-mysql-ghost-memory-issues/</link><guid isPermaLink="false">https://www.monkey.work/2017-09-08-mysql-ghost-memory-issues/</guid><pubDate>Fri, 08 Sep 2017 23:42:22 GMT</pubDate><content:encoded>&lt;p&gt;We have recently upgraded our Ghost servers from 0.11.11 to &lt;a href=&quot;https://github.com/TryGhost/Ghost/releases&quot;&gt;1.8.4&lt;/a&gt; and our blogs started crashing randomly.&lt;/p&gt;
&lt;p&gt;Since we installed Ghost using its &lt;a href=&quot;https://github.com/TryGhost/Ghost-CLI&quot;&gt;CLI tool&lt;/a&gt; it was easy to see what el problema was by running: &lt;code class=&quot;language-text&quot;&gt;ghost ls&lt;/code&gt; to get the name of the blog and then &lt;code class=&quot;language-text&quot;&gt;ghost log blog_name&lt;/code&gt; to see that the error was &lt;code class=&quot;language-text&quot;&gt;ECONNREFUSED 127.0.0.1:3306&lt;/code&gt; (I wish they would have come up with something better).&lt;/p&gt;
&lt;p&gt;Unfortunately stabilizing MySQL on our tiny server was not simple and in fact the only thing that helped was a SWAP file.&lt;/p&gt;
&lt;p&gt;Here is how to create it on Ubuntu (hosted by &lt;a href=&quot;https://www.digitalocean.com&quot;&gt;DigitalOcean&lt;/a&gt;, thanks to &lt;a href=&quot;https://www.digitalocean.com/community/questions/mysql-server-stops-very-frequently&quot;&gt;juanjo2988&lt;/a&gt;).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo dd if=/dev/zero of=/swap.dat bs=1024 count=512k
sudo mkswap /swap.dat
sudo swapon /swap.dat&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and adding the following row to the &lt;code class=&quot;language-text&quot;&gt;fstab&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo vi /etc/fstab
________________________________________________

/swap.dat      none    swap    sw      0       0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To check that the file was create we can run: &lt;code class=&quot;language-text&quot;&gt;sudo swapon -s&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;An additional and in our case optional step would be to update MySQL config file (ususaly my.cnf) with &lt;code class=&quot;language-text&quot;&gt;innodb_buffer_pool_size=64M&lt;/code&gt; and reload it &lt;code class=&quot;language-text&quot;&gt;sudo service mysqld reload&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/blog/content/images/2017/09/sunday_sushi_roll.JPG&quot; alt=&quot;Plate of sushi rolls on a table&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;related&quot;&gt;Related&lt;/h2&gt;
&lt;p&gt;For more context on why this blog eventually moved away from Ghost entirely, see &lt;a href=&quot;/ghost-the-final-chapter/&quot;&gt;Ghost: why this blog no longer runs on it&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ReactiveSwift flatten Operator: latest, concat, and merge Explained]]></title><description><![CDATA[TL;DR  lets you model asynchronous units of work that can be started and composed.  turns a producer-of-producers into a single stream of…]]></description><link>https://www.monkey.work/2017-05-09-reactiveswift-flatten-and-some-of-its-strategies/</link><guid isPermaLink="false">https://www.monkey.work/2017-05-09-reactiveswift-flatten-and-some-of-its-strategies/</guid><pubDate>Tue, 09 May 2017 20:22:34 GMT</pubDate><content:encoded>&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;SignalProducer&lt;/code&gt; lets you model asynchronous units of work that can be started and composed.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;flatten&lt;/code&gt; turns a producer-of-producers into a single stream of values.&lt;/li&gt;
&lt;li&gt;Pick a strategy based on behavior: &lt;code class=&quot;language-text&quot;&gt;latest&lt;/code&gt; (keep newest), &lt;code class=&quot;language-text&quot;&gt;concat&lt;/code&gt; (run serially), &lt;code class=&quot;language-text&quot;&gt;merge&lt;/code&gt; (merge concurrent work).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;why-the-code-classlanguage-textflattencode-operator-matters&quot;&gt;Why the &lt;code class=&quot;language-text&quot;&gt;flatten&lt;/code&gt; operator matters&lt;/h2&gt;
&lt;p&gt;The power of ReactiveSwift lies in its operators. In this post you will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;See a callback-based solution for running an asynchronous task several times.&lt;/li&gt;
&lt;li&gt;Replace it with a &lt;code class=&quot;language-text&quot;&gt;SignalProducer&lt;/code&gt;-based solution.&lt;/li&gt;
&lt;li&gt;Learn how the &lt;code class=&quot;language-text&quot;&gt;flatten&lt;/code&gt; operator and its strategies &lt;code class=&quot;language-text&quot;&gt;latest&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;concat&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;merge&lt;/code&gt; change the behavior of your pipelines.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All examples use &lt;code class=&quot;language-text&quot;&gt;ReactiveSwift&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Result&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReactiveSwift&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foundation&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;the-problem-run-an-async-task-n-times-in-order&quot;&gt;The problem: run an async task N times, in order&lt;/h2&gt;
&lt;p&gt;Imagine your product manager gives you a function that does some work asynchronously:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;superSecretFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; completion&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token attribute atrule&quot;&gt;@escaping&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;qos&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userInitiated&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; diceRoll &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;arc4random_uniform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;99&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;usleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UInt32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;diceRoll&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Ran &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; on thread &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; for &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;diceRoll&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; milliseconds&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The requirement: &lt;strong&gt;run &lt;code class=&quot;language-text&quot;&gt;superSecretFunc&lt;/code&gt; several times, one after another, in order&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;naive-callback-solution&quot;&gt;Naive callback solution&lt;/h3&gt;
&lt;p&gt;The straightforward way is to nest callbacks:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CrappySolution1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;runTasks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Solution 1 ...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;superSecretFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;superSecretFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;superSecretFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;3&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;forTimeInterval&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; so1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CrappySolution1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
so1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;runTasks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This works, but even with three calls the nesting is already ugly:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Solution 1 ...
Ran 1 on thread &amp;lt;NSThread ...&gt; for 64 milliseconds
Ran 2 on thread &amp;lt;NSThread ...&gt; for 97 milliseconds
Ran 3 on thread &amp;lt;NSThread ...&gt; for 73 milliseconds&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;scaling-the-naive-approach&quot;&gt;Scaling the naive approach&lt;/h3&gt;
&lt;p&gt;What happens when the PM asks you to run it &lt;strong&gt;five&lt;/strong&gt; times?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CrappySolution2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;runTasks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Solution 2 ...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;superSecretFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;superSecretFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;superSecretFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;3&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token function&quot;&gt;superSecretFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;4&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;superSecretFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;5&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; so2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CrappySolution2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
so2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;runTasks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The code still works, but the indentation and nesting keep growing. If the requirement changes to 7, 9 or 100 times, the callback approach quickly becomes unmaintainable.&lt;/p&gt;
&lt;p&gt;That is where &lt;strong&gt;ReactiveSwift&lt;/strong&gt; comes to the rescue.&lt;/p&gt;
&lt;h2 id=&quot;a-better-approach-with-code-classlanguage-textsignalproducercode&quot;&gt;A better approach with &lt;code class=&quot;language-text&quot;&gt;SignalProducer&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;In ReactiveSwift, a &lt;code class=&quot;language-text&quot;&gt;SignalProducer&amp;lt;Value, Error&gt;&lt;/code&gt; represents a &lt;em&gt;description of work&lt;/em&gt; that can be started later. Each time you start it, it can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Send one or more &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt; events.&lt;/li&gt;
&lt;li&gt;Eventually send &lt;code class=&quot;language-text&quot;&gt;completed&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;failed&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Be composed with other producers using operators.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We can model each call to &lt;code class=&quot;language-text&quot;&gt;superSecretFunc&lt;/code&gt; as a &lt;code class=&quot;language-text&quot;&gt;SignalProducer&amp;lt;Void, NoError&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReactiveSolution1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;runTasks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;times&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Reactive Solution 1 ...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; producers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SignalProducer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NoError&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; i &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;times &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; sp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignalProducer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NoError&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; observer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;superSecretFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    observer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    observer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendCompleted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            producers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; fsp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignalProducer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SignalProducer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NoError&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NoError&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;producers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        fsp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flatten&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;concat&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;completed&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Done!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; rso1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReactiveSolution1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
rso1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;runTasks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;times&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now you can easily change &lt;code class=&quot;language-text&quot;&gt;times&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;100&lt;/code&gt; without changing any control flow code. The key operator here is &lt;code class=&quot;language-text&quot;&gt;flatten&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-code-classlanguage-textflattencode-operator&quot;&gt;The &lt;code class=&quot;language-text&quot;&gt;flatten&lt;/code&gt; operator&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;flatten&lt;/code&gt; is used when you have a &lt;strong&gt;producer of producers&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;SignalProducer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SignalProducer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It turns this into a &lt;strong&gt;single producer&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;SignalProducer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The behavior is controlled by the &lt;code class=&quot;language-text&quot;&gt;FlattenStrategy&lt;/code&gt; you pass to &lt;code class=&quot;language-text&quot;&gt;flatten&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;latest&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;concat&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;merge&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each strategy answers the question: &lt;em&gt;when several inner producers are involved, which ones are allowed to send values, and when does the whole chain complete?&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;code-classlanguage-textlatestcode&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;latest&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Subscribes to inner producers as they appear.&lt;/li&gt;
&lt;li&gt;Only the &lt;strong&gt;most recently started&lt;/strong&gt; inner producer is allowed to send values.&lt;/li&gt;
&lt;li&gt;When a new inner producer starts, previous ones are interrupted.&lt;/li&gt;
&lt;li&gt;Useful for things like “search as you type”: only the latest request matters.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-classlanguage-textconcatcode&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;concat&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Queues inner producers and starts them &lt;strong&gt;one after another&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The next producer does not start until the current one completes.&lt;/li&gt;
&lt;li&gt;The outer producer completes when &lt;strong&gt;all&lt;/strong&gt; inner producers have completed.&lt;/li&gt;
&lt;li&gt;Perfect when you want asynchronous work to run strictly in sequence.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can explore a marble diagram for &lt;code class=&quot;language-text&quot;&gt;concat&lt;/code&gt; here:&lt;br&gt;
&lt;a href=&quot;http://neilpa.me/rac-marbles/#concat&quot;&gt;Visualization&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;code-classlanguage-textmergecode&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;merge&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Starts all inner producers as they arrive.&lt;/li&gt;
&lt;li&gt;Values from all inner producers are &lt;strong&gt;interleaved&lt;/strong&gt; into a single stream.&lt;/li&gt;
&lt;li&gt;Completes when the outer producer and &lt;strong&gt;all&lt;/strong&gt; inner producers complete.&lt;/li&gt;
&lt;li&gt;Good when you care about every result, regardless of order.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is also a marble diagram for &lt;code class=&quot;language-text&quot;&gt;merge&lt;/code&gt;:&lt;br&gt;
&lt;a href=&quot;http://neilpa.me/rac-marbles/#merge&quot;&gt;Visualization&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;seeing-code-classlanguage-textlatestcode-code-classlanguage-textconcatcode-and-code-classlanguage-textmergecode-in-action&quot;&gt;Seeing &lt;code class=&quot;language-text&quot;&gt;latest&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;concat&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;merge&lt;/code&gt; in action&lt;/h2&gt;
&lt;p&gt;The following example uses three simple producers and then flattens them in different ways:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; s1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignalProducer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NoError&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; observer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    observer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//    observer.sendCompleted()&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; s2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignalProducer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NoError&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; observer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    observer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//    observer.sendCompleted()&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; s3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignalProducer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NoError&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; observer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    observer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;3&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    observer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendCompleted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; s5 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignalProducer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SignalProducer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NoError&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NoError&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;s1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; s2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; s3&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\n ---&gt; latest&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
s5&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flatten&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;latest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;completed&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;latest completed&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\n ---&gt; concat&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;concat never completes because it is waiting for all inputs (producers) to complete&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
s5&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flatten&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;concat&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;completed&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// will never complete&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;concat completed&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\n ---&gt; merge&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;like concat, merge never completes because it is waiting for all inputs (producers) to complete&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;unlike concat, merge will send all the values from each input&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
s5&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flatten&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;merge&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;completed&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// will never complete&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;merge completed&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Sample output might look like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt; ---&gt; latest
1
2
3
latest completed

 ---&gt; concat
concat never completes because it is waiting for all inputs (producers) to complete
1

 ---&gt; merge
like concat, merge never completes because it is waiting for all inputs (producers) to complete
unlike concat, merge will send all the values from each input
1
2
3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Callback-based code quickly becomes unreadable when you need to compose many asynchronous steps.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;SignalProducer&lt;/code&gt; lets you model “a unit of work” that can be started and composed.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;flatten&lt;/code&gt; turns a producer-of-producers into a single stream of values.&lt;/li&gt;
&lt;li&gt;Different strategies (&lt;code class=&quot;language-text&quot;&gt;latest&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;concat&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;merge&lt;/code&gt;) give you different behavior:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;latest&lt;/code&gt; → keep only the most recent work.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;concat&lt;/code&gt; → run work serially, one after another.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;merge&lt;/code&gt; → run work concurrently and merge all results.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can find the original source code here:&lt;br&gt;
&lt;a href=&quot;https://gist.github.com/seifeet/7e80c7ebead341df8bb78c0cef7109a1&quot;&gt;source code&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ReactiveSwift Playground Setup: Fix “No Such Module ‘Result’” in Xcode]]></title><description><![CDATA[According to the documentation, to use ReactiveSwift.playground the following steps must be performed: Retrieve the project dependencies…]]></description><link>https://www.monkey.work/2017-05-08-reactiveswift-opening-a-playground/</link><guid isPermaLink="false">https://www.monkey.work/2017-05-08-reactiveswift-opening-a-playground/</guid><pubDate>Mon, 08 May 2017 21:34:53 GMT</pubDate><content:encoded>&lt;p&gt;According to the documentation, to use ReactiveSwift.playground the following steps must be performed:&lt;/p&gt;
&lt;p&gt;Retrieve the project dependencies using one of the following terminal commands from the ReactiveSwift project root directory:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;git submodule update --init&lt;/code&gt;&lt;strong&gt;OR&lt;/strong&gt;, if you have Carthage installed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;carthage checkout&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open &lt;code class=&quot;language-text&quot;&gt;ReactiveSwift.xcworkspace&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Build &lt;code class=&quot;language-text&quot;&gt;Result-Mac&lt;/code&gt; scheme&lt;/li&gt;
&lt;li&gt;Build &lt;code class=&quot;language-text&quot;&gt;ReactiveSwift-macOS&lt;/code&gt; scheme&lt;/li&gt;
&lt;li&gt;Finally open the &lt;code class=&quot;language-text&quot;&gt;ReactiveSwift.playground&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Choose View &gt; Show Debug Area&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt; is that we get the following error once we open the playground:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;playground no such module &apos;result&apos;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The solution&lt;/strong&gt; is to open the playground from the workspace.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open &lt;code class=&quot;language-text&quot;&gt;ReactiveSwift.xcworkspace&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;In the project navigator expand &lt;code class=&quot;language-text&quot;&gt;ReactiveSwift&lt;/code&gt; and then&lt;/li&gt;
&lt;li&gt;Click on &lt;code class=&quot;language-text&quot;&gt;Sandbox&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/blog/content/images/2017/05/reactive_sandbox.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ReactiveSwift: The Key Difference Between Signal and SignalProducer]]></title><description><![CDATA[A  in ReactiveSwift allows us to track its changes over time. A  is thread-safe and observable. A  is a  that can also be changed…]]></description><link>https://www.monkey.work/2017-03-08-reactiveswift-a-very-important-difference-between-a-signal-and-a-signalproducer/</link><guid isPermaLink="false">https://www.monkey.work/2017-03-08-reactiveswift-a-very-important-difference-between-a-signal-and-a-signalproducer/</guid><pubDate>Thu, 09 Mar 2017 00:36:40 GMT</pubDate><content:encoded>&lt;p&gt;A &lt;code class=&quot;language-text&quot;&gt;Property&lt;/code&gt; in ReactiveSwift allows us to track its changes over time.&lt;/p&gt;
&lt;p&gt;A &lt;code class=&quot;language-text&quot;&gt;Property&lt;/code&gt; is thread-safe and observable.&lt;/p&gt;
&lt;p&gt;A &lt;code class=&quot;language-text&quot;&gt;MutableProperty&lt;/code&gt; is a &lt;code class=&quot;language-text&quot;&gt;Property&lt;/code&gt; that can also be changed.&lt;/p&gt;
&lt;p&gt;Frequently we will use the following pattern to expose only non-muttable property in the interface:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;interface&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var nonMutableProperty: Property&amp;lt;Bool&gt; { get }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;implementation&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;public var nonMutableProperty: Property&amp;lt; Bool&gt; { return Property(_nonMutableProperty) }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;…&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;fileprivate let _ nonMutableProperty: MutableProperty&amp;lt;Bool&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A &lt;code class=&quot;language-text&quot;&gt;Property&lt;/code&gt; exposes a &lt;code class=&quot;language-text&quot;&gt;signal&lt;/code&gt; and a &lt;code class=&quot;language-text&quot;&gt;producer&lt;/code&gt; to which we subscribe to track changes.
&lt;em&gt;For example:&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;nonMutableProperty.producer
    .on(value: { embeddableView in

        log.verbose(&quot;do something&quot;)
    })
    .on(failed: { error in
        log.error(error.localizedDescription)
    })
    .start()&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;http://www.martinrichter.net/blog/2016/07/30/exposing-view-model-properties-in-rac/&quot;&gt;The difference&lt;/a&gt; is that the &lt;code class=&quot;language-text&quot;&gt;signal&lt;/code&gt; will only send value changes that happen &lt;strong&gt;after subscribing&lt;/strong&gt;, whereas the &lt;code class=&quot;language-text&quot;&gt;SignalProducer&lt;/code&gt; creates a &lt;code class=&quot;language-text&quot;&gt;signal&lt;/code&gt; that sends the current value &lt;strong&gt;immediately&lt;/strong&gt; followed by all changes later on.&lt;/p&gt;
&lt;p&gt;In other words &lt;code class=&quot;language-text&quot;&gt;signal&lt;/code&gt; is a &lt;strong&gt;hot&lt;/strong&gt; observable because like a TV broadcast it sends values even if it does not have any subscribers and as a result does not introduce any side-effects. &lt;code class=&quot;language-text&quot;&gt;SignalProducer&lt;/code&gt; is a &lt;strong&gt;cold&lt;/strong&gt; observable because it is created only after it is being subscribed to.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Optimize UICollectionView and UITableView Updates with Batch Inserts in Swift]]></title><description><![CDATA[The problem: Collection view is highly optimized and works great out of the box. It is really hard to slow it down or to make scrolling…]]></description><link>https://www.monkey.work/2016-12-22-a-simple-optimization-for-updating-a-uicollectionview-or-uitableview/</link><guid isPermaLink="false">https://www.monkey.work/2016-12-22-a-simple-optimization-for-updating-a-uicollectionview-or-uitableview/</guid><pubDate>Thu, 22 Dec 2016 18:30:13 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Collection view is highly optimized and works great out of the box. It is really hard to slow it down or to make scrolling sloppy when the number of items displayed is relatively small (~ a couple of hundreds). Even doing &lt;code class=&quot;language-text&quot;&gt;reloadData&lt;/code&gt; every other second will not make a huge difference.&lt;/p&gt;
&lt;p&gt;Troubles begin once the number of items starts growing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One simple way to improve CPU usage is to perform batch updates instead of reloading the entire data source each time new items are added to the data source.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;func appendCollectionView(numberOfItems count:Int){

    if let viewModel = viewModel {
        // calculate indexes for the items to be added
        let firstIndex = dataSource.count - count
        let lastIndex = dataSource.count - 1

        var indexPaths = [IndexPath]()
        for index in firstIndex...lastIndex {
            let indexPath = IndexPath(item: index, section: 0)
            indexPaths.append(indexPath)
        }

        // finally update the collection view
        collectionView.performBatchUpdates({ () -&gt; Void in
            collectionView.insertItems(at: indexPaths)
        }, completion: { (finished) -&gt; Void in

        })
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In our case, CPU usage went down from 90% to 10%!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Swift 3 Compiler Segmentation Fault 11: Generics and Nested Functions Workaround]]></title><description><![CDATA[The problem: The following code is causing a compiler crash when compiled with Swift 3 and Xcode 8: By the process of elimination we were…]]></description><link>https://www.monkey.work/2016-10-06-swift-compiler-error-when-using-generics-and-nested-functions-command-failed-due-to-signal-segmentation-fault-11/</link><guid isPermaLink="false">https://www.monkey.work/2016-10-06-swift-compiler-error-when-using-generics-and-nested-functions-command-failed-due-to-signal-segmentation-fault-11/</guid><pubDate>Thu, 06 Oct 2016 17:53:20 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The following code is causing a compiler crash when compiled with Swift 3 and Xcode 8:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import Foundation

public typealias IntCompletion = (Int) -&gt; Void
public typealias FetchIntBlock = (IntCompletion) -&gt; Void

class ATSwiftTest&amp;lt;T: NSURL&gt;: NSObject {

    var fetchIntBlock: FetchIntBlock

    override init() {
        fetchIntBlock = { (aFunc) in }

        super.init()
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;By the process of elimination we were able to figure out that the problem is caused by the nested function declaration.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;var fetchIntBlock: FetchIntBlock&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The code crashes a Playground compiler as well.&lt;/p&gt;
&lt;p&gt;Solution:&lt;/p&gt;
&lt;p&gt;If we make the &lt;code class=&quot;language-text&quot;&gt;fetchIntBlock&lt;/code&gt; var &lt;code class=&quot;language-text&quot;&gt;private&lt;/code&gt; the code compiles just fine.&lt;/p&gt;
&lt;p&gt;It would still be nice for the var to be public.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Xcode 8 Core Data: Create NSManagedObject Subclasses in a Swift Project]]></title><description><![CDATA[The problem: In Xcode 8, it is no longer possible to create NSManagedObject subclasses by going to File menu and choosing New > File and…]]></description><link>https://www.monkey.work/2016-09-21-creating-nsmanagedobject-subclasses-from-a-model-in-xcode-8/</link><guid isPermaLink="false">https://www.monkey.work/2016-09-21-creating-nsmanagedobject-subclasses-from-a-model-in-xcode-8/</guid><pubDate>Wed, 21 Sep 2016 15:47:13 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In Xcode 8, it is no longer possible to create NSManagedObject subclasses by going to File menu and choosing New &gt; File and then selecting NSManagedObject subclass from the iOS &gt; Core Data section. That option simply does not exist.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The solution:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Select the model in the Project Navigator on the left and go to Editor menu and choose Create NSManagedObject Subclasses from there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A sidenote:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If the project was upgraded to Swift3 there is a good chance that the wizard will generate Objective C files instead of Swift files.&lt;/p&gt;
&lt;p&gt;To fix that go to the selected model’s File Inspector and change Code Generation language to swift.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Swift Error Handling: Catching Throwing Functions with a Helper Wrapper]]></title><description><![CDATA[Recently we had a need in a general way to catch calls that can throw an exception. Like with any functional or semi-functional language it…]]></description><link>https://www.monkey.work/2016-08-31-catching-an-error-in-swift-using-functions-2/</link><guid isPermaLink="false">https://www.monkey.work/2016-08-31-catching-an-error-in-swift-using-functions-2/</guid><pubDate>Wed, 31 Aug 2016 23:07:30 GMT</pubDate><content:encoded>&lt;p&gt;Recently we had a need in a general way to catch calls that can throw an exception.&lt;/p&gt;
&lt;p&gt;Like with any functional or semi-functional language it is fairly easy to create weird looking constructs with Swift.&lt;/p&gt;
&lt;p&gt;Let’s create one but first a function that simply throws an error:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;func throwAnError() throws -&gt; Void {
    throw DivisionError.ByZero
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If we call &lt;code class=&quot;language-text&quot;&gt;throwAnError()&lt;/code&gt; it must be surrounded by a &lt;code class=&quot;language-text&quot;&gt;do...catch&lt;/code&gt; block.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;do {
    try throwAnError()
} catch let error as NSError {
    print(&quot;Unable to divide by zero:&quot;, error)
} catch {
    print(&quot;Failed miserably and have no idea what happened.&quot;)
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And it is all great until we have to call &lt;code class=&quot;language-text&quot;&gt;throwAnError()&lt;/code&gt; many many times in many different places…
For that we can create a function that will do that for us!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;func printError(completion: () throws -&gt; Void)
{
    do {
        try completion()
    } catch let error as NSError {
        print(&quot;Unable to divide by zero:&quot;, error)
    } catch {
        print(&quot;Failed miserably and have no idea what happened.&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And now we can simply run it as a block:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;printError({
    try throwAnError()
})&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Fix “was deallocated while key value observers were still registered” Crashes in Swift]]></title><description><![CDATA[Debugging errors that are a result of observers being added to an object and not removed from it can be hard because the error “xxx was…]]></description><link>https://www.monkey.work/2016-08-03-debugging-xxx-was-deallocated-while-key-value-observers-were-still-registered-with-it/</link><guid isPermaLink="false">https://www.monkey.work/2016-08-03-debugging-xxx-was-deallocated-while-key-value-observers-were-still-registered-with-it/</guid><pubDate>Thu, 04 Aug 2016 01:02:20 GMT</pubDate><content:encoded>&lt;p&gt;Debugging errors that are a result of observers being added to an object and not removed from it can be hard because the error “xxx was deallocated while key value observers were still registered with it” does not tell us anything about the observers in question.&lt;/p&gt;
&lt;p&gt;An approach we find useful is to override &lt;code class=&quot;language-text&quot;&gt;addObserver&lt;/code&gt; function and put a breakpoint the to see what observers are being added to the object.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;override func addObserver(observer: NSObject, forKeyPath keyPath: String, options: NSKeyValueObservingOptions, context: UnsafeMutablePointer&amp;lt;Void&gt;) {
    print(keyPath)
    super.addObserver(observer, forKeyPath: keyPath, options: options, context: context)
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If the object is a collection view, a table view, or any other object in a storyboard or a xib file, we can always inherit and use our inherited object in the Class field of the identity inspector.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;class CustomCollectionView: UICollectionView {
    deinit {
        print(&quot;deinit&quot;)
    }

    override func addObserver(observer: NSObject, forKeyPath keyPath: String, options: NSKeyValueObservingOptions, context: UnsafeMutablePointer&amp;lt;Void&gt;) {
        print(keyPath)
        super.addObserver(observer, forKeyPath: keyPath, options: options, context: context)
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Update Git to respect a newly added .gitignore]]></title><description><![CDATA[Adding a  file to an existing project will prevent untracked files being added to the working tree but the files will still remain at the…]]></description><link>https://www.monkey.work/2016-08-01-update-git-to-respect-a-newly-added-gitignore/</link><guid isPermaLink="false">https://www.monkey.work/2016-08-01-update-git-to-respect-a-newly-added-gitignore/</guid><pubDate>Mon, 01 Aug 2016 18:09:08 GMT</pubDate><content:encoded>&lt;p&gt;Adding a &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; file to an existing project will prevent untracked files being added to the working tree but the files will still remain at the staging area (Git Index).&lt;/p&gt;
&lt;p&gt;But not all is lost and it is possible to remove the files we want to ignore from Git Index.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;git rm -r --cached .
git add .&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Importing a Shapefile into MongoDB using GeoJSON]]></title><description><![CDATA[Majority of map-related data provided by government agencies is in the form of geospatial vector data stored in a semi-proprietary and…]]></description><link>https://www.monkey.work/2016-06-30-importing-a-shapefile-into-mongodb/</link><guid isPermaLink="false">https://www.monkey.work/2016-06-30-importing-a-shapefile-into-mongodb/</guid><pubDate>Fri, 01 Jul 2016 02:55:48 GMT</pubDate><content:encoded>&lt;p&gt;Majority of map-related data provided by government agencies is in the form of geospatial vector data stored in a semi-proprietary and complex format known as a Shapefile.&lt;/p&gt;
&lt;p&gt;Shapefiles are technology from the past when desktops ruled supreme. Nowadays, developers want a simpler way to access data.&lt;/p&gt;
&lt;p&gt;It is not a snap, importing a Shapefile (a file containing geospatial vector data) into a document-oriented database.&lt;/p&gt;
&lt;p&gt;Step 1:&lt;/p&gt;
&lt;p&gt;We will use &lt;code class=&quot;language-text&quot;&gt;ogr2ogr&lt;/code&gt; to convert a Shapefile to GeoJSON.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;$ brew install goal&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;$ ogr2ogr -f GeoJSON -t_srs crs:84 file_name.geojson file_name.shp&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;-t_srs cos:84 option ensures that the right projection is being rendered and that GPS coordinates are properly encoded.&lt;/p&gt;
&lt;p&gt;Step 2:&lt;/p&gt;
&lt;p&gt;We will use &lt;code class=&quot;language-text&quot;&gt;mongoimport&lt;/code&gt; to import the file into MongoDB.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;mongoimport --db dev -c points --file &quot;file_name.geojson&quot; --jsonArray&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;--jsonArray&lt;/code&gt; tells mongoimport to expect an array of objects so we might have to open the Shapefile and pull out the “features” array.&lt;/p&gt;
&lt;p&gt;Alternatively we can use &lt;code class=&quot;language-text&quot;&gt;--type json&lt;/code&gt; option instead:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;mongoimport --db dev -c points --file &quot;file_name.geojson&quot; --type json&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Step 3:&lt;/p&gt;
&lt;p&gt;To make the data queryable we have to add a &lt;code class=&quot;language-text&quot;&gt;2dsphere&lt;/code&gt; index to the field that contains geometry information. In our case the field was actually named “geometry.”&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;geometry&quot;: {
	&quot;type&quot;: &quot;LineString&quot;,
	&quot;coordinates&quot;: [
		[-122.466597727410203, 37.72592447531234],
		[-122.466538207925268, 37.726008134595659],
		[-122.466545014733939, 37.726092655875313],
		[-122.46662269919527, 37.726149395842484],
		[-122.466715781686247, 37.726165938701818],
		[-122.466832802132004, 37.726142308565606],
		[-122.466897677698299, 37.72606137786606],
		[-122.466866500919124, 37.7259772625764],
		[-122.466740909948271, 37.725929815795403],
		[-122.466597727410203, 37.72592447531234]
	]
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;db.sfsweeproutes.ensureIndex({&quot;geometry&quot;:&quot;2dsphere&quot;})&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Step 4:&lt;/p&gt;
&lt;p&gt;Let’s have fun!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;db.sfsweeproutes.find({geometry:{ $near :{$geometry: { type: &quot;Point&quot;,  coordinates:[ -122.46654501473394, 37.72609265587531 ] },$minDistance: 0, $maxDistance: 10}}})&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[VIPER Architecture in Swift: How to Decouple iOS Modules]]></title><description><![CDATA[Decouple - separate, disengage, or dissociate (something) from something else. Why do we need it? Decoupling makes our code easier to…]]></description><link>https://www.monkey.work/2016-06-29-viper-architecture-with-swift/</link><guid isPermaLink="false">https://www.monkey.work/2016-06-29-viper-architecture-with-swift/</guid><pubDate>Wed, 29 Jun 2016 23:16:55 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Decouple&lt;/strong&gt; - separate, disengage, or dissociate (something) from something else.&lt;/p&gt;
&lt;p&gt;Why do we need it?&lt;/p&gt;
&lt;p&gt;Decoupling makes our code easier to maintain because changing one part of the code will not cause issues with the rest of the code. It also simplifies code testing, especially unit testing. If many developers work on the same project, it is easier to divide the work into independent tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Module&lt;/strong&gt; - a distinctive attribute or aspect of something, is just another word for a feature. A Module in iOS presents itself to the outside world as a standard UIViewController&lt;/p&gt;
&lt;p&gt;That is where &lt;strong&gt;VIPER&lt;/strong&gt; enters the scene.
&lt;strong&gt;VIPER&lt;/strong&gt; is just a way to separate an application into distinct layers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;V&lt;/strong&gt;iew simply holds the layout. It can be stored as a XIB file, a storyboard, or even a text file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I&lt;/strong&gt;nteractor is responsible for networking operations, persistence, business logic, data management and so on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P&lt;/strong&gt;resenter is responsible for strictly updating the view (EventHandler) and Wireframe initializes modules and defines the navigation to the next module.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;E&lt;/strong&gt;ntity is the object model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;R&lt;/strong&gt;outer is in charge of navigation.&lt;/p&gt;
&lt;p&gt;Let’s see what happens with VIPER when a user taps a button on the screen…&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Flow&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/blog/content/images/2016/08/viper_overview.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;View -&gt; ViewController -&gt; EventHandler -&gt; Presenter -&gt; InteractorInput -&gt; Interactor {do some long operation} ... response ... Interactor -&gt; InteractorOutput -&gt; Presenter -&gt; Wireframe -&gt; ViewController -&gt; View&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;To glue all the parts together VIPER is extensively using protocols.&lt;/p&gt;
&lt;p&gt;EventHandler is a protocol that connects a View Controller to a Presenter.&lt;/p&gt;
&lt;p&gt;InteractorInput and  InteractorOutput  are the protocols that associate a Presenter with an Interactor.&lt;/p&gt;
&lt;p&gt;Some helpful links:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/mutualmobile/VIPER-SWIFT&quot;&gt;A to-do list example app that uses VIPER architecture&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.objc.io/issues/13-architecture/viper/&quot;&gt;Intro to VIPER architecture by objc.io&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Debug Auto Layout Constraints with Xcode 7’s Debug View Hierarchy]]></title><description><![CDATA[Until a tiny barely noticeable button was added to the debug area of Xcode, it used to take a number of breakpoints and print statements to…]]></description><link>https://www.monkey.work/2016-06-28-undebug-view-hierarchy-titled/</link><guid isPermaLink="false">https://www.monkey.work/2016-06-28-undebug-view-hierarchy-titled/</guid><pubDate>Tue, 28 Jun 2016 16:50:12 GMT</pubDate><content:encoded>&lt;p&gt;Until a tiny barely noticeable button was added to the debug area of Xcode, it used to take a number of breakpoints and print statements to debug view constraints.&lt;/p&gt;
&lt;p&gt;Not anymore! A small button named &lt;strong&gt;“Debug view hierarchy,”&lt;/strong&gt; displays current view hierarchy in 3D. All we have to do is to start our app in debug mode and click on the button when a view we want to debug is being displayed.&lt;/p&gt;
&lt;p&gt;We can rotate the view by click-dragging the mouse and at the by selecting one of the views and clicking on it at the top we can view all its constraints!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Using guard in Swift to Test for Empty Strings Safely]]></title><description><![CDATA[The code to check and bail out if a string is NULL or empty most of the time looks something like that: In Swift, we have a syntax candy…]]></description><link>https://www.monkey.work/2016-06-15-using-guard-to-test-for-an-empty-string/</link><guid isPermaLink="false">https://www.monkey.work/2016-06-15-using-guard-to-test-for-an-empty-string/</guid><pubDate>Wed, 15 Jun 2016 23:05:27 GMT</pubDate><content:encoded>&lt;p&gt;The code to check and bail out if a string is NULL or empty most of the time looks something like that:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;if self.text == nil || self.text!.isEmpty  {
    return
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In Swift, we have a syntax candy called &lt;code class=&quot;language-text&quot;&gt;guard&lt;/code&gt; that executes statements based on a Boolean value of an expression. With guard in mind we can re-write the code above:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;guard let gText = self.text where !gText.isEmpty else {
    return
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Of course the question is whether using guard makes the code cleaner or more confusing.&lt;/p&gt;
&lt;p&gt;It is up to you to decide!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Stack Views on iOS 8 with Auto Layout (Without UIStackView)]]></title><description><![CDATA[While starting iOS 9 we have UIStackView, it is a little bit tricky to stack views vertically or horizontally on iOS 8 using AutoLayout and…]]></description><link>https://www.monkey.work/2016-05-25-stack-views-vertically-or-horizontally-on-ios-8-using-autolayout/</link><guid isPermaLink="false">https://www.monkey.work/2016-05-25-stack-views-vertically-or-horizontally-on-ios-8-using-autolayout/</guid><pubDate>Thu, 26 May 2016 00:21:05 GMT</pubDate><content:encoded>&lt;p&gt;While starting iOS 9 we have UIStackView, it is a little bit tricky to stack views vertically or horizontally on iOS 8 using AutoLayout and without using any helper views.&lt;/p&gt;
&lt;p&gt;For a horizontal arrangement the trick is to position the views and add “Align Center X to superview” to all subviews that we want to stack. Constraint’s Constant should be set to 0 and Multiplier should be set to adjust view’s position relative to the center.&lt;/p&gt;
&lt;p&gt;For example to position 2 views the multiplier can be set to 3:5 for the first view and 7:5 for the second view.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[iOS Auto Layout in Xcode: Position Views Relative to Each Other]]></title><description><![CDATA[Positioning views relative to one another with auto-layout was fairly tricky until, Adding Layout Constraints by Control-Dragging, was…]]></description><link>https://www.monkey.work/2016-04-21-new-autolayout-features/</link><guid isPermaLink="false">https://www.monkey.work/2016-04-21-new-autolayout-features/</guid><pubDate>Thu, 21 Apr 2016 09:33:09 GMT</pubDate><content:encoded>&lt;p&gt;Positioning views relative to one another with auto-layout was fairly tricky until, &lt;a href=&quot;https://developer.apple.com/library/mac/recipes/xcode_help-IB_auto_layout/chapters/add_constraints-control_drag.html&quot;&gt;Adding Layout Constraints by Control-Dragging&lt;/a&gt;, was intruded in Xcode.&lt;/p&gt;
&lt;p&gt;Now, to make one view 10pt below another view, all we have to do is to ctrl-drag first view into the second view and select a constraint.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Swift Cheat Sheet: Common Syntax, Patterns, and Shortcuts]]></title><description><![CDATA[- readonly var   - Optional (can be not set)   - unwrap an optional (will crash if nil) all properties have to be initialized and optionals…]]></description><link>https://www.monkey.work/2016-03-27-swift-cheat-sheet/</link><guid isPermaLink="false">https://www.monkey.work/2016-03-27-swift-cheat-sheet/</guid><pubDate>Sun, 27 Mar 2016 10:18:27 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt; - readonly var&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;println(“ \() “)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;?&lt;/code&gt; - Optional (can be not set)&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;      enum Optional&amp;lt;T&gt; {          case None          case Some(T)       }&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;!&lt;/code&gt; - unwrap an optional (will crash if nil)&lt;/p&gt;
&lt;p&gt;all properties have to be &lt;strong&gt;initialized&lt;/strong&gt; and optionals are nil by default&lt;/p&gt;
&lt;p&gt;getters and setters
&lt;code class=&quot;language-text&quot;&gt;var name:type {   get {   }   set {     newValue   } }&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;function signature shortcuts
&lt;code class=&quot;language-text&quot;&gt;{ (op1: Double, op2: Double) -&gt; Double in return op1 * op2 } { (op1, op2) in op1 * op2 } { $0 * $1 }&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;array and map definition shortcuts
&lt;code class=&quot;language-text&quot;&gt;Array&amp;lt;Double&gt; - [Double] Dictionary&amp;lt;String, Double&gt; - [String:Double]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Only classes are passed by reference (not structs)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Printable protocol:
&lt;code class=&quot;language-text&quot;&gt;var description: String {  get {  } }&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;final&lt;/code&gt; prevents subclasses from using override&lt;/p&gt;
&lt;p&gt;observe changes to a property &lt;code class=&quot;language-text&quot;&gt;willSet&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;didSet&lt;/code&gt; {newValue oldValue}&lt;/p&gt;
&lt;p&gt;a property can be &lt;code class=&quot;language-text&quot;&gt;lazy&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;required &amp;#x26; convenience init&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;init?(…)&lt;/code&gt; is a &lt;strong&gt;failable&lt;/strong&gt; init&lt;/p&gt;
&lt;p&gt;casting as or as? (will not crash)
&lt;code class=&quot;language-text&quot;&gt;if let buttonItem = item as? UIButton { … }&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;preferredFontForTextStyle&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;set &lt;code class=&quot;language-text&quot;&gt;contentMode&lt;/code&gt; to properly redraw on view bounds change&lt;/p&gt;
&lt;p&gt;add &lt;code class=&quot;language-text&quot;&gt;@IBDesignable&lt;/code&gt; to a view to allow Xcode to draw it and &lt;code class=&quot;language-text&quot;&gt;@IBInspectable&lt;/code&gt; to allow to modify properties from Xcode&lt;/p&gt;
&lt;p&gt;use class in protocol to allow only classes (and not structs or enums) to implement it&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;??&lt;/code&gt; if left side is nil use right side&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;fallthrough&lt;/code&gt; in switch means execute the next case&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Location block and rewriting URLs]]></title><description><![CDATA[URI - the part of URL that comes after the domain (or IP/Port) name.
http://curbsidr.com/guests/35/services/34/locations/33  is the URI…]]></description><link>https://www.monkey.work/2015-08-04-nginx-redirecting-some/</link><guid isPermaLink="false">https://www.monkey.work/2015-08-04-nginx-redirecting-some/</guid><pubDate>Tue, 04 Aug 2015 20:20:21 GMT</pubDate><content:encoded>&lt;p&gt;URI - the part of URL that comes after the domain (or IP/Port) name.
&lt;a href=&quot;http://curbsidr.com/guests/35/services/34/locations/33&quot;&gt;http://curbsidr.com/guests/35/services/34/locations/33&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;/guests/35/services/34/locations/33&lt;/code&gt; is the URI&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;location block allows 4 optional modifiers and a string to match against the URI:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;=&lt;/strong&gt; URI exactly matches location given&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;~&lt;/strong&gt; case sensitive RegEx match&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;~&lt;/strong&gt;* case insensitive RegEx match&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;^~&lt;/strong&gt; optional RegEx match (RegEx is evaluated only if a regular match did not occur)&lt;/p&gt;
&lt;p&gt;If no modifier is present, the match is interpreted as a prefix match.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;location = /guests/35&lt;/code&gt; will only match&lt;/p&gt;
&lt;p&gt;/guests/35&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;location ~ /guests/35&lt;/code&gt; will match&lt;/p&gt;
&lt;p&gt;/guests/35&lt;/p&gt;
&lt;p&gt;/guests/35/services/34/locations/33&lt;/p&gt;
&lt;p&gt;but will NOT match&lt;/p&gt;
&lt;p&gt;/Guests/35/services/34/locations/33&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;location ~ /guests/35&lt;/code&gt; will match&lt;/p&gt;
&lt;p&gt;/guests/35&lt;/p&gt;
&lt;p&gt;/guests/35/services/34/locations/33&lt;/p&gt;
&lt;p&gt;/Guests/35/services/34/locations/33&lt;/p&gt;
&lt;p&gt;and so on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;the tricky part&lt;/strong&gt; is the order in which multiple location blocks are evaluated.
&lt;br /&gt;In general the longest most specific match wins but there are exceptions:
&lt;br /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;no modifier&lt;/strong&gt; or &lt;strong&gt;=&lt;/strong&gt; gives an exact match &lt;strong&gt;done&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;else find the longest matching prefix&lt;/li&gt;
&lt;li&gt;the longest matching prefix has &lt;strong&gt;^~&lt;/strong&gt; modifier &lt;strong&gt;done&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;evaluate regular expressions, first match &lt;strong&gt;done&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;no match, use previously stored longest matching prefix&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So the order in which location block are positioned matters!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Renewing GoDaddy SSL certificate]]></title><description><![CDATA[locate  in the Nginx config file, for example:
 locate  in the Nginx config file, for example:
 you will not need the private key file but…]]></description><link>https://www.monkey.work/2015-08-02-renewing-godaddy-ssl-certificate-on-nginx/</link><guid isPermaLink="false">https://www.monkey.work/2015-08-02-renewing-godaddy-ssl-certificate-on-nginx/</guid><pubDate>Sun, 02 Aug 2015 08:07:35 GMT</pubDate><content:encoded>&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;locate &lt;code class=&quot;language-text&quot;&gt;ssl_certificate entry&lt;/code&gt; in the Nginx config file, for example:
&lt;code class=&quot;language-text&quot;&gt;ssl_certificate /etc/ssl/secure/yoursite.com_chained.crt;&lt;/code&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;locate &lt;code class=&quot;language-text&quot;&gt;ssl_certificate_key&lt;/code&gt; in the Nginx config file, for example:
&lt;code class=&quot;language-text&quot;&gt;ssl_certificate_key /etc/ssl/secure/yoursite.com.key;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;you will not need the private key file but you will need the public one &lt;code class=&quot;language-text&quot;&gt;yoursite.com.csr&lt;/code&gt; and the file is likely to be located in the same folder as the private key file.&lt;/em&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;find &lt;code class=&quot;language-text&quot;&gt;yoursite.com.csr&lt;/code&gt; file (most likely should be located at &lt;code class=&quot;language-text&quot;&gt;/etc/ssl/secure/&lt;/code&gt; folder)&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Purchase a new SSL certificate from GoDaddy “Set Up” and “Launch” the certificate. Provide &lt;code class=&quot;language-text&quot;&gt;yoursite.com.csr&lt;/code&gt; contents when asked.&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After the domain is validated and certificate is issued Download Certificate for Apache server&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You will receive a zip file that contains two files &lt;code class=&quot;language-text&quot;&gt;xxxxxx.crt&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;gd_bundle-g2-g1.crt&lt;/code&gt;. Nginx requires us to combine the two files into one.
&lt;code class=&quot;language-text&quot;&gt;cat xxxxxx.crt gd_bundle-g2-g1.crt &gt; yoursite.com_chained.crt&lt;/code&gt;&lt;/p&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Rename &lt;code class=&quot;language-text&quot;&gt;xxxxxx.crt&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;yoursite.com.crt&lt;/code&gt;&lt;/p&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Copy the new &lt;code class=&quot;language-text&quot;&gt;yoursite.com.crt&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;yoursite.com_chained.crt&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;/etc/ssl/secure/&lt;/code&gt; and restart Nginx.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[pip IncompleteRead error after Ubuntu upgrade]]></title><description><![CDATA[After upgrading ubuntu pip started throwing the following error:  It looks like the problem is caused by system installing old version of…]]></description><link>https://www.monkey.work/2015-07-18-python-pip-incompleteread-error-after-ubuntu-upgrade/</link><guid isPermaLink="false">https://www.monkey.work/2015-07-18-python-pip-incompleteread-error-after-ubuntu-upgrade/</guid><pubDate>Sun, 19 Jul 2015 02:51:38 GMT</pubDate><content:encoded>&lt;p&gt;After upgrading ubuntu pip started throwing the following error:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;ImportError: cannot import name IncompleteRead&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;It looks like the problem is caused by system installing old version of pip:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;remove pip
&lt;code class=&quot;language-text&quot;&gt;sudo apt-get purge python-pip &lt;/code&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;install latest version of pip
&lt;code class=&quot;language-text&quot;&gt;wget https://bootstrap.pypa.io/get-pip.py&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;python get-pip.py&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;reboot the server
&lt;code class=&quot;language-text&quot;&gt;sudo reboot&lt;/code&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Adding an external library]]></title><description><![CDATA[We needed to add Volley networking library into our project and a quick search returned the following solution:  While this is great and…]]></description><link>https://www.monkey.work/2015-07-07-android-how-add-an-external-library-into-android-studio-project/</link><guid isPermaLink="false">https://www.monkey.work/2015-07-07-android-how-add-an-external-library-into-android-studio-project/</guid><pubDate>Wed, 08 Jul 2015 00:26:08 GMT</pubDate><content:encoded>&lt;p&gt;We needed to add Volley networking library into our project and a quick search returned the following solution:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;  dependencies {       compile &apos;com.mcxiaoke.volley:library-aar:1.0.0&apos;   }&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;While this is great and mcxiaoke.volley has thousands of stars on github this is not the official repository.&lt;/p&gt;
&lt;p&gt;No giving up! There is an easy way to do it using the official repository, thanks to &lt;a href=&quot;http://stackoverflow.com/users/1680919/levit&quot;&gt;Levit&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;add Volley as a submodule
&lt;code class=&quot;language-text&quot;&gt;git submodule add -b master https://android.googlesource.com/platform/frameworks/volley volley&lt;/code&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;add Volley as a grade module in settings.gradle
&lt;code class=&quot;language-text&quot;&gt;include &apos;:volley&apos;&lt;/code&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;add Volley as a compile dependency in build.graddle (Module.app) in dependencies
&lt;code class=&quot;language-text&quot;&gt;compile project(&apos;:volley&apos;)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Later, if there is a need, it is easy to update the submodule.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;1. cd volley
2. git pull
3. cd ..
4. git commit -am &quot;great new update from volley&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Cutting off songs early]]></title><description><![CDATA[We all understand the frustration users experience when software malfunctions. My problems began with after I switched to a bright new MBP…]]></description><link>https://www.monkey.work/2015-07-02-itunes-cutting-off-songs-early/</link><guid isPermaLink="false">https://www.monkey.work/2015-07-02-itunes-cutting-off-songs-early/</guid><pubDate>Thu, 02 Jul 2015 07:47:36 GMT</pubDate><content:encoded>&lt;p&gt;We all understand the frustration users experience when software malfunctions.&lt;/p&gt;
&lt;p&gt;My problems began with after I switched to a bright new MBP and copied my iTunes library onto it.&lt;/p&gt;
&lt;p&gt;All songs that were 100% legally acquired at Google Play store suddenly started cutting off early; after 3 minutes and 6 seconds to be more precise.&lt;/p&gt;
&lt;p&gt;We all know Apple loves Google so at first I thought it was a funny joke and completely removed all Google songs from iTunes and then reimported them. Alas, with the same results.&lt;/p&gt;
&lt;p&gt;After hours of fruitless search I found a hint &lt;a href=&quot;http://18and5.blogspot.com/2012/08/if-your-itunes-songs-are-getting-cut-off.html&quot;&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Unfortunately iTunes has changed since 2012 and creating a new library was different (hold down the Option key while starting it), the general direction was correct.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;1. Create a new iTunes library
2. Reimport all your songs and books&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[mysql2 gem error after upgrading MySQL database]]></title><description><![CDATA[After upgrading to MySQL 5.6 from 5.5 we started getting an error: Incorrect MySQL client library version!  Solution is pretty simple…]]></description><link>https://www.monkey.work/2015-07-01-ror-upgrading-mysql-database/</link><guid isPermaLink="false">https://www.monkey.work/2015-07-01-ror-upgrading-mysql-database/</guid><pubDate>Wed, 01 Jul 2015 07:17:12 GMT</pubDate><content:encoded>&lt;p&gt;After upgrading to MySQL 5.6 from 5.5 we started getting an error:&lt;/p&gt;
&lt;p&gt;Incorrect MySQL client library version!&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;This gem was compiled for 5.5.17 but the client library is 5.6.58. (RuntimeError)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Solution is pretty simple - reinstalling mysql2 gem.&lt;/p&gt;
&lt;p&gt;The tricky part is how to do that.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;bundle show mysql2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;returned&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;... shared/bundle/ruby/2.2.0/gems/mysql2-0.3.18&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;After Uninstalling mysql2 gem bundle show still located the gem.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;gem uninstall mysql2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Here is the trick&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;bundle exec gem uninstall mysql2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And don’t worry about the error:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;ERROR: While executing gem ... (NoMethodError) undefined method &lt;/code&gt;delete’`&lt;/p&gt;
&lt;p&gt;This is Rails after all. Just do bundle show mysql2 again to see that the gem was actually removed.&lt;/p&gt;
&lt;p&gt;And finally:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;bundle install&lt;/code&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Using environment variables under Nginx]]></title><description><![CDATA[Rails is heavily using environment variables in its configuration files. While configuring them in development is fairly easy… For instance…]]></description><link>https://www.monkey.work/2015-05-04-phusion-passenger-using-environment-variables-under-nginx/</link><guid isPermaLink="false">https://www.monkey.work/2015-05-04-phusion-passenger-using-environment-variables-under-nginx/</guid><pubDate>Tue, 05 May 2015 00:52:41 GMT</pubDate><content:encoded>&lt;p&gt;Rails is heavily using environment variables in its configuration files.&lt;/p&gt;
&lt;p&gt;While configuring them in development is fairly easy…&lt;/p&gt;
&lt;p&gt;For instance if we are using bash shell on OSX we could just add them to ~/.bashrc file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;export VAR_NAME=&quot;var_value&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For Nginx configuration is a little more involved; simply because Nginx does not normally run under our user and does not have access to variables defined in &lt;code class=&quot;language-text&quot;&gt;.bashrc&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Nevertheless, configuring environment variables that Passenger process can access is simple. We will have to modify Nginx configuration file (/opt/nginx/conf/nginx.conf in our case):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo vi /opt/nginx/conf/nginx.conf&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Under the “server {” part we can add &lt;strong&gt;passenger_env_var&lt;/strong&gt; directive:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;server {
	...
	passenger_env_var VAR_NAME &quot;var_value&quot;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Don’t forget the ; at the end!&lt;/p&gt;
&lt;p&gt;Now Rails should have access to VAR_NAME under:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ENV[&apos;VAR_NAME&apos;]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Minimum rights policy for S3 that works with CarrierWave]]></title><description><![CDATA[After a few good hours of getting “AWS::S3::Errors::AccessDenied” we finally figured out the minimum rights policy for S3 that works with…]]></description><link>https://www.monkey.work/2015-04-10-ror-minimum-rights-policy-for-s3-that-works-with-carrierwave/</link><guid isPermaLink="false">https://www.monkey.work/2015-04-10-ror-minimum-rights-policy-for-s3-that-works-with-carrierwave/</guid><pubDate>Fri, 10 Apr 2015 22:52:50 GMT</pubDate><content:encoded>&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;token property&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;s3:AbortMultipartUpload&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3:CreateBucket&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3:DeleteBucket&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3:DeleteObject&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3:PutObject&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3:PutObjectAcl&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:s3:::images_mysite_com/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After a few good hours of getting “AWS::S3::Errors::AccessDenied” we finally figured out the minimum rights policy for S3 that works with CarrierWave (see above)!&lt;/p&gt;
&lt;p&gt;One more thing to notice is that Amazon S3 does not like dots in bucket names. And the reason is that their SSL certificate is only valid for *.s3&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Installing Nginx with Phusion Passenger, RVM, and Ruby on Rails on Ubuntu]]></title><description><![CDATA[Digital Ocean has a great tutorial on how to install  Nginx with Phusion Passenger, RVM, and RoR on Ubuntu. We recently had to do exactly…]]></description><link>https://www.monkey.work/2015-04-07-rails-installing-ror-with-phusion-passenger-and-nginx-on-ubuntu/</link><guid isPermaLink="false">https://www.monkey.work/2015-04-07-rails-installing-ror-with-phusion-passenger-and-nginx-on-ubuntu/</guid><pubDate>Tue, 07 Apr 2015 18:52:51 GMT</pubDate><content:encoded>&lt;p&gt;Digital Ocean has a great &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/how-to-install-rails-and-nginx-with-passenger-on-ubuntu&quot;&gt;tutorial&lt;/a&gt; on how to install  Nginx with Phusion Passenger, RVM, and RoR on Ubuntu.&lt;/p&gt;
&lt;p&gt;We recently had to do exactly that and created an updated guide.&lt;/p&gt;
&lt;p&gt;Update Ubuntu packages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sudo apt-get update&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If this is a clean install it can also be a good idea to install all the security updates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sudo apt-get upgrade&lt;/li&gt;
&lt;li&gt;sudo apt-get autoclean&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A tip: if we plan to use mysql2 gem libmysqlclient-dev is a requirement:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sudo apt-get install libmysqlclient-dev&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Install and start RVM (we might need to download and install GPG signature, gpg —keyserver hkp://keys.gnupg.net —recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;curl -L get.rvm.io | bash -s stable&lt;/li&gt;
&lt;li&gt;source ~/.rvm/scripts/rvm&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Check to see if any required libraries are missing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rvm requirements&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If anything is missing install it with rvmsudo and run rvm requirements to make sure all is good.&lt;/p&gt;
&lt;p&gt;Install ruby 2.2.1 and start using it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rvm install 2.2.1&lt;/li&gt;
&lt;li&gt;rvm use 2.2.1 —default&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Install rails and passenger gems.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;gem install rails&lt;/li&gt;
&lt;li&gt;gem install passenger&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Passenger install scripts will let us know if anything is missing and will install Nginx and all the required components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rvmsudo passenger-install-nginx-module&lt;/li&gt;
&lt;li&gt;sudo apt-get install nodejs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To be able to use nginx as a service:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sudo service nginx start&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We first need to install startup scripts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;wget -O init-deb.sh &lt;a href=&quot;https://www.linode.com/docs/assets/660-init-deb.sh&quot;&gt;https://www.linode.com/docs/assets/660-init-deb.sh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;sudo mv init-deb.sh /etc/init.d/nginx&lt;/li&gt;
&lt;li&gt;sudo chmod +x /etc/init.d/nginx&lt;/li&gt;
&lt;li&gt;sudo /usr/sbin/update-rc.d -f nginx defaults&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we can point the configuration file (assuming the default location) to point to our rails project on the file-system.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sudo nano /opt/nginx/conf/nginx.conf&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;server { 	listen 80; 	server_name example.com; 	passenger_enabled on; 	root /var/www/my_app/current/public; }&lt;/code&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[NSURL URLWithString vs fileURLWithPath: Loading Local Files Correctly in iOS]]></title><description><![CDATA[URLWithString creates a url as is! i.e. it does not add a scheme. fileURLWithPath creates a url with a file (file://) scheme. A frequent…]]></description><link>https://www.monkey.work/2015-03-31-ios-nsurl-urlwithstring-vs-fileurlwithpath/</link><guid isPermaLink="false">https://www.monkey.work/2015-03-31-ios-nsurl-urlwithstring-vs-fileurlwithpath/</guid><pubDate>Tue, 31 Mar 2015 23:58:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;URLWithString&lt;/strong&gt; creates a url as is! i.e. it does not add a scheme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;fileURLWithPath&lt;/strong&gt; creates a url with a file (file://) scheme.&lt;/p&gt;
&lt;p&gt;A frequent mistake when playing a local video using [AVAudioPlayer initWithContentsOfURL:url] is to use URLWithString instead of fileURLWithPath to create the url. The player will report no error but the video will not play.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Fix fileExistsAtPath Always Returning NO in iOS 8 Cache Directories]]></title><description><![CDATA[The problem: fileExistsAtPath always returns NO (even though the app container definitely contains the file). We used to save some files in…]]></description><link>https://www.monkey.work/2015-03-04-ios8-fileexistsatpath-always-returns-no-false/</link><guid isPermaLink="false">https://www.monkey.work/2015-03-04-ios8-fileexistsatpath-always-returns-no-false/</guid><pubDate>Wed, 04 Mar 2015 23:56:48 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;fileExistsAtPath always returns NO (even though the app container definitely contains the file).&lt;/p&gt;
&lt;p&gt;We used to save some files in the Cache folder and persist the file path into a sqlite table. The URL to the file to be saved was stored in NSURL and .path would return the path. Here is an example:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;/var/mobile/Containers/Data/Application/A586EFC6-EBA4-499F-A3F5-BA505CA44696/Library/Caches/OurFiles/ea347f7c-e25d-4538-9f0a-f83b22a66a7c&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Later, we would check if the file is still there using fileExistsAtPath:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;if ([[NSFileManager defaultManager] fileExistsAtPath:path])&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We tested it and it all worked just fine. But a couple of weeks ago we accidentally discovered that fileExistsAtPath always returned NO.&lt;/p&gt;
&lt;p&gt;There are at least two problems with our approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Caches folder might change for future iOS versions.&lt;/li&gt;
&lt;li&gt;Unlike absoluteString, NSUrl path does not contain the scheme.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The solution:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now we only store the name of the file (and not the full path) and generate the path dynamically with NSSearchPathForDirectoriesInDomains and it works just fine.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Xcode: Set iOS App Display Name per Build Configuration]]></title><description><![CDATA[Under Targets/(Target Name) go to Build Settings tab. On the top-left-side click on the + sign to add a user defined setting under User…]]></description><link>https://www.monkey.work/2015-02-04-xcode-set-application-depending-on-the-build-type/</link><guid isPermaLink="false">https://www.monkey.work/2015-02-04-xcode-set-application-depending-on-the-build-type/</guid><pubDate>Wed, 04 Feb 2015 18:52:23 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;Under Targets/(Target Name) go to Build Settings tab.&lt;/li&gt;
&lt;li&gt;On the top-left-side click on the + sign to add a user defined setting under User-Defined.&lt;/li&gt;
&lt;li&gt;Name it &lt;code class=&quot;language-text&quot;&gt;BUNDLE_DISPLAY_NAME&lt;/code&gt; and set the name for each build configuration.&lt;/li&gt;
&lt;li&gt;In the Info.plist file change Bundle display name to &lt;code class=&quot;language-text&quot;&gt;${BUNDLE_DISPLAY_NAME}&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Send Apple Push Notifications with Python 2.7]]></title><description><![CDATA[pytInstall Python (on OS X Yosemite) Yosemite has Python 2.7.6 built in  but for many reasons (one of them is sudo) we prefer a clean up-to…]]></description><link>https://www.monkey.work/2015-01-20-send_apple_push_notifications_with_python/</link><guid isPermaLink="false">https://www.monkey.work/2015-01-20-send_apple_push_notifications_with_python/</guid><pubDate>Wed, 21 Jan 2015 01:12:21 GMT</pubDate><content:encoded>&lt;p&gt;pytInstall Python (on OS X Yosemite)&lt;/p&gt;
&lt;p&gt;Yosemite has Python 2.7.6 built in  but for many reasons (one of them is sudo) we prefer a clean up-to-date install.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;brew install python&lt;/p&gt;
&lt;p&gt;Make sure the newly installed Python is in the path:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;vi ~/.zshrc
PATH=$PATH:/usr/local:/usr/local/bin: # Add Python to PATH
source ~/.zshrc&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Double check we have the latest version:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;python -V&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Virtual environments is a must to keep all the dependencies in check:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;pip install virtualenv&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Create a new virtual environment called pyenv and activate it:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;virtualenv pyenv
source pyenv/bin/activate&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Install Python APN library:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;pip install apns&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now it is a good idea to make our installation easily repeatable:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;pip freeze &gt; requirements.txt&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Later we can restore all the required libraries:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;pip install -r requirements.txt&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or even update them all:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;pip freeze --local | grep -v &apos;^\-e&apos; | cut -d = -f 1  | xargs pip install -U&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;From developer.apple.com download “APNs Production iOS” certificate, &lt;strong&gt;aps_production.cer&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Now, this is the tricky part. We have to convert it to a .p12 certificate.&lt;/p&gt;
&lt;p&gt;To do that, double-click on the certificate so that it is added to the keychain.&lt;/p&gt;
&lt;p&gt;Open “Keychan Access” and right-click on the certificate to export it as .p12 certificate.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;It is very important to export the certificate and not the private key!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Lets export it into a PEM certificate that either contains both the private key and the certificate:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;openssl pkcs12 -in aps_production.p12 -out aps_production_olinguito.pem -nodes&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or into two separate files, one with the key and the other one with the certificate (preferred  method):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;openssl pkcs12 -nocerts -in aps_production.p12 -out aps_prod_key.pem -nodes
openssl pkcs12 -nokeys -in aps_production.p12 -out aps_prod_cert.pem -nodes&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A very simple script to test our notifications:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import time
from apns import APNs, Frame, Payload

apns = APNs(use_sandbox=False, cert_file=&apos;aps_prod_cert.pem&apos;, key_file=&apos;aps_prod_key.pem&apos;, enhanced=True)

# Send a notification
token_hex = &apos;dd6b492f92546f8a02d9a36e689b4be28b0c663db7b6c278379866a7e1behell&apos;
payload = Payload(alert=&quot;Hello World!&quot;, sound=&quot;default&quot;, badge=1)
apns.gateway_server.send_notification(token_hex, payload)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Thunderbolt Display firmware Update 1.2]]></title><description><![CDATA[The problem: It took me almost a month to install thunderbolt display firmware update 1.2 on OSX Yosemite. The update just failed every time…]]></description><link>https://www.monkey.work/2015-01-16-thunderbolt-display-firmware-update-1-2/</link><guid isPermaLink="false">https://www.monkey.work/2015-01-16-thunderbolt-display-firmware-update-1-2/</guid><pubDate>Sat, 17 Jan 2015 00:36:42 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It took me almost a month to install thunderbolt display firmware update 1.2 on OSX Yosemite.&lt;/p&gt;
&lt;p&gt;The update just failed every time I tried to install. Even more annoying, it miraculously reappeared in the updates a couple of days after the botched install.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The solution:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;My MacBook was connected to my Thunderbolt display with its shell closed. All I had to do is to open the shell and the update installed just fine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A Warning:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Doing what Apple suggest &lt;a href=&quot;http://support.apple.com/en-us/HT203758&quot;&gt;here&lt;/a&gt; just wasted  time.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Upgrading Ghost to the latest version]]></title><description><![CDATA[Keeping Ghost installation up-to-date is important not only because of new features added but also because of all the security updates…]]></description><link>https://www.monkey.work/2015-01-16-ghost-update/</link><guid isPermaLink="false">https://www.monkey.work/2015-01-16-ghost-update/</guid><pubDate>Fri, 16 Jan 2015 17:33:49 GMT</pubDate><content:encoded>&lt;p&gt;Keeping Ghost installation up-to-date is important not only because of new features added but also because of all the security updates.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;1. BACKUP /var/www/ghost/content folder
   * optional but highly recommended
2. sudo systemctl stop ghost
   * or any other service manager
3. cd /var/www
   * assuming the blog is located at /var/www/ghost
4. curl -LOk https://ghost.org/zip/ghost-latest.zip
5. sudo rm -rf ghost/core
   sudo rm -rf content/themes/
   * optional
6. unzip -uo ghost-latest.zip -d ghost
   * copy only new and updated files
7. sudo chown -R ghost:ghost ghost/*
   * optional
8. cd /var/www/ghost
9. sudo npm install --production
10. sudo systemctl start ghost&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;optional (before npm install run):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;
- sudo rm -rf node_modules/
- sudo npm cache clean
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;unzip -uo&lt;/code&gt; only copies files that are newer than the one that exist. This option is important to preserve all the data files.&lt;/p&gt;
&lt;p&gt;once in a while installation will with weird errors such as “ERROR: AppField is already defined in the registry.”&lt;/p&gt;
&lt;p&gt;in that case it is a good idea to replace &lt;code class=&quot;language-text&quot;&gt;/var/www/ghost/core&lt;/code&gt; folder or even the entire installation except for &lt;code class=&quot;language-text&quot;&gt;content&lt;/code&gt; folder and &lt;code class=&quot;language-text&quot;&gt;config.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;it also helps to troubleshoot problematic installs by starting ghost with npm start&lt;/p&gt;
&lt;p&gt;for instance: &lt;code class=&quot;language-text&quot;&gt;npm start --production&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;to start ghost as a &lt;code class=&quot;language-text&quot;&gt;service&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;download init script&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ sudo curl https://raw.githubusercontent.com/TryGhost/Ghost-Config/master/init.d/ghost \   -o /etc/init.d/ghost&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;make sure &lt;code class=&quot;language-text&quot;&gt;/etc/init.d/ghost DAEMON&lt;/code&gt; variable is the same as the output of &lt;code class=&quot;language-text&quot;&gt;which node&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ sudo useradd -r ghost -U $ sudo chown -R ghost:ghost /var/www/ghost $ sudo chmod 755 /etc/init.d/ghost&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;start the service on boot&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ sudo update-rc.d ghost defaults $ sudo update-rc.d ghost enable&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Updating Homebrew after OS upgrade]]></title><description><![CDATA[Unfortunatrly, Homebrew breaks after an upgrade to OS X Yosemite.  or any other brew request brings the following error: /usr/local/bin/brew…]]></description><link>https://www.monkey.work/2014-10-27-os-x/</link><guid isPermaLink="false">https://www.monkey.work/2014-10-27-os-x/</guid><pubDate>Mon, 27 Oct 2014 18:26:13 GMT</pubDate><content:encoded>&lt;p&gt;Unfortunatrly, Homebrew breaks after an upgrade to OS X Yosemite. &lt;code class=&quot;language-text&quot;&gt;brew update&lt;/code&gt; or any other brew request brings the following error:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;/usr/local/bin/brew: /usr/local/Library/brew.rb: /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby: bad interpreter: No such file or directory&lt;/p&gt;
&lt;p&gt;/usr/local/bin/brew: line 23: /usr/local/Library/brew.rb: Undefined error: 0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Apple updated its default Ruby version to 2.0 and completely removed 1.8.&lt;/p&gt;
&lt;p&gt;We found a couple of interesting &lt;a href=&quot;http://ryantvenge.com/2014/09/ruby-homebrea-yosemite/&quot;&gt;ideas&lt;/a&gt; but here is what worked for us:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;1) Trick Homebrew onto believing that Ruby 1.8 is still there&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ cd /System/Library/Frameworks/Ruby.framework/Versions
$ sudo ln -s Current 1.8&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;2) Remove Homebrew&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ cd \`brew --prefix

$ rm -rf Cellar
$ bin/brew prune
$ pbpaste | xargs -0 rm

$ rm -r Library/Homebrew Library/Aliases Library/Formula Library/Contributions

$ rmdir -p bin Library share/man/man1 2&gt; /dev/null

$ rm -rf .git

$ rm -rf ~/Library/Caches/Homebrew

$ rm -rf ~/Library/Logs/Homebrew&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;3) Install Homebrew&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ ruby -e &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)&quot;

$ brew update&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;4) Cleanup&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ cd /System/Library/Frameworks/Ruby.framework/Versions

$ sudo rm 1.8&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Support Native iPhone 6 and 6 Plus Resolutions with Launch XIBs in iOS 8]]></title><description><![CDATA[By default all the apps (even compiled with Xcode 6 and iOS 8) will run in a scaled mode on both iPhone 6 & iPhone 6 plus. Here is an…]]></description><link>https://www.monkey.work/2014-10-01-ios-8/</link><guid isPermaLink="false">https://www.monkey.work/2014-10-01-ios-8/</guid><pubDate>Thu, 02 Oct 2014 00:06:55 GMT</pubDate><content:encoded>&lt;p&gt;By default all the apps (even compiled with Xcode 6 and iOS 8) will run in a scaled mode on both iPhone 6 &amp;#x26; iPhone 6 plus.&lt;/p&gt;
&lt;p&gt;Here is an example of how our app looked like in a scaled mode on iPhone 6.&lt;/p&gt;
&lt;p&gt;For some reason, the only way to natively support iPhone 6 &amp;#x26; iPhone 6 plus is to add a launch XIB file.&lt;/p&gt;
&lt;p&gt;You use a launch XIB or storyboard file to indicate that your app runs on iPhone 6 Plus or iPhone 6. &lt;a href=&quot;https://developer.apple.com/library/ios/documentation/userexperience/conceptual/mobilehig/LaunchImages.html&quot;&gt;Launch Images&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So after adding a launch XIB iOS 8 has finally recognized that our app is optimized for iPhone 6 &amp;#x26; iPhone 6 plus. And it looks much better now!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[iOS 8 Push Notifications: Fix registerForRemoteNotificationTypes Deprecation]]></title><description><![CDATA[We upgraded to iOS 8 and XCode 6 and all was good until we noticed that push notifications stopped working.
For some time we could not…]]></description><link>https://www.monkey.work/2014-09-19/</link><guid isPermaLink="false">https://www.monkey.work/2014-09-19/</guid><pubDate>Fri, 19 Sep 2014 00:34:34 GMT</pubDate><content:encoded>&lt;p&gt;We upgraded to iOS 8 and XCode 6 and all was good until we noticed that push notifications stopped working.
For some time we could not figure out what was going on but then we noticed the following NSLog message in the console:
&lt;code class=&quot;language-text&quot;&gt;registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;To make it work we had to update registerForRemoteNotificationTypes code to:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;UIApplication&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;application &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UIApplication&lt;/span&gt; sharedApplication&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;application respondsToSelector&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;registerUserNotificationSettings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;UIUserNotificationSettings&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;settings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UIUserNotificationSettings&lt;/span&gt; settingsForTypes&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UIUserNotificationTypeAlert&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIUserNotificationTypeBadge&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIUserNotificationTypeSound&lt;/span&gt; categories&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;application registerUserNotificationSettings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;settings&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;application registerForRemoteNotificationTypes&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UIRemoteNotificationTypeAlert&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIRemoteNotificationTypeBadge&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIRemoteNotificationTypeSound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And to add the following code in our AppDelegate:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;void&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UIApplication&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;application didRegisterUserNotificationSettings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UIUserNotificationSettings&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;notificationSettings&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;br&lt;span class=&quot;token operator&quot;&gt;/&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;br&lt;span class=&quot;token operator&quot;&gt;/&gt;&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;//register to receive notifications&amp;lt;br/&gt;    [application registerForRemoteNotifications];&amp;lt;br/&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;note: &lt;code class=&quot;language-text&quot;&gt;#ifdef __IPHONE_8_0&lt;/code&gt; is only needed if at some point we want to compile for old iOS SDKs.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Adding a tag]]></title><description><![CDATA[We use tags to mark release points.
Tags are easy to add:
 Tags do not get pushed with: 
  option must be added to push the tags:
 It is…]]></description><link>https://www.monkey.work/2014-09-15T07/</link><guid isPermaLink="false">https://www.monkey.work/2014-09-15T07/</guid><pubDate>Mon, 15 Sep 2014 07:29:42 GMT</pubDate><content:encoded>&lt;p&gt;We use tags to mark release points.
Tags are easy to add:
&lt;br /&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;git tag -a prod-0.9.7 -m &quot;add analytics&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Tags do not get pushed with: &lt;code class=&quot;language-text&quot;&gt;git push&lt;/code&gt;
&lt;br /&gt; &lt;code class=&quot;language-text&quot;&gt;-- tag&lt;/code&gt; option must be added to push the tags:
&lt;br /&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;git push --tag&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It is also easy to remove the tags:
&lt;br /&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;git tag -d prod-0.9.7
git push origin :refs/tags/prod-0.9.7&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Checkout a file (just one file) from a specific commit (revision)]]></title><description><![CDATA[If, for instance, we want to checkout project.pbxproj from commit 6c1d18623f5e42c1b309bd008dd8a28f8d166d03:]]></description><link>https://www.monkey.work/2014-09-15/</link><guid isPermaLink="false">https://www.monkey.work/2014-09-15/</guid><pubDate>Mon, 15 Sep 2014 05:26:31 GMT</pubDate><content:encoded>&lt;p&gt;If, for instance, we want to checkout project.pbxproj from commit 6c1d18623f5e42c1b309bd008dd8a28f8d166d03:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;git checkout 6c1d1862 &lt;span class=&quot;token class-name&quot;&gt;AppConsumer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Curbsidr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;xcodeproj&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;project&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pbxproj&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Compiling Facebook SDK with Xcode 6]]></title><description><![CDATA[The problem:

Compiling Facebook SDK with Xcode 6 is not yet an option.  gives the following error: The solution:

The SDK can easily be…]]></description><link>https://www.monkey.work/2014-09-11/</link><guid isPermaLink="false">https://www.monkey.work/2014-09-11/</guid><pubDate>Thu, 11 Sep 2014 05:39:36 GMT</pubDate><content:encoded>&lt;p&gt;&lt;b&gt;The problem:&lt;/b&gt;
&lt;br /&gt;
Compiling Facebook &lt;a href=&quot;https://github.com/facebook/facebook-ios-sdk&quot;&gt;SDK&lt;/a&gt; with Xcode 6 is not yet an option. &lt;code class=&quot;language-text&quot;&gt;swiftsh build_all.sh&lt;/code&gt; gives the following error:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;AttributeError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &apos;&lt;span class=&quot;token class-name&quot;&gt;NoneType&lt;/span&gt;&apos; object has no attribute &apos;group&apos;
&lt;span class=&quot;token class-name&quot;&gt;Command&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;../&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Vendor&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;clang&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ios&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;dylib&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;links&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cc&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;iphonesimulator&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;latest&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;targeting&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5.0&lt;/span&gt; failed with exit code &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;BUILD&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;FAILED&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;b&gt;The solution:&lt;/b&gt;
&lt;br /&gt;
The SDK can easily be build with Xcode 5.
We renamed Xcode.app to Xcode_5.app prior to installing Xcode 6.
&lt;br /&gt;
And then used &lt;code class=&quot;language-text&quot;&gt;code-select&lt;/code&gt; to force &lt;code class=&quot;language-text&quot;&gt;sh build_all.sh&lt;/code&gt; to use Xcode 5.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;sudo xcode&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;select &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Applications&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Xcode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;_5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;app&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Contents&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Developer&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Objective-C: Format Currency Strings from Double Values with NSNumberFormatter]]></title><description><![CDATA[In our app we deal with money, virtual money. We needed a simple formatter that would format a :]]></description><link>https://www.monkey.work/2014-09-04/</link><guid isPermaLink="false">https://www.monkey.work/2014-09-04/</guid><pubDate>Thu, 04 Sep 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In our app we deal with money, virtual money. We needed a simple formatter that would format a &lt;code class=&quot;language-text&quot;&gt;double&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;54.36233 --&gt; 54.36
26.00    --&gt; 26&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;objc&quot;&gt;&lt;pre class=&quot;language-objc&quot;&gt;&lt;code class=&quot;language-objc&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;NSString &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;currencyStringFromNumber&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;number
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    NSNumberFormatter &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;NSNumberFormatter alloc&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; init&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//remove .00 if needed&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;formatter setMaximumFractionDigits&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;formatter setNumberStyle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;NSNumberFormatterCurrencyStyle&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    NSNumber &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;aNumber &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;NSNumber numberWithDouble&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;formatter stringFromNumber&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;aNumber&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item></channel></rss>