<?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" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[⚡️ Blixt Dev ⚡️]]></title><description><![CDATA[News and tips for lightning fast development of React, Elixir & More]]></description><link>https://blixtdev.com/</link><image><url>https://blixtdev.com/favicon.png</url><title>⚡️ Blixt Dev ⚡️</title><link>https://blixtdev.com/</link></image><generator>Ghost 5.38</generator><lastBuildDate>Mon, 02 Mar 2026 15:50:43 GMT</lastBuildDate><atom:link href="https://blixtdev.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[All About Amazon Prime for Students]]></title><description><![CDATA[Students can get a great deal and some unique benefits on Amazon Prime.  Read on to find out how.]]></description><link>https://blixtdev.com/amazon-prime-student-discount/</link><guid isPermaLink="false">65c28666863f970107d39b69</guid><category><![CDATA[Lifestyle]]></category><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Wed, 07 Feb 2024 17:26:03 GMT</pubDate><media:content url="https://blixtdev.com/content/images/2024/02/FK7JNG3NWNN3BNCMD7TS3REZ2U.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blixtdev.com/content/images/2024/02/FK7JNG3NWNN3BNCMD7TS3REZ2U.jpg" alt="All About Amazon Prime for Students"><p>Surely you&apos;ve heard of Amazon Prime, and maybe you even used it at home before heading off to school. &#xA0;But did you know that <a href="https://amzn.to/3UrNRqx?ref=blixt-dev">students can get a great deal on Amazon Prime</a>&#x2013;and all sorts of unique benefits? &#xA0;Read on to find out about how students can make the best use of Amazon Prime.</p><h2 id="what-are-the-main-amazon-prime-student-benefits">What are the Main Amazon Prime Student Benefits?</h2><figure class="kg-card kg-image-card"><img src="https://blixtdev.com/content/images/2024/02/Screenshot-2024-02-07-at-9.58.01-AM.png" class="kg-image" alt="All About Amazon Prime for Students" loading="lazy" width="1976" height="459" srcset="https://blixtdev.com/content/images/size/w600/2024/02/Screenshot-2024-02-07-at-9.58.01-AM.png 600w, https://blixtdev.com/content/images/size/w1000/2024/02/Screenshot-2024-02-07-at-9.58.01-AM.png 1000w, https://blixtdev.com/content/images/size/w1600/2024/02/Screenshot-2024-02-07-at-9.58.01-AM.png 1600w, https://blixtdev.com/content/images/2024/02/Screenshot-2024-02-07-at-9.58.01-AM.png 1976w" sizes="(min-width: 720px) 720px"></figure><p>You&apos;re probably already familiar with the main benefits of Amazon Prime:</p><ul><li>Free two-day shipping on tons of eligible items from <a href="https://amzn.to/482ZLud?ref=blixt-dev">Amazon.com</a></li><li>Unlimited movie and TV streaming with <a href="https://amzn.to/3HQR6QN?ref=blixt-dev">Prime Video</a></li><li>Unlimited music with <a href="https://amzn.to/4963Dfc?ref=blixt-dev">Amazon Music Prime</a></li><li>Prime prices at <a href="https://amzn.to/49osPgx?ref=blixt-dev">Whole Foods Market</a>, both in-store and pickup/delivery</li></ul><p>But there are tons of other benefits that are especially appealing to students. &#xA0;We&apos;ll cover them all in this post. &#xA0;But first, let&apos;s address the most important question. &#xA0;What does it cost? </p><h3 id="what-does-amazon-prime-cost-for-students">What Does Amazon Prime Cost for Students?</h3><ul><li><strong>6-Month Free Trial</strong>: The journey into Amazon Prime Student begins with a wallet-friendly proposition. For starters, there&apos;s a six-month free trial, courtesy of a partnership with Sprint, allowing students to dip their toes into the Prime experience without spending a dime.</li><li><strong>$6.99/month After the Trial</strong>: Once the trial period ends, the subscription transitions into a half-priced haven of Prime benefits, costing significantly less than the regular Amazon Prime membership. This adjusted pricing structure is designed with the student budget in mind, ensuring that even those surviving on a diet of ramen and coffee can afford the luxury of Prime.</li></ul><h2 id="who-qualifies-for-amazon-prime-student">Who Qualifies for Amazon Prime Student?</h2><p>There are two ways to qualify for Amazon Prime Student&#x2013;and one doesn&apos;t even involve being a student!</p><ul><li><strong>Verify enrollment as a student</strong>: &#xA0;if you&apos;re enrolled as a student, a valid .edu email address is your golden ticket to entry.</li><li><strong>Verify eligibility by being age 18-24</strong>: That&apos;s right&#x2013;if you&apos;re between 18 and 24 years old, you <em>don&apos;t even need to be a student</em> to qualify.</li></ul><p>Either way, head over to the <a href="https://amzn.to/4bsBxwg?ref=blixt-dev">Amazon Prime Student</a> page to get started!</p><h2 id="whats-the-difference-between-amazon-prime-student-and-regular-prime">What&apos;s the Difference Between Amazon Prime Student and Regular Prime?</h2><p>While Amazon Prime for Students offers most of the same benefits as the normal Amazon Prime offering, there are a couple of important differences to be aware of.</p><ul><li><strong>Pricing and Trial Period</strong>: The most striking difference lies in the pricing and the extended six-month trial period offered to students, compared to the standard 30-day trial for regular Prime members.</li><li><strong>Special Benefits for Students</strong>: While the core benefits like free shipping and streaming services are similar, Prime Student is enhanced with exclusive deals and promotions specifically aimed at students.</li><li><strong>Sharing of Household Benefits</strong>: Unlike the Amazon Prime offering you might be used to with your family at home, Amazon Prime for Students does not allow you to share benefits with others in your household such as roommates.</li></ul><h2 id="amazon-prime-student-entertainment-benefits">Amazon Prime Student Entertainment Benefits</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blixtdev.com/content/images/2024/02/Group-2.png" class="kg-image" alt="All About Amazon Prime for Students" loading="lazy" width="1800" height="310" srcset="https://blixtdev.com/content/images/size/w600/2024/02/Group-2.png 600w, https://blixtdev.com/content/images/size/w1000/2024/02/Group-2.png 1000w, https://blixtdev.com/content/images/size/w1600/2024/02/Group-2.png 1600w, https://blixtdev.com/content/images/2024/02/Group-2.png 1800w" sizes="(min-width: 720px) 720px"><figcaption>Logos for Amazon Music, Prime Video and Twitch</figcaption></figure><ul><li><strong><a href="https://amzn.to/3HQR6QN?ref=blixt-dev">Prime Video</a></strong>: A treasure trove of movies and TV shows to unwind after a grueling study session.</li><li><strong><a href="https://amzn.to/4963Dfc?ref=blixt-dev">Amazon Music</a></strong>: Over two million songs ad-free, creating the perfect study ambiance or soundtrack for a break.</li><li><strong><a href="https://amzn.to/3UAPS3F?ref=blixt-dev">Twitch Prime</a></strong>: Free games and exclusive in-game content for the gaming aficionados looking for a leisurely escape.</li></ul><h2 id="unexpected-amazon-prime-benefits">Unexpected Amazon Prime Benefits</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blixtdev.com/content/images/2024/02/Group-3.png" class="kg-image" alt="All About Amazon Prime for Students" loading="lazy" width="1800" height="310" srcset="https://blixtdev.com/content/images/size/w600/2024/02/Group-3.png 600w, https://blixtdev.com/content/images/size/w1000/2024/02/Group-3.png 1000w, https://blixtdev.com/content/images/size/w1600/2024/02/Group-3.png 1600w, https://blixtdev.com/content/images/2024/02/Group-3.png 1800w" sizes="(min-width: 720px) 720px"><figcaption>Logos for Calm, CourseHero and GrubHub</figcaption></figure><ul><li><strong><a href="https://www.calm.com/?ref=blixt-dev">Calm App Subscription</a></strong>: 3 complimentary months of Calm, the meditation app, to help manage the stresses of student life, then $8.99 a year.</li><li><strong><a href="https://www.coursehero.com/?ref=blixt-dev">CourseHero</a></strong>: one free month of AI-powered study assistance from CourseHero. &#xA0;While this is a great tool, we wish the free offering was a bit more generous.</li><li><strong><a href="http://grubhub.com/?ref=blixt-dev">GrubHub+</a></strong>: Prime for Students gives 12 months free of GrubHub+, which gives free delivery and discounts on GrubHub orders, perfect for nights when cooking is just not on the agenda</li></ul><h2 id="does-amazon-prime-for-students-pay-for-itself">Does Amazon Prime for Students Pay for Itself?</h2><p>Amazon Prime for Students easily pays for itself every month through a series of benefits:</p><ul><li><strong>Free Shipping</strong>: save about $2.99 per order, though shipping varies by item and speed. &#xA0;The savings on shipping alone, especially for frequent purchasers, can quickly offset the cost of the membership.</li><li><strong>Prime Video</strong>: save $6.99/month compared to Netflix with ads. &#xA0;While the content differs between Prime Video and other platforms, you&apos;ll always find something to watch</li><li><strong>GrubHub+</strong>: save $9.99/month on GrubHub+, which can save $3.50 or more per delivery order and 5% off of pickup orders. &#xA0;If you order food frequently (and let&apos;s be honest, you probably do), the savings can be significant.</li><li><strong>Amazon Music</strong>: saves $5.99/month compared to Spotify, though to be fair, real music lovers who want on-demand access to any song may still prefer the Spotify offering.</li></ul><p>Assuming you make three Amazon purchases and four GrubHub purchases per month, and use Prime Video &amp; Amazon Music instead of other services, <strong>students can save an estimated $36 every month</strong>. &#xA0;Exact savings will vary based on your usage of the benefits above, but this estimate is not unrealistic and could be even higher if you take advantage of other benefits.</p><h2 id="can-housemates-share-amazon-prime-student-benefits">Can Housemates Share Amazon Prime Student Benefits?</h2><p>Unlike normal Amazon Prime which does allow for sharing within a household, the <a href="https://amzn.to/3UrNRqx?ref=blixt-dev">Prime Student</a> offering does not allow for sharing Amazon Prime benefits. &#xA0;Certain features such as Prime Video can of course be used on shared devices like communal TVs, but other benefits like free shipping and GrubHub will be harder to take advantage of unless the primary account holder does the ordering. </p><h2 id="does-amazon-prime-student-have-a-referral-program">Does Amazon Prime Student Have a Referral Program?</h2><p>Though there are many references to an <a href="https://amzn.to/3UrNRqx?ref=blixt-dev">Amazon Prime Student</a> referral program online, as of this writing (Feb. 2024) Amazon&apos;s site does not indicate an active referral program. &#xA0;While we&apos;re always on the lookout for deals like these and will update this post if we learn anything new, it looks like Amazon is not <em>currently </em>offering a referral program.</p><h2 id="can-i-get-amazon-prime-for-free-with-sprint-or-t-mobile">Can I Get Amazon Prime for Free With Sprint or T-Mobile?</h2><p>There are lots of posts out there claiming that you can get Amazon Prime for Free with a Sprint or T-Mobile subscription. &#xA0;Unfortunately, it seems that these posts are all outdated: the offer ended in April 2023. &#xA0;<strong>As of February 2024, Sprint (now owned by T-Mobile) has ended its free Amazon Prime promotions for new customers</strong>. &#xA0;If this changes and the promotion becomes available again, we&apos;ll update it here!</p><h2 id="are-international-students-eligible-for-amazon-prime-student">Are International Students Eligible for Amazon Prime Student?</h2><p>International students aren&apos;t left out of the Prime loop; with a valid .edu email and proof of enrollment, they too can enjoy the myriad benefits of <a href="https://amzn.to/3UrNRqx?ref=blixt-dev">Amazon Prime Student</a>, making their transition to studying in the U.S. a bit smoother.</p><h3 id="how-to-maximize-savings-with-amazon-prime-student">How to Maximize Savings with Amazon Prime Student</h3><ul><li><strong>Take Advantage of the Special Offers</strong>: make sure to try out the subscriptions to Calm and CourseHero, as well as any other special offers that pop up.</li><li><strong>Stay Alert for Deals</strong>: Come back to Amazon once in a while to keep an eye out for exclusive deals and promotions, especially during back-to-school and holiday seasons. &#xA0;While the information in this post is up-to-date as of February 2024, benefits do change periodically.</li><li><strong>Use the Amazon App</strong>: The Amazon app is a great tool for tracking deals and managing subscriptions, ensuring you never miss out on an opportunity to save.</li><li><strong>Cancel Any Subscriptions you Don&apos;t Use! &#xA0;</strong>While the free offerings of GrubHub+, Calm &amp; CourseHero are great benefits, they <em>may </em>auto-renew after expiration. &#xA0;Be sure to check the terms when you sign up and don&apos;t forget to cancel if you don&apos;t find the app helpful. </li></ul><h2 id="conclusion">Conclusion</h2><p><a href="https://amzn.to/4bsBxwg?ref=blixt-dev">Amazon Prime Student</a> is more than just a subscription service; it&apos;s a strategic partner in the quest for academic and personal success. It works perfectly with the student lifestyle, offering an array of benefits that cater to both the necessities and pleasures of college life. From substantial savings on textbooks and essentials to providing a much-needed entertainment escape, Prime Student offers a solution to a wide array of everyday problems students encounter.</p><p>To embark on this journey of convenience, savings, and enjoyment, <a href="https://amzn.to/4bsBxwg?ref=blixt-dev">head over to Amazon and follow the straightforward process</a> to verify eligibility either by student status or age. Amazon Prime Student awaits to make your student life richer, easier, and infinitely more enjoyable. Join the ranks of savvy students who have already discovered this essential college hack.</p><p><em>Updated 2/23/2024</em></p>]]></content:encoded></item><item><title><![CDATA[How to Use Ghost as a Headless CMS in an Elixir/Phoenix Project]]></title><description><![CDATA[In this post, we'll see how to use the Ghost publishing platform as a headless CMS in any Elixir project, including  those with Phoenix and LiveView.]]></description><link>https://blixtdev.com/ghost-as-a-headless-cms-in-elixir/</link><guid isPermaLink="false">65ae4d4b00d25c0107288a11</guid><category><![CDATA[Elixir]]></category><category><![CDATA[Ghost]]></category><category><![CDATA[How To]]></category><category><![CDATA[Phoenix]]></category><category><![CDATA[LiveView]]></category><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Mon, 22 Jan 2024 11:32:53 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1606483460225-be04d908614a?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDh8fGhlYWRsZXNzfGVufDB8fHx8MTcwNTkyMTg4NHww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1606483460225-be04d908614a?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDh8fGhlYWRsZXNzfGVufDB8fHx8MTcwNTkyMTg4NHww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="How to Use Ghost as a Headless CMS in an Elixir/Phoenix Project"><p>I recently wanted to add some new blog &amp; page content to a <a href="https://runningshoescore.com/?ref=blixt-dev" rel="noopener">running shoe price comparison site</a> I built with Elixir/Phoenix and LiveView. I also happen to maintain a couple of different sites using the <a href="https://ghost.org/?ref=blixt-dev" rel="noopener">Ghost publishing platform</a>. So naturally, I asked the question: can I use Ghost&#x2019;s content creation tools to manage the content for my Elixir/Phoenix project?</p><p>Of course! Though I did have to write a small <a href="http://github.com/jonklein/ghost_content?ref=blixt-dev" rel="noopener">Elixir client for the Ghost content API</a>, it was simple enough to access the content and take advantage of all the powerful Ghost CMS tools in my Phoenix app.</p><figure class="kg-card kg-image-card"><img src="https://cdn-images-1.medium.com/max/1600/1*ik35_Eb3-HO4RQCV1Idc7g.png" class="kg-image" alt="How to Use Ghost as a Headless CMS in an Elixir/Phoenix Project" loading="lazy" width="1600" height="564"></figure><p>In this post, we&#x2019;ll learn how to use the Ghost publishing platform as a simple CMS for presenting content in any Elixir project, including those built with the Phoenix Framework and LiveView.</p><h4 id="why-use-a-headless-cms">Why Use a Headless CMS</h4><p>First of all, what is a headless CMS?</p><p>A headless CMS is a content management system that allows content creators to create, edit and manage content on a different site than where it is ultimately displayed. It also provides API access so that the content can be displayed by a variety of frontends and interfaces. With a headless CMS, developers can offload managing content creation tools, and at the same time have more control over how the content is presented.</p><p>In practical terms, a headless CMS gives a developer pre-built tools for creating and managing content that can be displayed on a site they&#x2019;re building, without building out a full blogging platform themselves.</p><p>Though some developers will be tempted to build their own content management, there are tons of features including SEO tools, media management, permissions &amp; powerful content editors which make a headless CMS a better choice in most cases.</p><h4 id="what-is-ghost">What is Ghost?</h4><p><a href="https://ghost.org/?ref=blixt-dev" rel="noopener">Ghost</a> is a popular open-source blogging platform that is known for its simplicity and power. It is lightweight and built on a modern technology stack, making it a great choice for developers who want a straightforward and easy-to-use CMS. Though most people use Ghost as the frontend for their content as well, the platform also features a robust content API, which allows it to function as a headless CMS.</p><p>On the content creation side, Ghost offers a range of other features, including a simple and easy-to-use editor, media management, user management and built-in SEO tools. It even includes tools for mailing lists, audience management, payments and more.</p><p>Overall, Ghost is a great option for developers looking to use a headless CMS in their project, and its feature set and API make it a strong contender for managing blog-like content in Elixir/Phoenix and LiveView applications.</p><p>While there are a number of other headless CMS options that can work with Elixir, here are some of the things I really like about Ghost:</p><ul><li>lightweight &amp; responsive</li><li>clean &amp; modern content management interface</li><li>options for both fully-managed hosting or <a href="https://blixtdev.com/how-to-host-a-ghost-blog-for-free-on-fly-io/" rel="noopener">self-hosted</a> content</li><li>completely free for self-hosted &amp; <a href="https://ghost.org/pricing/?ref=blixt-dev" rel="noopener">reasonably pricing for managed</a></li><li>fully open-source</li></ul><p>On the last two points: many headless CMS providers are closed-source and don&#x2019;t allow the option to self-host. While they may offer free or affordable tiers for small sites, they come with a real risk of vendor lock-in or spiraling costs as a site grows. I recommend the Ghost&#x2019;s managed hosting to help support the development of the platform, but also appreciate the peace of mind that the self-hosted, open-source option provides.</p><h3 id="how-to-use-ghost-in-your-elixirphoenix-project">How to Use Ghost in Your Elixir/Phoenix Project</h3><p>Let&#x2019;s consider a simple use case where we have blog content in a Ghost CMS, and we&#x2019;d like to display it in our Phoenix/LiveView app. We&#x2019;ll integrate the client, then build a simple LiveView to display the blog index, and then individual posts.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/jonklein/ghost_content?ref=blixt-dev"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - jonklein/ghost_content: An Elixir client for the Ghost Content API</div><div class="kg-bookmark-description">An Elixir client for the Ghost Content API. Contribute to jonklein/ghost_content development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="How to Use Ghost as a Headless CMS in an Elixir/Phoenix Project"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">jonklein</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/369a5d42bc9b294f354fa54d57ca2093959b2f8e63841c993e76cc199e1a661b/jonklein/ghost_content" alt="How to Use Ghost as a Headless CMS in an Elixir/Phoenix Project"></div></a></figure><p>First, you&#x2019;ll need to add the <code><a href="https://github.com/jonklein/ghost_content?ref=blixt-dev" rel="noopener">ghost_content</a></code> package to your project.</p><figure class="kg-card kg-code-card"><pre><code class="language-Elixir"># mix.exs

def deps do
  [    
    {:ghost_content, &quot;~&gt; 0.1.0&quot;}
  ]
end</code></pre><figcaption>mix.exs changes to add ghost_content package</figcaption></figure><p>Next, you&#x2019;ll need to configure the package using your Ghost API host, and API key. For help on getting the proper values for your Ghost blog, consult the <a href="https://ghost.org/docs/content-api/?ref=blixt-dev" rel="noopener">Ghost Content API documentation</a>.</p><figure class="kg-card kg-code-card"><pre><code class="language-Elixir"># config.exs

