<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Khoa Pham</title>
    <description>The latest articles on Forem by Khoa Pham (@onmyway133).</description>
    <link>https://forem.com/onmyway133</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F20120%2Ff6355e24-68e5-42a1-bf8f-07dcb87e14b5.jpeg</url>
      <title>Forem: Khoa Pham</title>
      <link>https://forem.com/onmyway133</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/onmyway133"/>
    <language>en</language>
    <item>
      <title>Craft stunning code and image screenshot for free</title>
      <dc:creator>Khoa Pham</dc:creator>
      <pubDate>Sun, 25 Feb 2024 08:58:50 +0000</pubDate>
      <link>https://forem.com/onmyway133/craft-stunning-code-and-image-screenshot-for-free-3065</link>
      <guid>https://forem.com/onmyway133/craft-stunning-code-and-image-screenshot-for-free-3065</guid>
      <description>&lt;p&gt;Do you know you can craft stunning code and image screenshots with this 100% free Screenshot tool &lt;a href="https://indiegoodies.com/screenshot" rel="noopener noreferrer"&gt;https://indiegoodies.com/screenshot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A2000%2Fformat%3Awebp%2F1%2A1oFF0i7J2bwIVDJ8D9K1Jg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A2000%2Fformat%3Awebp%2F1%2A1oFF0i7J2bwIVDJ8D9K1Jg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🌟✨ Introducing Screenshot! ✨🌟&lt;/p&gt;

&lt;p&gt;Looking for a simple way to showcase your code in all its glory? Say hello to Screenshot, your new go-to web tool that turns code into eye-catching masterpieces - and it's absolutely FREE!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A2000%2Fformat%3Awebp%2F1%2AfuZKlDqtsJ9RD62LtYVwuw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A2000%2Fformat%3Awebp%2F1%2AfuZKlDqtsJ9RD62LtYVwuw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Screenshot, you're all set to:&lt;/p&gt;

&lt;p&gt;🎨 Elevate your code with vibrant syntax highlighting for clarity and aesthetics.&lt;br&gt;
🌈 Choose from stunning gradient backgrounds that make your snippets pop.&lt;br&gt;
⚙️ Fully customize your screenshots, ensuring your code looks as unique as it is.&lt;br&gt;
Perfect for flaunting your coding projects on social media, enhancing your documentation, or just admiring your programming prowess, Screenshot is here to make your code visually unforgettable.&lt;/p&gt;

&lt;p&gt;Dive in now and let your code shine brighter than ever! 🚀&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I visualized 8 lessons from 1 year doing indie development</title>
      <dc:creator>Khoa Pham</dc:creator>
      <pubDate>Thu, 14 Apr 2022 18:31:46 +0000</pubDate>
      <link>https://forem.com/onmyway133/i-visualized-8-lessons-from-1-year-doing-indie-development-1mgj</link>
      <guid>https://forem.com/onmyway133/i-visualized-8-lessons-from-1-year-doing-indie-development-1mgj</guid>
      <description>&lt;p&gt;It's been a year since I returned to Twitter and started making apps seriously.&lt;/p&gt;

&lt;p&gt;What I've got besides 💰💸? Lots of new friends and valuable lessons along the way 🥰&lt;/p&gt;

&lt;p&gt;It all started with a change in mindset ⚡️&lt;/p&gt;

&lt;p&gt;Therefore I've made these 8 visuals to share what I've learned&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take risk. 
If you're so afraid to make mistake, you've already made your 1st mistake.
When you win, you win. 
When you fail, you've learned yourself a lesson.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xRgaPJcl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCa2yVQAESdGD%3Fformat%3Djpg%26name%3Dlarge" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xRgaPJcl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCa2yVQAESdGD%3Fformat%3Djpg%26name%3Dlarge" alt="" width="880" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build in public&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Don't start with an idea and build in the dark.&lt;br&gt;
Build in public, validate and reiterate your ideas, together with your audience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SJU4HCwO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCbVcVcAAn34E%3Fformat%3Djpg%26name%3Dlarge" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SJU4HCwO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCbVcVcAAn34E%3Fformat%3Djpg%26name%3Dlarge" alt="" width="880" height="767"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Just do it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are no unique ideas. It's all about execution.&lt;br&gt;
When in doubt, start small, start many.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R4XZnp_x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCb2EVQAclfwO%3Fformat%3Djpg%26name%3Dlarge" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R4XZnp_x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCb2EVQAclfwO%3Fformat%3Djpg%26name%3Dlarge" alt="" width="880" height="673"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The maker mindset&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When people complain, you realize the problem. Make a solution that heals their pain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RmcMJbtE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCcTHUcAg057X%3Fformat%3Djpg%26name%3Dlarge" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RmcMJbtE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCcTHUcAg057X%3Fformat%3Djpg%26name%3Dlarge" alt="" width="880" height="593"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the community&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Twitter is all about the community, the relation.&lt;/p&gt;

&lt;p&gt;Don't shout to the void. Make friends. Interact with people. Grow together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4wD-PE-0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCcx3VcAgZC_v%3Fformat%3Djpg%26name%3Dmedium" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4wD-PE-0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCcx3VcAgZC_v%3Fformat%3Djpg%26name%3Dmedium" alt="" width="880" height="725"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give, then ask.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To give is to receive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QsDqvmXf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCdM7VkAIPJrH%3Fformat%3Djpg%26name%3Dmedium" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QsDqvmXf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCdM7VkAIPJrH%3Fformat%3Djpg%26name%3Dmedium" alt="" width="880" height="780"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build. Fail. Learn.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Learning without doing is a waste of time. Only learning is a sign of procrastination.&lt;/p&gt;

&lt;p&gt;Build something. You will learn a lot along the way. All the practical lessons that you can't learn from books.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b6fEBmDN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCdnDVgAMviPf%3Fformat%3Djpg%26name%3Dlarge" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b6fEBmDN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCdnDVgAMviPf%3Fformat%3Djpg%26name%3Dlarge" alt="" width="880" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You may be able to do anything, but you cannot do everything&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Time is a limited resource. &lt;br&gt;
Successful people say no most of the time.&lt;br&gt;
Spend your time on things that truly matter&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7fD9kbFl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCeC3VUAQIZCB%3Fformat%3Djpg%26name%3Dlarge" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7fD9kbFl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FQUCeC3VUAQIZCB%3Fformat%3Djpg%26name%3Dlarge" alt="" width="880" height="696"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading. If you love this sharing, please say hi on Twitter as well &lt;a href="https://twitter.com/onmyway133"&gt;https://twitter.com/onmyway133&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>My year in review 2020</title>
      <dc:creator>Khoa Pham</dc:creator>
      <pubDate>Thu, 17 Dec 2020 05:25:02 +0000</pubDate>
      <link>https://forem.com/onmyway133/my-year-in-review-2020-42mc</link>
      <guid>https://forem.com/onmyway133/my-year-in-review-2020-42mc</guid>
      <description>&lt;p&gt;I remember this time last year in December 2019, I spent almost every single bit of my free time on &lt;a href="https://github.com/pumaswift/Puma" rel="noopener noreferrer"&gt;Puma&lt;/a&gt; because I want a Swift friendly version of fastlane that suits my need and leverages Swift 5 features. &lt;/p&gt;

&lt;p&gt;Here's my review of my work in year 2020.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blogging
&lt;/h2&gt;

&lt;p&gt;I started blogging on GitHub issue, starting from &lt;a href="https://github.com/onmyway133/blog/issues/1" rel="noopener noreferrer"&gt;Issue 1 Hello world, again&lt;/a&gt;, now I have over 670 issues, which were generated into blog posts at my website &lt;a href="https://onmyway133.com/" rel="noopener noreferrer"&gt;https://onmyway133.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🍏 I used to use onmyway133.github.io domain, but then it feels right to have my own domain &lt;br&gt;
🍎 I used to write a lot at Medium &lt;a href="https://medium.com/@onmyway133" rel="noopener noreferrer"&gt;https://medium.com/@onmyway133&lt;/a&gt; for many publications and my own Fantageek publication, I have got 2.3k followers with around 60k views per month&lt;br&gt;
🍓 I list my most favorite articles, usually articles that I spent most time polishing here &lt;a href="https://onmyway133.com/writing/" rel="noopener noreferrer"&gt;https://onmyway133.com/writing/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of my very first articles published on Flawless iOS publication was &lt;a href="https://medium.com/flawless-app-stories/a-better-way-to-update-uicollectionview-data-in-swift-with-diff-framework-924db158db86" rel="noopener noreferrer"&gt;A better way to update UICollectionView data in Swift with diff framework&lt;/a&gt; gets the highest traffic ever, and was rated most trending Swift article for 2018.&lt;/p&gt;