config :my_app, :ghost_content,
  host: &quot;https://my.ghost.host&quot;, 
  api_key: &quot;my-ghost-api-key&quot;</code></pre><figcaption>config.exs changes to configure ghost_content package</figcaption></figure><p>Once you&#x2019;ve added &amp; configured <code>ghost_content</code> in your project, we&#x2019;ll add the LiveViews to fetch and display the blog content. In the examples below, we&#x2019;ll show the bare minimum to get up and running, but it should be simple enough to expand on the functionality once you get the blog index and posts displaying in your app.</p><p>First, let&#x2019;s add simple routes to our new blog functionality. We&#x2019;ll show a list of all posts at <code>/blog</code>, and show individual posts at <code>/blog/:slug</code> , where <code>:slug</code> is the identifier for the post we&#x2019;ve configured in the Ghost CMS.</p><figure class="kg-card kg-code-card"><pre><code class="language-Elixir"># router.ex

live(&quot;/blog/&quot;, MyApp.BlogLive.Index, :index)
live(&quot;/blog/:slug&quot;, MyApp.BlogLive.Show, :show)</code></pre><figcaption>Routing for a simple blog with ghost_content</figcaption></figure><p>With our routing in place, we&#x2019;ll create the index page showing our list of blog posts. We simply make a call to the Ghost content API to fetch the posts in our <code>mount()</code> call, then render the contents, linking out to the individual posts:</p><figure class="kg-card kg-code-card"><pre><code class="language-Elixir">defmodule MyApp.BlogLive.Index do 
  use MyAppWeb, :live_view
  
  def mount(_params, _session, socket) do
    {:ok, %{posts: posts}} =
      GhostContent.config(:my_app)
      |&gt; GhostContent.get_posts()
    
    {:ok, assign(socket, :posts, posts)}
  end  

  def render(assigns) do
    ~H&quot;&quot;&quot;
      &lt;h1&gt;Recent Blog Posts&lt;/h1&gt;
      &lt;ul&gt;
      &lt;%= for post &lt;- @posts do %&gt;
        &lt;li&gt;&lt;a href={~p&quot;/blog/#{post.slug}&quot;}&gt;&lt;%= post.title %&gt;&lt;/a&gt;&lt;/li&gt;
      &lt;% end %&gt;
      &lt;/ul&gt;
    &quot;&quot;&quot;  
  end
end</code></pre><figcaption>Sample LiveView blog index page with ghost_content</figcaption></figure><p>Finally, we&#x2019;ll add a simple LiveView to show individual posts. In this case, we&#x2019;ll use the slug from the route to lookup the post content:</p><figure class="kg-card kg-code-card"><pre><code class="language-Elixir">defmodule MyApp.BlogLive.Show do
  use MyAppWeb, :live_view
  def mount(%{&quot;slug&quot; =&gt; slug}, _session, socket) do
    {:ok, %{posts: [post]}} =
      GhostContent.config(:my_app)
      |&gt; GhostContent.get_post_by_slug(slug)
    
    {:ok, assign(socket, :post, post)}
  end
 
  def render(assigns) do
    ~H&quot;&quot;&quot;
      &lt;%= @post.title %&gt;  
      &lt;div class=&quot;ghost-blog-content&quot;&gt;
        &lt;%= raw(@post.html) %&gt;
      &lt;/div&gt;
    &quot;&quot;&quot;
  end
end</code></pre><figcaption>Sample LiveView blog display page with ghost_content</figcaption></figure><p>Note that to display the content we use <code>raw(@post.html)</code>. This is because the content API is giving us actual HTML content and we want to render it as-is (rather than with HTML content escaped). Using <code>raw()</code> can be dangerous with <em>untrusted </em>content, but in this case, because we have authored the content ourselves, it is trusted.</p><p>Regarding styling, because we&#x2019;re displaying raw HTML content that was authored in Ghost, the content we render in our app is <em>unstyled. </em>We won&#x2019;t be able to take common styling approaches like adding class names to the individual HTML elements in our content. Instead, we simply wrap the entire blog content with a <code>div</code> with a special class name (<code>ghost-blog-content</code>), then apply styles to elements within that div in our <code>app.css</code>:</p><pre><code># app.css 

.ghost-blog-content h1 { font-size: 2em; }
.ghost-blog-content h2 { font-size: 1.5em; }

# more .ghost-blog-content styles ...</code></pre><p>And voila! With these simple LiveViews in place, we have the foundations of a Ghost blog in our Phoenix app. There are a number of details we&#x2019;ve ignored like pagination, displaying tag &amp; author data, navigation, etc., but these are simple enough to add once we have the basics in place.</p><p>One final note on SEO: to take full advantage of the SEO functionality of Ghost, be sure to grab the proper values from the Ghost post structure (title, feature_image and description) and expose them in the proper meta tags in your HTML content.</p><hr><p>Jonathan is software developer &amp; engineering leader who loves Elixir. When he&#x2019;s not writing code, he can be found running or <a href="https://runningshoescore.com/?ref=blixt-dev" rel="noopener ugc nofollow noopener">scouring the internet for great deals on running shoes</a>.</p>]]></content:encoded></item><item><title><![CDATA[Candy Land: Will This Game Ever End?]]></title><description><![CDATA[Is it possible to for a game of Candy Land to go on literally forever? The author f’s around and finds out.]]></description><link>https://blixtdev.com/candy-land-will-this-game-ever-end/</link><guid isPermaLink="false">65a3cdf800d25c01072889ee</guid><category><![CDATA[Math]]></category><category><![CDATA[Humor]]></category><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Sun, 14 Jan 2024 12:08:07 GMT</pubDate><media:content url="https://blixtdev.com/content/images/2024/01/1-4JkakNSTiwRYxuEM-bwFjg.jpg" medium="image"/><content:encoded><![CDATA[<h4 id="is-it-possible-to-for-a-game-of-candy-land-to-go-on-literally-forever-the-author-f%E2%80%99s-around-and-finds-out">Is it possible to for a game of Candy Land to go on <em>literally</em> forever? The author f&#x2019;s around and finds out.</h4><img src="https://blixtdev.com/content/images/2024/01/1-4JkakNSTiwRYxuEM-bwFjg.jpg" alt="Candy Land: Will This Game Ever End?"><p>&#x201C;No, please&#x201D;, I begged her, &#x201C;don&#x2019;t do it&#x201D;.</p><p>My wife looked up from her phone and locked eyes with mine. Slowly, she raised the phone and turned it so I could bear witness. She lifted her other hand to the phone and&#x2013;with a wry smile&#x2013;tapped &#x201C;Purchase&#x201D;.</p><p><a href="https://www.amazon.com/Hasbro-Gaming-Kingdom-Adventures-Exclusive/dp/B00000DMF5?crid=26WBWXA7305VQ&amp;3Bkeywords=candyland&amp;3Bqid=1705183217&amp;3Bsprefix=candly+land%2Caps%2C98&amp;3Bsr=8-3&amp;3BlinkCode=ll1&amp;3Btag=blixtblg-20&amp;3BlinkId=75677af97cc8456d9eeb13441a8abe2b&amp;3Blanguage=en_US&amp;3Bref_=as_li_ss_tl&amp;ref=blixt-dev" rel="noopener"><em>Candy Land</em></a><em>. </em>With that simple tap, my wife had purchased Candy Land for our <em>darling</em> 5-year-old for Christmas. And in doing so, doomed us to hours of insufferable gameplay.</p><p>If you&#x2019;ve played the kids&#x2019; game Candy Land, you know that it feels like it goes on forever. But can it <em>actually, literally </em>go on forever? In this post, I set off to find out.</p><h3 id="the-first-rule-of-candy-club">The First Rule of Candy Club</h3><p>If you are unfamiliar with Candy Land, congratulations! But sadly, I&#x2019;ll need to explain the rules to you so you can understand the rest of this post.</p><p>Candy Land is a kids&#x2019; boardgame in which players attempt to navigate over a series of colored squares through a land made out of candy. A &#x201C;CANDY-LAND&#x201D; if you will. The goal is to reach the Candy Castle, but various obstacles along the way make the task more difficult.</p><p>What&#x2019;s <em>great </em>(I guess) about Candy Land is that it&#x2019;s a board game that even the smallest kids can play. With a bit of assistance, even 3-year-olds can participate in game night. Sure, I can appreciate that.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-images-1.medium.com/max/1600/1*gsZIty3DjJ34030HhY7ZAw.jpeg" class="kg-image" alt="Candy Land: Will This Game Ever End?" loading="lazy" width="1200" height="910"><figcaption>The Candy Land Board (source&#xA0;Hasbro)</figcaption></figure><p>So why is Candy Land such an aggravating game then? Well, for one thing, there is <em>no strategy at all. </em>There are, in fact, no decisions made by the players. Each turn, a player picks up the next card from the deck which tells them how far to advance. Occasionally, a special card will send the player to another square far away on the board&#x2013;sometimes forward, sometimes back.</p><p>So without any player influence on how each turn unfolds, you have no control whatsoever over the outcome. <em>You are just a passive vessel for the game of Candy Land. </em>In mathematical terms, the game is fully <em>deterministic.</em> The outcome was decided before you even started, you just don&#x2019;t know it yet.</p><blockquote>You have no control whatsoever over the outcome. You are just a passive vessel for the game of Candy Land.</blockquote><p>The fact that the game is deterministic is aggravating enough, but what makes it worse is that the game&#x2013;seemingly&#x2013;goes on forever. Because the special cards can send you back in the board and erase most of your progress, the game can drag on seemingly <em>indefinitely</em>.</p><p>Most of our games with our son eventually <em>do </em>end, after far too long. And sometimes we have to simply stop playing when the game has outlived its welcome (though the 5 year old disagrees). But during a particularly painful game, I asked the question: is it possible for a Candy Land game to <em>actually </em>go on forever?</p><h3 id="constructing-an-imperfect-game">Constructing an Imperfect Game</h3><p>To figure out if a never ending-game is actually possible, we started to analyze the game:</p><ul><li>there are 64 cards, or 66 depending on who you ask (the official rules state 64, while people with the <em>actual game</em>count 66)</li><li>each card determines how the player moves: either a single color (move to the next square of that color), a double color (move forward two squares of that color), or a special candy image (move to the special square)</li><li>there are 134 spaces from the start of the game to the Candy Castle</li><li>after a card is are used, is it placed back at the bottom of the deck</li></ul><p>With these rules in mind, we can describe how a never-ending sequence would work. A never-ending game sequence <em>for a single player</em> would be a sequence of cards that form a loop of destinations: if a player ends up on the same square at the same point in the card sequence, we have defined a loop.</p><p>So to get a never-ending <em>two player game</em>, <strong>we simply need to find <em>two </em>distinct loops of 33 cards and interleave them</strong>.</p><p>With this realization in mind, our Infinite Candy Land seemed so close I could, uh, taste it. &#x201C;I know!&#x201D;, I announced proudly, &#x201C;I&#x2019;ll write a Candy Land simulation and use some search techniques to find overlapping Candy Land loops. Maybe a genetic algorithm or Monte Carlo simulation would be a good app-&#x2026;&#x201D; I trailed off as I opened my laptop.</p><p>My wife rolled her eyes and pushed the laptop closed. &#x201C;It&#x2019;s <em>simple</em>&#x201D;, she said.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-images-1.medium.com/max/1600/1*VqEl3IqhmBX1rMQ60dmxaQ.jpeg" class="kg-image" alt="Candy Land: Will This Game Ever End?" loading="lazy" width="1946" height="1460"><figcaption>Cards in the game of Candy Land (source&#xA0;Hasbro)</figcaption></figure><h3 id="don%E2%80%99t-overthink-it">Don&#x2019;t Overthink It</h3><p>So it turns out, no fancy search techniques are needed. It&#x2019;s <em>trivial </em>to create a never ending game. Once we figured out how, we were able to do so on our first attempt.</p><p>Here&#x2019;s how we did it. The following is a description of the loop for a <em>single player</em>, so is done with 33 cards&#x2013;4 &#x201C;special&#x201D; cards and 29 regular color cards:</p><ul><li>The first card in the loop is a &#x201C;special&#x201D; card near the start of the board&#x2013;it brings the player to the specified square on the first turn.</li><li>The other &#x201C;special cards&#x201D; are distributed <em>evenly</em> throughout the deck so that when a player starts to approach the goal, they are pulled back to elsewhere on the board.</li><li>After playing the 33rd card, the sequence starts again and the player starts again on the initial &#x201C;special&#x201D; square on their 34th turn.</li></ul><p>It turns out constructing a deck following these rules is simple. And if we do the same thing for the other subset of 33 cards, we can have a sequence for the other player. If we then interleave these two sequences, <em>voila</em> the game of Candy Land will in fact <em>go on forever.</em></p><h3 id="game-over">Game Over</h3><p>So, as it turns out, the game doesn&#x2019;t just <em>feel </em>like it goes on forever. It can <em>literally</em> go on forever. In fact, some readers of this may be caught in an endless loop of Candy Land at this very moment. Condolences.</p><p>Of course, this all makes the assumption that you don&#x2019;t make the common sense modification of shuffling the cards when they run out. But hey, <em>rules is rules. </em>Also, I&#x2019;m informed that some newer editions of Candy Land actually come with a <em>spinner </em>instead of cards. But for the sake of this rant, I am a strict Candy Land Originalist.</p><hr><p>Jonathan is a master-level Candy Land player. When he&#x2019;s not playing candy land, he can be found running or <a href="https://runningshoescore.com/?ref=blixt-dev" rel="noopener">scouring the internet for running shoes</a>.</p>]]></content:encoded></item><item><title><![CDATA[ChatGPT Extension for Safari – GPTWebHelper]]></title><description><![CDATA[Introducing GPTWebHelper – the ChatGPT Extension for Safari which gives you the power of ChatGPT on every page you visit.]]></description><link>https://blixtdev.com/introducing-gpt-web-helper/</link><guid isPermaLink="false">653e83c642dd3e01077e2f42</guid><category><![CDATA[iOS]]></category><category><![CDATA[Safari]]></category><category><![CDATA[ChatGPT]]></category><category><![CDATA[LLM]]></category><category><![CDATA[AI]]></category><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Wed, 01 Nov 2023 12:36:36 GMT</pubDate><media:content url="https://blixtdev.com/content/images/2024/02/output.png" medium="image"/><content:encoded><![CDATA[<h3 id="introducing-gptwebhelper-%E2%80%93-unleash-the-power-of-openais-chatgpt-on-every-page-you-visit-in-safari">Introducing <a href="https://apps.apple.com/us/app/gptwebhelper/id6470203561?mt=12&amp;ref=blixt-dev">GPTWebHelper</a> &#x2013;&#xA0;unleash the power of OpenAI&apos;s ChatGPT on every page you visit in Safari.</h3><img src="https://blixtdev.com/content/images/2024/02/output.png" alt="ChatGPT Extension for Safari &#x2013; GPTWebHelper"><p>Have you ever wished you could have an AI-powered assistant right at your fingertips while browsing the web in Safari? &#xA0;We are thrilled to introduce GPTWebHelper, a new Safari extension that brings the incredible power of OpenAI&apos;s ChatGPT to any webpage you visit. </p><figure class="kg-card kg-video-card kg-card-hascaption"><div class="kg-video-container"><video src="https://blixtdev.com/content/media/2023/10/app_clip1.mp4" poster="https://img.spacergif.org/v1/1920x1080/0a/spacer.png" width="1920" height="1080" playsinline preload="metadata" style="background: transparent url(&apos;https://blixtdev.com/content/images/2023/11/Screenshot-2023-11-01-at-8.34.25-AM-1.png&apos;) 50% 50% / cover no-repeat;"></video><div class="kg-video-overlay"><button class="kg-video-large-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button></div><div class="kg-video-player-container"><div class="kg-video-player"><button class="kg-video-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button><button class="kg-video-pause-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/><rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/></svg></button><span class="kg-video-current-time">0:00</span><div class="kg-video-time">/<span class="kg-video-duration"></span></div><input type="range" class="kg-video-seek-slider" max="100" value="0"><button class="kg-video-playback-rate">1&#xD7;</button><button class="kg-video-unmute-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/></svg></button><button class="kg-video-mute-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/></svg></button><input type="range" class="kg-video-volume-slider" max="100" value="100"></div></div></div><figcaption>GPTWebHelper helps you read &amp; digest content</figcaption></figure><p>We built GPTWebHelper because we couldn&apos;t find a way to leverage the power of ChatGPT in a lot of the apps we use every day. &#xA0;We spend a lot of time on the web in the Safari browser, and on a lot of different sites&#x2013;and we couldn&apos;t find a solution to access ChatGPT on all of them without switching back and forth between windows or copy/pasting text around.</p><h2 id="the-power-of-chatgpt">The Power of ChatGPT</h2><p>So, how does it work? Once installed, GPTWebHelper is activated with a single click, right next to the address bar on any page. A handy chat popup will appear, ready to assist you.</p><figure class="kg-card kg-video-card kg-card-hascaption"><div class="kg-video-container"><video src="https://blixtdev.com/content/media/2023/10/app_clip2.mp4" poster="https://img.spacergif.org/v1/1920x1080/0a/spacer.png" width="1920" height="1080" playsinline preload="metadata" style="background: transparent url(&apos;https://blixtdev.com/content/images/2023/11/Screenshot-2023-11-01-at-8.34.19-AM.png&apos;) 50% 50% / cover no-repeat;"></video><div class="kg-video-overlay"><button class="kg-video-large-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button></div><div class="kg-video-player-container"><div class="kg-video-player"><button class="kg-video-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button><button class="kg-video-pause-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/><rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/></svg></button><span class="kg-video-current-time">0:00</span><div class="kg-video-time">/<span class="kg-video-duration"></span></div><input type="range" class="kg-video-seek-slider" max="100" value="0"><button class="kg-video-playback-rate">1&#xD7;</button><button class="kg-video-unmute-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/></svg></button><button class="kg-video-mute-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/></svg></button><input type="range" class="kg-video-volume-slider" max="100" value="100"></div></div></div><figcaption>GPTWebHelper helps you create &amp; edit content</figcaption></figure><p>GPTWebHelper can help you read &amp; digest content by answering questions, explaining concepts, translating texts, or summarizing pages. With GPT Web Helper, you no longer need to open countless tabs or switch between applications. Simply ask your questions directly within the chat window, and the AI assistant will promptly provide relevant and accurate answers, acting as your knowledgeable virtual guide.</p><p>But it&apos;s not just for reading content&#x2013;GPTWebHelper also helps you <em>create</em>. &#xA0;Emails, blog articles, social media posts &amp; more are a breeze&#x2013;anywhere on the web you need to create content, GPTWebHelper is there to assist you. &#xA0;From creating content from scratch to editing, correcting &amp; modifying text, you get the full power of ChatGPT at your disposal.</p><h2 id="try-out-gptwebhelper">Try out GPTWebHelper</h2><p>Whether you&apos;re a student looking for additional learning resources, a professional seeking quick and reliable information, or simply a curious individual exploring the web, GPT Web Helper is your go-to extension that brings the limitless potential of ChatGPT directly to your fingertips.</p><p>Discover the transformative power of ChatGPT in Safari &#x2013; find <a href="https://apps.apple.com/us/app/gptwebhelper/id6470203561?mt=12&amp;ref=blixt-dev">GPTWebHelper in the App Store</a> and give it a try!</p>]]></content:encoded></item><item><title><![CDATA[Concurrency Patterns in Swift with Async/Await]]></title><description><![CDATA[In this post, we will explore the use of async & await patterns of handling asynchronous operations in Swift for those coming from a JavaScript background.]]></description><link>https://blixtdev.com/async-await-swift-javascript/</link><guid isPermaLink="false">652987f642dd3e01077e2bf3</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Swift]]></category><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Wed, 18 Oct 2023 18:20:59 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1653750365027-2a9455d460f7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDQxfHxqdWdnbGV8ZW58MHx8fHwxNjk3NjMzMjIxfDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1653750365027-2a9455d460f7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDQxfHxqdWdnbGV8ZW58MHx8fHwxNjk3NjMzMjIxfDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Concurrency Patterns in Swift with Async/Await"><p><code>async</code> &amp; <code>await</code> are key concurrency tools for most JavaScript developers and have been in common use for many years. &#xA0;In the Swift language, on the other hand, <code>async</code> and <code>await</code> are relatively new, appearing in Swift 5.5, and are generally not the most common idiom for dealing with concurrency. &#xA0;While they may not be the most common approach, there are definitely scenarios in which they can be used to craft a cleaner solution. &#xA0;In this post, we will explore the use of <code>async</code> &amp; <code>await</code> patterns of handling asynchronous operations in Swift for those coming from a JavaScript background.</p><h2 id="concurrency-and-callbacks">Concurrency and Callbacks</h2><p>Concurrency, broadly speaking, is the ability for a language to <em>manage</em> multiple streams of computation at the same time. &#xA0;Note that <em>concurrency </em>is distinct from <em>parallelism</em>, which is the ability to improve performance by actively <em>performing</em> two tasks at the same time (e.g., on multiple processors or cores). &#xA0;</p><p>In both JavaScript and in Swift, simple concurrency can be achieved through the use of functions that accept <em>callbacks, </em>and the two have comparable syntax:</p><figure class="kg-card kg-code-card"><pre><code>// JavaScript:
readFile(path, r =&gt; { console.log(result) })
performExpensiveComputation()

// Swift:
readFile(path: path, completion: { result in print(result) })
performExpensiveComputation()
</code></pre><figcaption>Callback pattern in JavaScript and Swift</figcaption></figure><p>In the two example above, we make use concurrency to do two things at once: read a file, performing some operation with the result, and perform some other expensive computation. &#xA0;In this example, we don&apos;t know and don&apos;t care which operation completes first&#x2013;only that both streams of computation are performed.</p><h2 id="why-asyncawait">Why Async/Await?</h2><p>For simple concurrency, callbacks work just fine. &#xA0;To understand why we need <code>async</code> and <code>await</code>, we need to consider some a complicated example. &#xA0; Let&apos;s consider a completely fictional (but plausible) chain of asynchronous computation to read a file, parse the contents, fetch &amp; process some metadata, and post the result to a server:</p><figure class="kg-card kg-code-card"><pre><code>fetchFile(url: url) { contents in 
  parseFile(contents: contents) { parsed in
    fetchMetadata(parsedData: parsed) { metadata in 
      processMetadata(metadata: metadata) { processed in 
        postProcessedDataToServer(data: processed) { result in
          logResult(result)
        }
      }
    }
  }
}</code></pre><figcaption>An example of &quot;callback hell&quot;</figcaption></figure><p>Yikes! &#xA0;We&apos;ve created &quot;callback hell&quot;. &#xA0;This is where <code>async</code> and <code>await</code> come into play: <strong>they replace callbacks with a linear syntax for perfoming concurrent computations</strong>. &#xA0;Let&apos;s refactor our code to make use of <code>async</code> and <code>await</code>. First, the finished computation using <code>await</code>:</p><figure class="kg-card kg-code-card"><pre><code>let fileContents = await fetchFile(url: url)
let parsed = await parseFile(contents: fileContents)
let metadata = await fetchMetadata(parsedData: parsed)
let processed = await processMetadata(metadata: metadata)
let result = await postProcessedDataToServer(data: processed)
logResult(result)</code></pre><figcaption>&quot;Callback hell&quot; resolved with async/await</figcaption></figure><p>Behind the scenes, we&apos;ve also updated the signature for the functions we&apos;re using. &#xA0;We&apos;ll get into the exact implementations later, but here&apos;s how the declarations change:</p><figure class="kg-card kg-code-card"><pre><code>// with callbacks:
func fetchFile(url: URL, callback: (Data) -&gt; Void) { ... }

// with async/await:
func fetchFile(url: URL) async -&gt; Data { ... }</code></pre><figcaption>A function definition with a callback, and with async</figcaption></figure><h2 id="asyncawait-patterns-in-swift-javascript">Async/Await Patterns in Swift &amp; JavaScript</h2><p>Let&apos;s take a look at some common <code>async</code>/<code>await</code> patterns you may be familiar with in the JavaScript world, and see how they are implemented in Swift.</p><h3 id="basic-async-function-implementation">Basic async Function Implementation</h3><p>First, let&apos;s take a look at the most basic usage. &#xA0;In order to use <code>await</code>, you&apos;ll need to write <code>async</code> functions. &#xA0;In both Swift and JavaScript, the simplest possible way to implement an <code>async</code> function is to <code>await</code> the result of <em>another </em><code>async</code> function! &#xA0;</p><p>First, a simple example in JavaScript:</p><pre><code>async function loadData(url) {
  return await fetch(url)
}</code></pre><p>In Swift, we can do the same thing using existing async Foundation function on <code>URLSession</code> to read data from a URL in <code>async</code> fashion:</p><pre><code>func loadData(url: String) async throws -&gt; Data {
  let request = URLRequest(url: URL(string: url)!)
  let (data, _) = try await URLSession.shared.data(for: request)
  return data
}</code></pre><h3 id="composing-async-functions-from-callbacks">Composing Async Functions from Callbacks</h3><p>What about creating an async function when no existing async function exists? &#xA0;For example, when adapting a function using a callback?</p><p>In JavaScript, we can create an async function by returning a Promise. &#xA0;In this case, we wrap an existing <code>apiCall</code> function in a promise to make it <code>async</code>:</p><pre><code class="language-javascript">async function asyncApiCall() {
  return new Promise((resolve, reject) =&gt; {
    apiCall((result, error) =&gt; {
      if (result) {
      	resolve(result)
      } else {
     	reject(error)
      }
    })
  })
}
</code></pre><p>Similarly, in Swift, we can construct an <code>async</code> call using a <em>Continuation.</em> &#xA0;The full details of continuations in Swift are a <a href="https://developer.apple.com/documentation/swift/checkedcontinuation?ref=blixt-dev">much larger topic</a>, but in context of composing an <code>async</code> function, they can be used in a way similar to the JavaScript Promise in the example above:</p><pre><code>func fetch(url: String) async throws -&gt; Data? {
  let request = URLRequest(url: URL(string: url)!)
        
  return try await withCheckedThrowingContinuation { continuation in
    URLSession.shared.dataTask(with: request) { data, _, err in
      if(err == nil) {
        continuation.resume(returning: data)
      } else {
        continuation.resume(throwing: err!)
      }
    }
  }
}
</code></pre><p>Here we use <code>continuation.resume</code> in the same way as the JavaScript Promise functions <code>resolve</code> and <code>reject</code>: we can <em>resume </em>with either a return value, or an error.</p><h3 id="combining-multiple-async-calls">Combining Multiple Async Calls</h3><p>Combining <code>async</code> calls works a a bit differently in JavaScript and Swift.</p><p>As we alluded to earlier, async calls in JavaScript are just promises under the hood. &#xA0;Because of this, if we call an <code>async</code> function <em>without </em>await, we can get a Promise object, and thus handle multiple async calls using &#xA0;<code>Promise.all()</code>:</p><pre><code>async function fetchData() { ... }

func fetchMultiple(urls) {
  let promises = urls.map(url =&gt; {
    return fetchData(url) // note -- we&apos;re not using await! 
  })
  
  return Promises.all(promises)
}</code></pre><p>In Swift, we can achieve something similar with a <a href="https://developer.apple.com/documentation/swift/taskgroup?ref=blixt-dev">TaskGroup</a>:</p><pre><code class="language-swift">// assume we have the following:
func fetchData() async -&gt; Data { ... }

func fetchMultiple(urls: [String]) async -&gt; [Data] {
  return await withTaskGroup(of: Data.self) { group in
    urls.forEach { url in group.addTask { await self.fetch(url: url) } }
    
    var data: [Data] = []
    for await i in group { data.append(i) }        
    return data
  }
}