&lt;p&gt;My one of few articles published in Medium in 2019 was &lt;a href="https://medium.com/flawless-app-stories/how-to-make-auto-layout-more-convenient-in-ios-df3b42fed37f" rel="noopener noreferrer"&gt;How to make Auto Layout more convenient in iOS&lt;/a&gt; got featured in iOS Dev Weekly, and used to promote my library &lt;a href="https://github.com/onmyway133/EasyAnchor" rel="noopener noreferrer"&gt;EasyAnchor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My big article this year is &lt;a href="https://onmyway133.com/blog/how-to-test-push-notifications-in-simulator-and-production-ios-apps/" rel="noopener noreferrer"&gt;How to test push notifications in simulator and production iOS apps&lt;/a&gt;, which was also featured on iOS Dev Weekly, used to summary the changes in push notifications from iOS 7 to iOS 14, and to promote my push notification testing tool &lt;a href="https://twitter.com/pushheroapp" rel="noopener noreferrer"&gt;Push Hero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My blog at &lt;a href="https://onmyway133.com/" rel="noopener noreferrer"&gt;https://onmyway133.com/&lt;/a&gt; has around 15k views each month. I can write proper, lengthy articles to get more views but I don't want to. I want to write blog about solutions I have found, so that my future self can benefit from it without searching too much.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2284279%2F102402948-cfb8fd80-3fe5-11eb-9f7a-b7a85d0ad450.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2284279%2F102402948-cfb8fd80-3fe5-11eb-9f7a-b7a85d0ad450.jpeg" alt="views"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Open source
&lt;/h2&gt;

&lt;p&gt;I have done quite a lot of open source, you can view here &lt;a href="https://onmyway133.com/opensource/" rel="noopener noreferrer"&gt;Open source&lt;/a&gt;. These have helped tons of apps, with 45k+ apps touched, and 3.4m+ downloads as stats on CocoaPods&lt;/p&gt;

&lt;p&gt;My 4 libraries this year get inspired by SwiftUI and property wrappers&lt;/p&gt;

&lt;p&gt;🍌 &lt;a href="https://github.com/onmyway133/Spek" rel="noopener noreferrer"&gt;Spek&lt;/a&gt; leverages property wrapper to provide Spec syntax, similar to Quick, but simpler and can generate tests&lt;br&gt;
🍈 &lt;a href="https://github.com/onmyway133/Micro" rel="noopener noreferrer"&gt;Micro&lt;/a&gt; imitates SwiftUI State and ForEach syntax but use UICollectionView with diffable datasource, powered by my another library &lt;a href="https://github.com/onmyway133/DeepDiff" rel="noopener noreferrer"&gt;DeepDiff&lt;/a&gt;&lt;br&gt;
🍑 &lt;a href="https://github.com/onmyway133/EasySwiftUI" rel="noopener noreferrer"&gt;EasySwiftUI&lt;/a&gt; contains many extensions and useful modifier that I use in my SwiftUI apps&lt;br&gt;
🥝 &lt;a href="https://github.com/onmyway133/FontAwesomeSwiftUI" rel="noopener noreferrer"&gt;FontAwesomeSwiftUI&lt;/a&gt; I was tired of using bitmap with dark and light variants, and I can't use SFSymbols as I want to support macOS 10.15, so FontAwesome is a perfect choice. I couldn't find library that has support for SwiftUI and easy to use with Swift Package Manager for iOS and macOS, so I made one&lt;/p&gt;

&lt;p&gt;Besides, for all my libraries &lt;a href="https://github.com/onmyway133/EasyStash" rel="noopener noreferrer"&gt;EasyStash&lt;/a&gt;, &lt;a href="https://github.com/onmyway133/EasyAnchor" rel="noopener noreferrer"&gt;EasyAnchor&lt;/a&gt;, &lt;a href="https://github.com/onmyway133/EasyTheme" rel="noopener noreferrer"&gt;EasyTheme&lt;/a&gt;, &lt;a href="https://github.com/onmyway133/EasyClosure" rel="noopener noreferrer"&gt;EasyClosure&lt;/a&gt;, ... I have now support Swift Package Manager, which is nicer to integrate. Thank you CocoaPods for all these years.&lt;/p&gt;

&lt;p&gt;Lately, I open source &lt;a href="https://github.com/onmyway133/awesome-swiftui" rel="noopener noreferrer"&gt;awesome-swiftui&lt;/a&gt; which I curate all SwiftUI resources, articles and libraries that I find useful for my apps&lt;/p&gt;

&lt;p&gt;❤️ One day, I got sponsor from my dear friend &lt;a href="https://twitter.com/zenangst" rel="noopener noreferrer"&gt;Chris&lt;/a&gt; for my GitHub open source. Chris is my open source idol who ignited my desire for open source. No one loves open source more than Chris&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This means the world to me! If everyone was as lucky as me to just get to inspire one person, the world would be an amazing place. Let's make it amazing together! Keep rocking because you are amazing &amp;amp; you can bring out the best in others! ❤️ &lt;a href="https://t.co/pJx9obWH7I" rel="noopener noreferrer"&gt;https://t.co/pJx9obWH7I&lt;/a&gt;&lt;/p&gt;— Christoffer Winterkvist (@zenangst) &lt;a href="https://twitter.com/zenangst/status/1309403013237403649?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;September 25, 2020&lt;/a&gt;
&lt;/blockquote&gt; 
&lt;h2&gt;
  
  
  Apps
&lt;/h2&gt;

&lt;p&gt;I started making some apps late last year, first I published them on Gumroad, but it didn't feel right, and then I published all my apps on AppStore. I like sandboxed apps from AppStore because they limit what the apps can do.&lt;/p&gt;

&lt;p&gt;Apps without Twitter account and landing page seem off, so in May I started revamping my websites, and I wrote my &lt;a href="https://onmyway133.com/apps/" rel="noopener noreferrer"&gt;apps&lt;/a&gt; page firstly with pure HTML and CSS, then I rewrote in React, because I like React and Javascript.&lt;/p&gt;

&lt;p&gt;Notably, early 2020 I made &lt;a href="https://twitter.com/pushheroapp" rel="noopener noreferrer"&gt;Push Hero&lt;/a&gt;, &lt;a href="https://twitter.com/PastePalApp" rel="noopener noreferrer"&gt;PastePal&lt;/a&gt; then I made a complete overhaul lately with more features, thanks to all the feedback. I also took the time to revamp landing pages a lot, you can check landing pages for &lt;a href="https://onmyway133.com/pushhero" rel="noopener noreferrer"&gt;Push Hero&lt;/a&gt; for example because I have a white label solution&lt;/p&gt;

&lt;p&gt;Learn how I did &lt;a href="https://onmyway133.github.io/blog/How-to-make-white-label-React-app-for-landing-pages/" rel="noopener noreferrer"&gt;white label landing page using React&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have a lot of ideas, but very little time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Learning
&lt;/h2&gt;

&lt;p&gt;Coding can be done, but design is never finished. When making apps, I feel like I'm not certain at some design decisions and no matter how I landed with some designs, I didn't feel happy. Then I took some design courses and books.&lt;/p&gt;

&lt;p&gt;Below is my tweet that I share some design resources that I found useful&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Developer with design skill is like tiger with wings. Here are my recommended courses&lt;br&gt;&lt;br&gt;The UI Design Bootcamp by &lt;a href="https://twitter.com/designcoursecom?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@designcoursecom&lt;/a&gt; a massive UI design course teaches you to build beautiful and well-functioning user interfaces using HTML and CSS.&lt;a href="https://t.co/vfusXamuhJ" rel="noopener noreferrer"&gt;https://t.co/vfusXamuhJ&lt;/a&gt;&lt;/p&gt;— Khoa 🔥 (&lt;a class="mentioned-user" href="https://dev.to/onmyway133"&gt;@onmyway133&lt;/a&gt;) &lt;a href="https://twitter.com/onmyway133/status/1320464953548091393?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;October 25, 2020&lt;/a&gt;
&lt;/blockquote&gt; 

&lt;h2&gt;
  
  
  Listening
&lt;/h2&gt;

&lt;p&gt;I used to listen to tech podcasts, then I was bored. Discussions like whether to use MVVM vs MVC, SwiftUI vs Catalyst, Swift vs Objective don't interest me anymore.&lt;/p&gt;

&lt;p&gt;If you ask me for choices, my default answer will be NO. Those who are super passionate about their idea will just ignore my advice and do it anyway.&lt;/p&gt;

&lt;p&gt;Then I listen to indie and product development podcasts, it was inspiring. Once I got the mindset, they also became boring.&lt;br&gt;
Now I listen to mostly books on Storytel, some books about habit and making time really make my days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tweet
&lt;/h2&gt;

&lt;p&gt;I came back to Twitter early this year after quitting it for a while because I got enough of all negative and nonsense political debates. But then I found that I can decide who I can follow and what content I want to view. Then I started organizing List, making my own Chrome extensions to automate things and control what I want to view. I have followed quite many indie developers and great product people, I've learnt a lot. The downside is I'm overwhelmingly inspired, I can't sleep.&lt;/p&gt;

&lt;p&gt;I try to tweet more about what I've learned, sharing articles that I have written. For example here I share about how to gain product idea&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How to gain product ideas?&lt;br&gt;&lt;br&gt;1) Scratch your own itch. If you don't have any itch to scratch, stop here. This is awkward. Go travelling. Go exploring the world. The world always has problems and needs solution. &lt;a href="https://t.co/07gIoJfJet" rel="noopener noreferrer"&gt;pic.twitter.com/07gIoJfJet&lt;/a&gt;&lt;/p&gt;— Khoa 🔥 (&lt;a class="mentioned-user" href="https://dev.to/onmyway133"&gt;@onmyway133&lt;/a&gt;) &lt;a href="https://twitter.com/onmyway133/status/1330399823921557507?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;November 22, 2020&lt;/a&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  WWDC Together
&lt;/h2&gt;

&lt;p&gt;Notable this year is the website I make &lt;a href="https://wwdctogether.com/" rel="noopener noreferrer"&gt;WWDC Together&lt;/a&gt; as a place for developers like us to hangout and watch videos together. Each video acts like its own chat room, you can also create private chatroom. My colleagues use this and we watched WWDC together with coke and pizza, it was a lot of fun.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'm excited to launch &lt;a href="https://twitter.com/wwdctogether?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@wwdctogether&lt;/a&gt; as a free place to watch and hangout during WWDC. We can now view upcoming and previous sessions via modern playback, with a chatroom for each session. Hope this provides a similar WWDC vibe during this hard time ❤️🔥&lt;a href="https://t.co/0SgGnUAqst" rel="noopener noreferrer"&gt;https://t.co/0SgGnUAqst&lt;/a&gt; &lt;a href="https://t.co/f9H9wDi62P" rel="noopener noreferrer"&gt;pic.twitter.com/f9H9wDi62P&lt;/a&gt;&lt;/p&gt;— Khoa 🔥 (&lt;a class="mentioned-user" href="https://dev.to/onmyway133"&gt;@onmyway133&lt;/a&gt;) &lt;a href="https://twitter.com/onmyway133/status/1273243022826835970?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;June 17, 2020&lt;/a&gt;
&lt;/blockquote&gt; 

&lt;p&gt;I had the idea like 2 weeks before WWDC, so I had to make it quick. Working with React is fun to me, it is like playing video game.&lt;/p&gt;

&lt;p&gt;I was lucky to be asked by John to write a guest post &lt;a href="https://wwdcbysundell.com/2020/wwdc-together/" rel="noopener noreferrer"&gt;Behind the scenes of WWDC Together with Khoa Pham&lt;/a&gt; on his website.&lt;/p&gt;

&lt;p&gt;It was also mentioned by Paul in his WWDC wrap up &lt;a href="https://www.hackingwithswift.com/articles/222/wwdc20-wrap-up-and-recommended-talks" rel="noopener noreferrer"&gt;WWDC20: Wrap up and recommended talks&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One area that absolutely flourished this year was community organization. Sites like &lt;a href="https://wwdcwatch.party" rel="noopener noreferrer"&gt;https://wwdcwatch.party&lt;/a&gt; and &lt;a href="https://wwdctogether.com" rel="noopener noreferrer"&gt;https://wwdctogether.com&lt;/a&gt; took a huge amount of work to organize, but meant that people had the chance to have some interaction – had the chance to actually chat about WWDC and share their excitement. I’m really grateful to Michie, Khoa, and other community organizers for making this happen.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I also got to share about in in a well known Norwegian tech website &lt;a href="https://www.kode24.no/kodenytt/norske-wwdc-together-lar-oss-se-pa-apples-konferanse-sammen/72584043" rel="noopener noreferrer"&gt;orske WWDC Together lar oss se på Apples konferanse sammen&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Speaking
&lt;/h2&gt;

&lt;p&gt;In 2019, I made several meetup talks, and one pre conf talk for Mobile Era conf&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An overview of what we as mobile developers have for &lt;a href="https://twitter.com/hashtag/MachineLearning?src=hash&amp;amp;ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;#MachineLearning&lt;/a&gt; in the session by &lt;a href="https://twitter.com/onmyway133?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@onmyway133&lt;/a&gt; at &lt;a href="https://twitter.com/mobileeraconf?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@mobileeraconf&lt;/a&gt; Community Evening &lt;a href="https://t.co/bgcE3huGkI" rel="noopener noreferrer"&gt;pic.twitter.com/bgcE3huGkI&lt;/a&gt;&lt;/p&gt;— Mobile Meetup Oslo (@mobileoslo) &lt;a href="https://twitter.com/mobileoslo/status/1192154165751033856?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;November 6, 2019&lt;/a&gt;
&lt;/blockquote&gt; 

&lt;p&gt;In 2020, I'm lucky to be invited to talk in some events&lt;/p&gt;

&lt;p&gt;🍅 WWDC Watch Party&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thank you &lt;a href="https://twitter.com/JohnEstropia?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@johnestropia&lt;/a&gt; &lt;a href="https://twitter.com/thesunshinejr?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@thesunshinejr&lt;/a&gt; &lt;a href="https://twitter.com/onmyway133?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@onmyway133&lt;/a&gt; for sharing your experience to the community about Open Source Life and thank you for making our dev life better because of your projects! You are all awesome! ❤️&lt;a href="https://twitter.com/hashtag/WWDCWatchParty?src=hash&amp;amp;ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;#WWDCWatchParty&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/WWDC20?src=hash&amp;amp;ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;#WWDC20&lt;/a&gt; &lt;a href="https://t.co/tfsX7PtKcO" rel="noopener noreferrer"&gt;pic.twitter.com/tfsX7PtKcO&lt;/a&gt;&lt;/p&gt;— wwdcwatchparty (&lt;a class="mentioned-user" href="https://dev.to/wwdcwatchparty"&gt;@wwdcwatchparty&lt;/a&gt;) &lt;a href="https://twitter.com/wwdcwatchparty/status/1277214349246042112?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;June 28, 2020&lt;/a&gt;
&lt;/blockquote&gt; 

&lt;p&gt;I'm happy to be invited by Michie to talk along side with &lt;a href="https://twitter.com/JohnEstropia" rel="noopener noreferrer"&gt;John&lt;/a&gt; and &lt;a href="https://twitter.com/thesunshinejr" rel="noopener noreferrer"&gt;Łukasz&lt;/a&gt;, whom I really admire for their awesome open source contribution, to talk about how to start and contribute to open source&lt;/p&gt;

&lt;p&gt;🥦 Bitrise webminar&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;During the 4th installment of our BUG &lt;a href="https://twitter.com/hashtag/webinar?src=hash&amp;amp;ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;#webinar&lt;/a&gt; series, our main focus was &lt;a href="https://twitter.com/hashtag/iOSDev?src=hash&amp;amp;ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;#iOSDev&lt;/a&gt; and &lt;a href="https://twitter.com/hashtag/WWDC20?src=hash&amp;amp;ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;#WWDC20&lt;/a&gt;. Let's recap what we discussed with our guests, &lt;a href="https://twitter.com/StuFFmc?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@StuffMC&lt;/a&gt;, &lt;a href="https://twitter.com/axbotkin?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@axbotkin&lt;/a&gt;, &lt;a href="https://twitter.com/Moatazeldebsy?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@Moatazeldebsy&lt;/a&gt;, and &lt;a href="https://twitter.com/onmyway133?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@onmyway133&lt;/a&gt; in our freshest blog post! ✨🔥&lt;a href="https://t.co/rrA8cyYXus" rel="noopener noreferrer"&gt;https://t.co/rrA8cyYXus&lt;/a&gt;&lt;/p&gt;— Bitrise (&lt;a class="mentioned-user" href="https://dev.to/bitrise"&gt;@bitrise&lt;/a&gt;) &lt;a href="https://twitter.com/bitrise/status/1283431144726368258?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;July 15, 2020&lt;/a&gt;
&lt;/blockquote&gt; 