</code></pre><p>In this example, we add the individual calls to a TaskGroup (using <code>await</code> while makign the call!) and then assemble the results manually. &#xA0;</p><h2 id="conclusion">Conclusion</h2><p><code>async/await</code> are powerful tools for dealing with asynchronous code and relatively new to the Swift ecosystem. &#xA0;While <code>async</code> &amp; <code>await</code> are not the only approach to building concurrency in Swift, they are especially useful when representing a linear flow of logic which relies on complex asynchronous tasks, and helping to unwind the &quot;callback hell&quot; that can occur when building this logic with callbacks.</p>]]></content:encoded></item><item><title><![CDATA[React Native 0.72: What’s New and Why it Matters]]></title><description><![CDATA[New features in React Native bring a ton of important improvements.  In this post, we’ll see what’s new in React Native 0.72.]]></description><link>https://blixtdev.com/react-native-0-72-whats-new/</link><guid isPermaLink="false">6493446297d32502106f72c3</guid><category><![CDATA[React Native]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Wed, 21 Jun 2023 18:51:50 GMT</pubDate><media:content url="https://blixtdev.com/content/images/2023/06/Blue-Computer-Icon-Background-Technology-Presentation.png" medium="image"/><content:encoded><![CDATA[<h4 id="new-features-in-react-native-bring-important-improvements-to-the-developer-experience-and-much-more">New features in React Native bring important improvements to the developer experience and much more!</h4><img src="https://blixtdev.com/content/images/2023/06/Blue-Computer-Icon-Background-Technology-Presentation.png" alt="React Native 0.72: What&#x2019;s New and Why it Matters"><p>After a long wait, React Native 0.72 is finally here! It brings a number of highly requested features, improvements in performance and the developer experience, and several other important changes. &#xA0;In this post, we&#x2019;ll see what&#x2019;s new in React Native 0.72, and most importantly, why it matters to React Native developers.</p><h4 id="new-metro-features">New Metro Features</h4><p>React Native 0.72 introduces several important features in the <a href="https://facebook.github.io/metro/?ref=blixt-dev">Metro</a>, the default JavaScript bundler in React Native projects.</p><p>One of the more important highlights of this release is the addition of beta support for <em>symlinks</em> in Metro, a long-requested feature by the community. In short, the feature allows the Metro bundler to follow symbolic links, which enables a variety of previously unsupported project structures and tools. This change lets React Native work seamlessly with monorepo setups and <a href="https://pnpm.io/?ref=blixt-dev">pnpm</a>, eliminate the need for complicated workarounds or third party tools.</p><p>Similarly, support for <strong>Package Exports</strong> has been added to Metro, providing an alternative to the &#x201C;main&#x201D; field in package.json. This feature allows npm packages maintainers to define their public API more precisely and target React Native specifically. By enabling support for <strong>Package Exports</strong>, your app is more compatible with the wider JavaScript ecosystem.</p><p>Both of these new Metro features are considered to be beta, and you&#x2019;ll need to update your configuration to enable them. To enable these features in your project, update your <code>metro.config.js</code> file and add the following options:</p><pre><code class="language-JavaScript">const config = { 
  // &#x2026; 
  resolver: {   
    unstable_enableSymlinks: true,
    unstable_enablePackageExports: true, 
  },
};</code></pre><p>Also note that the default setup of <code>metro.config.js</code> has changed in the new release, so be sure to update your configuration according to the <a href="https://github.com/facebook/react-native/blob/76a42c292de838a0dd537935db792eaa81410b9b/packages/react-native/template/metro.config.js?ref=blixt-dev" rel="noopener">template</a>.</p><h4 id="improved-error-handling-reporting-in-development">Improved Error Handling &amp; Reporting in Development</h4><p>One ongoing pain-point of React Native development is error reporting. During development, it&#x2019;s not uncommon for small errors or typos to unleash a swarm of cryptic errors in the logs or a blank screen or &#x201C;redbox&#x201D; error. React Native 0.72 improves on the error reporting in development with several changes.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYYAAACBCAMAAADzLO3bAAABC1BMVEX///8rtnMeISb7+/s9OzwAAAA9Ojx/06s9PT0quXQAAAja6uMAAA6usLDl5uayvLgaHSP19fUtLy8ABhCwtrQACxCRkZHR09MtpWs9NjoxNTgsrm++v8A7RkEoMi44NzdQUlQPDxU7XkwTFx3N19M6UkXLzMwjJSnb3Nw+Mjnk5eVcXl8gISIsq24qKCo0MjM2bFGfoaEvoWoykGIzf1kTGx6OlZODioc7TEQzl2Z4f323uLhsbW4LFhhscXBDRUVXYV02imA4c1U6Wkoyd1V4vZxfeG1XTFNvpoyFrptrenRLXVR9zKdmhXZdZmN8tZpDa1ddoYBlmYBnsY2GzKqer6cbJyaNm5UzPTmHe7/9AAAP4ElEQVR4nO1dC0PayBZGMqQDBZoECwaMAaM8lYhVoaCk9rG3e7vrdnfv7vr/f8mdmYS85iRgu6S1zoeQZAiBzJfznJMxlxMQEHgIpG/9AwQEvhsIacgAEnskLwQywPpuFkRkBSnyiG8KIgSeCDa50oU0ZAHPDkuRdX8hOPhOIIjYPlwTDFjmlbsqSPheIJjIBklhm7AMAk8K6Ze7CN+yQzxozomO/wYAu1yqpkNV1Qd/kRo9wlf/8B8JntKJ/eWuxloqHOfNfLrbf0Bf9uttHDoAHtUFETyk0CtZtk2EMaIPAm+B/AVZYtO2bW00727YmRXTpocMYGrOcDun8mgR44A8lyZG+kyfyQZ2DGTIhuFggy7IpoFkWR4MBrqMTVNrTzYhoj7GiHyAfJL+kaeuY9MRJsjHioJAO1H8Y8ufi63iabG1Qx87rRb92zl1F+ene+evbg9n+oAwoeHK2i95cYJl4/a8db5HcEr/zi90rE22eF6PDlJsSVe6J8hoFXcSQN4oUpzeHiId4ZPRGvWiOkg+Jh9xP0j/yPNCR44wDyEAukF18OBVIg0BH8W9t5ioF3s/9Qu6J1g/jR+tRZRTdUtn9PggRdb9rbotv1vLAmPi9GyA8DhVMV3Z8hnHaVHG9tHWTuvxwjfUdKU/3kgcWH++dmQ8TtPzd6Z+yx1LSAMPKeotkcXURjNOkSQJxExOkwcV48F5/FDF17rZFq4ShHAqo0o81uPNaCA8GMRl6icdtq8h45T7zIVuzrM5rUcDCViraFj//AAezES/50UP8YQWD2XhsAaIJPMC35WqJcLD8c6GeulcRtpVwldMbZ230DsY292tn90jghRfWS2XNtZnrzcjgugYfJBgcUeAhS7uEUdJhA0RSDwVDIQHWT87Z/HWOrRmsj0Fj05iEJ230K901N7uWT0mSNF0ht/ENhsnGGN58O7ifI+Fzani8ErHGnh5N0AL/V5YaA6QiSYY3rNkqqwP5ON3Z4SMNCpODTQGlf1iLB/zrB3K9u6Wz+qRQfKfoQiObbQR+vRmYNMEN02KouP354mmonimw1oJtNAtA48bGZ7jd454ABXdnozRp2cffvqPbQ9sRKJepOvHtwlEJGp70EKfI3QigrcQwoP+vvfqrQyJz/rxGcHPH//75pNJuCCmYsYnJlytpGModFANJCz0JghV7sWb5xpyfnnm4Zfffm3rMkaDzzANRFyAjDex0E6LE533up0UZjxFRMO2XLxErIqxeefzQPDbT7KMdTj5aiATUPe7tvyOF593srZ+tOgJImyjVzV7OWodsP3rszA+fNKxfgjR4CAoLp5CWe4WcauEheaQbC3nNh78EeHh2R3hAUqCOwhK770BLbSMx1s8nccIKdFlpa/qiPDwJsrDZxkaE2rJCPPpjCqx0Hu8hR6Y7SzP8bEA8JdWGe+RiU3j9zANP9sY8117TjwlXqoaZF/QQtczOa9HgkiNqgS4rrlcH5NYWjf++BDwQKz0a46GW92847+gogkLvTFiFcOBiabKqY0YEdj59PHjx9///PPD/z7L+sWGNMxN/T1PwwwdJI4SPUlIOU4jhW+FoyBaybBNwoQ8GOi6juUZxgAN5zoyryoxXCE84AWH7pqS5V5ohPdewFM/T7bz6dUfPxBiCe/Vc2Kj0WLp2LZpIiYYCKJhp/hOx7amjclDG9vsVdNsOnTE7Xk7gATHR6WkKEohcGgbBbr9Ymvn/X0gSOX5W5F3+xiZ3edHu9P5ddvBJmUDpGHP0OU4dN3gCwuKb9Nj6Eo5n8+HaDgqkO3SD01D0n0k4fB6ZJrL5wxHDC/uEVDxslNs3X4+jOHsAir+O0630E+QBmjUJ7yki30SSu8+D3B0gHhPiRHBX/jQbi2EtTQLzWgoPS0agDsNOem4Ns32UUDDro0wN562OWiEgdPGoePS0PjxaUjI6oXtBbEOPWyOAhr+soHxtAfQQCz0Mu03ERp6qdKwP3e0sXO98H/vcLFYME9qMdJ61Po3vIbG1Bhr7TrL/EqL6/vx/fUi8l39yag5brYn/WibMW4a15NvWToSs9FsvaJh+9qnYWlDscDmNJytiaHXSMPi74JFXCfFKll/eU27hVLBquaGDnnHojTUSUO+qo4KHbpjrUC+r9sr0Y91SgfBkckONcvd43qViBmOCmX3+LWalVUplXe9R72l6NvuXVjY9vWSgwBH6QE0HKP0GDrdNtQLCtmkz7xS8MoKdl/m87XusGmRRotKWr2Wz5f3R7W8h8KkUbO8dUtZDYv0853VDvlO0xWI4UHQlu+8+Zq+fRjAvo+9IU01bOIJI+LIxIgbT3sAWjK2U2PouDT0C1RJeTRMKCfKS6XXsVj3skZKQ6c+Yh3YoSPilAalZ+WVTocRlr8kxFneRs0jb6gx2pTeDf2g5Y4dLil1yo1yQ3cuZVpzHimTCblOYa91oplIa1MiXtjI4LJ1DxCG1+tuMGE0WHU/FK93AmkYsl7SJsPqfps2F5guoTTkLxXa6zdUATEaCF4686nhXt2KcnlXn/eYJF26p8R6vHN9VG1M2TfQD6p0TTEqjf3JXCvhf7+vE5FWFhDSU4u2TW+xGtXnpnz25Syw5NMytRygUu71yDVbWoF1qUcD67AekxTVIX1aY3Ego4FQdzKv7Ffomy4NtSuV6n+LCBOhjprsPuOkwISxoaxkh1xkVHqaZO8qU4CuGVcnWSdQ4iF0zGtyf1V9bNNbQE38lRZaXpPlZtIQQc+nQSN91/GUSoX2nUPXXBo6bX8snNFgtdlP7zNKOq76mtKNEnOBrpjicuVSourqJWlWKQ3W6Cu68osg+bOVAB0ft9nViXOimRhIcz+EhhnSok5jHJSGXpwJl4YG7cUb7yLtU0Nt0Z/IaFBCqo7RUHN7XkXMIrgU7ZfosegBJCIlQYfTjRJxHCRFYXbim9WySbkYHbxc5KiHPjUR+nISiIUerLHQK6VUXqGT761MNOvFSy+RO6GavkONA6OhHPIuGQ2elZfaZDel7XLU6KxoUJuUxJFnf0Zko/wPaZ7X6BWg1Hob3Wn8L4OLo5OV97624W1xCcLweoDa6WfIlFKt0l2hUvOlgerwXr5TKpXJn6tsKKWMhk4o2nKV0nN3ox267AMaqj2FmoxyqUyPZa3sRFXzTHqtlN1EBlK4yyNjDWFfKYKprb/9Gp10u7aIOCW1V19FAspq6dOgKCHvku3X8yKyttUDaDiKKj3CSY2Z6367rHhEXGZrokOdH2pIAFQM+RAaDtdZ6LTwzaPh8jJ/c3l5qZDny01ogKSh73Y/Owg91M1lyfWa1Al+aTHbpJQzTmdwZWPeNs/HEKHBV0QNO0UH2WuydCnSQJVS/nLRDWFlojkalHQaqtTpsubhQ61Mlro7YsEh8bU2676vhS8HoKeUA+SiqyEjuY9DSNilJSNzzawCzETDOaUuNdE3XGz7JTSoOOT7cv2y33blLqshcym+khbN5eiQKHQnW5HNq7GzF0ILLP2mMfQaC52mlIaWH7KFAdOgpdKQW3ZoyJakf6UllYdaprWFErCQYKW0NKEB0AuD1t2jyCjoMVfLtLORhU7NsNIuVULlAm5Pf4mJzlWoZL0MKJXo7qpf8dYvZ0hDdD709Xqp6uABXyp2OEAcsMyX67kWel32GJAGP7X3gvadMnZdGGlxXWArbvj2QGlQMbvep15AcXVDf1e1cLfrNtStkF7LBjG/Vcol5L1pnTzWgVpIHJ20ik1chaH4omig8To3MHUsmulspXQyGrWbhZKVQsMaT8mLBfOdgjNq40KpVqDZ92rBKhf+Hs2XTRZ5vHlIL/5b4LudqxioaPIhLwyyPR/Gp+PbtTHa42g4xVhbd4Wljr6pjms7LYuaifxXSAOxcyV32MI9VMmlgTV0WItSy9JhXV38ubiOyuU4auY2UCkmY+DOBvhGXGqhU9OrOb5O6ShSp1Rd1jp+8FbzaKCfsMI00Ia8T4OidFY0sGOv5LGilRXvWIq1kobg4Jff612SahvxVXinA+zwV/hQQw4nDXSqjNRxaIpFr9ls5kPDPnmy3QuCjf2ldlMrlcqdEzztBp9ohmiY0IZVgflIaza1a3e9odFj+2pxeIVvajSfcdO8q1CbIE1GvUuaY6/dNOeZF3iGqsUiwhDPZ/SbfPBGnZ8Rf4V3x2jGm4YNLHROUtXoDKPx7Vy1u6hUKvv+1JfcJyINbF2CNuh2nx5q0fXbJPfgi0bmqb1Y7CDFWkPYB/qWFuEB6Ykrm7citIgYvnv6aUPiOAi38ymOKyB4SxhAGEG13HsYK2KqDBi+GgIc1ZhIgHk9HZquDbQi4kbcJESzq1zBWBRDjAfxer3i3gC6IZpYEdhCi6kyQAAmQIorqBVoXg+y0ECZ/D7ZlTcNm1joJwgoh5Hi1k/GMncvboKFrtMZdTkajoWFhgGlLKSkDTCvZyANGEC4NgfwjbjCQieBqwbghyAY6Cy5fL2eDN0PrToImm5S3IgLIy4MYTdViu/Q1zBXUU/nswKK2/pQ8T210PAUZALxUoBcRA4iJO1CpoF07TV/zF0NzSALLW7E3QBhCx2JJlzMgaIMWoQHOD/wdJMzMVVGCqSoRCRaaAeaLINYaGAA4c7kJ5cunmN0ICz0WnBFY9GW4QHmB9ROMQJm8qyCFvrVwMy8OvSRAIiio8FEwMT+WJ5xwRux0A5/1D44mRUcYQgEiOYu4OLJKyAiS7TQMjwhtLDQCeADZyn6hv9mGwrePoNdmzDdJDoQFhpG1DkF+fCg3gOTF8LzhEkjE0iv7g1Q6o24Txf8gE8Qs0nhlhzT93zwdo7w33zX0iw3b6GJ/rpbNw79dJEiBFFHyQRoONOh9ASVBp6GGRIWeg34kjF+KPQ+PmNJsfgq4f+YkEDvsBjb90xPn6PhKSOtGiae37saI3QRGspp7b2VMfyvYboa1s/2WgH2Xr8jLIiEUiqiekmKvvhQHRPr8rEPQ9eReQ9f4HMNywMjgDyQsbYUBjoZgRYKDHY8ueSica9hLIfKVM1xO8EDVa/HJg5XtGLzZC5YSIUUsQQS0O6hOnUOTgIcpPxHUKkyOgjDWYphty8C6Fuq/QZ7sJc1//W5OhT/IHpDRJKrfBzHj8AJbBO8UhIMZIlox/uNSU6sQCZIGv8U2DqCMC0mFvEqJoHtAYjTBLJGeOBfWm1KEQERFGUB0cvfDfxJlSKzREvCa80SXEeLzv9GgO+CFlxkBdhPkrgVgSzAl2cEjYKKDBCvzEi7801gSwCHGfihOIGtQ4q8BvOBCqWUMeBcniAgc0jcJl8eI5ABUq59QUU2kJKufUFAVoj5RBE+JN6FEtgepKi7JPo9eySMOUs8OQLbRqQWIzwQJCjIDkm5JCENWQO6+kXvfyvAbpNABkga8pf8p2Bj6wi7qPGiSSEQ2SJmknPJqwLbwP8B1OWWwMo170gAAAAASUVORK5CYII=" class="kg-image" alt="React Native 0.72: What&#x2019;s New and Why it Matters" loading="lazy" width="390" height="130"><figcaption>The Hermes JavaScript engine logo (via heremesengine.dev)</figcaption></figure><p>First, the core Hermes JavaScript engine has improved error messages for attempting to invoking undefined functions, now showing the variable name that was called. This might sound small, but it&#x2019;s a great improvement over tracking down a cryptic &#x201C;undefined is not a function&#x201D; message! Additionally, the stack traces now filter out internal byte code frames, giving a far more readable call stack.</p><p>For component styling, previously an invalid property in StyleSheet would result in a redbox error. This release relaxes this requirement, allowing such errors to fail silently <em>during development</em>, similar to how invalid CSS property errors are handled in browsers. The errors can still be caught and reported at build-time.</p><p>Finally, the React Native CLI itself is updated with this release, enhancing error output by reducing duplication, clarifying wording, reducing verbose stack traces, and adding deep links to relevant docs for certain commands.</p><h4 id="new-architecture-performance-improvements">New Architecture &amp; Performance Improvements</h4><p>If you&#x2019;ve been following React Native development for a while, you&#x2019;ll know that each new release brings incremental improvements to the &#x201C;<a href="https://reactnative.dev/docs/the-new-architecture/landing-page?ref=blixt-dev" rel="noopener">New Architecture</a>&#x201D;, and React Native 0.72 is no exception.</p><p>The new Fabric rendering engine gets a boost with improved text rendering performance of up to 27% on iOS and 18% on Android. More detailed info on Fabric performance improvements is available in <a href="https://github.com/reactwg/react-native-new-architecture/discussions/123?ref=blixt-dev" rel="noopener">this blog post</a>.</p><p>Also, the new architecture JavaScript engine, Hermes, has also seen a number of performance improvements, with faster compilation of large object literals and multiple optimizations to JSON parsing. These improvements will especially benefit apps that do a lot of JSON manipulation or bundling of many objects.</p><p>Finally, for iOS, the New Architecture is now compatible with the commonly used <code>use_frameworks!</code> CocoaPod configuration&#x200A;&#x2014;&#x200A;this was a blocker for many apps in integrating the New Architecture in previous released.</p><p>As a side note on the New Architecture, as of React Native 0.72, New Architecture updates will be managed by a dedicated working group, allowing for more focused and frequent updates. If you&#x2019;re interested in New Architecture updates, be sure to follow <a href="https://github.com/reactwg/react-native-new-architecture/discussions?ref=blixt-dev" rel="noopener">working group</a>.</p><h4 id="breaking-changes-and-deprecated-components">Breaking Changes and Deprecated Components</h4><p>React Native 0.72 has a great set of improvements, but there are also some breaking changes around packages and features included by default. Be sure to review these changes and update your dependencies accordingly!</p><p>The primary change is that a few components such as Slider, DatePickerIOS, and ProgressViewIOS have been removed from the React Native core and have been extracted into community packages. If you&#x2019;re using the components, simply find and install the recommended community package.</p><p>Additionally, several packages have been renamed and now live under react-native/packages and are published under the <code>@react-native</code> npm scope. These changes will not affect developers who do not have a direct dependency on a renamed package. However, developers should update any renamed dependency to its ~0.72 version when upgrading to React Native 0.72.</p><h4 id="react-native-072-not-dead-better-than-ever">React Native 0.72: Not Dead, Better Than Ever</h4><p>If you&#x2019;re a long time React Native developer, it&#x2019;s worth thinking a couple of years when everybody seemed to be asking &#x201C;<a href="https://www.quora.com/Is-React-Native-dead?ref=blixt-dev" rel="noopener">Is React Native Dead</a>&#x201D;? Suffering at the time from poor performance, <a href="https://medium.com/airbnb-engineering/sunsetting-react-native-1868ba28e30a?ref=blixt-dev">some high profile projects moving <em>off </em>of React Native</a> and increased competition from new frameworks like <a href="http://flutter.dev/?ref=blixt-dev" rel="noopener">Flutter</a>, it seemed like React Native had some fundamental challenges to tackle. It&#x2019;s safe to say that over the past several releases, and in particular with the introduction of the New Architecture, the team has lived up the challenge.</p><p>React Native 0.72 continues this progress with an impressive step forward, driven by community feedback and featuring many significant updates and improvements. If you&#x2019;re using an older version of React Native, especially a version without the Hermes JavaScript engine, it&#x2019;s worth the time and effort to get up and running in 0.72.</p><p>React Native upgrades <em>can </em>be a bit tricky, so when you are ready to upgrade to 0.72, it&#x2019;s always a good idea to consult the <a href="https://react-native-community.github.io/upgrade-helper/?ref=blixt-dev" rel="noopener">React Native Upgrade Helper tool</a> and look at the upgrade documentation. If you are using Expo, React Native version 0.72 will be supported in the Expo SDK 49 release.</p><p>Good luck, and happy hacking on your React Native apps!</p><p><br></p>]]></content:encoded></item><item><title><![CDATA[Monitor Website Content in Safari with Expedition]]></title><description><![CDATA[Expedition is a simple Safari extension to monitor prices, social media stats, sporting events and more.]]></description><link>https://blixtdev.com/introducing-expedition/</link><guid isPermaLink="false">649052da97d32502106f72a7</guid><category><![CDATA[Tools]]></category><category><![CDATA[MacOS]]></category><category><![CDATA[Safari]]></category><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Mon, 19 Jun 2023 13:08:15 GMT</pubDate><media:content url="https://blixtdev.com/content/images/2023/06/256-2.png" medium="image"/><content:encoded><![CDATA[<h3 id="a-simple-browser-extension-to-monitor-prices-social-media-stats-sporting-events-and-more">A simple browser extension to monitor prices, social media stats, sporting events and more</h3><img src="https://blixtdev.com/content/images/2023/06/256-2.png" alt="Monitor Website Content in Safari with Expedition"><p>Earlier this year, during the run-up to the Boston Marathon, I needed a new pair of shoes for race day. <a href="http://nike.com/?ref=blixt-dev" rel="noopener">Nike&#x2019;s</a> new <a href="https://www.nike.com/t/vaporfly-3-mens-road-racing-shoes-Q4PBPK?ref=blixt-dev" rel="noopener">VaporFly 3</a> shoes had been announced, but with no specific release date in the US&#x200A;&#x2014;&#x200A;only rumors.</p><p>So over the course of several days, I found myself in a near constant &#x201C;refresh loop&#x201D;, looking to see when I could get my hands on the new shoes and make sure I got them in time for race day.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-images-1.medium.com/max/1600/1*Gx9AOy05OM4B5wCsFBspiw.gif" class="kg-image" alt="Monitor Website Content in Safari with Expedition" loading="lazy" width="520" height="293"><figcaption>Me, checking to see if a new pair of Nike VaporFly shoes had been released (Clip via &#x201C;The Social Network&#x201D;, Sony Pictures)</figcaption></figure><p>The story has a happy ending, as the shoes were released in early April and I got them in time for race day, but the whole thing got me thinking: there&#x2019;s got to be a better way to keep an eye out for changes to web content!</p><p>Well, of course, there <em>are </em>better ways&#x200A;&#x2014;&#x200A;a number of Chrome extensions can keep an eye out for changing content on websites. But there were none for the Safari Browser! Until now.</p><h3 id="introducing-expedition-website-monitoring-for-safari-made-simple">Introducing Expedition: Website Monitoring for Safari, Made Simple</h3><p>Inspired by my obsessive refreshing to get a new pair of shoes, I developed Expedition: a simple browser extension to help you monitor web content in Safari.</p><p>Expedition lets you select targeted content from websites, and automatically checks the content periodically in the background. When content changes, you can easily access the updates from the Expedition toolbar icon.</p><figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/h6QiuJd4k-U?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen title="Expedition - Website Monitor for Safari"></iframe></figure><p>Expedition is great for tracking changes in headlines, prices, sports scores, social media stats and much more.</p><p>Want to give it a try? Check out <a href="https://apps.apple.com/us/app/web-expedition/id6449189045?mt=12&amp;ref=blixt-dev" rel="noopener">Expedition in the Mac OS App Store</a>!</p>]]></content:encoded></item><item><title><![CDATA[Origins of Life: Building a Hypercycle Simulation with WebGL]]></title><description><![CDATA[Though the exact origins of life on earth remain a mystery, the hypercycle helps explain the emergence of self-replication.  In this post we'll learn about hypercycles and how to build a simulation using WebGL.]]></description><link>https://blixtdev.com/hypercycle-simulation-with-webgl/</link><guid isPermaLink="false">6414a9df2441b60210ca9c46</guid><category><![CDATA[How To]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[WebGL]]></category><category><![CDATA[Science]]></category><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Mon, 03 Apr 2023 10:37:09 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1525872827408-db3ae470c3ce?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDE0MHx8Y2lyY2xlfGVufDB8fHx8MTY4MDUxNzkxOA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1525872827408-db3ae470c3ce?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDE0MHx8Y2lyY2xlfGVufDB8fHx8MTY4MDUxNzkxOA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Origins of Life: Building a Hypercycle Simulation with WebGL"><p>Though the exact origins of life on earth remain a mystery, there are a number of tools we can use to try to understand and explain the emergence of self-replicating systems, a key pre-requisite. One of the most interesting models is the <em><a href="https://www.google.com/search?client=safari&amp;3Brls=en&amp;3Bq=hypercycle&amp;3Bie=UTF-8&amp;3Boe=UTF-8&amp;ref=blixt-dev">hypercycle</a></em>, which describes a set of cyclical chemical reactions which together form self-replicating loops of emergent behaviors.</p><p>These self-replicating loops represent a plausible model of the origin of life. &#xA0;Also, they look pretty freakin&apos; cool. In this post, we&apos;ll explore what a hypercycle is, how it can teach us about the origins of life, and how we can build a hypercycle simulation using <a href="https://get.webgl.org/?ref=blixt-dev">WebGL</a> pixel shaders.</p><figure class="kg-card kg-video-card"><div class="kg-video-container"><video src="https://blixtdev.com/content/media/2023/03/hypercycle_sm.mp4" poster="https://img.spacergif.org/v1/360x360/0a/spacer.png" width="360" height="360" loop autoplay muted playsinline preload="metadata" style="background: transparent url(&apos;https://blixtdev.com/content/images/2023/03/media-thumbnail-ember336.jpg&apos;) 50% 50% / cover no-repeat;"></video><div class="kg-video-overlay"><button class="kg-video-large-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button></div><div class="kg-video-player-container kg-video-hide"><div class="kg-video-player"><button class="kg-video-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button><button class="kg-video-pause-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/><rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/></svg></button><span class="kg-video-current-time">0:00</span><div class="kg-video-time">/<span class="kg-video-duration"></span></div><input type="range" class="kg-video-seek-slider" max="100" value="0"><button class="kg-video-playback-rate">1&#xD7;</button><button class="kg-video-unmute-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/></svg></button><button class="kg-video-mute-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/></svg></button><input type="range" class="kg-video-volume-slider" max="100" value="100"></div></div></div></figure><h2 id="what-is-a-hypercycle">What is a &quot;Hypercycle&quot;?</h2><p>A hypercycle is a mathematical model that describes a set of chemical reactions, leading to a cycle of molecules that depend on each other for their survival. Each molecule in the hypercycle catalyzes the replication of another molecule in the system, forming a cycle of replication that is self-sustaining. </p><figure class="kg-card kg-image-card"><img src="https://blixtdev.com/content/images/2023/03/Hypercycle-eigen.svg.png" class="kg-image" alt="Origins of Life: Building a Hypercycle Simulation with WebGL" loading="lazy" width="300" height="293"></figure><p><a href="https://en.wikipedia.org/wiki/Self-replication?ref=blixt-dev">Self-replication</a> is the ability of a molecule or system to make copies of itself: a process that is essential for the emergence of life. In a hypercycle, each molecule catalyzes the replication of another molecule in the system, creating a <em>cycle</em> of self-replication.</p><p>If we introduce a <em>spatial </em>element into the model, we can simulate the hypercycle in a <em>reaction-diffusion</em> system. &#xA0;Reaction-diffusion systems describe the behavior of chemicals that interact with each other through chemical reactions and are transported through a medium by diffusion. These systems can create complex patterns and behaviors that are essential to the formation of life. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blixtdev.com/content/images/2023/03/hypercycle.jpg" class="kg-image" alt="Origins of Life: Building a Hypercycle Simulation with WebGL" loading="lazy" width="2000" height="1239" srcset="https://blixtdev.com/content/images/size/w600/2023/03/hypercycle.jpg 600w, https://blixtdev.com/content/images/size/w1000/2023/03/hypercycle.jpg 1000w, https://blixtdev.com/content/images/size/w1600/2023/03/hypercycle.jpg 1600w, https://blixtdev.com/content/images/size/w2400/2023/03/hypercycle.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Our hypercycle simulation at frames: 5, 150, 300, 600, 1200 (from top-left). Each color represents a different type of molecule</figcaption></figure><p></p><h2 id="what-can-the-hypercycle-teach-us-about-the-origins-of-life">What can the Hypercycle teach us about the origins of life?</h2><p>The hypercycle is a simple model for explaining the emergence of self-replicating systems. The model proposes that the first self-replicating systems on earth were sets of molecules that catalyzed each other&apos;s replication, forming a self-sustaining cycle of replication. &#xA0;This interdependence creates a selection pressure that favors the evolution and adaptation of the system <em>as a whole</em>. &#xA0;Over time, the theory goes, these self-replicating systems evolved and adapted, eventually giving rise to the first living cells.</p><p>It&apos;s worth noting that the hypercycle is an <em>abstract </em>model. &#xA0;Generally speaking, the model does not propose a <em>specific</em> set of chemicals or reactions. &#xA0;Instead, it&apos;s simply a model which lets us probe some of the properties that could give rise to such self-replicating systems. &#xA0;In principle, hypercycle behavior can emerge from simple chemical reactions, RNA molecules, &#xA0;proteins &#xA0;or even entire organisms (of the three options, the &quot;<a href="https://en.wikipedia.org/wiki/RNA_world?ref=blixt-dev">RNA world</a>&quot; hypothesis is the most popular).</p><p>So while the model doesn&apos;t predict the exact agents involved in the origin of life on earth, we <em>can</em> use simulation of hypercycles to explore all kinds of questions related to the origins of self-replication and life:</p><ul><li>What kinds of networks of chemical reactions can form self-replicating systems?</li><li>What kinds of temporospatial dynamics give rise to stable populations?</li><li>What are the effects of changing variables like reactivity and diffusion rates</li><li>What other kinds of changes to the system (like temperature, introduction of other agents), can stabilize or destabilize self-replicating hypercycles?</li></ul><p>In short, while it doesn&apos;t predict a specific set of agents or behaviors responsible for the origin of life, it can help us understand the conditions under which self-replication and life can emerge.</p><h2 id="how-do-we-build-a-hypercycle-simulation">How do we build a Hypercycle simulation?</h2><p>To build a hypercycle simulation, we need to understand the math behind the hypercycle. &#xA0;The hypercycle can be described mathematically as a set of differential equations that describe the replication of each molecule in the system. These equations can be solved numerically to simulate the behavior of the system over time.</p><p>Formally, the hypercycle equations are defined as:</p><figure class="kg-card kg-image-card"><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/e06bea3ef4423095b83c53a7d71e0fc298074d35" class="kg-image" alt="Origins of Life: Building a Hypercycle Simulation with WebGL" loading="lazy"></figure><p>At each timestep, the chemical <em>x<sub>i</sub> </em>is produced through a reaction with chemical <em>x<sub>j</sub> </em>at a rate specified by <em>k</em><sub><em>i</em>,<em>j</em></sub><em>. &#xA0;</em>In most models, each chemical <em>i </em>reacts only with <em>i-1, </em>so this can be simplified&#x2013;the summation term is replaced with a single reaction term<em>. &#xA0;k</em><sub><em>i</em></sub><em> </em>represents the rate of self-reproduction and degradation of a chemical itself (and can sometimes be ignored). &#xA0;<em> </em>The <em>&#x3C6; </em>term at the end is a degradation expression based on the concentration of <em>all </em>of the chemicals,<em> </em>which keeps the total concentration constant.</p><p>Additionally, to satisfy the <em>cyclical </em>nature of the model, we assume that the &quot;first&quot; and &quot;last&quot; chemical <em>x</em> refer to the same entity:</p><figure class="kg-card kg-image-card"><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/c186553ad18bf7fb83b07da45130d7b049b6ea75" class="kg-image" alt="Origins of Life: Building a Hypercycle Simulation with WebGL" loading="lazy"></figure><p>Things get <em>really </em>interesting when we introduce a spatial dynamic as well. &#xA0;The math is similar, but occurs at every point on a huge grid<em>. &#xA0;</em>Additionally, the chemicals spread out or <em>diffuse </em>slightly<em> </em>at each time step. &#xA0;</p><p>To simulate and visualize the hypercycle, we&apos;ll build a <em>discretized </em>version of the reaction-diffusion system with spatial dynamics using OpenGL shaders in order to &#xA0;create a realistic real-time simulation of a hypercycle. &#xA0;Our strategy will be to create a texture that represents the spatial concentration of the chemicals at each pixel. &#xA0;We&apos;ll use the standard 4 color channels (red, green, blue and alpha) to represent the concentrations of 4 different chemicals which comprise the hypercycle. &#xA0;</p><p>Before getting into the details, <a href="https://codepen.io/jonklein-the-builder/full/yLxqzYa?ref=blixt-dev">here&apos;s a demo of our model</a> that you can inspect and play with. &#xA0;You can make changes to the model, or adjust the reaction, diffusion and decay constants to see what kinds of effect it has on the behavior of the system.</p><figure class="kg-card kg-embed-card kg-card-hascaption"><iframe id="cp_embed_yLxqzYa" src="https://codepen.io/jonklein-the-builder/embed/preview/yLxqzYa?default-tabs=js%2Cresult&amp;height=300&amp;host=https%3A%2F%2Fcodepen.io&amp;slug-hash=yLxqzYa" title="Hypercycle Simulation in WebGL" scrolling="no" frameborder="0" height="300" allowtransparency="true" class="cp_embed_iframe" style="width: 100%; overflow: hidden;"></iframe><figcaption>Our Hypercycle simulation in a Codepen</figcaption></figure><p>You can play with the model, including adjusting the core reaction and diffusion parameter values to see what effects it has on the system as a whole. &#xA0;By modifying those values, we can create different kinds of visual patterns, transition the system from stable to chaotic dynamics &#xA0;or &quot;blow up&quot; the simulation completely.</p><h3 id="the-hypercycle-simulation-in-webgl">The Hypercycle Simulation in WebGL</h3><p>To build our interactive hypcercycle simulation, we&apos;ll create a couple of simple pixel shaders in WebGL. &#xA0;We use a simple wrapper called <a href="https://google.github.io/swissgl/?ref=blixt-dev#ParticleLife3d">SwissGL</a> to help us setup the WebGL environment and execute the shaders. &#xA0;In this post we show only the shader code itself &#x2013; if you wish to see the full code used to setup and execute the shaders, you can find it in the Codepen example above.</p><p>Our simulation is made up of three simple shader programs: one to initialize the data, one to perform our simulation at each timestep, and the last to display our simulation data on screen. &#xA0;</p><p>First, we have a simple shader to initialize our data to some pseudo-random values. &#xA0;This shader could be almost anything that produces initial data (including an existing image texture), but in this case, we use SwissGL&apos;s built-in random <em>hash </em>function to sample random data based on a seed we provide:</p><figure class="kg-card kg-code-card"><pre><code class="language-GLSL">vec4 random = vec4(
  hash(ivec3(I, seed * 1.0)).x,
  hash(ivec3(I, seed * 2.0)).x,
  hash(ivec3(I, seed * 3.0)).x,
  hash(ivec3(I, seed * 4.0)).x
);
out0 = normalize(random);</code></pre><figcaption>The initialization shader</figcaption></figure><p>The output of this shader is stored in a texture that represents the state of our system.</p><p>Next is the core simulation loop. &#xA0;At each step, we compute the next state by taking the current state, then calculating and combining terms for reaction, diffusion and decay, and storing the results in an updated state texture:</p><pre><code class="language-GLSL">precision highp float;

// Diffusion: sample a pixel in each direction, scale by diffusionRate 
vec4 current = texture(points, UV);
float dx = 1.0 / size;
vec4 n1 = texture(points, UV + vec2(dx, 0));
vec4 n2 = texture(points, UV + vec2(0, dx));
vec4 n3 = texture(points, UV + vec2(-dx, 0));
vec4 n4 = texture(points, UV + vec2(0, -dx));
vec4 diffusion = diffusionRate * (((n1 + n2 + n3 + n4) / 4.0) - current);

// Reaction: chemical x reacts with (x-1) scaled by reactionRate:
vec4 reaction = (reactionRate * current * current.argb);

// Decay: chemicals decay at decayRate (can be 0)
vec4 decay = current * (1.0 - decayRate);

// Output: sum our terms and normalize
out0 = normalize(decay + reaction + diffusion);
</code></pre><p>Note that our reaction term uses <em>pixel swizzling </em>to multiple each chemical concentration by the previous chemical concentration. &#xA0;This is the &quot;cycle&quot; part of our simulation: &#xA0;<code>current.rgba * current.argb</code> .</p><p>Finally, with the simulation state updated, we can display the state of our simulation on screen. &#xA0;Though we <em>could</em> sample the simulation state texture directly and use the result as the color value to display, it doesn&apos;t always produce the best results. &#xA0;Instead, we&apos;ll assign a color to each chemical in the simulation, and produce a final color value weighted by the concentrations of each chemical:</p><pre><code>vec4 s = texture(points, UV);
vec3 color = (s.x * c1 + s.y * c2 + s.z * c3 + s.w * c4);
out0 = vec4(color, 1.0);</code></pre><h2 id="probing-the-origins-of-life-%E2%80%93-in-software">Probing the Origins of Life &#x2013; In Software</h2><p>The hypercycle model provides an intriguing framework for understanding the origins of self-replicating systems and the emergence of life on earth. &#xA0;By studying the behavior of self-replicating systems, we can gain insight into the fundamental principles that govern the behavior of living systems. </p><p>The hypercycle is just one example of how mathematical models and computer simulations can help us better understand the real world, and in some cases, help us explore the mysteries and origins of life itself.</p>]]></content:encoded></item><item><title><![CDATA[Direct, SecureFile Uploads to AWS S3 from Web and Mobile]]></title><description><![CDATA[In this post, we'll learn how to build secure, direct file uploads from web and mobile clients to AWS S3, skipping the step of sending the data to the server.]]></description><link>https://blixtdev.com/direct-s3-uploads-from-web-or-mobile/</link><guid isPermaLink="false">63be93b7523b9c0210cb9dde</guid><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Mon, 06 Mar 2023 11:21:56 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1569235186275-626cb53b83ce?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGZpbGVzfGVufDB8fHx8MTY3MzQ0NTM3NA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1569235186275-626cb53b83ce?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGZpbGVzfGVufDB8fHx8MTY3MzQ0NTM3NA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Direct, SecureFile Uploads to AWS S3 from Web and Mobile"><p>If you&apos;re developing an app which allows users to upload user-generated content such as photos, video or any other kind of document, you&apos;ll need to spend some time building out some infrastructure to handle <em>file uploads. &#xA0;</em></p><p>Typically these uploads are stored in a CDN or other storage provider such as Amazon S3. While the most straightforward solution is to upload files first to your backend server and then to the storage provider, this approach can cause performance issues and other problems.</p><p>In this post, we&apos;ll learn how to build secure, direct file uploads from web and mobile clients to AWS S3, skipping the step of sending the data to the server. &#xA0;Though the code examples are presented in JavaScript both the server and client calls, this approach can work with any server and client technologies, including native mobile client apps.</p><h3 id="sever-proxied-uploads-vs-direct-client-uploads">Sever-Proxied Uploads vs Direct Client Uploads</h3><p>The most straightforward approach to file uploads is to build out a backend API to process and deliver the file content to the storage provider. &#xA0;This approach works okay, but it&apos;s a bit inefficient. Instead of delivering the file upload to its ultimate destination, you&apos;re transferring the file <em>twice: </em>first to your server, and then to your storage provider. &#xA0;This means your upload is slow, and it consumes unnecessary server resources.</p><p>Furthermore, while the configuration of course varies by server software and hosting provider, you&apos;ll often find your app bumps up against file-size limits when you try to upload large files. &#xA0;Just as an example, the popular <code>nginx</code> server, used to &quot;front&quot; many web services, has a default upload limit of 1MB. &#xA0;This can be increased, but may be out of your control as an application developer, and even a larger setting may be too small for your application. </p><p>Direct uploads, in contrast, upload your content directly to the storage provider, by passing the need to upload the content to your own server. &#xA0;This sounds simple, but is a bit tricky in practice! &#xA0;To do a direct upload in a secure fashion, there are a couple of additional considerations.</p><h3 id="security-considerations-of-direct-client-uploads">Security Considerations of Direct Client Uploads</h3><p>Direct client uploads offer a way to improve on the user experience and resource use problems associated with server-proxied uploads. &#xA0;But implementing direct client uploads must be done with caution: if implemented incorrectly, it can lead to security issues like incorrect permissions or leaking of credentials.</p><p>The challenge is giving the client permission to upload the file in a secure fashion. &#xA0;If we were to try to use the same credentials we use on the backend, it means they pass through the client and can be intercepted by an attacker. &#xA0;Even if we create separate keys with limited access for the client, an attacker could still upload arbitrary content allowing them to fill up storage, perform DOS attacks, etc. &#xA0;The only secure solution is to let a client upload <em>only </em>the content that we authorize. </p><p>Essential what we want is a <em>one-time key </em>that allows a direct upload of a file that we allow. &#xA0;As it turns out, S3 (and other storage providers) allow us to generate something like this in the form of a <em>pre-signed URL. &#xA0;</em>This is a URL referencing our S3 object, with a signature parameter appended. &#xA0;This special URL allows us to write to the referenced object <em>and only </em>the referenced object.</p><h3 id="implementing-direct-client-uploads">Implementing Direct Client Uploads</h3><p>Our approach to direct client uploads will leverage pre-signed URLs to let the client upload directly to our storage provider. &#xA0;Of course, generating the pre-signed URL requires service provided credentials with sufficient credentials, so the signing cannot be done on the client. &#xA0;Instead, we&apos;ll make a server request to validate the upload metadata (checking user authorization, file type, size, etc), generate a signed URL, and return it to the client.</p><p>The image below shows the difference in flow between a server-proxied upload and &#xA0;a direct client upload.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blixtdev.com/content/images/2023/01/s3_post.png" class="kg-image" alt="Direct, SecureFile Uploads to AWS S3 from Web and Mobile" loading="lazy" width="429" height="404"><figcaption>Left: a server-proxied upload. Right: a direct client upload using a server signed URL. Image taken from AWS S3 documentation</figcaption></figure><p>Here are the basic steps to building a secure, direct client upload with a pre-signed URL:</p><ul><li>Implement a file input handler in client to receive a file from the user</li><li>Send the file metadata to the server to validate the request</li><li>Generate a pre-signed upload URL on the server and return it to the client</li><li>Make a PUT request from the client to upload the file to S3</li></ul><h3 id="server-side-signed-url-generation">Server-Side Signed URL Generation</h3><p>In the typical upload approach, our server holds AWS credentials which allow it to upload files. &#xA0;In the direct upload approach, the client won&apos;t have direct access to these credentials&#x2013;it would be highly insecure to put them in the client!</p><p>So in order to give the client permission to upload a file directly, we need to generate a <em>pre-signed upload URL </em>on the server, and send it back to the client<em>.</em> &#xA0;The pre-signed URL is a URL to an S3 object which contains a signature that grants write permission to that resource (and of course, only that resource).</p><p>We&apos;ll implement a service on our backend to fetch the upload URL using the AWS SDK:</p><figure class="kg-card kg-code-card"><pre><code>import AWS from &quot;aws-sdk&quot;

const s3 = new AWS.S3()

const getUploadURL = async (bucket: string, path: string, contentType: string) =&gt; {
  let putURL = await s3.getSignedUrlPromise(&apos;putObject&apos;, {
    Bucket: bucket,
    Key: path,
    Expires: 120,
    ContentType: contentType
  })

  return putURL
}
</code></pre><figcaption>Backend code for generating a pre-signed S3 URL</figcaption></figure><p>This function to generates our signed URL&#x2013;next we&apos;ll write a quick service to expose the functionality. &#xA0;For example, using the Express web server, we could write a service as follows:</p><pre><code>app.post(&quot;/upload_url&quot;, async (req: Request, res: Response) =&gt; {
  let url = await uploadURL(userUploadBucket, req.body[&quot;path&quot;], req.body[&quot;content_type&quot;])
  res.send({ put_url: url })
})</code></pre><h3 id="client-side-upload-implementation">Client-Side Upload Implementation</h3><p>Once we&apos;ve implemented the backend service, we&apos;ll use it to fetch the upload destination URL, then deliver our file content to the URL via a PUT request.</p><p>We&apos;ll typically implement this in the <code>onChange</code> handler of our file input.</p><figure class="kg-card kg-code-card"><pre><code>const handler = (e: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
  const files = e?.target?.files;

  if (files &amp;&amp; files.length &gt; 0) {
    let extension = files[0].name.split(&quot;.&quot;).pop()

    if (extension != null) {
        let response = await fetch(`${serverURL}/upload_url`, {
        	method: &apos;POST&apos;,
	        headers: { &apos;Content-Type&apos;: &apos;application/json&apos; },
    	    body: JSON.stringify({ directory, extension, content_type: contentType })
       	})
            
        let json = await response.json()
    
        let uploadResponse = await fetch(json.url, {
          method: &quot;PUT&quot;,
          body: content
        })
        
        // handle completion, update state to display uploaded file, etc
    }
  }
}</code></pre><figcaption>Client-side file input handling to upload our file</figcaption></figure><p>Tada! &#xA0;With this code in place, we can perform uploads direct to S3 without sending them to a backend server first.</p><h3 id="one-last-catch-displaying-uploaded-files">One Last Catch! &#xA0;Displaying Uploaded Files</h3><p>Though our upload implementation may be complete, the feature is probably not done yet! &#xA0;In most web apps, after uploading the content, the uploaded file gets displayed back to the user. &#xA0;The simplest way to do this is to use the newly uploaded URL. &#xA0;The catch is that the upload URL we&apos;ve generated doesn&apos;t actually allow us to view the object! &#xA0;Instead, we need to generate <em>another </em>URL that will be used to view the object after it&apos;s uploaded. &#xA0;Don&apos;t worry, this is simple! &#xA0;We just have our original service return two URLs:</p><pre><code>const uploadURL = async (bucket: string, path: string, contentType: string) =&gt; {
    var getURL = s3.getSignedUrl(&apos;getObject&apos;, {
        Bucket: bucket,
        Expires: 60 * 60 * 24 * 7,
        Key: path
    });

    let putURL = await s3.getSignedUrlPromise(&apos;putObject&apos;, {
        Bucket: bucket,
        Key: path,
        Expires: 120,
        ContentType: contentType
    })

    return { getURL, putURL }
}</code></pre><h3 id="recap-%E2%80%93-faster-uploads-for-web-mobile-apps-%F0%9F%8E%89">Recap &#x2013; Faster Uploads for Web &amp; Mobile Apps &#x1F389;</h3><p>Direct client uploads to S3 are a great technique for building more responsive frontend applications. &#xA0;By uploading your content directly to S3, you avoid the performance and resource consumption issues associated with sending large files through your backend server.</p>]]></content:encoded></item><item><title><![CDATA[Phoenix Framework 1.7 — What’s New & Why It Matters]]></title><description><![CDATA[Phoenix 1.7 is now live, and it's packed with great new features.  We'll take a look at what's new in Phoenix version 1.7, and why it matters.]]></description><link>https://blixtdev.com/whats-new-phoenix-1-7/</link><guid isPermaLink="false">636d57366d03460210cb09f4</guid><category><![CDATA[Elixir]]></category><category><![CDATA[Phoenix]]></category><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Mon, 27 Feb 2023 11:07:07 GMT</pubDate><media:content url="https://blixtdev.com/content/images/2023/02/1WvDl2WlPs7cR8TTBvrjpyw.png" medium="image"/><content:encoded><![CDATA[<img src="https://blixtdev.com/content/images/2023/02/1WvDl2WlPs7cR8TTBvrjpyw.png" alt="Phoenix Framework 1.7&#x200A;&#x2014;&#x200A;What&#x2019;s New &amp; Why It Matters"><p>In case you haven&apos;t used it before, the <a href="http://phoenixframework.org/?ref=blixt-dev">Phoenix Framework</a> is an amazingly powerful web development framework for the <a href="http://elixir-lang.org/?ref=blixt-dev">Elixir</a> programming language. &#xA0;It packs a ton of powerful features like <em>channels </em>for real-time websocket based communication, and <em>LiveView, </em>a server-rendered technology which enables the development of real-time interactive apps in the browser, <a href="https://blixtdev.com/you-dont-need-javascript-to-build-rich-interactive-webapps/"><em>without writing a line of JavaScript</em></a>. &#xA0;And because it&apos;s built on the <a href="https://www.erlang.org/?ref=blixt-dev">Erlang/OTP</a>, Elixir and Phoenix bring kick-ass support for scalability, fault tolerance and concurrency right out of the box.</p><p>Version 1.7 of Phoenix is now live, and it&apos;s packed with great new features, especially for <a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html?ref=blixt-dev">LiveView</a>. In this post, we&apos;ll take a look at the new features in Phoenix version 1.7, and why they matter.</p><blockquote class="kg-blockquote-alt">Though LiveView already provided a great developer experience, this release feels like &#xA0;&quot;LiveView 2.0&quot; in terms of the well thought out API changes and improvements.</blockquote><h2 id="verified-routes">Verified Routes</h2><p><a href="https://hexdocs.pm/phoenix/Phoenix.VerifiedRoutes.html?ref=blixt-dev">Verified routes</a> are a new &amp; greatly improved way to construct links within your Phoenix application.</p><p>When creating internal links in previous versions of Phoenix, we would typically use a <em><a href="https://hexdocs.pm/phoenix/Phoenix.Router.html?ref=blixt-dev#module-helpers">route helper</a> </em>to generate a URL fragment for us. &#xA0;For example, if we had a <code>PageController</code> defined in our router, and wanted to generate a link to it from somewhere else in the app, we might have code like the following:</p><pre><code># router.ex
get &quot;/pages/:page&quot;, PageController, :show


# elsewhere in our app:
MyAppWeb.Router.Helpers.page_path(conn_or_endpoint, :show, &quot;landing&quot;, options)</code></pre><p>That works well, but can sometimes be a pain: it&#x2019;s verbose, and requires knowledge of exactly how routes and actions are named. In practice, using helpers to generate URLs often involves consulting the <code>router.ex</code> file or using <code>mix phx.routes</code> to make sure you get the right module and action names.</p><p>It would be far simpler to <em>just refer to the path directly </em>when creating a link:</p><p><code>&lt;a href=&quot;/app/pages/landing&quot; /&gt;...&lt;/a&gt;</code></p><p>However, writing the path like this comes with a major downside: you have no way of knowing at build-time if you get the path wrong or if it changes later on. There are no errors or warnings to tell you you&#x2019;re using a broken path. (In fact, as an example, the path used above is incorrect, but we&apos;d have no way of knowing!)</p><p>This is where <em><a href="https://hexdocs.pm/phoenix/routing.html?ref=blixt-dev#verified-routes">verified routes</a> </em>come in: using Elixir&apos;s <a href="https://elixir-lang.org/getting-started/sigils.html?ref=blixt-dev">sigil syntax</a>, we can reference a route with <code><a href="https://hexdocs.pm/phoenix/Phoenix.VerifiedRoutes.html?ref=blixt-dev#sigil_p/2">~p</a></code> in front of a string and take advantage of compile-time route validation. &#xA0;It also allows us to add query params the way we would with the route helpers:</p><pre><code>iex&gt; query = [posts: 1, search: &quot;elixir&quot;]
iex&gt; ~p&quot;/pages/landing?#{query}&quot;
&quot;/pages/landing?posts=1&amp;search=elixir&quot;</code></pre><h2 id="liveview-streams">LiveView Streams</h2><p>A neat new feature to LiveView in 1.7 is the addition of <em>streams. &#xA0;</em><a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html?ref=blixt-dev#stream/4"><a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html?ref=blixt-dev#stream/4">LiveVie</a>w <a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html?ref=blixt-dev#stream/4">Streams</a></a> allow you to manage updates, inserts and deletes to large datasets without needing to store and reload the entire collection on the server as one would with a regular LiveView assignment. &#xA0;They also helps to reduce the amount of re-rendering required to modify items on the frontend.</p><p>To create a stream, call <a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html?ref=blixt-dev#stream/4"><code>stream/3</code></a> , providing the socket, the name of the stream, and the initial dataset:</p><pre><code>  def mount(_params, _session, socket) do
    {:ok, stream(socket, :posts, Blog.list_posts())}
  end