&lt;p&gt;I shared my thoughts about my favorites at WWDC and the future ahead&lt;/p&gt;

&lt;p&gt;🍇 Contributing.today&lt;/p&gt;

&lt;p&gt;During Hacktoberfest 2020, I was contacted by my friend &lt;a href="https://twitter.com/webmaxru" rel="noopener noreferrer"&gt;Maxim&lt;/a&gt; to share about open source &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I had a pleasure to interview &lt;a href="https://twitter.com/hashtag/OSS?src=hash&amp;amp;ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;#OSS&lt;/a&gt; contributors from Norway &lt;a href="https://twitter.com/DrMowinckels?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@DrMowinckels&lt;/a&gt; and &lt;a href="https://twitter.com/onmyway133?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@onmyway133&lt;/a&gt; for CONTRIBUTING project. October 2nd we kickoff &lt;a href="https://twitter.com/hashtag/Hacktoberfest?src=hash&amp;amp;ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;#Hacktoberfest&lt;/a&gt; by online meetup about getting started with Open Source at 16:00 CEST. Join our sessions &amp;amp; workshops: &lt;a href="https://t.co/hKkMKEOJJe" rel="noopener noreferrer"&gt;https://t.co/hKkMKEOJJe&lt;/a&gt; &lt;a href="https://t.co/1yr2AhQ4B6" rel="noopener noreferrer"&gt;https://t.co/1yr2AhQ4B6&lt;/a&gt;&lt;/p&gt;— Maxim Salnikov (&lt;a class="mentioned-user" href="https://dev.to/webmaxru"&gt;@webmaxru&lt;/a&gt;) &lt;a href="https://twitter.com/webmaxru/status/1311674525604622337?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;October 1, 2020&lt;/a&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Work
&lt;/h2&gt;

&lt;p&gt;I'm happy to continue another awesome year at Shortcut and DNB, where I have awesome and super nice colleagues. I 've made lots of friends who I can talk with, who invited me to play badminton, tennis, football, swimming, hiking. Why didn't we meet earlier?&lt;/p&gt;

&lt;p&gt;I was lucky to attend a workshop hosted by John Sundell at work, I learned a lot&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ran my first ever large-scale remote workshop today, with over 25 participants, 10 different ongoing video calls for pair programming, and a ton of live coding 😀 Was so much fun!&lt;br&gt;&lt;br&gt;Thanks a lot to the wonderful team at &lt;a href="https://twitter.com/shortcut_no?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@shortcut_no&lt;/a&gt; for inviting me 😊 &lt;a href="https://t.co/CE4P8Slo1G" rel="noopener noreferrer"&gt;https://t.co/CE4P8Slo1G&lt;/a&gt;&lt;/p&gt;— John Sundell (@johnsundell) &lt;a href="https://twitter.com/johnsundell/status/1298992825422327808?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;August 27, 2020&lt;/a&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Life
&lt;/h2&gt;

&lt;p&gt;Thanks for a memorable year, despite all the lockdown. And remember, balance work and life. You don't need to be super rich to be happy. Having lots of money in the bank while living alone is not fun at all.&lt;/p&gt;

&lt;p&gt;Another year is coming to an end. When looking back, do you miss the time you didn't spend with your friends and family, or do you miss the time you didn't get to do your work?&lt;/p&gt;

&lt;p&gt;May your code continue to compile 🙏&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to make white label React app for landing pages</title>
      <dc:creator>Khoa Pham</dc:creator>
      <pubDate>Thu, 14 May 2020 04:26:50 +0000</pubDate>
      <link>https://forem.com/onmyway133/how-to-make-white-label-react-app-for-landing-pages-404k</link>
      <guid>https://forem.com/onmyway133/how-to-make-white-label-react-app-for-landing-pages-404k</guid>
      <description>&lt;p&gt;A good landing page is one of the most crucial part of a successful launch. Recently I started creating landing pages for my &lt;a href="https://onmyway133.com/apps/" rel="noopener noreferrer"&gt;apps&lt;/a&gt;, I was lazy that I ended creating a white label React app as a landing template and then write a script to build multiple similar pages.&lt;/p&gt;

&lt;p&gt;Here are a few examples, at first the pages share same look and feel, but we can add more configuration parameters later on. The cool thing with this approach is fixing bugs and adding features become easier, as they will all be deployed with the generator script.&lt;/p&gt;

&lt;p&gt;Here are how it looks in my apps &lt;a href="https://onmyway133.com/pastepal/" rel="noopener noreferrer"&gt;PastePal&lt;/a&gt; and &lt;a href="https://onmyway133.com/pushhero/" rel="noopener noreferrer"&gt;PushHero&lt;/a&gt;, look at how the footer parts are so consistent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2284279%2F81890497-66cc5b80-95a6-11ea-88bb-43f99dcb6ad6.png" class="article-body-image-wrapper"&gt;&lt;img alt="Screenshot 2020-05-14 at 05 47 28" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2284279%2F81890497-66cc5b80-95a6-11ea-88bb-43f99dcb6ad6.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2284279%2F81890540-7c418580-95a6-11ea-95ae-8b095440d26f.png" class="article-body-image-wrapper"&gt;&lt;img alt="Screenshot 2020-05-14 at 05 48 01" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2284279%2F81890540-7c418580-95a6-11ea-95ae-8b095440d26f.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2284279%2F81890555-8499c080-95a6-11ea-9028-c2bd09f66b0a.png" class="article-body-image-wrapper"&gt;&lt;img alt="Screenshot 2020-05-14 at 05 47 45" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2284279%2F81890555-8499c080-95a6-11ea-9028-c2bd09f66b0a.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a landing page in pure html and javascript
&lt;/h2&gt;

&lt;p&gt;The first version that I built is with pure html and javascript. It has a lot of boilerplate and I need to deal with Webpack eventually to obfuscate my code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cards&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// top&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageContainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;imageContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image-container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageContainer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;imageContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backgroundColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;imageContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backgroundColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(200, 200, 200, 1.0)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`../&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/icon.png`&lt;/span&gt;
    &lt;span class="nx"&gt;imageContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since they are in pure html and javascript, everyone can just open browser and view source code, which is not ideal, so I need to fiddle with Webpack and other uglify and minimize tools to obfuscate the code, like &lt;a href="https://onmyway133.com/blog/how-to-use-webpack-to-bundle-html-css-js/" rel="noopener noreferrer"&gt;How to use webpack to bundle html css js&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init
npm install webpack webpack-cli --save-dev
npm install babel-minify-webpack-plugin --save-dev
npm install html-webpack-plugin --save-dev