</code></pre><p>Then, in then template, instead of accessing a <code>@posts</code> assign, you simply use <code>@stream.posts</code> instead. &#xA0;If the contents of the collection change, you can use <a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html?ref=blixt-dev#stream_insert/4"><code>stream_insert/4</code></a> or <a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html?ref=blixt-dev#stream_delete/3"><code>stream_delete/3</code></a>. &#xA0;The &#xA0;<a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html?ref=blixt-dev#stream_insert/4"><code>stream_insert/4</code></a> function handles both new records and updates, and also supports re-ordering of existing records.</p><h2 id="unified-rendering-for-liveview-static-html">Unified Rendering for LiveView &amp; Static HTML</h2><p>Phoenix 1.7 introduces a new <code><a href="https://hexdocs.pm/phoenix_template/Phoenix.Template.html?ref=blixt-dev">Phoenix.Template</a></code> rendering system which unifies how rendering works between static HTML and LiveView content. </p><p>Phoenix offers two distinct ways to render content: traditional static HTML, and &#xA0;LiveView for building rich, dynamic web applications. &#xA0;In practice, many applications use <em>both </em>approaches, depending on the type of content being rendered. &#xA0;For example, you might use a controller-based static approach for a login page, but an interactive LiveView once the user is logged in.</p><p>In the past, the two different approaches to rendering had different APIs and conventions making it difficult to reuse templates and other functionality between the two. &#xA0;With Phoenix 1.7, these differences are resolved with unified APIs using <code><a href="https://hexdocs.pm/phoenix_template/Phoenix.Template.html?ref=blixt-dev">Phoenix.Template</a></code> instead of <code><a href="https://hexdocs.pm/phoenix_view/Phoenix.View.html?ref=blixt-dev">Phoenix.View</a></code>.</p><p>One of the most important changes is the introduction of unified <em>function components </em>that work in both LiveViews and in static-rendered controllers. &#xA0;Previously, sub-templates would be rendered differently in a controller vs. within a LiveView. &#xA0;Now, both kinds of content can use the new component approach:</p><pre><code># Rendering a table from a controller-based template (pre 1.7)
&lt;%= render(&quot;table&quot;, user: user) %&gt;

# Rendering a table from LiveView or controller (1.7)
&lt;.table user={@user}&gt;</code></pre><p>This change greatly simplifies the developer experience for building content and enhances template reusability.</p><p>For backwards compatibility, you can still use <a href="Phoenix.View"><code>Phoenix.View</code></a> in your application, but may wish to migrate to <code><a href="https://hexdocs.pm/phoenix_template/Phoenix.Template.html?ref=blixt-dev">Phoenix.Template</a></code> to take advantage of the unified APIs. &#xA0;Note that both <code><a href="https://hexdocs.pm/phoenix_template/Phoenix.Template.html?ref=blixt-dev">Phoenix.Template</a></code> and <code><a href="https://hexdocs.pm/phoenix_view/Phoenix.View.html?ref=blixt-dev">Phoenix.View</a></code> are separate dependencies, so be sure to install the one(s) you need.</p><h2 id="collocated-views-updated-directory-structure">Collocated Views &amp; Updated Directory Structure</h2><p>A related change which helps to unify LiveView and controller-based rendering is in the directory structure: prior to 1.7, controller-based pages kept views and templates in a separate <code>lib/my_app_web/views</code> hierarchy, while LiveView typically placed template files right alongside the <code>LiveView</code> or <code>LiveComponent</code> module. </p><p>In 1.7, the &quot;colocated&quot; view approach is brought over to controller-based templates to unify the approach between the two. &#xA0;Note that this applies not just to HTML templates, but also to other formats such as JSON views. &#xA0;Keeping the template content closer to the controller code helps with project organization and consistency. &#xA0;Like many of the other changes here, however, this change is optional and fully backwards compatible.</p><h2 id="component-based-generators-with-tailwind-and-more">Component-Based Generators with Tailwind and More</h2><p>Building on the new unified function component support, Phoenix 1.7 introduces a new set of standard <a href="https://hexdocs.pm/phoenix/components.html?ref=blixt-dev#corecomponents">Core Components</a> for key HTML functionality such as forms, tables, etc:</p><pre><code class="language-Elixir">&lt;.simple_form :let={f} for={@changeset} action={~p&quot;/posts&quot;}&gt;
  &lt;.error :if={@changeset.action}&gt;
    Oops, something went wrong! Please check the errors below.
  &lt;/.error&gt;

  &lt;input field={{f, :title}} type=&quot;text&quot; label=&quot;Title&quot; /&gt;
  &lt;input field={{f, :views}} type=&quot;number&quot; label=&quot;Views&quot; /&gt;

  &lt;:actions&gt;
    &lt;.button&gt;Save Post&lt;/.button&gt;
  &lt;/:actions&gt;
&lt;/.simple_form&gt;</code></pre><p>These default components will be created for you in a <code>core_components.ex</code> file when creating a project with <code>phx.new</code>. The components, by default, use <a href="https://tailwindcss.com/?ref=blixt-dev">Tailwind</a> for styling, but can be fully customized or replaced with other styling libraries such as <a href="https://getbootstrap.com/?ref=blixt-dev">Bootstrap</a>, <a href="https://bulma.io/?ref=blixt-dev">Bulma</a>, or your own plain CSS if you&apos;d like.</p><h2 id="how-to-upgrade">How to Upgrade</h2><p>To update, you&#x2019;ll want to <a href="https://gist.github.com/chrismccord/00a6ea2a96bc57df0cce526bd20af8a7?ref=blixt-dev" rel="noopener">follow the upgrade guide</a>. In addition to simply updating dependency versions, you&#x2019;ll need to make some changes throughout your existing project to make use of new modules and patterns.</p><p>I found it was useful to create an empty new 1.7 project from scratch and compare the generated files&#x200A;&#x2014;&#x200A;there were a few files that were missing or changed after following the upgrade instructions. For example, the <code>core_components.ex</code> file mentioned above was missing since it&apos;s generated with <code>phx.new</code>, and the root-level &#xA0;template HTML files were both updated (and moved).</p><h2 id="recap-%E2%80%94-the-best-web-framework-keeps-getting-getter">Recap&#x200A;&#x2014;&#x200A;the Best Web Framework Keeps Getting Getter</h2><p>Phoenix 1.7 adds a number of great new features and refines the developer experience especially when working with LiveView. &#xA0;Honestly, though LiveView already provided a great developer experience, this release feels a bit like &quot;LiveView 2.0&quot; just in terms of the well thought out API changes and improvements.</p><p>With this release, Phoenix remains one of the most exciting web frameworks out there today. It earns that distinction owing to the marriage of innovative and powerful high-level features like channels and LiveView, built on the rock solid, highly performance foundation of Erlang/OTP.</p><p>If you haven&#x2019;t used the Phoenix Framework before, the 1.7 release is a great time to get started! Check out the following for further reading on what makes the Phoenix framework so powerful.</p><ul><li><a href="https://hexdocs.pm/phoenix/overview.html?ref=blixt-dev">Phoenix Framework official documentation &amp; getting started guides</a></li><li><a href="https://blixtdev.com/real-time-chat-with-phoenix-and-liveview/">Building real-time chat with Phoenix and LiveView in 50 lines of code</a></li><li><a href="https://blixtdev.com/you-dont-need-javascript-to-build-rich-interactive-webapps/">Interactive Server-Rendered Webapps: You Don&#x2019;t Need JavaScript</a><br></li></ul>]]></content:encoded></item><item><title><![CDATA[The Two Most Useful Elixir Functions You May Not Know: tap() & then()]]></title><description><![CDATA[Two simple, recently added Elixir functions can help enable longer and more readable computational pipelines.]]></description><link>https://blixtdev.com/two-useful-elixir-functions-you-may-not-know/</link><guid isPermaLink="false">63946a47523b9c0210cb8a9a</guid><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Tue, 24 Jan 2023 17:55:19 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1538474705339-e87de81450e8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fHBpcGV8ZW58MHx8fHwxNjc0NTgyODc3&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1538474705339-e87de81450e8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fHBpcGV8ZW58MHx8fHwxNjc0NTgyODc3&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="The Two Most Useful Elixir Functions You May Not Know: tap() &amp; then()"><p>The <a href="https://blixtdev.com/how-to-use-the-pipeline-operator-in-elixir/">pipe operator</a> in the wonderful <a href="http://elixir-lang.org/?ref=blixt-dev">Elixir</a> language is one of the most underrated features for developer experience. &#xA0;Two simple, recently added functions can greatly enhance how the pipe operator is used, enabling longer and more readable computational pipelines.</p><!--kg-card-begin: html--><a href="https://www.amazon.com/Programming-Elixir-1-6-Functional-Concurrent/dp/1680502999?crid=MJVUM2BA2XUU&amp;keywords=elixir+language&amp;qid=1670842329&amp;sprefix=elixir+language%2Caps%2C90&amp;sr=8-1&amp;linkCode=li3&amp;tag=blixtdev-20&amp;linkId=806e303fcdf90dbf53a7c156c128f9dd&amp;language=en_US&amp;ref_=as_li_ss_il&amp;ref=blixt-dev" target="_blank" style="display: flex; text-decoration: none;"><img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=1680502999&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=blixtdev-20&amp;language=en_US" alt="The Two Most Useful Elixir Functions You May Not Know: tap() &amp; then()">
    <div style="margin-left: 12px">
    <span style="text-decoration: none; font-size: 1.2em; font-weight: 500;">Programming Elixir &#x2265; 1.6: Functional |&gt; Concurrent |&gt; Pragmatic |&gt; Fun</span> <span style="color: #333; font-size: 1.0em">1st Edition</span>
    <p style="font-family: sans-serif; margin: 0; text-decoration: none; font-size: 1.0em; font-weight: 300;">by Dave Thomas</p>
        <p style="margin: 0;">&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;</p>
    </div>
</a><img src="https://ir-na.amazon-adsystem.com/e/ir?t=blixtdev-20&amp;language=en_US&amp;l=li3&amp;o=1&amp;a=1680502999" width="1" height="1" border="0" alt="The Two Most Useful Elixir Functions You May Not Know: tap() &amp; then()" style="border:none !important; margin:0px !important;"><!--kg-card-end: html--><p>In this post, we&apos;ll explore these two new functions, <code>tap()</code> and <code>then()</code>. &#xA0;We&apos;ll learn what they are, how they&apos;re used and see how they can be used with the pipe operator to write cleaner, simpler code in Elixir.</p><h2 id="the-pipe-operator-in-elixir">The Pipe operator in Elixir</h2><p>If you&apos;ve written any code in Elixir, you&apos;re probably familiar with the pipe operator. &#xA0;<a href="https://blixtdev.com/how-to-use-the-pipeline-operator-in-elixir/">The pipe operator in Elixir is a powerful tool</a> that allows developers to write more readable and expressive code. It allows developers to chain together function calls, &quot;piping&quot; the output of one expression into the first argument of the next:</p><pre><code># A nested expression without the pipeline operator

function_d(function_c(function_b(function_a(value))))

# The same expression using the pipeline operator

value
|&gt; function_a
|&gt; function_b
|&gt; function_c
|&gt; function_d</code></pre><p>The pipe operator helps to solve the &quot;parenthesis hell&quot; found in many languages, in which a deeply nested series of function calls leads to a mess of opening and closing parenthesis which can be difficult to read or to spot missing characters in.</p><p>Additionally, pipelines are easy to read because they present computation as a simple linear flow without variable bindings or inline flow control</p><h2 id="pipeline-side-effects-with-tap">Pipeline Side Effects with <code>tap()</code></h2><p>The first function to enhance the power of pipelines is <code>tap()</code>. &#xA0;The <code>tap()</code> function lets you add <em>side effects </em>to a pipeline. &#xA0;In a functional programming context, s<em>ide effects</em> refers to actions which have effects other than manipulating the function&apos;s input value. &#xA0;In practical terms this might include logging, saving data, analytics calls, etc.</p><p>If you use <code>IO.inspect()</code> or <code>dbg()</code> in your Elixir code, you&apos;re probably already familiar with the idea of inserting them into the middle of a pipeline. &#xA0;This is especially common during development to provide additional logging to an application. &#xA0;In the following example, we show a simple pipeline with an <code>IO.inspect()</code> call in the middle:</p><pre><code>get_user_id()
|&gt; MyApp.get_user()
|&gt; IO.inspect()
|&gt; render_user()</code></pre><p>The <code>tap()</code> function is similar in spirit, but allows for other kinds of side effects than simple logging. &#xA0;The function takes two arguments: an input value <code>v</code>, and a function <code>f</code>. &#xA0;It executes the function <em>f</em> with the input, <code>f(v)</code>, then returns the same input <code>v</code> unmodified. &#xA0;Consider a scenario in which we need to perform some analytics logging when looking up and rendering a user record:</p><pre><code>user = get_user_id()
|&gt; MyApp.get_user()

Analytics.track(:user_viewed, user.id)

render_user(user)</code></pre><p>Using the <code>tap()</code> function, we can insert the analytics call into our pipeline and continue with the computation: </p><pre><code>get_user_id()
|&gt; MyApp.get_user()
|&gt; tap(fn user -&gt; Analytics.track(:user_viewed, user.id) end)
|&gt; render_user()</code></pre><p>This lets us write longer pipelines without having to store intermediate values, and improves readability.</p><h2 id="transforming-pipeline-data-with-then">Transforming Pipeline Data with <code>then()</code></h2><p>Another challenge with pipelines is that the data we receive as the output from one function is <em>not quite </em>the input we need to another. &#xA0;A common example of this might be when we need a particular field from a struct to perform a computation.</p><p>For example, imagine an authorization use-case in which we need to look up a user, fetch a permission field off of the user struct, then perform some sort of computation to determine whether the user has permission to take an action. &#xA0;We might write this code as follows:</p><pre><code># Look up a user, get their permissions, see if they can edit the page

user = get_user_id()
|&gt; MyApp.get_user()

can_edit = user.permissions
|&gt; can_edit_page(page_id)
</code></pre><p>Using the <code>then()</code> function, we can improve on this and simplify our pipeline:</p><pre><code>can_edit = get_user_id()
|&gt; MyApp.get_user()
|&gt; then(fn u -&gt; u.permissions end)
|&gt; can_edit_page(page_id)</code></pre><p>Or, using the function capture operator (&amp;): </p><pre><code>can_edit = get_user_id()
|&gt; MyApp.get_user()
|&gt; then(&amp; &amp;1.permissions)
|&gt; can_edit_page(page_id)</code></pre><p>In effect, <code>then()</code> allows us to pipe computations into <em>anonymous</em> functions without breaking the pipeline. &#xA0;This simple change lets us build longer pipelines without intermediate bindings or calls to functions defined elsewhere.</p><h2 id="recap-build-better-pipelines-with-tap-and-then">Recap: Build Better Pipelines with <code>tap()</code> and <code>then()</code></h2><p>Pipelines help improve code readability by implementing simple, linear flows of information, removing (or at the very least abstracting out) flow control and variable bindings. &#xA0;While deceptively simple, these two new Elixir functions, <code>tap()</code> and <code>then()</code> can enable longer and more readable pipelines, and are part of what makes the Elixir language such a joy to use.</p>]]></content:encoded></item><item><title><![CDATA[What’s New In React Native 0.71—  How to Upgrade & Why it Matters]]></title><description><![CDATA[React Native 0.71 has just been released with a number of changes.  In this post, we'll take a look at what's new and why it matters.]]></description><link>https://blixtdev.com/whats-new-in-react-0-71/</link><guid isPermaLink="false">63cbd10a523b9c0210cb9e9e</guid><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Mon, 23 Jan 2023 11:35:29 GMT</pubDate><media:content url="https://blixtdev.com/content/images/2023/01/1-4bIsUtmfOQD45_2dt_Of5Q.png" medium="image"/><content:encoded><![CDATA[<img src="https://blixtdev.com/content/images/2023/01/1-4bIsUtmfOQD45_2dt_Of5Q.png" alt="What&#x2019;s New In React Native 0.71&#x2014;&#x200A; How to Upgrade &amp; Why it Matters"><p><a href="http://reactnative.dev/?ref=blixt-dev">React Native</a> 0.71 has just been released, touting a number of new features and improvements. &#xA0;In this post, we&apos;ll take a look at what&apos;s new, why it matters, and how to upgrade your React Native app to take advantage of the latest version.</p><h2 id="first-class-support-for-typescript">First-Class Support for TypeScript</h2><p>The year is 2023, and <a href="https://medium.com/javascript-in-plain-english/why-are-you-still-writing-javascript-f66921b3f43e?ref=blixt-dev">there&apos;s really no reason to not be using TypeScript</a>. &#xA0;Well, actually, <em>sometimes</em> the thing that holds people back from using it is lack of support in the libraries and frameworks they rely on. &#xA0;Though React Native has had TypeScript support for years, enabling it has involved bringing your own configuration and manually installing separate type definition packages.</p><p>As of React Native 0.71, there&apos;s no longer any excuse: the new version has first-class support for <a href="https://www.typescriptlang.org/?ref=blixt-dev">TypeScript</a>, including bundled type definitions, eliminating the need for external dependencies. &#xA0;This means that enabling TypeScript in your project is even simpler than before. In fact, if you create a new project via the React Native CLI, TypeScript is now enabled by default.</p><p>As part of the effort to add first-class TypeScript support, the React Native team has updated all of the documentation to include type information. &#xA0;This is a very nice touch, as projects sometimes add TypeScript support, but frustratingly don&apos;t provide type information in the docs, forcing developers to sift through source files to hunt down the types themselves.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blixtdev.com/content/images/2023/02/image.png" class="kg-image" alt="What&#x2019;s New In React Native 0.71&#x2014;&#x200A; How to Upgrade &amp; Why it Matters" loading="lazy" width="191" height="250"><figcaption><a href="https://amzn.to/3jofU9P?ref=blixt-dev">Learning TypeScript</a> on Amazon</figcaption></figure><h3 id="why-it-matters">Why it Matters</h3><p>Using TypeScript in your React Native project will help you find and fix issues in your code before they&apos;re shipped. &#xA0;While the lack of formal TypeScript support in the past may have kept you from adding it to your project, first-class support in React Native 0.71 means now is the time to start using it. &#xA0;TypeScript is a massive win for code quality and developer tooling, and is simply where the industry is going. &#xA0;</p><h2 id="improved-layout-with-flexbox-gap">Improved Layout with Flexbox Gap</h2><p>React Native 0.71 adds a number of enhancements to layout &amp; styling. &#xA0;One of the most notable changes is the addition of <a href="https://css-tricks.com/minding-the-gap/?ref=blixt-dev#aa-flexbox-gaps">flexbox gap</a> support to the React Native layout engine.</p><p>React Native&apos;s flexbox-based layout engine already provides a powerful way to produce responsive mobile designs. &#xA0;But managing the spacing <em>between </em>elements can be a bit tricky and sometimes downright annoying. &#xA0;</p><p>The usual approach to managing the space between elements is with the <code>margin</code> style. &#xA0;The main problem with <code>margin</code> is that it&apos;s typically applied to all sibling elements (like all Views in a list), resulting in unwanted margins at the start or end of the elements (or both), as well as doubled margins between elements:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/09/s_D3423FF09810746194954299B85CB2B742310C544423B71E9C9779FCF3745708_1630075999623_image.png?resize=815%2C128&amp;ssl=1" class="kg-image" alt="What&#x2019;s New In React Native 0.71&#x2014;&#x200A; How to Upgrade &amp; Why it Matters" loading="lazy" width="815" height="128"><figcaption>Unwanted margins at the start and end of a list, image from <a href="https://css-tricks.com/minding-the-gap/?ref=blixt-dev#aa-flexbox-gaps">css-tricks.com</a></figcaption></figure><p>The usual solution is simple enough, but a bit ugly: apply margin only on the right, and then use <em>no margin</em> for the last item. &#xA0;While this approach works well enough, it does require special case logic and a style change for one item in the list. &#xA0;Okay, okay, that&apos;s not the end of the world, but now there&apos;s a better way: flexbox gap.</p><p><code>gap</code> is a style attribute applied at the <em>container</em> level rather than at the <em>item</em> level, and does exactly what we want: it controls the gap size between items without introducing any unwanted space.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blixtdev.com/content/images/2023/01/image-3.png" class="kg-image" alt="What&#x2019;s New In React Native 0.71&#x2014;&#x200A; How to Upgrade &amp; Why it Matters" loading="lazy" width="753" height="128" srcset="https://blixtdev.com/content/images/size/w600/2023/01/image-3.png 600w, https://blixtdev.com/content/images/2023/01/image-3.png 753w" sizes="(min-width: 720px) 720px"><figcaption>Gaps between items, image from <a href="https://css-tricks.com/minding-the-gap/?ref=blixt-dev#aa-flexbox-gaps">css-tricks.com</a></figcaption></figure><h3 id="why-it-matters-1">Why it Matters</h3><p><code>gap</code> is a relatively new addition to flexbox, but I&apos;ve been using it happily on the web for a while now. &#xA0;While it is a simple feature, it does invoke a feeling of &quot;where has this been all my life?&quot;, and it removes the need for hacky CSS selectors or extra styling logic in your JavaScript. &#xA0;Introduction of flexbox gap support in React Native is a huge win for developer and designer ergonomics.</p><h2 id="new-architecture-updates">New Architecture Updates</h2><p>One of the most important change in React 0.71 is the continued rollout of the <em>New Architecture</em>.</p><p>If you&apos;ve been following React Native development for a while, you&apos;ll note that &#xA0;&quot;New Architecture Updates&quot; have been touted in the last several versions. &#xA0;You might cynically think that the &quot;New Architecture&quot; is some kind of vaporware that is never actually completed &#x2013; but this is far from the truth! &#xA0;There are a couple of reasons that the New Architecture updates are an ongoing effort over several releases.</p><p>First, the term refers not to just one architectural change, but several independent improvements to React Native:</p><ul><li>Hermes &#x2013; a high performance JavaScript engine </li><li>Turbo Modules &#x2013; the new native module system</li><li>Fabric &#x2013; new optimized rendering system</li></ul><p>Second, the changes have mostly opt-in and are at various levels of maturity. &#xA0;For example, the Hermes engine has been stable and enabled by default since version 0.70. &#xA0;Other changes, such as the Fabric rendering system, need to be manually enabled and are still considered in beta.</p><p>Finally, the rollout of some of the changes is, in practice, dependent on upgrades from popular 3rd party packages. &#xA0;The current stable release of the popular Reanimated package, for example, is not compatible with the new Fabric renderer, though a pre-release version does include support.</p><p>0.71 continues the rollout of the new architecture with <a href="https://github.com/reactwg/react-native-new-architecture/discussions/105?ref=blixt-dev"><em>massively </em>improved build performance</a> using pre-built artifacts, a greatly simplified integration process on iOS (via the new <a href="https://reactnative.dev/docs/new-architecture-app-intro?ref=blixt-dev#ios---make-your-appdelegate-conform-to-rctappdelegate">RCTAppDelegate</a> class) and a number of bug fixes &amp; other improvements</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blixtdev.com/content/images/2023/01/210798202-782412c2-6d0f-4c82-bdc6-74a9b8885ea0.png" class="kg-image" alt="What&#x2019;s New In React Native 0.71&#x2014;&#x200A; How to Upgrade &amp; Why it Matters" loading="lazy" width="1840" height="966" srcset="https://blixtdev.com/content/images/size/w600/2023/01/210798202-782412c2-6d0f-4c82-bdc6-74a9b8885ea0.png 600w, https://blixtdev.com/content/images/size/w1000/2023/01/210798202-782412c2-6d0f-4c82-bdc6-74a9b8885ea0.png 1000w, https://blixtdev.com/content/images/size/w1600/2023/01/210798202-782412c2-6d0f-4c82-bdc6-74a9b8885ea0.png 1600w, https://blixtdev.com/content/images/2023/01/210798202-782412c2-6d0f-4c82-bdc6-74a9b8885ea0.png 1840w" sizes="(min-width: 720px) 720px"><figcaption>Image from <a href="https://github.com/reactwg?ref=blixt-dev">React Working Group</a> on GitHub&#xA0;</figcaption></figure><p>With the changes in 0,71 (and using the pre-release version of Reanimated), I was able to get my app fully up and running with Fabric and the new architeture, though I did encounter some cosmetic issues due to another 3rd party package in my project. &#xA0;I&apos;ll probably hold off on leaving it enabled until the 3rd party issues are fully resolved, but it&apos;s amazing to see the full new architecture in action.</p><h3 id="why-it-matters-2">Why it Matters</h3><p>Though React Native&apos;s new architecture is a work-in-progress, every new release provides real, incremental value to React Native developers. &#xA0;If you haven&apos;t already enabled Hermes, for example, it&apos;s now fully supported and will your app give a major performance boost. &#xA0;And while Fabric is still considered experimental, it&apos;s definitely time to start getting your app ready for the change with React Native 0.71.</p><h2 id="how-to-upgrade">How to Upgrade</h2><p>Upgrading React Native can be tricky, but there are a couple of tricks to make the process a bit smoother. </p><p>First, double-check the latest version of React Native. &#xA0;As of this writing, version 0.71.1 of React Native has been released with some minor improvements, so when following the instructions below, be sure to use the latest version!</p><p>Next, you&apos;ll need to update a number of core dependencies. &#xA0;Updating React Native to 0.71.1 is the main change, but a number of other packages will need to be upgraded as well. &#xA0;While you can try to do this manually, there&apos;s a <a href="https://microsoft.github.io/rnx-kit/?ref=blixt-dev">community maintained tool</a> you can use to show you all of the outdated dependencies in your project:</p><pre><code>npx @rnx-kit/align-deps --init
npx @rnx-kit/align-deps --requirements react-native@0.71</code></pre><p>Once you&apos;ve update your core dependencies, you&apos;ll also need to make a number of changes in some supporting files in your project, such as the native project build files.</p><p>The best way to see the full list of changes needed is to use the <a href="https://react-native-community.github.io/upgrade-helper/?ref=blixt-dev">React Native upgrade helper tool</a>. &#xA0;This tool lets you specify your current version of React Native and shows all the changes needed to upgrade to version 0.71.1. &#xA0;For example, if you&apos;re upgrading from 0.70.6 to 0.71.1, <a href="https://react-native-community.github.io/upgrade-helper/?from=0.70.6&amp;3Bto=0.71.1&amp;ref=blixt-dev">here&apos;s the full list of changes</a> you&apos;ll need to make. </p><p>If you&apos;re upgrading from a <em>much </em>older version of React Native, the list of changes can be huge and frankly, pretty intimidating. In this case, it can sometimes be more efficient to <a href="https://reactnative.dev/docs/environment-setup?ref=blixt-dev">create a pristine new React Native 0.71.1 project</a> and migrate your components &amp; application code over to the new project. &#xA0;Or, use the new project for reference and copy over files individually to your existing one.</p><p>My own upgrades from 0.70 went pretty well, though I did enounter one issue with the popular <code>react-native-splash-screen</code> package. &#xA0;Fortunately, others had already encountered the issue and found a simple workaround:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/crazycodeboy/react-native-splash-screen/issues/606?ref=blixt-dev"><div class="kg-bookmark-content"><div class="kg-bookmark-title">not working splash screen for react-native 0.71.0 &#xB7; Issue #606 &#xB7; crazycodeboy/react-native-splash-screen</div><div class="kg-bookmark-description">Run react-native info in your project and share the content. What react-native-splash-screen version are you using? 3.3.0 What platform does your issue occur on? Both Describe your issue as precise...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="What&#x2019;s New In React Native 0.71&#x2014;&#x200A; How to Upgrade &amp; Why it Matters"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">crazycodeboy</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/6e9d24e27281d329fe8e7271cbf541a6ac3bd51d372b970d292dbdb436fb5a97/crazycodeboy/react-native-splash-screen/issues/606" alt="What&#x2019;s New In React Native 0.71&#x2014;&#x200A; How to Upgrade &amp; Why it Matters"></div></a></figure><h2 id="recap">Recap</h2><p>React Native 0.71 introduces a number of new features, some of which offer immediate value to React Native developers, while others provide steady infrastructure improvements and longer term value.</p><p>For a full list of changes in React Native 0.71, checkout the <a href="https://reactnative.dev/blog/2023/01/12/version-071?ref=blixt-dev">official blog post</a> on the topic.</p><!--kg-card-begin: html--><a href="https://www.amazon.com/Professional-React-Native-cross-platform-production-ready/dp/180056368X?keywords=professional+react+native&amp;3Bqid=1671800338&amp;3Bsprefix=professional+react+nati%2Caps%2C109&amp;3Bsr=8-1&amp;3BlinkCode=li3&amp;3Btag=blixtdev-20&amp;3BlinkId=166be9da5677ed6150c5df4e341aa2f5&amp;3Blanguage=en_US&amp;3Bref_=as_li_ss_il&amp;ref=blixt-dev" target="_blank" style="text-decoration: none; display: flex"><img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=180056368X&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=blixtdev-20&amp;language=en_US" alt="What&#x2019;s New In React Native 0.71&#x2014;&#x200A; How to Upgrade &amp; Why it Matters"><div style="margin-left: 12px">
    <span style="font-size: 1.2em; font-weight: 500;">Professional React Native: Expert techniques and solutions for building high-quality, cross-platform, production-ready apps</span> <span style="color: #333; font-size: 1.0em"></span>
    <p style="font-family: sans-serif; margin: 0; text-decoration: none; font-size: 1.0em; font-weight: 300;">by Alexander Benedikt Kuttig</p>
        <p style="margin: 0;">&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;</p>
    </div>
</a><img src="https://ir-na.amazon-adsystem.com/e/ir?t=blixtdev-20&amp;language=en_US&amp;l=li3&amp;o=1&amp;a=180056368X" width="1" height="1" border="0" alt="What&#x2019;s New In React Native 0.71&#x2014;&#x200A; How to Upgrade &amp; Why it Matters" style="border:none !important; margin:0px !important;"><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Introducing Blixtcode – Create & Share Videos of Your Code]]></title><description><![CDATA[Blixtcode, a new tool for creating & sharing beautiful videos of code samples.]]></description><link>https://blixtdev.com/introducing-blixt-code/</link><guid isPermaLink="false">63cd9e43523b9c0210cba22e</guid><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Mon, 23 Jan 2023 05:04:00 GMT</pubDate><media:content url="https://blixtdev.com/content/images/2023/01/Screenshot-2023-01-23-at-6.47.04-AM.png" medium="image"/><content:encoded><![CDATA[<img src="https://blixtdev.com/content/images/2023/01/Screenshot-2023-01-23-at-6.47.04-AM.png" alt="Introducing Blixtcode &#x2013; Create &amp; Share Videos of Your Code"><p>Today I&apos;m happy to introduce <a href="http://video.blixtdev.com/?ref=blixt-dev">Blixtcode</a>, a new tool for creating &amp; sharing beautiful videos of code samples. &#xA0;Here&apos;s a quick example of the kind of video you can build:</p><figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/YDO59_ZnKVQ?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen title="Blixt Code"></iframe></figure><p>Why Blixtcode? &#xA0;I&apos;ve seen lots of great tools for sharing static images of code samples, but couldn&apos;t find anything for videos, so I decided to make our own.</p><p>The tool is still in development, so your feedback is greatly appreciated. &#xA0;<a href="http://video.blixtdev.com/?ref=blixt-dev">Give it a try for yourself</a> and <a href="code@blixtdev.com">let me know how you like it</a>!</p>]]></content:encoded></item><item><title><![CDATA[The Best Books for Learning JavaScript in 2023]]></title><description><![CDATA[If you haven't learned JavaScript yet, or are looking to expand your horizons, now is the perfect time.  In this post, we'll look at 5 different recommended books for learning JavaScript.]]></description><link>https://blixtdev.com/the-best-books-to-learn-javascript-in-2023/</link><guid isPermaLink="false">63a8570e523b9c0210cb994a</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Books]]></category><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Mon, 26 Dec 2022 15:02:35 GMT</pubDate><media:content url="https://blixtdev.com/content/images/2022/12/Purple-3D-and-Modern-Free-Programming-Course-Instagram-Post.png" medium="image"/><content:encoded><![CDATA[<img src="https://blixtdev.com/content/images/2022/12/Purple-3D-and-Modern-Free-Programming-Course-Instagram-Post.png" alt="The Best Books for Learning JavaScript in 2023"><p>According to <a href="https://survey.stackoverflow.co/2022/?ref=blixt-dev">Stack Overflow&apos;s yearly developer study for 2022</a>, JavaScript remains the <a href="https://survey.stackoverflow.co/2022/?ref=blixt-dev#section-most-popular-technologies-programming-scripting-and-markup-languages">most popular language for the 10th year in a row</a>. &#xA0;The language continues to grow in popularity and is now the language of choice for many not only on the frontend, but also the backend.</p><p>JavaScript has also matured greatly over the past several years, with new tooling and frameworks that make JavaScript development a more consistent and pleasurable experience. &#xA0;And the adoption of <a href="https://www.typescriptlang.org/?ref=blixt-dev">TypeScript</a> means JavaScript developers are now producing better quality code with fewer bugs, making it a more defensible choice for serious projects. </p><p>If you haven&apos;t learned JavaScript yet, or are looking to expand your horizons, now is the perfect time. &#xA0;In this post, we&apos;ll look at 5 different recommended books for learning JavaScript, each coming at the language from a unique angle.</p><h3 id="learn-the-basics">Learn the Basics</h3><p>If you&apos;ve never worked with JavaScript before and are looking to learn the fundamentals, <a href="https://amzn.to/3PRrR3T?ref=blixt-dev">JavaScript from Beginner to Professional</a> is an excellent choice. &#xA0;The book is filled with hands on examples of real-world JavaScript applications and covers just enough HTML, CSS and canvas background to build a variety of projects.</p><figure class="kg-card kg-image-card kg-card-hascaption"><a href="https://www.amazon.com/JavaScript-Beginner-Professional-building-interactive/dp/1800562527?crid=3K7A8E0DCR0AJ&amp;3Bkeywords=javascript&amp;3Bqid=1671977497&amp;3Bsprefix=javascript%2Caps%2C167&amp;3Bsr=8-4&amp;3BlinkCode=li3&amp;3Btag=blixtdev-20&amp;3BlinkId=9051b2d9d64cf7fa168770ee2ecfb954&amp;3Blanguage=en_US&amp;3Bref_=as_li_ss_il&amp;ref=blixt-dev"><img src="https://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=1800562527&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=blixtdev-20&amp;language=en_US" class="kg-image" alt="The Best Books for Learning JavaScript in 2023" loading="lazy"></a><figcaption><a href="https://amzn.to/3PRrR3T?ref=blixt-dev">JavaScript from Beginner to Professional</a> on Amazon</figcaption></figure><p>This is an excellent choice for those approaching JavaScript for the first time, or for more novice JavaScript developers looking to expand their horizons.</p><h3 id="javascript-on-the-backend-with-nodejs">JavaScript on the Backend with Node.js</h3><p>While most JavaScript resources are focused on frontend development, JavaScript is a solid choice for building out backends as well. &#xA0;The book <a href="https://amzn.to/3GrS4TT?ref=blixt-dev">Node.js: The Comprehensive Guide to Server-Side JavaScript Programming</a> teaches JavaScript with a focus on the server-side.</p><figure class="kg-card kg-image-card kg-card-hascaption"><a href="https://www.amazon.com/Node-js-Comprehensive-Server-Side-JavaScript-Programming/dp/1493222929?crid=ZETX2HZCR53Q&amp;3Bkeywords=javascript&amp;3Bqid=1671976497&amp;3Bsprefix=javascrip%2Caps%2C123&amp;3Bsr=8-23&amp;3BlinkCode=li3&amp;3Btag=blixtdev-20&amp;3BlinkId=52097f36dea4f25813c548bb9ffd8dde&amp;3Blanguage=en_US&amp;3Bref_=as_li_ss_il&amp;ref=blixt-dev"><img src="https://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=1493222929&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=blixtdev-20&amp;language=en_US" class="kg-image" alt="The Best Books for Learning JavaScript in 2023" loading="lazy"></a><figcaption><a href="https://amzn.to/3GrS4TT?ref=blixt-dev">Node.js: The Comprehensive Guide to Server-Side JavaScript Programming</a> on Amazon</figcaption></figure><p>The book covers building backends using the <a href="https://expressjs.com/?ref=blixt-dev">Express</a> and <a href="https://nestjs.com/?ref=blixt-dev">Nest.js</a> frameworks, using <a href="https://restfulapi.net/?ref=blixt-dev">REST</a> and <a href="https://graphql.org/?ref=blixt-dev">GraphQL</a> to build out server-side APIs. &#xA0;It also covers more advanced server-side topics such as scalability, performance, testing and security.</p><p>This book is an excellent resource for frontend developers looking to apply their JavaScript knowledge to the backend and become more full-stack developers.</p><h3 id="frontend-essentials-with-javascript-html-css">Frontend Essentials with JavaScript, HTML &amp; CSS</h3><p>If you&apos;re learning JavaScript with a focus on fonrtned design, but haven&apos;t yet mastered HTML &amp; CSS, you&apos;ll definitely want a resource that teaches all three. &#xA0;The book <a href="https://amzn.to/3hP2LWS?ref=blixt-dev">Teach Yourself HTML, CSS and JavaScript</a> covers the basics of JavaScript, but also covers HTML and CSS in detail at the same time.</p><figure class="kg-card kg-image-card kg-card-hascaption"><a href="https://www.amazon.com/HTML-JavaScript-Sams-Teach-Yourself/dp/0672338084?crid=ZETX2HZCR53Q&amp;3Bkeywords=javascript&amp;3Bqid=1671976497&amp;3Bsprefix=javascrip%2Caps%2C123&amp;3Bsr=8-35&amp;3BlinkCode=li3&amp;3Btag=blixtdev-20&amp;3BlinkId=0ac89f558295990e0f9c04fe093183de&amp;3Blanguage=en_US&amp;3Bref_=as_li_ss_il&amp;ref=blixt-dev"><img src="https://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=0672338084&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=blixtdev-20&amp;language=en_US" class="kg-image" alt="The Best Books for Learning JavaScript in 2023" loading="lazy"></a><figcaption><a href="https://amzn.to/3hP2LWS?ref=blixt-dev">Teach Yourself HTML, CSS and JavaScript</a> on Amazon</figcaption></figure><p>This book is a good choice for developers who are learning JavaScript specifically for frontend development, and for those who are more interested in the visual and design aspect. &#xA0;It&apos;s also a good choice for people completely new to web development who haven&apos;t yet learned how HTML, CSS and JavaScript work together to build interactive applications.</p><h3 id="level-up-with-typescript">Level Up with TypeScript</h3><p>If you already know &#xA0;JavaScript and are looking to bring your skills up to a more professional level, I definitely recommend mastering the <a href="http://typescriptlang.org/?ref=blixt-dev">TypeScript</a> language. TypeScript is essentially a typed version of JavaScript, which means it helps you produce better quality code with fewer bugs. &#xA0;The book <a href="https://amzn.to/3jofU9P?ref=blixt-dev">Learning TypeScript</a> is a great resource to get you started.</p><figure class="kg-card kg-image-card kg-card-hascaption"><a href="https://www.amazon.com/Learning-TypeScript-Development-Type-Safe-JavaScript/dp/1098110331?crid=ZETX2HZCR53Q&amp;3Bkeywords=javascript&amp;3Bqid=1671976497&amp;3Bsprefix=javascrip%2Caps%2C123&amp;3Bsr=8-37&amp;3BlinkCode=li3&amp;3Btag=blixtdev-20&amp;3BlinkId=f23739023791ae57fe3cb16468416570&amp;3Blanguage=en_US&amp;3Bref_=as_li_ss_il&amp;ref=blixt-dev"><img src="https://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=1098110331&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=blixtdev-20&amp;language=en_US" class="kg-image" alt="The Best Books for Learning JavaScript in 2023" loading="lazy"></a><figcaption><a href="https://amzn.to/3jofU9P?ref=blixt-dev">Learning TypeScript</a> on Amazon</figcaption></figure><p>If you&apos;re learning JavaScript to land a position and a tech company, TypeScript is practically a prerequisite these days&#x2013;it&apos;s where the industry is heading. I actually recommend that<em> all </em>developers<em> </em>learning JavaScript should learn TypeScript, but because TypeScript is a <em>superset </em>of JS, it&apos;s okay to learn JavaScript first before diving in.</p><h3 id="for-younger-javascript-learners-or-for-the-young-at-heart">For Younger JavaScript Learners (or for the Young at Heart)</h3><p>For younger learners, <a href="https://amzn.to/3PTwV83?ref=blixt-dev">JavaScript for Kids</a> is a great option. It teaches the basics of JavaScript and programming in general, and builds out kid-friendly projects like games and animations along the way.</p><figure class="kg-card kg-image-card kg-card-hascaption"><a href="https://www.amazon.com/JavaScript-Kids-Playful-Introduction-Programming/dp/1593274084?crid=ZETX2HZCR53Q&amp;3Bkeywords=javascript&amp;3Bqid=1671976497&amp;3Bsprefix=javascrip%2Caps%2C123&amp;3Bsr=8-22&amp;3BlinkCode=li3&amp;3Btag=blixtdev-20&amp;3BlinkId=ebd0bf67c828afd4e4ef063d42122294&amp;3Blanguage=en_US&amp;3Bref_=as_li_ss_il&amp;ref=blixt-dev"><img src="https://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=1593274084&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=blixtdev-20&amp;language=en_US" class="kg-image" alt="The Best Books for Learning JavaScript in 2023" loading="lazy"></a><figcaption><a href="https://amzn.to/3PTwV83?ref=blixt-dev">JavaScript for Kids</a> on Amazon</figcaption></figure><p>In spite of the title, this book isn&apos;t <em>just</em> for kids&#x2013;many of the reviews mention this book is great for adults too. &#xA0;In particular, for adults learners without <em>any</em> kind of programming experience, this book teaches basic programming techniques in approachable, easy-to-understand lessons. &#xA0;The book is a great choice for kids, or for adults who are looking to learn the fundamentals of JavaScript and programming in general before taking on some of the more advanced resources.</p><h3 id="learn-javascript-in-2023">Learn JavaScript in 2023!</h3><p>In 2023, the JavaScript language is more popular than ever. &#xA0;If you&apos;ve been putting off learning JavaScript, there&apos;s no better time than the present to dig in. &#xA0;Whether you&apos;re looking to broaden your knowledge to land a backend developer job, or are just getting started with programming, there are resources here to help you on your journey. &#xA0;And in case none of these books are of interest to you, here are some other places to get started on your JavaScript learning:</p><ul><li>JavaScript basics on <a href="https://www.javascript.com/learn/strings?ref=blixt-dev">JavaScript.com</a></li><li>A wealth of tutorials from <a href="https://www.w3schools.com/js/?ref=blixt-dev">W3schools.com</a></li><li>More JavaScript articles on <a href="https://blixtdev.com/tag/javascript/">Blixtdev</a></li></ul><p></p>]]></content:encoded></item><item><title><![CDATA[The Best Books to Learn React in 2023]]></title><description><![CDATA[We've compiled a list of 5 books that address different aspects of React development and can help you become a React master in 2023.]]></description><link>https://blixtdev.com/the-best-books-to-learn-react-in-2023/</link><guid isPermaLink="false">63a472e0523b9c0210cb96d2</guid><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[React Native]]></category><category><![CDATA[Books]]></category><dc:creator><![CDATA[Jonathan]]></dc:creator><pubDate>Fri, 23 Dec 2022 14:01:24 GMT</pubDate><media:content url="https://blixtdev.com/content/images/2023/02/photo-1614849963640-9cc74b2a826f-1.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://blixtdev.com/content/images/2023/02/photo-1614849963640-9cc74b2a826f-1.jpeg" alt="The Best Books to Learn React in 2023"><p>Though there are more choices than ever, <a href="http://reactjs.org/?ref=blixt-dev">React</a> remains the <a href="https://survey.stackoverflow.co/2022/?ref=blixt-dev#section-most-popular-technologies-web-frameworks-and-technologies">king of web frontend development frameworks</a> and there&apos;s never been a better time to start learn it. &#xA0;We&apos;ve compiled a list of 5 books that address different aspects of React development and can help you become a React master in 2023.</p><p>When choosing a resource on learning React, it&apos;s important to get one that&apos;s up to date. &#xA0;React has changed a lot over the years, so be sure to pick one that teaches &#xA0;React 18 and addresses the latest techniques: functional components, hooks, error boundaries, etc.</p><p>Our list of React books features several high-quality, up-to-date resources for learning React and related technologies. &#xA0;While all of these books cover some React basics, they each also address React development from a slightly different angle, covering various advanced techniques as well, such as React Native development, backend integrations, etc.</p><p>Without further ado, here are our favorite books you can use to learn React in 2023!</p><h3 id="learning-the-basics">Learning the Basics</h3><p>If you&apos;re not at all familiar with React or relatively new to programming in general, we recommend <a href="https://www.amazon.com/React-Running-Building-Web-Applications/dp/1492051462?crid=50BP12LPKND5&amp;3Bamp%3Bkeywords=react+up+and+running&amp;3Bamp%3Bqid=1671801102&amp;3Bamp%3Bs=books&amp;3Bamp%3Bsprefix=react+up+and+running%2Cstripbooks%2C113&amp;3Bamp%3Bsr=1-1&amp;3Bamp%3BlinkCode=li3&amp;3Bamp%3Btag=blixtdev-20&amp;3Bamp%3BlinkId=6fef9c121af1c2109c2f80495e5f0a21&amp;3Bamp%3Blanguage=en_US&amp;3Bamp%3Bref_=as_li_ss_il&amp;ref=blixt-dev">React: Up &amp; Running: Building Web Applications</a>. &#xA0;This book covers all of the basics of React app development: JSX, tooling, basic hooks, basic state management, and much more.</p><!--kg-card-begin: html--><a href="https://www.amazon.com/React-Running-Building-Web-Applications/dp/1492051462?crid=50BP12LPKND5&amp;3Bkeywords=react+up+and+running&amp;3Bqid=1671801102&amp;3Bs=books&amp;3Bsprefix=react+up+and+running%2Cstripbooks%2C113&amp;3Bsr=1-1&amp;3BlinkCode=li3&amp;3Btag=blixtdev-20&amp;3BlinkId=6fef9c121af1c2109c2f80495e5f0a21&amp;3Blanguage=en_US&amp;3Bref_=as_li_ss_il&amp;ref=blixt-dev" target="_blank" style="text-decoration: none; display: flex"><img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=1492051462&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=blixtdev-20&amp;language=en_US" alt="The Best Books to Learn React in 2023"><div style="margin-left: 12px">
    <span style="font-size: 1.2em; font-weight: 500;">React: Up &amp; Running: Building Web Applications</span> <span style="color: #333; font-size: 1.0em">2nd Edition</span>
    <p style="font-family: sans-serif; margin: 0; text-decoration: none; font-size: 1.0em; font-weight: 300;">by Stoyan Stefanov</p>
        <p style="margin: 0;">&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;</p>
    </div>