const MinifyPlugin = require('babel-minify-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    entry: "./index.js",
    mode: 'production',
    output: {
        filename: "./index.js"
    },
    plugins: [
        new MinifyPlugin(),
        new HtmlWebpackPlugin({
            template: 'index.html',
            filename: 'index.html',
            minify: {
                collapseWhitespace: true
            }
        })
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with external css sheets, finding and renaming class list names take some time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a landing page in React
&lt;/h2&gt;

&lt;p&gt;I use &lt;a href="https://create-react-app.dev/" rel="noopener noreferrer"&gt;create-react-app&lt;/a&gt; to generate my React app as it sets up JSX, Babel, Webpack, hot reloading and development server for me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inline css
&lt;/h3&gt;

&lt;p&gt;I like js, css and html be part of the same component file, so I prefer inline css. I tried &lt;a href="https://styled-components.com/" rel="noopener noreferrer"&gt;styled-components&lt;/a&gt; before but then I found &lt;a href="https://emotion.sh/docs/introduction" rel="noopener noreferrer"&gt;emotion&lt;/a&gt; to be much easier to use and close to css. I also don't like declaring unnecessary local variables style in styled-components.&lt;/p&gt;

&lt;p&gt;Here is a good comparison between the 2 &lt;a href="https://github.com/jsjoeio/styled-components-vs-emotion" rel="noopener noreferrer"&gt;styled-components-vs-emotion&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// styled-components&lt;/span&gt;
&lt;span class="c1"&gt;// CSS syntax in tagged template literal&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="s2"&gt;`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`&lt;/span&gt;
&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hiya&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Title&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;
&lt;span class="c1"&gt;// Object syntax&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;button&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.5em&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;textAlign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;palevioletred&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// emotion&lt;/span&gt;
&lt;span class="c1"&gt;// CSS syntax in tagged template literal&lt;/span&gt;
&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;
    &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
      font-size: 1.5em;
      text-align: center;
      color: palevioletred;
    `&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;Hiya&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Object syntax&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;titleStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.5em&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;textAlign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;palevioletred&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;titleStyles&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hiya&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use emotion for inline css
&lt;/h3&gt;

&lt;p&gt;I detail here &lt;a href="https://onmyway133.com/blog/how-to-use-emotion-for-inline-css-in-react/" rel="noopener noreferrer"&gt;How to use emotion for inline css in React&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Emotion has core and styled styles. I usually use the &lt;code&gt;css&lt;/code&gt; inline syntax, so I can just install the core&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;emotion&lt;/span&gt;&lt;span class="sr"&gt;/cor&lt;/span&gt;&lt;span class="err"&gt;e
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we have to declare jsx directive at the top of every file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// this comment tells babel to convert jsx to calls to a function called jsx instead of React.createElement&lt;/span&gt;
&lt;span class="cm"&gt;/** @jsx jsx */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jsx&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@emotion/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
    &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
      padding: 32px;
      background-color: hotpink;
      font-size: 24px;
      border-radius: 4px;
      &amp;amp;:hover {
        color: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;;
      }
    `&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;Hover&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One cool thing with inline css is they are just javascript code so it's pretty easy to apply logic code, like in &lt;a href="https://onmyway133.com/blog/how-to-conditionally-apply-css-in-emotion-js/" rel="noopener noreferrer"&gt;How to conditionally apply css in emotion js&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shadowCss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadow&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
        border-radius: 5px;
        box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
    `&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;``&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Components based
&lt;/h3&gt;

&lt;p&gt;When a component gets too big, I extract it to small components, in the end I have a bunch of them&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Footer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/Footer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Download&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/Download&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ProductHunt&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/ProductHunt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Header&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/Header&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Headline&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/Headline&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Features&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/Features
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and I stack them vertically, using flexbox and css grid&lt;/p&gt;

&lt;h3&gt;
  
  
  Responsiveness with flexbox and css grid
&lt;/h3&gt;

&lt;p&gt;I used flexbox mostly at first, but then I gradually convert some of them to css grid when I see fit.  To stack vertically with flexbox, I use &lt;code&gt;flex-direction&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;flex&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;flex-direction&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;column&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where as in css grid items are stacked vertically by default, if we want multiple columns, specify &lt;code&gt;grid-template-columns&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;grid&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;grid-template-columns&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;fr&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;fr&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use &lt;code&gt;flex-wrap: wrap&lt;/code&gt; in some places to wrap content, but in some places I see specifying media query and changing columns in css grid is more easier and predictable&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;grid&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;grid-template-columns&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;fr&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;fr&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;grid-gap&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;8&lt;/span&gt;&lt;span class="nt"&gt;vw&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;align-items&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;center&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Audit with Lighthouse
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/web/tools/lighthouse" rel="noopener noreferrer"&gt;Google Lighthouse&lt;/a&gt; is the most popular tool to audit website for performance and SEO. I use it to reduce image size, add correct html attributes and make it more SEO friendly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prepare a list of app
&lt;/h3&gt;

&lt;p&gt;I have my list of apps in 1 javascript file, called &lt;code&gt;factory.js&lt;/code&gt;, for example here with &lt;a href="https://onmyway133.com/pastepal/" rel="noopener noreferrer"&gt;PastePal&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PastePal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pastepal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#5488E5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;headline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your next favorite pasteboard manager&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Never miss what you just type. PastePal is a native Mac application that manages your pasteboard history with a variety of file types support like text and images. It also comes with a nifty note and shortcut manager.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;banner.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my &lt;code&gt;package.json&lt;/code&gt; for my landing page, I declare a property called &lt;code&gt;currentApp&lt;/code&gt;. This is to specify which app I'm currently work on. Later in the generator script, we can just update this for every app that we build.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;landing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.1.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;homepage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;currentApp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pastepal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is how to read that value from my landing app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./apps/factory&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;componentWillMount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentApp&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pastepal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;})[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;p&gt;One thing with &lt;a href="https://create-react-app.dev/docs/deployment/" rel="noopener noreferrer"&gt;create-react-app&lt;/a&gt; is that built assets are relative to the root, not your index.html&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;npm run build creates a build directory with a production build of your app. Set up your favorite HTTP server so that a visitor to your site is served index.html, and requests to static paths like /static/js/main..js are served with the contents of the /static/js/main..js file. For more information see the production build section.&lt;/p&gt;

&lt;p&gt;If you are not using the HTML5 pushState history API or not using client-side routing at all, it is unnecessary to specify the URL from which your app will be served. Instead, you can put this in your package.json:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"homepage": ".",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This will make sure that all the asset paths are relative to index.html. You will then be able to move your app from &lt;a href="http://mywebsite.com" rel="noopener noreferrer"&gt;http://mywebsite.com&lt;/a&gt; to &lt;a href="http://mywebsite.com/relativepath" rel="noopener noreferrer"&gt;http://mywebsite.com/relativepath&lt;/a&gt; or even &lt;a href="http://mywebsite.com/relative/path" rel="noopener noreferrer"&gt;http://mywebsite.com/relative/path&lt;/a&gt; without having to rebuild it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Build a generator script to generate many landing pages
&lt;/h2&gt;

&lt;p&gt;I make another nodejs project called &lt;code&gt;generator&lt;/code&gt;, it will use my landing project as template, changes a few parameters and build each app defined in &lt;code&gt;factory.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My factory use &lt;code&gt;export default&lt;/code&gt; syntax, so I need to use Babel in my node app to import that, see &lt;a href="https://onmyway133.com/blog/how-to-use-babel-7-in-node-project/" rel="noopener noreferrer"&gt;How to use babel 7 in node project&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init generator
npm install @babel/core
npm install @babel/cli
npm install @babel/preset-env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "presets": ["@babel/preset-env"]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update currentApp
&lt;/h3&gt;

&lt;p&gt;I use &lt;code&gt;sync&lt;/code&gt; methods of &lt;code&gt;fs&lt;/code&gt; to not have to deal with asynchrony.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pkPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`/Users/khoa/projects/anding/package.json`&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pkPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pkPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Execute shell command
&lt;/h3&gt;

&lt;p&gt;I use &lt;code&gt;shelljs&lt;/code&gt; to execute shell commands, and &lt;code&gt;fs&lt;/code&gt; to read and write. In &lt;code&gt;public/index.html&lt;/code&gt; specify some placeholder and we will replace those in our script.&lt;/p&gt;

&lt;p&gt;In landing app, the &lt;code&gt;public/index.html&lt;/code&gt; acts as the shell when building the app, so I have a few placeholder called &lt;code&gt;CONSTANTS&lt;/code&gt;, these will be replaced at generation time in my node app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shelljs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;indexHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicIndexHtmlPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;indexHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;indexHtml&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CONSTANT_HTML_TITLE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CONSTANT_HTML_META_DESCRIPTION&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicIndexHtmlPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;indexHtml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// build&lt;/span&gt;
&lt;span class="nx"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;projects/my_react_app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;npm run build&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// copy&lt;/span&gt;
&lt;span class="nx"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`cp -a projects/my_react_app web_server/public`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;❤️ Support my apps ❤️ &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://onmyway133.com/pushhero" rel="noopener noreferrer"&gt;Push Hero - pure Swift native macOS application to test push notifications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://onmyway133.com/pastepal" rel="noopener noreferrer"&gt;PastePal - Pasteboard, note and shortcut manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://onmyway133.com/quickcheck" rel="noopener noreferrer"&gt;Quick Check - smart todo manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://onmyway133.com/alias" rel="noopener noreferrer"&gt;Alias - App and file shortcut manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://onmyway133.com/apps/" rel="noopener noreferrer"&gt;My other apps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❤️❤️😇😍🤘❤️❤️&lt;/p&gt;

</description>
      <category>react</category>
      <category>landing</category>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to build SwiftUI style UICollectionView data source in Swift</title>
      <dc:creator>Khoa Pham</dc:creator>
      <pubDate>Sun, 09 Feb 2020 22:03:44 +0000</pubDate>
      <link>https://forem.com/onmyway133/how-to-build-swiftui-style-uicollectionview-data-source-in-swift-1101</link>
      <guid>https://forem.com/onmyway133/how-to-build-swiftui-style-uicollectionview-data-source-in-swift-1101</guid>
      <description>&lt;p&gt;It's hard to see any iOS app which don't use UITableView or UICollectionView, as they are the basic and important foundation to represent data. &lt;code&gt;UICollectionView&lt;/code&gt; is very basic to use, yet a bit tedious for common use cases, but if we abstract over it, then it becomes super hard to  customize. Every app is unique, and any attempt to wrap around UICollectionView will fail horribly. A sensable approach for a good abstraction is to make it super easy for normal cases, and easy to customize for advanced scenarios.&lt;/p&gt;

&lt;p&gt;I'm always interested in how to make UICollectionView easier and fun to write and have curated many open sources here &lt;a href="https://github.com/onmyway133/fantastic-ios-architecture#data-source"&gt;data source&lt;/a&gt;. Many of these data source libraries try to come up with totally different namings and complex paradigm which makes it hard to onboard, and many are hard to customize.&lt;/p&gt;

&lt;p&gt;In its simplest form, what we want in a UICollectionView data source is &lt;code&gt;cell = f(state)&lt;/code&gt;, which means our cell representation is just a function of the state. We just want to set model to the cell, the correct cell, in a type safe manner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generic data source
&lt;/h2&gt;

&lt;p&gt;The basic is to make a generic data source that sticks with a particular cell&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;DataSource&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;UICollectionViewCell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Void&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UICollectionViewCell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;IndexPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Void&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This works for basic usage, and we can create multiple DataSource for each kind of model. The problem is it's hard to subclass DataSource as generic in Swift and inheritance for ObjcC NSObject don't work well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check for the types
&lt;/h2&gt;

&lt;p&gt;Seeing the problem with generic data source, I've tried another approach with &lt;a href="https://github.com/onmyway133/Upstream"&gt;Upstream&lt;/a&gt; where it's easier to declare sections and models.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;sections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Section&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="kt"&gt;Section&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Information"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;viewType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;HeaderView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nv"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="kt"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;avatarUrl&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;cellType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AvatarCell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="kt"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Thor"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;cellType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NameCell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="kt"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;location&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Asgard"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;cellType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NameCell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="kt"&gt;Section&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Skills"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;viewType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;HeaderView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nv"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="kt"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;skill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"iOS"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;cellType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;SkillCell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="kt"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;skill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Android"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;cellType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;SkillCell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;adapter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;sections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sections&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This uses the Adapter pattern and we need to handle &lt;code&gt;AdapterDelegate&lt;/code&gt;. To avoid the generic problem, this Adapter store items as &lt;code&gt;Any&lt;/code&gt;, so we need to type cast all the time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;extension&lt;/span&gt; &lt;span class="kt"&gt;ProfileViewController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AdapterDelegate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;indexPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;IndexPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;cell&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Avatarcell&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;cell&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;NameCell&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;view&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;HeaderView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The benefit is that we can easily subclass this Adapter manager to customize the behaviour, here is how to make accordion&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;AccordionManager&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Manager&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;collapsedSections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;tableView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;tableView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UITableView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numberOfRowsInSection&lt;/span&gt; &lt;span class="nv"&gt;section&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;collapsedSections&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sections&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;section&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;collapsedSections&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;collapsedSections&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;collapsedSections&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;indexSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;IndexSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;tableView&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reloadSections&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;indexSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;automatic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  SwiftUI
&lt;/h2&gt;

&lt;p&gt;SwiftUI comes in iOS 13 with a very concise and easy to use syntax. SwiftUI has good diffing so we just need to update our models so the whole content will be diffed and rendered again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;ForEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blogs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;blog&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="kt"&gt;VStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onTap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cell was tapped"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  SwiftUI style with diffing
&lt;/h2&gt;

&lt;p&gt;I built &lt;a href="https://github.com/onmyway133/DeepDiff"&gt;DeepDiff&lt;/a&gt; before and it was used by many people. Now I'm pleased to introduce &lt;a href="https://github.com/onmyway133/Micro"&gt;Micro&lt;/a&gt; which is a SwiftU style with DeepDiff powered  so it performs fast diffing whenever state changes.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;Micro&lt;/code&gt; we can just use the familiar &lt;code&gt;forEach&lt;/code&gt; to declare &lt;code&gt;Cell&lt;/code&gt;, and the returned &lt;code&gt;State&lt;/code&gt; will tell &lt;code&gt;DataSource&lt;/code&gt; to update the &lt;code&gt;UICollectionView&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Every time &lt;code&gt;state&lt;/code&gt; is assigned, &lt;code&gt;UICollectionView&lt;/code&gt; will be fast diffed and reloaded. The only requirement is that your model should conform to &lt;code&gt;DiffAware&lt;/code&gt; with &lt;code&gt;diffId&lt;/code&gt; so DeepDiff knows how to diff for changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="n"&gt;dataSource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blogs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;blog&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="kt"&gt;Cell&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;BlogCell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nameLabel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onSelect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; 
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cell at index &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indexPath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt; is selected"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onSize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; 
        &lt;span class="kt"&gt;CGSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;collectionView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;






&lt;p&gt;❤️ Support my app ❤️ &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.producthunt.com/posts/push-hero-2"&gt;Push Hero - pure Swift native macOS application to test push notifications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.producthunt.com/posts/quick-access"&gt;Quick Access - Organise files in the Mac menu bar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.producthunt.com/posts/frame-recorder"&gt;Frame recorder - Recorder gif and video with frame&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://onmyway133.github.io/projects/"&gt;Other apps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❤️❤️😇😍🤘❤️❤️&lt;/p&gt;

</description>
      <category>swift</category>
      <category>ios</category>
      <category>swiftui</category>
      <category>datasource</category>
    </item>
    <item>
      <title>How to add AdMob to Android app</title>
      <dc:creator>Khoa Pham</dc:creator>
      <pubDate>Tue, 01 Oct 2019 10:24:36 +0000</pubDate>
      <link>https://forem.com/onmyway133/how-to-add-admob-to-android-app-53bg</link>
      <guid>https://forem.com/onmyway133/how-to-add-admob-to-android-app-53bg</guid>
      <description>&lt;h2&gt;
  
  
  Use AdMob with Firebase
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://firebase.google.com/docs/admob/android/quick-start"&gt;https://firebase.google.com/docs/admob/android/quick-start&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;build.gradle&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;buildscript&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;repositories&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;jcenter&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;classpath&lt;/span&gt; &lt;span class="s1"&gt;'com.google.gms:google-services:4.3.2'&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;app/build.gradle&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Version&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Firebase&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;analytics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"17.2.0"&lt;/span&gt;
        &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;ads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"18.2.0"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"com.google.firebase:firebase-analytics:$Version.Firebase.analytics"&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"com.google.firebase:firebase-ads:$Version.Firebase.ads"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="nl"&gt;plugin:&lt;/span&gt; &lt;span class="s1"&gt;'com.google.gms.google-services'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Manifest.xml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;manifest&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;application&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Sample AdMob App ID: ca-app-pub-3940256099942544~3347511713 --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;meta-data&lt;/span&gt;
            &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"com.google.android.gms.ads.APPLICATION_ID"&lt;/span&gt;
            &lt;span class="na"&gt;android:value=&lt;/span&gt;&lt;span class="s"&gt;"[ADMOB_APP_ID]"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/application&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/manifest&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;MyApplication.kt&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyApplication: Application() {
    override fun onCreate() {
        super.onCreate()

        MobileAds.initialize(this)
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  AdView
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;fragment.xml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;com.google.android.gms.ads.AdView&lt;/span&gt;
        &lt;span class="na"&gt;xmlns:ads=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.android.com/apk/res-auto"&lt;/span&gt;
        &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/adView"&lt;/span&gt;
        &lt;span class="na"&gt;android:layout_width=&lt;/span&gt;&lt;span class="s"&gt;"match_parent"&lt;/span&gt;
        &lt;span class="na"&gt;android:layout_height=&lt;/span&gt;&lt;span class="s"&gt;"wrap_content"&lt;/span&gt;
        &lt;span class="na"&gt;ads:adSize=&lt;/span&gt;&lt;span class="s"&gt;"BANNER"&lt;/span&gt;
        &lt;span class="na"&gt;ads:adUnitId=&lt;/span&gt;&lt;span class="s"&gt;"ca-app-pub-123456/123456"&lt;/span&gt;
        &lt;span class="na"&gt;ads:layout_constraintBottom_toBottomOf=&lt;/span&gt;&lt;span class="s"&gt;"parent"&lt;/span&gt;
        &lt;span class="na"&gt;ads:layout_constraintLeft_toLeftOf=&lt;/span&gt;&lt;span class="s"&gt;"parent"&lt;/span&gt;
        &lt;span class="na"&gt;ads:layout_constraintRight_toRightOf=&lt;/span&gt;&lt;span class="s"&gt;"parent"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Fragment.kt&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.AdView

val request = AdRequest.Builder().build()
adView.loadAd(request)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Link your app to Firebase option not showing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.google.com/admob/answer/6383165?authuser=0"&gt;https://support.google.com/admob/answer/6383165?authuser=0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Use standalone AdMob &lt;a href="https://developers.google.com/admob/android/quick-start"&gt;https://developers.google.com/admob/android/quick-start&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;app/build.gradle&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'com.google.android.gms:play-services-ads:18.2.0'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Cannot fit requested classes in a single dex file
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/48249633/errorcannot-fit-requested-classes-in-a-single-dex-file-try-supplying-a-main-dex"&gt;https://stackoverflow.com/questions/48249633/errorcannot-fit-requested-classes-in-a-single-dex-file-try-supplying-a-main-dex&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;app/build.gradle&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;
&lt;span class="n"&gt;android&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;defaultConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;multiDexEnabled&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'com.android.support:multidex:1.0.3'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Read more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://thetechnocafe.com/a-complete-guide-to-integrating-admob-in-your-android-app/"&gt;http://thetechnocafe.com/a-complete-guide-to-integrating-admob-in-your-android-app/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Original post &lt;a href="https://github.com/onmyway133/blog/issues/431"&gt;https://github.com/onmyway133/blog/issues/431&lt;/a&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>admob</category>
    </item>
    <item>
      <title>How to show context menu from NSButton in macOS </title>
      <dc:creator>Khoa Pham</dc:creator>
      <pubDate>Tue, 01 Oct 2019 10:23:55 +0000</pubDate>
      <link>https://forem.com/onmyway133/how-to-show-context-menu-from-nsbutton-in-macos-e57</link>
      <guid>https://forem.com/onmyway133/how-to-show-context-menu-from-nsbutton-in-macos-e57</guid>
      <description>&lt;p&gt;Use &lt;code&gt;NSMenu&lt;/code&gt; and &lt;code&gt;popUp&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;showQuitMenu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;menu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSMenu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;aboutItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSMenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"About"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;#selector(&lt;/span&gt;&lt;span class="nf"&gt;onAboutTouched(_:)&lt;/span&gt;&lt;span class="kd"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;keyEquivalent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;quitItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSMenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Quit Hacker Pad"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;#selector(&lt;/span&gt;&lt;span class="nf"&gt;onQuitTouched(_:)&lt;/span&gt;&lt;span class="kd"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;keyEquivalent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;aboutItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;
    &lt;span class="n"&gt;quitItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;

    &lt;span class="n"&gt;menu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aboutItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;menu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quitItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;menu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;popUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;positioning&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aboutItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bottomView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quitButton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bottomView&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Use &lt;a href="https://github.com/onmyway133/Omnia"&gt;Omnia&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;menuHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;MenuHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;menuHandler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"About"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;NSWorkspace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://onmyway133.github.io/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;menuHandler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Quit Hacker Pad"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;NSApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;menuHandler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bottomView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gearButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bottomView&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Original post &lt;a href="https://github.com/onmyway133/blog/issues/435"&gt;https://github.com/onmyway133/blog/issues/435&lt;/a&gt;&lt;/p&gt;

</description>
      <category>swift</category>
      <category>macos</category>
      <category>appkit</category>
      <category>button</category>
    </item>
    <item>
      <title>How to easily parse deep json in Swift</title>
      <dc:creator>Khoa Pham</dc:creator>
      <pubDate>Thu, 12 Sep 2019 13:36:05 +0000</pubDate>
      <link>https://forem.com/onmyway133/how-to-easily-parse-deep-json-in-swift-da8</link>
      <guid>https://forem.com/onmyway133/how-to-easily-parse-deep-json-in-swift-da8</guid>
      <description>&lt;p&gt;JSONCodable is awesome, but sometimes we just need to quickly get value in a deepy nested JSON. In the same way I did for Dart &lt;a href="https://github.com/onmyway133/blog/issues/198"&gt;How to resolve deep json object in Dart&lt;/a&gt;, let's make that in Swift.&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://github.com/onmyway133/Omnia/blob/master/Sources/Shared/JSON.swift"&gt;https://github.com/onmyway133/Omnia/blob/master/Sources/Shared/JSON.swift&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;jsonDictionary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;keyPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonDictionary&lt;/span&gt;

    &lt;span class="n"&gt;keyPath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;separator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;maybeInt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;maybeInt&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="kt"&gt;JSONDictionary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So we can just resolve via key path&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;JSONTests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;XCTestCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s"&gt;"outside"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="s"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s"&gt;"arrayOfObjects"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="s"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"two"&lt;/span&gt;
                    &lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="s"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"three"&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s"&gt;"arrayOfArrays"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="s"&gt;"one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"two"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"three"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"four"&lt;/span&gt;
                    &lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="s"&gt;"five"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"six"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"seven"&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="kt"&gt;XCTAssertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;keyPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"outside.object.number"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kt"&gt;XCTAssertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;keyPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"outside.object.text"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kt"&gt;XCTAssertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;keyPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"outside.arrayOfObjects.1.number"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kt"&gt;XCTAssertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;keyPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"outside.arrayOfArrays.1.1"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"six"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Original post &lt;a href="https://onmyway133.github.io/blog/How-to-easily-parse-deep-json-in-Swift/"&gt;https://onmyway133.github.io/blog/How-to-easily-parse-deep-json-in-Swift/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>json</category>
      <category>swift</category>
      <category>ios</category>
    </item>
    <item>
      <title>Back to static site</title>
      <dc:creator>Khoa Pham</dc:creator>
      <pubDate>Tue, 03 Sep 2019 20:18:45 +0000</pubDate>
      <link>https://forem.com/onmyway133/back-to-static-site-71c</link>
      <guid>https://forem.com/onmyway133/back-to-static-site-71c</guid>
      <description>&lt;p&gt;It's been a while since I wrote &lt;a href="https://github.com/onmyway133/blog/issues/1"&gt;Hello world, again&lt;/a&gt;, the ease of GitHub issue indeed motivates me to write more.&lt;/p&gt;

&lt;p&gt;In the mean time I also wrote on &lt;a href="https://medium.com/@onmyway133"&gt;https://medium.com/@onmyway133&lt;/a&gt; and &lt;a href="https://dev.to/onmyway133"&gt;https://dev.to/onmyway133&lt;/a&gt; and got some traction.&lt;/p&gt;

&lt;p&gt;Then I started using GitHub pages again, with Jekyll and remote theme, it works great. But then I needed to manually link the GitHub issues to my page, that's just labor work.&lt;/p&gt;

&lt;p&gt;The best combo is to have a GitHub page backed by GitHub issue. After a bit comparison between different static site generators, I actually tried them all, I chose Hexo because I simply like Javascript&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/onmyway133/blog/issues/392"&gt;How to use Hexo to deploy static site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/onmyway133/blog/issues/393"&gt;How to get all GitHub issues using GraphQL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I use the simple &lt;a href="https://github.com/probberechts/hexo-theme-cactus"&gt;cactus&lt;/a&gt; theme for now with local searched power by &lt;a href="https://www.npmjs.com/package/hexo-generator-search"&gt;https://www.npmjs.com/package/hexo-generator-search&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then I wrote a node.js script to mirror my GitHub issue to my page, with correct tags and updated date.&lt;/p&gt;

&lt;p&gt;If you by any chance visit my new page &lt;a href="https://onmyway133.github.io/"&gt;https://onmyway133.github.io/&lt;/a&gt;, ohayou from me 👋 &lt;/p&gt;

</description>
      <category>github</category>
      <category>hexo</category>
      <category>static</category>
      <category>site</category>
    </item>
    <item>
      <title>How to get Hacker News top stories using parallel coroutine and Retrofit</title>
      <dc:creator>Khoa Pham</dc:creator>
      <pubDate>Tue, 03 Sep 2019 12:04:07 +0000</pubDate>
      <link>https://forem.com/onmyway133/how-to-get-hacker-news-top-stories-using-parallel-coroutine-and-retrofit-23pn</link>
      <guid>https://forem.com/onmyway133/how-to-get-hacker-news-top-stories-using-parallel-coroutine-and-retrofit-23pn</guid>
      <description>&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface Api {
    @GET("topstories.json?print=pretty")
    suspend fun getTopStories(): List&amp;lt;Int&amp;gt;

    @GET("item/{id}.json?print=pretty")
    suspend fun getStory(@Path("id") id: Int): Item
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Repo {
    fun api(): Api {
        return Retrofit.Builder()
            .baseUrl("https://hacker-news.firebaseio.com/v0/")
            .addConverterFactory(MoshiConverterFactory.create())
            .build()
            .create(Api::class.java)
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ViewModel(val repo: Repo): ViewModel() {
    val items = MutableLiveData&amp;lt;ArrayList&amp;lt;Item&amp;gt;&amp;gt;()

    suspend fun load() {
        try {
            val ids = repo.api()
                .getTopStories()
                .take(20)

            val items = ids.map {
                repo.api().getStory(it)
            }
            this.items.value = items.toCollection(ArrayList())
        } catch (e: Exception) {
            this.items.value = arrayListOf()
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Running parallel
&lt;/h2&gt;

&lt;p&gt;The above run in serial. To run in parallel, we can use &lt;code&gt;async&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope

class ViewModel(val repo: Repo): ViewModel() {
    val items = MutableLiveData&amp;lt;ArrayList&amp;lt;Item&amp;gt;&amp;gt;()

    suspend fun load() {
        try {
            val ids = repo.api()
                .getTopStories()
                .take(20)

            coroutineScope {
                val items = ids
                    .map { async { repo.api().getStory(it) } }
                    .awaitAll()

                this@ViewModel.items.value = items.toCollection(ArrayList())
            }

        } catch (e: Exception) {
            this.items.value = arrayListOf()
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Parallel decomposition
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://medium.com/@elizarov/structured-concurrency-722d765aa952"&gt;https://medium.com/@elizarov/structured-concurrency-722d765aa952&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With structured concurrency async coroutine builder became an extension on CoroutineScope just like launch did. You cannot simply write async { … } anymore, you have to provide a scope. A proper example of parallel decomposition becomes:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  coroutineScope
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://proandroiddev.com/part-2-coroutine-cancellation-and-structured-concurrency-2dbc6583c07d"&gt;https://proandroiddev.com/part-2-coroutine-cancellation-and-structured-concurrency-2dbc6583c07d&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;coroutineScope function can be used to create a custom scope that suspends and only completes when all coroutines launched within that scope complete. If any of the children coroutines within the coroutineScope throws an exception, all other running sibling coroutines gets cancelled and this exception is propagated up the hierarchy. If the parent coroutine at the top of the hierarchy does not handle this error, it will also be cancelled.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  awaitAll
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/await-all.html"&gt;https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/await-all.html&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Awaits for completion of given deferred values without blocking a thread and resumes normally with the list of values when all deferred computations are complete or resumes with the first thrown exception if any of computations complete exceptionally including cancellation.&lt;/p&gt;

&lt;p&gt;This function is not equivalent to deferreds.map { it.await() } which fails only when it sequentially gets to wait for the failing deferred, while this awaitAll fails immediately as soon as any of the deferreds fail.&lt;/p&gt;

&lt;p&gt;This suspending function is cancellable. If the Job of the current coroutine is cancelled or completed while this suspending function is waiting, this function immediately resumes with CancellationException.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Read more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jivimberg.io/blog/2018/05/04/parallel-map-in-kotlin/"&gt;https://jivimberg.io/blog/2018/05/04/parallel-map-in-kotlin/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/53535977/coroutines-runblocking-vs-coroutinescope"&gt;https://stackoverflow.com/questions/53535977/coroutines-runblocking-vs-coroutinescope&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;awaitAll &lt;a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/await-all.html"&gt;https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/await-all.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>coroutine</category>
      <category>parallel</category>
    </item>
    <item>
      <title>How to safely access deeply nested object in Javascript</title>
      <dc:creator>Khoa Pham</dc:creator>
      <pubDate>Tue, 03 Sep 2019 12:03:25 +0000</pubDate>
      <link>https://forem.com/onmyway133/how-to-safely-access-deeply-nested-object-in-javascript-3n62</link>
      <guid>https://forem.com/onmyway133/how-to-safely-access-deeply-nested-object-in-javascript-3n62</guid>
      <description>&lt;p&gt;An object 's property can be null or undefined.&lt;/p&gt;

&lt;p&gt;Accessing step by step is tedious&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;comments&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Dynamic parsing path is too clever and involves string in the end, which is a no no&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xs&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getUserComments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;comments&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Instead let's use function and catch errors explicitly, and defaults with a fallback&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isNotNullOrUndefined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;comments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;comments&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Read more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/javascript-inside/safely-accessing-deeply-nested-values-in-javascript-99bf72a0855a"&gt;https://medium.com/javascript-inside/safely-accessing-deeply-nested-values-in-javascript-99bf72a0855a&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>How to make scrollable NSTextView in AppKit</title>
      <dc:creator>Khoa Pham</dc:creator>
      <pubDate>Thu, 27 Jun 2019 10:11:58 +0000</pubDate>
      <link>https://forem.com/onmyway133/how-to-make-scrollable-nstextview-in-appkit-986</link>
      <guid>https://forem.com/onmyway133/how-to-make-scrollable-nstextview-in-appkit-986</guid>
      <description>&lt;p&gt;When adding &lt;code&gt;NSTextView&lt;/code&gt; in xib, we see it is embedded under &lt;code&gt;NSClipView&lt;/code&gt;. But if we try to use &lt;code&gt;NSClipView&lt;/code&gt; to replicate what's in the xib, it does not scroll. &lt;/p&gt;

&lt;p&gt;To make it work, we can follow &lt;a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TextUILayer/Tasks/TextInScrollView.html"&gt;Putting an NSTextView Object in an NSScrollView&lt;/a&gt; and &lt;a href="https://github.com/onmyway133/blog/issues/173"&gt;How to make scrollable vertical NSStackView&lt;/a&gt; to make our &lt;code&gt;ScrollableInput&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For easy Auto Layout, we use &lt;a href="https://github.com/onmyway133/Anchors"&gt;Anchors&lt;/a&gt; for &lt;code&gt;UIScrollView&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Things worth mentioned for vertical scrolling&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="n"&gt;textContainer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;heightTracksTextView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;autoresizingMask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isVerticallyResizable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;ScrollableInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;scrollView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSScrollView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;textView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSTextView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="nv"&gt;frameRect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSRect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;frameRect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CGRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CGFloat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;greatestFiniteMagnitude&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;layoutManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSLayoutManager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;textContainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSTextContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;layoutManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTextContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textContainer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;textView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSTextView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;textContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;textContainer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CGFloat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;greatestFiniteMagnitude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;textContainer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;heightTracksTextView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="n"&gt;textContainer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;widthTracksTextView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

        &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isRichText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;importsGraphics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEditable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isSelectable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
        &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
        &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isVerticallyResizable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isHorizontallyResizable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

        &lt;span class="nf"&gt;addSubview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scrollView&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;scrollView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasVerticalScroller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;scrollView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drawsBackground&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="n"&gt;scrollView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drawsBackground&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drawsBackground&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

        &lt;span class="nf"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;scrollView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anchor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;edges&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;scrollView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;documentView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;textView&lt;/span&gt;
        &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;autoresizingMask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;?(&lt;/span&gt;&lt;span class="n"&gt;coder&lt;/span&gt; &lt;span class="nv"&gt;decoder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSCoder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;fatalError&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;






&lt;p&gt;Original post &lt;a href="https://github.com/onmyway133/blog/issues/330"&gt;https://github.com/onmyway133/blog/issues/330&lt;/a&gt;&lt;/p&gt;

</description>
      <category>swift</category>
      <category>appkit</category>
      <category>macos</category>
      <category>nstextview</category>
    </item>
  </channel>
</rss>