</a><img src="https://ir-na.amazon-adsystem.com/e/ir?t=blixtdev-20&amp;language=en_US&amp;l=li3&amp;o=1&amp;a=1492051462" width="1" height="1" border="0" alt="The Best Books to Learn React in 2023" style="border:none !important; margin:0px !important;"><!--kg-card-end: html--><p>The book does assume a bit of JavaScript knowledge, so be sure to brush up on your JavaScript before starting with this (though to be honest, one of the best ways to improve your JavaScript is to simply dive in).</p><h3 id="learn-common-patterns-tools-techniques">Learn Common Patterns, Tools &amp; Techniques</h3><p>Once you&apos;ve learned the fundamentals of React and start building an application, you&apos;ll almost certainly run into some bigger challenges: how do you manage state and server communication in a large application? &#xA0;How do you handle authentication? &#xA0;How do you test and debug applications?</p><!--kg-card-begin: html--><a href="https://www.amazon.com/React-Cookbook-Recipes-Mastering-Framework/dp/1492085847?crid=1F039E59QNZ96&amp;3Bkeywords=react&amp;3Bqid=1671796781&amp;3Bs=books&amp;3Bsprefix=react%2Cstripbooks%2C81&amp;3Bsr=1-9&amp;3BlinkCode=li2&amp;3Btag=blixtdev-20&amp;3BlinkId=49482be02be83ec1bb3f4b9695e9dd74&amp;3Blanguage=en_US&amp;3Bref_=as_li_ss_il&amp;ref=blixt-dev" target="_blank" style="text-decoration: none; display: flex"><img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=1492085847&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=blixtdev-20&amp;language=en_US" alt="The Best Books to Learn React in 2023"><div style="margin-left: 12px">
    <span style="font-size: 1.2em; font-weight: 500;">React Cookbook: Recipes for Mastering the React Framework</span> <span style="color: #333; font-size: 1.0em">1st Edition</span>
    <p style="font-family: sans-serif; margin: 0; text-decoration: none; font-size: 1.0em; font-weight: 300;">by David Griffiths and Dawn Griffiths</p>
        <p style="margin: 0;">&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;</p>
    </div>
</a><img src="https://ir-na.amazon-adsystem.com/e/ir?t=blixtdev-20&amp;language=en_US&amp;l=li2&amp;o=1&amp;a=1492085847" width="1" height="1" border="0" alt="The Best Books to Learn React in 2023" style="border:none !important; margin:0px !important;"><!--kg-card-end: html--><p>The book React Cookbook: <a href="https://www.amazon.com/React-Cookbook-Recipes-Mastering-Framework/dp/1492085847?crid=1F039E59QNZ96&amp;3Bamp%3Bkeywords=react&amp;3Bamp%3Bqid=1671796781&amp;3Bamp%3Bs=books&amp;3Bamp%3Bsprefix=react%2Cstripbooks%2C81&amp;3Bamp%3Bsr=1-9&amp;3Bamp%3BlinkCode=li2&amp;3Bamp%3Btag=blixtdev-20&amp;3Bamp%3BlinkId=49482be02be83ec1bb3f4b9695e9dd74&amp;3Bamp%3Blanguage=en_US&amp;3Bamp%3Bref_=as_li_ss_il&amp;ref=blixt-dev">Recipes for Mastering the React Framework</a> focuses on some of these common application development challenges and gives a cookbook for solving them. &#xA0;While these kinds of application development challenges are not unique to React, there are most certainly patterns and tools that experienced React developers use to solve them&#x2013;this book shows you how.</p><h3 id="master-react-hooks">Master React Hooks</h3><p>One of the biggest changes in the React ecosystem over the past several releases has been the migration to using functional components and hooks. &#xA0;Understanding how hooks work is key to understanding the lifecycle of functional components as well as the philosophy behind modern React development.</p><!--kg-card-begin: html--><a href="https://www.amazon.com/Learn-React-Hooks-refactor-applications/dp/1838641440?_encoding=UTF8&amp;3Bpd_rd_w=qrWtG&amp;3Bcontent-id=amzn1.sym.e4bd6ac6-9035-4a04-92a6-fc4ad60e09ad&amp;3Bpf_rd_p=e4bd6ac6-9035-4a04-92a6-fc4ad60e09ad&amp;3Bpf_rd_r=6V0PECKSVTVCEA13KVH3&amp;3Bpd_rd_wg=UJMIs&amp;3Bpd_rd_r=4c28e1ad-38cb-46a8-b9f3-de28aad6447a&amp;3BlinkCode=li3&amp;3Btag=blixtdev-20&amp;3BlinkId=9ef84ce40de09bc5adba7d9db8368226&amp;3Blanguage=en_US&amp;3Bref_=as_li_ss_il&amp;ref=blixt-dev" target="_blank" style="text-decoration: none; display: flex"><img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=1838641440&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=blixtdev-20&amp;language=en_US" alt="The Best Books to Learn React in 2023"><div style="margin-left: 12px">
    <span style="font-size: 1.2em; font-weight: 500;">Learn React Hooks: Build and refactor modern React.js applications using Hooks</span> <span style="color: #333; font-size: 1.0em"></span>
    <p style="font-family: sans-serif; margin: 0; text-decoration: none; font-size: 1.0em; font-weight: 300;">by Daniel Bugl</p>
        <p style="margin: 0;">&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;</p>
    </div>
</a><img src="https://ir-na.amazon-adsystem.com/e/ir?t=blixtdev-20&amp;language=en_US&amp;l=li3&amp;o=1&amp;a=1838641440" width="1" height="1" border="0" alt="The Best Books to Learn React in 2023" style="border:none !important; margin:0px !important;"><!--kg-card-end: html--><p> While any up-to-date React resource will address the basics of hooks like <code>useState</code> and <code>useEffect</code>, &#xA0;we recommend <a href="https://www.amazon.com/Learn-React-Hooks-refactor-applications/dp/1838641440?_encoding=UTF8&amp;3Bamp%3Bpd_rd_w=qrWtG&amp;3Bamp%3Bcontent-id=amzn1.sym.e4bd6ac6-9035-4a04-92a6-fc4ad60e09ad&amp;3Bamp%3Bpf_rd_p=e4bd6ac6-9035-4a04-92a6-fc4ad60e09ad&amp;3Bamp%3Bpf_rd_r=6V0PECKSVTVCEA13KVH3&amp;3Bamp%3Bpd_rd_wg=UJMIs&amp;3Bamp%3Bpd_rd_r=4c28e1ad-38cb-46a8-b9f3-de28aad6447a&amp;3Bamp%3BlinkCode=li3&amp;3Bamp%3Btag=blixtdev-20&amp;3Bamp%3BlinkId=9ef84ce40de09bc5adba7d9db8368226&amp;3Bamp%3Blanguage=en_US&amp;3Bamp%3Bref_=as_li_ss_il&amp;ref=blixt-dev">Learn React Hooks: Build and refactor modern React.js applications using Hooks</a> for learning more advanced hook techniques and in particular for developers working with code containing older-style class components. &#xA0;This book can help developers to adapt and refactor class components into modern functional components.</p><h3 id="go-full-stack-learn-the-backend">Go Full-Stack &amp; Learn the Backend</h3><p>If you&apos;re learning React because you&apos;re a full-stack kind of developer or if you&apos;re on a mission to build a particular application, you might want to learn frontend and backend development at the same time. &#xA0;In that case, you&apos;ll benefit from a resource which addresses both, and shows how they work together. </p><!--kg-card-begin: html--><a href="https://www.amazon.com/Full-Stack-Web-Development-GraphQL-React/dp/1801077886?crid=19FMQ8RXEESQG&amp;3Bkeywords=react+18&amp;3Bqid=1671796317&amp;3Bs=books&amp;3Bsprefix=react+18%2Cstripbooks%2C91&amp;3Bsr=1-6&amp;3BlinkCode=li3&amp;3Btag=blixtdev-20&amp;3BlinkId=28ad470f089734ebdde659a17d99ad84&amp;3Blanguage=en_US&amp;3Bref_=as_li_ss_il&amp;ref=blixt-dev" target="_blank" style="text-decoration: none; display: flex"><img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=1801077886&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=blixtdev-20&amp;language=en_US" alt="The Best Books to Learn React in 2023"><div style="margin-left: 12px">
    <span style="font-size: 1.2em; font-weight: 500;">Full-Stack Web Development with GraphQL and React: Taking React from frontend to full-stack with GraphQL and Apollo</span> <span style="color: #333; font-size: 1.0em">2nd Edition</span>
    <p style="font-family: sans-serif; margin: 0; text-decoration: none; font-size: 1.0em; font-weight: 300;">by Sebastian Grebe</p>
        <p style="margin: 0;">&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;</p>
    </div>
</a><img src="https://ir-na.amazon-adsystem.com/e/ir?t=blixtdev-20&amp;language=en_US&amp;l=li3&amp;o=1&amp;a=1801077886" width="1" height="1" border="0" alt="The Best Books to Learn React in 2023" style="border:none !important; margin:0px !important;"><!--kg-card-end: html--><p>The book <a href="https://www.amazon.com/Full-Stack-Web-Development-GraphQL-React/dp/1801077886?crid=19FMQ8RXEESQG&amp;3Bamp%3Bkeywords=react+18&amp;3Bamp%3Bqid=1671796317&amp;3Bamp%3Bs=books&amp;3Bamp%3Bsprefix=react+18%2Cstripbooks%2C91&amp;3Bamp%3Bsr=1-6&amp;3Bamp%3BlinkCode=li3&amp;3Bamp%3Btag=blixtdev-20&amp;3Bamp%3BlinkId=28ad470f089734ebdde659a17d99ad84&amp;3Bamp%3Blanguage=en_US&amp;3Bamp%3Bref_=as_li_ss_il&amp;ref=blixt-dev">Full-Stack Web Development with GraphQL and React: Taking React from frontend to full-stack with GraphQL and Apollo</a> shows not only how to build a React frontend, but also a fully functional backend and how to make them work together. &#xA0;This is a great resource which shows the &quot;big picture&quot; of full-stack web application development, not just a siloed frontend approach.z</p><h3 id="go-native-and-build-for-mobile-beyond">Go Native and Build for Mobile &amp; Beyond</h3><p>If you already know some React but are looking to start building native mobile apps for iOS and Android, React Native is a great tool. &#xA0;If you can build an app with React, you get started with React Native mobile apps in no time.</p><!--kg-card-begin: html--><a href="https://www.amazon.com/Professional-React-Native-cross-platform-production-ready/dp/180056368X?keywords=professional+react+native&amp;3Bqid=1671800338&amp;3Bsprefix=professional+react+nati%2Caps%2C109&amp;3Bsr=8-1&amp;3BlinkCode=li3&amp;3Btag=blixtdev-20&amp;3BlinkId=166be9da5677ed6150c5df4e341aa2f5&amp;3Blanguage=en_US&amp;3Bref_=as_li_ss_il&amp;ref=blixt-dev" target="_blank" style="text-decoration: none; display: flex"><img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=180056368X&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=blixtdev-20&amp;language=en_US" alt="The Best Books to Learn React in 2023"><div style="margin-left: 12px">
    <span style="font-size: 1.2em; font-weight: 500;">Professional React Native: Expert techniques and solutions for building high-quality, cross-platform, production-ready apps</span> <span style="color: #333; font-size: 1.0em"></span>
    <p style="font-family: sans-serif; margin: 0; text-decoration: none; font-size: 1.0em; font-weight: 300;">by Alexander Benedikt Kuttig</p>
        <p style="margin: 0;">&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;</p>
    </div>
</a><img src="https://ir-na.amazon-adsystem.com/e/ir?t=blixtdev-20&amp;language=en_US&amp;l=li3&amp;o=1&amp;a=180056368X" width="1" height="1" border="0" alt="The Best Books to Learn React in 2023" style="border:none !important; margin:0px !important;"><!--kg-card-end: html--><p>While React developers will have no problem getting started with the basics of React Native development, there are a number of more advanced topics you&apos;ll want to master to truly become a React Native developer such as native animations, build tools, CI, automation. &#xA0;We recommend <a href="https://www.amazon.com/Professional-React-Native-cross-platform-production-ready/dp/180056368X?keywords=professional+react+native&amp;3Bamp%3Bqid=1671800338&amp;3Bamp%3Bsprefix=professional+react+nati%2Caps%2C109&amp;3Bamp%3Bsr=8-1&amp;3Bamp%3BlinkCode=li3&amp;3Bamp%3Btag=blixtdev-20&amp;3Bamp%3BlinkId=166be9da5677ed6150c5df4e341aa2f5&amp;3Bamp%3Blanguage=en_US&amp;3Bamp%3Bref_=as_li_ss_il&amp;ref=blixt-dev">Professional React Native: Expert techniques and solutions for building high-quality, cross-platform, production-ready apps</a> to learn about these techniques.</p><h3 id="learn-react-in-2023">Learn React in 2023</h3><p>No matter which approach you choose, these are some great resources for learning React in 2023. &#xA0;In addition to these books, there are also some great free resources to help you learn. &#xA0;Here are some places to get started!</p><ul><li>The React team&apos;s <a href="https://reactjs.org/tutorial/tutorial.html?ref=blixt-dev">Intro to React tutorial</a></li><li>The official <a href="https://reactjs.org/docs/getting-started.html?ref=blixt-dev">React documentation</a></li><li>More React articles on <a href="https://blixtdev.com/tag/react/">Blixtdev</a></li></ul><p></p>]]></content:encoded></item></channel></rss>